diff options
Diffstat (limited to 'spec/support')
22 files changed, 603 insertions, 32 deletions
diff --git a/spec/support/api/boards_shared_examples.rb b/spec/support/api/boards_shared_examples.rb new file mode 100644 index 00000000000..943c1f6ffd7 --- /dev/null +++ b/spec/support/api/boards_shared_examples.rb @@ -0,0 +1,180 @@ +shared_examples_for 'group and project boards' do |route_definition, ee = false| + let(:root_url) { route_definition.gsub(":id", board_parent.id.to_s) } + + before do + board_parent.add_reporter(user) + board_parent.add_guest(guest) + end + + def expect_schema_match_for(response, schema_file, ee) + if ee + expect(response).to match_response_schema(schema_file, dir: "ee") + else + expect(response).to match_response_schema(schema_file) + end + end + + describe "GET #{route_definition}" do + context "when unauthenticated" do + it "returns authentication error" do + get api(root_url) + + expect(response).to have_gitlab_http_status(401) + end + end + + context "when authenticated" do + it "returns the issue boards" do + get api(root_url, user) + + expect(response).to have_gitlab_http_status(200) + expect(response).to include_pagination_headers + + expect_schema_match_for(response, 'public_api/v4/boards', ee) + end + + describe "GET #{route_definition}/:board_id" do + let(:url) { "#{root_url}/#{board.id}" } + + it 'get a single board by id' do + get api(url, user) + + expect_schema_match_for(response, 'public_api/v4/board', ee) + end + end + end + end + + describe "GET #{route_definition}/:board_id/lists" do + let(:url) { "#{root_url}/#{board.id}/lists" } + + it 'returns issue board lists' do + get api(url, user) + + expect(response).to have_gitlab_http_status(200) + expect(response).to include_pagination_headers + expect(json_response).to be_an Array + expect(json_response.length).to eq(2) + expect(json_response.first['label']['name']).to eq(dev_label.title) + end + + it 'returns 404 if board not found' do + get api("#{root_url}/22343/lists", user) + + expect(response).to have_gitlab_http_status(404) + end + end + + describe "GET #{route_definition}/:board_id/lists/:list_id" do + let(:url) { "#{root_url}/#{board.id}/lists" } + + it 'returns a list' do + get api("#{url}/#{dev_list.id}", user) + + expect(response).to have_gitlab_http_status(200) + expect(json_response['id']).to eq(dev_list.id) + expect(json_response['label']['name']).to eq(dev_label.title) + expect(json_response['position']).to eq(1) + end + + it 'returns 404 if list not found' do + get api("#{url}/5324", user) + + expect(response).to have_gitlab_http_status(404) + end + end + + describe "POST #{route_definition}/lists" do + let(:url) { "#{root_url}/#{board.id}/lists" } + + it 'creates a new issue board list for labels' do + post api(url, user), label_id: ux_label.id + + expect(response).to have_gitlab_http_status(201) + expect(json_response['label']['name']).to eq(ux_label.title) + expect(json_response['position']).to eq(3) + end + + it 'returns 400 when creating a new list if label_id is invalid' do + post api(url, user), label_id: 23423 + + expect(response).to have_gitlab_http_status(400) + end + + it 'returns 403 for members with guest role' do + put api("#{url}/#{test_list.id}", guest), position: 1 + + expect(response).to have_gitlab_http_status(403) + end + end + + describe "PUT #{route_definition}/:board_id/lists/:list_id to update only position" do + let(:url) { "#{root_url}/#{board.id}/lists" } + + it "updates a list" do + put api("#{url}/#{test_list.id}", user), + position: 1 + + expect(response).to have_gitlab_http_status(200) + expect(json_response['position']).to eq(1) + end + + it "returns 404 error if list id not found" do + put api("#{url}/44444", user), + position: 1 + + expect(response).to have_gitlab_http_status(404) + end + + it "returns 403 for members with guest role" do + put api("#{url}/#{test_list.id}", guest), + position: 1 + + expect(response).to have_gitlab_http_status(403) + end + end + + describe "DELETE #{route_definition}/lists/:list_id" do + let(:url) { "#{root_url}/#{board.id}/lists" } + + it "rejects a non member from deleting a list" do + delete api("#{url}/#{dev_list.id}", non_member) + + expect(response).to have_gitlab_http_status(403) + end + + it "rejects a user with guest role from deleting a list" do + delete api("#{url}/#{dev_list.id}", guest) + + expect(response).to have_gitlab_http_status(403) + end + + it "returns 404 error if list id not found" do + delete api("#{url}/44444", user) + + expect(response).to have_gitlab_http_status(404) + end + + context "when the user is parent owner" do + set(:owner) { create(:user) } + + before do + if board_parent.try(:namespace) + board_parent.update(namespace: owner.namespace) + else + board.parent.add_owner(owner) + end + end + + it "deletes the list if an admin requests it" do + delete api("#{url}/#{dev_list.id}", owner) + + expect(response).to have_gitlab_http_status(204) + end + + it_behaves_like '412 response' do + let(:request) { api("#{url}/#{dev_list.id}", owner) } + end + end + end +end diff --git a/spec/support/background_migrations_matchers.rb b/spec/support/background_migrations_matchers.rb index 423c0e4cefc..f4127efc6ae 100644 --- a/spec/support/background_migrations_matchers.rb +++ b/spec/support/background_migrations_matchers.rb @@ -1,4 +1,4 @@ -RSpec::Matchers.define :be_scheduled_migration do |delay, *expected| +RSpec::Matchers.define :be_scheduled_delayed_migration do |delay, *expected| match do |migration| BackgroundMigrationWorker.jobs.any? do |job| job['args'] == [migration, expected] && @@ -11,3 +11,16 @@ RSpec::Matchers.define :be_scheduled_migration do |delay, *expected| 'not scheduled in expected time!' end end + +RSpec::Matchers.define :be_scheduled_migration do |*expected| + match do |migration| + BackgroundMigrationWorker.jobs.any? do |job| + args = job['args'].size == 1 ? [BackgroundMigrationWorker.jobs[0]['args'][0], []] : job['args'] + args == [migration, expected] + end + end + + failure_message do |migration| + "Migration `#{migration}` with args `#{expected.inspect}` not scheduled!" + end +end diff --git a/spec/support/cycle_analytics_helpers.rb b/spec/support/cycle_analytics_helpers.rb index 26fd271ce31..d5ef80cfab2 100644 --- a/spec/support/cycle_analytics_helpers.rb +++ b/spec/support/cycle_analytics_helpers.rb @@ -5,10 +5,16 @@ module CycleAnalyticsHelpers end def create_commit(message, project, user, branch_name, count: 1) - oldrev = project.repository.commit(branch_name).sha + repository = project.repository + oldrev = repository.commit(branch_name).sha + + if Timecop.frozen? && Gitlab::GitalyClient.feature_enabled?(:operation_user_commit_files) + mock_gitaly_multi_action_dates(repository.raw) + end + commit_shas = Array.new(count) do |index| - commit_sha = project.repository.create_file(user, generate(:branch), "content", message: message, branch_name: branch_name) - project.repository.commit(commit_sha) + commit_sha = repository.create_file(user, generate(:branch), "content", message: message, branch_name: branch_name) + repository.commit(commit_sha) commit_sha end @@ -98,6 +104,25 @@ module CycleAnalyticsHelpers pipeline: dummy_pipeline, protected: false) end + + def mock_gitaly_multi_action_dates(raw_repository) + allow(raw_repository).to receive(:multi_action).and_wrap_original do |m, *args| + new_date = Time.now + branch_update = m.call(*args) + + if branch_update.newrev + _, opts = args + commit = raw_repository.commit(branch_update.newrev).rugged_commit + branch_update.newrev = commit.amend( + update_ref: "#{Gitlab::Git::BRANCH_REF_PREFIX}#{opts[:branch_name]}", + author: commit.author.merge(time: new_date), + committer: commit.committer.merge(time: new_date) + ) + end + + branch_update + end + end end RSpec.configure do |config| diff --git a/spec/support/db_cleaner.rb b/spec/support/db_cleaner.rb index edaee03ea6c..1809ae1d141 100644 --- a/spec/support/db_cleaner.rb +++ b/spec/support/db_cleaner.rb @@ -1,10 +1,26 @@ +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(:truncation, cache_tables: false) + DatabaseCleaner.clean_with(:deletion, cache_tables: false) end config.before(:each) do @@ -12,15 +28,15 @@ RSpec.configure do |config| end config.before(:each, :js) do - DatabaseCleaner.strategy = :truncation + DatabaseCleaner.strategy = :deletion end - config.before(:each, :truncate) do - DatabaseCleaner.strategy = :truncation + config.before(:each, :delete) do + DatabaseCleaner.strategy = :deletion end config.before(:each, :migration) do - DatabaseCleaner.strategy = :truncation, { cache_tables: false } + DatabaseCleaner.strategy = :deletion, { cache_tables: false } end config.before(:each) do diff --git a/spec/support/devise_helpers.rb b/spec/support/devise_helpers.rb index 890a2d9d287..66874e10f38 100644 --- a/spec/support/devise_helpers.rb +++ b/spec/support/devise_helpers.rb @@ -2,13 +2,16 @@ module DeviseHelpers # explicitly tells Devise which mapping to use # this is needed when we are testing a Devise controller bypassing the router def set_devise_mapping(context:) - env = - if context.respond_to?(:env_config) - context.env_config - elsif context.respond_to?(:env) - context.env - end + env = env_from_context(context) env['devise.mapping'] = Devise.mappings[:user] if env end + + def env_from_context(context) + if context.respond_to?(:env_config) + context.env_config + elsif context.respond_to?(:env) + context.env + end + end end diff --git a/spec/support/email_helpers.rb b/spec/support/email_helpers.rb index b39052923dd..1fb8252459f 100644 --- a/spec/support/email_helpers.rb +++ b/spec/support/email_helpers.rb @@ -30,4 +30,8 @@ module EmailHelpers def email_recipients(kind: :to) ActionMailer::Base.deliveries.flat_map(&kind) end + + def find_email_for(user) + ActionMailer::Base.deliveries.find { |d| d.to.include?(user.notification_email) } + end end diff --git a/spec/support/features/discussion_comments_shared_example.rb b/spec/support/features/discussion_comments_shared_example.rb index c24940393f9..c8662d41769 100644 --- a/spec/support/features/discussion_comments_shared_example.rb +++ b/spec/support/features/discussion_comments_shared_example.rb @@ -113,6 +113,7 @@ shared_examples 'discussion comments' do |resource_name| else expect(find(submit_selector).value).to eq 'Start discussion' end + expect(page).not_to have_selector menu_selector end @@ -142,15 +143,17 @@ shared_examples 'discussion comments' do |resource_name| end if resource_name == 'merge request' + let(:note_id) { find("#{comments_selector} .note", match: :first)['data-note-id'] } + it 'shows resolved discussion when toggled' do click_button "Resolve discussion" - expect(page).to have_selector('.note-row-1', visible: true) + expect(page).to have_selector(".note-row-#{note_id}", visible: true) refresh click_button "Toggle discussion" - expect(page).to have_selector('.note-row-1', visible: true) + expect(page).to have_selector(".note-row-#{note_id}", visible: true) end end end @@ -200,6 +203,7 @@ shared_examples 'discussion comments' do |resource_name| else expect(find(submit_selector).value).to eq 'Comment' end + expect(page).not_to have_selector menu_selector end diff --git a/spec/support/filtered_search_helpers.rb b/spec/support/filtered_search_helpers.rb index 05021ea9054..f3f96bd1f0a 100644 --- a/spec/support/filtered_search_helpers.rb +++ b/spec/support/filtered_search_helpers.rb @@ -61,9 +61,11 @@ module FilteredSearchHelpers token_emoji = tokens[index][:emoji_name] expect(el.find('.name')).to have_content(token_name) + if token_value expect(el.find('.value')).to have_content(token_value) end + # gl-emoji content is blank when the emoji unicode is not supported if token_emoji selector = %(gl-emoji[data-name="#{token_emoji}"]) diff --git a/spec/support/generate-seed-repo-rb b/spec/support/generate-seed-repo-rb index 4ee33f9725b..876b3b8242d 100755 --- a/spec/support/generate-seed-repo-rb +++ b/spec/support/generate-seed-repo-rb @@ -24,6 +24,7 @@ def main unless system(*%W[git clone --bare #{SOURCE} #{REPO_NAME}], chdir: dir) abort "git clone failed" end + repo = File.join(dir, REPO_NAME) erb = ERB.new(DATA.read) erb.run(binding) diff --git a/spec/support/google_api/cloud_platform_helpers.rb b/spec/support/google_api/cloud_platform_helpers.rb index 8a073e58db8..2fdbddd40c2 100644 --- a/spec/support/google_api/cloud_platform_helpers.rb +++ b/spec/support/google_api/cloud_platform_helpers.rb @@ -10,6 +10,16 @@ module GoogleApi request.session[GoogleApi::CloudPlatform::Client.session_key_for_expires_at] = 1.hour.ago.to_i.to_s end + def stub_cloud_platform_projects_list(options) + WebMock.stub_request(:get, cloud_platform_projects_list_url) + .to_return(cloud_platform_response(cloud_platform_projects_body(options))) + end + + def stub_cloud_platform_projects_get_billing_info(project_id, billing_enabled) + WebMock.stub_request(:get, cloud_platform_projects_get_billing_info_url(project_id)) + .to_return(cloud_platform_response(cloud_platform_projects_billing_info_body(project_id, billing_enabled))) + end + def stub_cloud_platform_get_zone_cluster(project_id, zone, cluster_id, **options) WebMock.stub_request(:get, cloud_platform_get_zone_cluster_url(project_id, zone, cluster_id)) .to_return(cloud_platform_response(cloud_platform_cluster_body(options))) @@ -40,6 +50,14 @@ module GoogleApi .to_return(status: [500, "Internal Server Error"]) end + def cloud_platform_projects_list_url + "https://cloudresourcemanager.googleapis.com/v1/projects" + end + + def cloud_platform_projects_get_billing_info_url(project_id) + "https://cloudbilling.googleapis.com/v1/projects/#{project_id}/billingInfo" + end + def cloud_platform_get_zone_cluster_url(project_id, zone, cluster_id) "https://container.googleapis.com/v1/projects/#{project_id}/zones/#{zone}/clusters/#{cluster_id}" end @@ -115,5 +133,32 @@ module GoogleApi "endTime": options[:endTime] || '' } end + + def cloud_platform_projects_body(**options) + { + "projects": [ + { + "projectNumber": options[:project_number] || "1234", + "projectId": options[:project_id] || "test-project-1234", + "lifecycleState": "ACTIVE", + "name": options[:name] || "test-project", + "createTime": "2017-12-16T01:48:29.129Z", + "parent": { + "type": "organization", + "id": "12345" + } + } + ] + } + end + + def cloud_platform_projects_billing_info_body(project_id, billing_enabled) + { + "name": "projects/#{project_id}/billingInfo", + "projectId": "#{project_id}", + "billingAccountName": "account-name", + "billingEnabled": billing_enabled + } + end end end diff --git a/spec/support/login_helpers.rb b/spec/support/login_helpers.rb index 50702a0ac88..b52b6a28c54 100644 --- a/spec/support/login_helpers.rb +++ b/spec/support/login_helpers.rb @@ -125,6 +125,13 @@ module LoginHelpers }) end + def stub_omniauth_provider(provider, context: Rails.application) + env = env_from_context(context) + + set_devise_mapping(context: context) + env['omniauth.auth'] = OmniAuth.config.mock_auth[provider] + end + def stub_omniauth_saml_config(messages) set_devise_mapping(context: Rails.application) Rails.application.routes.disable_clear_and_finalize = true diff --git a/spec/support/matchers/access_matchers_for_controller.rb b/spec/support/matchers/access_matchers_for_controller.rb index cdb62a5deee..42a9ed9ff34 100644 --- a/spec/support/matchers/access_matchers_for_controller.rb +++ b/spec/support/matchers/access_matchers_for_controller.rb @@ -43,6 +43,7 @@ module AccessMatchersForController user = create(:user) membership.public_send(:"add_#{role}", user) end + user end diff --git a/spec/support/project_forks_helper.rb b/spec/support/project_forks_helper.rb index d6680735aa1..2c501a2a27c 100644 --- a/spec/support/project_forks_helper.rb +++ b/spec/support/project_forks_helper.rb @@ -38,10 +38,6 @@ module ProjectForksHelper # so we have to explicitely call this method to clear the @exists variable. # of the instance we're returning here. forked_project.repository.after_import - - # We can't leave the hooks in place after a fork, as those would fail in tests - # The "internal" API is not available - FileUtils.rm_rf("#{forked_project.repository.path}/hooks") end forked_project diff --git a/spec/support/select2_helper.rb b/spec/support/select2_helper.rb index 55da961e173..90618ba5b19 100644 --- a/spec/support/select2_helper.rb +++ b/spec/support/select2_helper.rb @@ -17,6 +17,7 @@ module Select2Helper selector = options.fetch(:from) first(selector, visible: false) + if options[:multiple] execute_script("$('#{selector}').select2('val', ['#{value}']).trigger('change');") else diff --git a/spec/support/services_shared_context.rb b/spec/support/services_shared_context.rb index 3f1fd169b72..23f9b46ae0c 100644 --- a/spec/support/services_shared_context.rb +++ b/spec/support/services_shared_context.rb @@ -3,13 +3,9 @@ Service.available_services_names.each do |service| let(:dashed_service) { service.dasherize } let(:service_method) { "#{service}_service".to_sym } let(:service_klass) { "#{service}_service".classify.constantize } - let(:service_fields) { service_klass.new.fields } + let(:service_instance) { service_klass.new } + let(:service_fields) { service_instance.fields } let(:service_attrs_list) { service_fields.inject([]) {|arr, hash| arr << hash[:name].to_sym } } - let(:service_attrs_list_without_passwords) do - service_fields - .select { |field| field[:type] != 'password' } - .map { |field| field[:name].to_sym} - end let(:service_attrs) do service_attrs_list.inject({}) do |hash, k| if k =~ /^(token*|.*_token|.*_key)/ diff --git a/spec/support/shared_examples/features/creatable_merge_request_shared_examples.rb b/spec/support/shared_examples/features/creatable_merge_request_shared_examples.rb new file mode 100644 index 00000000000..5b0b609f7f2 --- /dev/null +++ b/spec/support/shared_examples/features/creatable_merge_request_shared_examples.rb @@ -0,0 +1,99 @@ +RSpec.shared_examples 'a creatable merge request' do + include WaitForRequests + + let(:user) { create(:user) } + let(:user2) { create(:user) } + let(:target_project) { create(:project, :public, :repository) } + let(:source_project) { target_project } + let!(:milestone) { create(:milestone, project: target_project) } + let!(:label) { create(:label, project: target_project) } + let!(:label2) { create(:label, project: target_project) } + + before do + source_project.add_master(user) + target_project.add_master(user) + target_project.add_master(user2) + + sign_in(user) + visit project_new_merge_request_path( + target_project, + merge_request: { + source_project_id: source_project.id, + target_project_id: target_project.id, + source_branch: 'fix', + target_branch: 'master' + }) + end + + it 'creates new merge request', :js do + click_button 'Assignee' + page.within '.dropdown-menu-user' do + click_link user2.name + end + expect(find('input[name="merge_request[assignee_id]"]', visible: false).value).to match(user2.id.to_s) + page.within '.js-assignee-search' do + expect(page).to have_content user2.name + end + + click_link 'Assign to me' + expect(find('input[name="merge_request[assignee_id]"]', visible: false).value).to match(user.id.to_s) + page.within '.js-assignee-search' do + expect(page).to have_content user.name + end + + click_button 'Milestone' + page.within '.issue-milestone' do + click_link milestone.title + end + expect(find('input[name="merge_request[milestone_id]"]', visible: false).value).to match(milestone.id.to_s) + page.within '.js-milestone-select' do + expect(page).to have_content milestone.title + end + + click_button 'Labels' + page.within '.dropdown-menu-labels' do + click_link label.title + click_link label2.title + end + page.within '.js-label-select' do + expect(page).to have_content label.title + end + expect(page.all('input[name="merge_request[label_ids][]"]', visible: false)[1].value).to match(label.id.to_s) + expect(page.all('input[name="merge_request[label_ids][]"]', visible: false)[2].value).to match(label2.id.to_s) + + click_button 'Submit merge request' + + page.within '.issuable-sidebar' do + page.within '.assignee' do + expect(page).to have_content user.name + end + + page.within '.milestone' do + expect(page).to have_content milestone.title + end + + page.within '.labels' do + expect(page).to have_content label.title + expect(page).to have_content label2.title + end + end + end + + it 'updates the branches when selecting a new target project' do + target_project_member = target_project.owner + CreateBranchService.new(target_project, target_project_member) + .execute('a-brand-new-branch-to-test', 'master') + visit project_new_merge_request_path(source_project) + + first('.js-target-project').click + find('.dropdown-target-project .dropdown-content a', text: target_project.full_path).click + + wait_for_requests + + first('.js-target-branch').click + + within('.dropdown-target-branch .dropdown-content') do + expect(page).to have_content('a-brand-new-branch-to-test') + end + end +end diff --git a/spec/support/shared_examples/features/editable_merge_request_shared_examples.rb b/spec/support/shared_examples/features/editable_merge_request_shared_examples.rb new file mode 100644 index 00000000000..645db41cddc --- /dev/null +++ b/spec/support/shared_examples/features/editable_merge_request_shared_examples.rb @@ -0,0 +1,140 @@ +RSpec.shared_examples 'an editable merge request' do + let(:user) { create(:user) } + let(:user2) { create(:user) } + let!(:milestone) { create(:milestone, project: target_project) } + let!(:label) { create(:label, project: target_project) } + let!(:label2) { create(:label, project: target_project) } + let(:target_project) { create(:project, :public, :repository) } + let(:source_project) { target_project } + let(:merge_request) do + create(:merge_request, + source_project: source_project, + target_project: target_project, + source_branch: 'fix', + target_branch: 'master') + end + + before do + source_project.add_master(user) + target_project.add_master(user) + target_project.add_master(user2) + + sign_in(user) + visit edit_project_merge_request_path(target_project, merge_request) + end + + it 'updates merge request', :js do + click_button 'Assignee' + page.within '.dropdown-menu-user' do + click_link user.name + end + expect(find('input[name="merge_request[assignee_id]"]', visible: false).value).to match(user.id.to_s) + page.within '.js-assignee-search' do + expect(page).to have_content user.name + end + + click_button 'Milestone' + page.within '.issue-milestone' do + click_link milestone.title + end + expect(find('input[name="merge_request[milestone_id]"]', visible: false).value).to match(milestone.id.to_s) + page.within '.js-milestone-select' do + expect(page).to have_content milestone.title + end + + click_button 'Labels' + page.within '.dropdown-menu-labels' do + click_link label.title + click_link label2.title + end + expect(page.all('input[name="merge_request[label_ids][]"]', visible: false)[1].value).to match(label.id.to_s) + expect(page.all('input[name="merge_request[label_ids][]"]', visible: false)[2].value).to match(label2.id.to_s) + page.within '.js-label-select' do + expect(page).to have_content label.title + end + + click_button 'Save changes' + + page.within '.issuable-sidebar' do + page.within '.assignee' do + expect(page).to have_content user.name + end + + page.within '.milestone' do + expect(page).to have_content milestone.title + end + + page.within '.labels' do + expect(page).to have_content label.title + expect(page).to have_content label2.title + end + end + end + + it 'description has autocomplete', :js do + find('#merge_request_description').native.send_keys('') + fill_in 'merge_request_description', with: '@' + + expect(page).to have_selector('.atwho-view') + end + + it 'has class js-quick-submit in form' do + expect(page).to have_selector('.js-quick-submit') + end + + it 'warns about version conflict' do + merge_request.update(title: "New title") + + fill_in 'merge_request_title', with: 'bug 345' + fill_in 'merge_request_description', with: 'bug description' + + click_button 'Save changes' + + expect(page).to have_content 'Someone edited the merge request the same time you did' + end + + it 'preserves description textarea height', :js do + long_description = %q( + Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam ac ornare ligula, ut tempus arcu. Etiam ultricies accumsan dolor vitae faucibus. Donec at elit lacus. Mauris orci ante, aliquam quis lorem eget, convallis faucibus arcu. Aenean at pulvinar lacus. Ut viverra quam massa, molestie ornare tortor dignissim a. Suspendisse tristique pellentesque tellus, id lacinia metus elementum id. Nam tristique, arcu rhoncus faucibus viverra, lacus ipsum sagittis ligula, vitae convallis odio lacus a nibh. Ut tincidunt est purus, ac vestibulum augue maximus in. Suspendisse vel erat et mi ultricies semper. Pellentesque volutpat pellentesque consequat. + + Cras congue nec ligula tristique viverra. Curabitur fringilla fringilla fringilla. Donec rhoncus dignissim orci ut accumsan. Ut rutrum urna a rhoncus varius. Maecenas blandit, mauris nec accumsan gravida, augue nibh finibus magna, sed maximus turpis libero nec neque. Suspendisse at semper est. Nunc imperdiet dapibus dui, varius sollicitudin erat luctus non. Sed pellentesque ligula eget posuere facilisis. Donec dictum commodo volutpat. Donec egestas dui ac magna sollicitudin bibendum. Vivamus purus neque, ullamcorper ac feugiat et, tempus sit amet metus. Praesent quis viverra neque. Sed bibendum viverra est, eu aliquam mi ornare vitae. Proin et dapibus ipsum. Nunc tortor diam, malesuada nec interdum vel, placerat quis justo. Ut viverra at erat eu laoreet. + + Pellentesque commodo, diam sit amet dignissim condimentum, tortor justo pretium est, non venenatis metus eros ut nunc. Etiam ut neque eget sem dapibus aliquam. Curabitur vel elit lorem. Nulla nec enim elit. Sed ut ex id justo facilisis convallis at ac augue. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Nullam cursus egestas turpis non tristique. Suspendisse in erat sem. Fusce libero elit, fermentum gravida mauris id, auctor iaculis felis. Nullam vulputate tempor laoreet. + + Nam tempor et magna sed convallis. Fusce sit amet sollicitudin risus, a ullamcorper lacus. Morbi gravida quis sem eget porttitor. Donec eu egestas mauris, in elementum tortor. Sed eget ex mi. Mauris iaculis tortor ut est auctor, nec dignissim quam sagittis. Suspendisse vel metus non quam suscipit tincidunt. Cras molestie lacus non justo finibus sodales quis vitae erat. In a porttitor nisi, id sollicitudin urna. Ut at felis tellus. Suspendisse potenti. + + Maecenas leo ligula, varius at neque vitae, ornare maximus justo. Nullam convallis luctus risus et vulputate. Duis suscipit faucibus iaculis. Etiam quis tortor faucibus, tristique tellus sit amet, sodales neque. Nulla dapibus nisi vel aliquet consequat. Etiam faucibus, metus eget condimentum iaculis, enim urna lobortis sem, id efficitur eros sapien nec nisi. Aenean ut finibus ex. + ) + + fill_in 'merge_request_description', with: long_description + + height = get_textarea_height + find('.js-md-preview-button').click + find('.js-md-write-button').click + new_height = get_textarea_height + + expect(height).to eq(new_height) + end + + context 'when "Remove source branch" is set' do + before do + merge_request.update!(merge_params: { 'force_remove_source_branch' => '1' }) + end + + it 'allows to unselect "Remove source branch"', :js do + expect(merge_request.merge_params['force_remove_source_branch']).to be_truthy + + visit edit_project_merge_request_path(target_project, merge_request) + uncheck 'Remove source branch when merge request is accepted' + + click_button 'Save changes' + + expect(page).to have_unchecked_field 'remove-source-branch-input' + expect(page).to have_content 'Remove source branch' + end + end +end + +def get_textarea_height + page.evaluate_script('document.getElementById("merge_request_description").offsetHeight') +end diff --git a/spec/support/shared_examples/requests/api/issuable_participants_examples.rb b/spec/support/shared_examples/requests/api/issuable_participants_examples.rb new file mode 100644 index 00000000000..96d59e0c472 --- /dev/null +++ b/spec/support/shared_examples/requests/api/issuable_participants_examples.rb @@ -0,0 +1,29 @@ +shared_examples 'issuable participants endpoint' do + let(:area) { entity.class.name.underscore.pluralize } + + it 'returns participants' do + get api("/projects/#{project.id}/#{area}/#{entity.iid}/participants", user) + + expect(response).to have_gitlab_http_status(200) + expect(response).to include_pagination_headers + expect(json_response).to be_an Array + expect(json_response.size).to eq(entity.participants.size) + + last_participant = entity.participants.last + expect(json_response.last['id']).to eq(last_participant.id) + expect(json_response.last['name']).to eq(last_participant.name) + expect(json_response.last['username']).to eq(last_participant.username) + end + + it 'returns a 404 when iid does not exist' do + get api("/projects/#{project.id}/#{area}/999/participants", user) + + expect(response).to have_gitlab_http_status(404) + end + + it 'returns a 404 when id is used instead of iid' do + get api("/projects/#{project.id}/#{area}/#{entity.id}/participants", user) + + expect(response).to have_gitlab_http_status(404) + end +end diff --git a/spec/support/slack_mattermost_notifications_shared_examples.rb b/spec/support/slack_mattermost_notifications_shared_examples.rb index 17f3a861ba8..e827a8da0b7 100644 --- a/spec/support/slack_mattermost_notifications_shared_examples.rb +++ b/spec/support/slack_mattermost_notifications_shared_examples.rb @@ -57,6 +57,7 @@ RSpec.shared_examples 'slack or mattermost notifications' do @issue = issue_service.execute @issues_sample_data = issue_service.hook_data(@issue, 'open') + project.add_developer(user) opts = { title: 'Awesome merge_request', description: 'please fix', diff --git a/spec/support/stub_env.rb b/spec/support/stub_env.rb index f621463e621..695152e2d4e 100644 --- a/spec/support/stub_env.rb +++ b/spec/support/stub_env.rb @@ -4,6 +4,7 @@ module StubENV def stub_env(key_or_hash, value = nil) init_stub unless env_stubbed? + if key_or_hash.is_a? Hash key_or_hash.each { |k, v| add_stubbed_value(k, v) } else diff --git a/spec/support/test_env.rb b/spec/support/test_env.rb index 1d99746b09f..fd6368e7b40 100644 --- a/spec/support/test_env.rb +++ b/spec/support/test_env.rb @@ -1,4 +1,5 @@ require 'rspec/mocks' +require 'toml' module TestEnv extend self @@ -89,10 +90,6 @@ module TestEnv setup_forked_repo end - def cleanup - stop_gitaly - end - def disable_mailer allow_any_instance_of(NotificationService).to receive(:mailer) .and_return(double.as_null_object) @@ -147,6 +144,9 @@ module TestEnv version: Gitlab::GitalyClient.expected_server_version, task: "gitlab:gitaly:install[#{gitaly_dir}]") do + # Always re-create config, in case it's outdated. This is fast anyway. + Gitlab::SetupHelper.create_gitaly_configuration(gitaly_dir, force: true) + start_gitaly(gitaly_dir) end end @@ -159,6 +159,8 @@ module TestEnv spawn_script = Rails.root.join('scripts/gitaly-test-spawn').to_s @gitaly_pid = Bundler.with_original_env { IO.popen([spawn_script], &:read).to_i } + Kernel.at_exit { stop_gitaly } + wait_gitaly end @@ -305,7 +307,7 @@ module TestEnv # Before we used Git clone's --mirror option, bare repos could end up # with missing refs, clearing them and retrying should fix the issue. - cleanup && clean_gitlab_test_path && init unless reset.call + clean_gitlab_test_path && init unless reset.call end end @@ -321,6 +323,7 @@ module TestEnv if component_needs_update?(install_dir, version) # Cleanup the component entirely to ensure we start fresh FileUtils.rm_rf(install_dir) + unless system('rake', task) raise ComponentFailedToInstallError end @@ -347,6 +350,9 @@ module TestEnv end def component_needs_update?(component_folder, expected_version) + # Allow local overrides of the component for tests during development + return false if Rails.env.test? && File.symlink?(component_folder) + version = File.read(File.join(component_folder, 'VERSION')).strip # Notice that this will always yield true when using branch versions diff --git a/spec/support/wait_for_requests.rb b/spec/support/wait_for_requests.rb index f4130d68271..fda0e29f983 100644 --- a/spec/support/wait_for_requests.rb +++ b/spec/support/wait_for_requests.rb @@ -53,6 +53,7 @@ module WaitForRequests wait_until = Time.now + max_wait_time.seconds loop do break if yield + if Time.now > wait_until raise "Condition not met: #{condition_name}" else |