diff options
Diffstat (limited to 'spec')
37 files changed, 899 insertions, 189 deletions
diff --git a/spec/controllers/projects/todo_controller_spec.rb b/spec/controllers/projects/todo_controller_spec.rb index 193a3f6b5a3..415c264e0dd 100644 --- a/spec/controllers/projects/todo_controller_spec.rb +++ b/spec/controllers/projects/todo_controller_spec.rb @@ -110,7 +110,7 @@ describe Projects::TodosController do end end - context 'when not authorized' do + context 'when not authorized for project' do it 'does not create todo for merge request user has no access to' do sign_in(user) expect do @@ -128,6 +128,19 @@ describe Projects::TodosController do expect(response).to have_http_status(302) end end + + context 'when not authorized for merge_request' do + before do + project.update!(visibility_level: Gitlab::VisibilityLevel::PUBLIC) + project.project_feature.update!(merge_requests_access_level: ProjectFeature::PRIVATE) + sign_in(user) + end + + it "doesn't create todo" do + expect{ go }.not_to change { user.todos.count } + expect(response).to have_http_status(404) + end + end end end end diff --git a/spec/controllers/sessions_controller_spec.rb b/spec/controllers/sessions_controller_spec.rb index 48d69377461..b56c7880b64 100644 --- a/spec/controllers/sessions_controller_spec.rb +++ b/spec/controllers/sessions_controller_spec.rb @@ -22,7 +22,6 @@ describe SessionsController do it 'authenticates user correctly' do post(:create, user: { login: user.username, password: user.password }) - expect(response).to set_flash.to /Signed in successfully/ expect(subject.current_user). to eq user end diff --git a/spec/factories/ci/stages.rb b/spec/factories/ci/stages.rb new file mode 100644 index 00000000000..ee3b17b8bf1 --- /dev/null +++ b/spec/factories/ci/stages.rb @@ -0,0 +1,13 @@ +FactoryGirl.define do + factory :ci_stage, class: Ci::Stage do + transient do + name 'test' + status nil + pipeline factory: :ci_empty_pipeline + end + + initialize_with do + Ci::Stage.new(pipeline, name: name, status: status) + end + end +end diff --git a/spec/features/environments_spec.rb b/spec/features/environments_spec.rb index c7fe622c477..e1b97b31e5d 100644 --- a/spec/features/environments_spec.rb +++ b/spec/features/environments_spec.rb @@ -85,14 +85,14 @@ feature 'Environments page', :feature, :js do end scenario 'does show a play button' do - find('.dropdown-play-icon-container').click + find('.js-dropdown-play-icon-container').click expect(page).to have_content(manual.name.humanize) end scenario 'does allow to play manual action', js: true do expect(manual).to be_skipped - find('.dropdown-play-icon-container').click + find('.js-dropdown-play-icon-container').click expect(page).to have_content(manual.name.humanize) expect { click_link(manual.name.humanize) } diff --git a/spec/features/merge_requests/target_branch_spec.rb b/spec/features/merge_requests/target_branch_spec.rb new file mode 100644 index 00000000000..b6134540273 --- /dev/null +++ b/spec/features/merge_requests/target_branch_spec.rb @@ -0,0 +1,41 @@ +require 'spec_helper' + +describe 'Target branch', feature: true do + let(:user) { create(:user) } + let(:merge_request) { create(:merge_request) } + let(:project) { merge_request.project } + + def path_to_merge_request + namespace_project_merge_request_path( + project.namespace, + project, merge_request + ) + end + + before do + login_as user + project.team << [user, :master] + end + + it 'shows link to target branch' do + visit path_to_merge_request + expect(page).to have_link('feature', href: namespace_project_commits_path(project.namespace, project, merge_request.target_branch)) + end + + context 'when branch was deleted' do + before do + DeleteBranchService.new(project, user).execute('feature') + visit path_to_merge_request + end + + it 'shows a message about missing target branch' do + expect(page).to have_content( + 'Target branch feature does not exist' + ) + end + + it 'does not show link to target branch' do + expect(page).not_to have_link('feature') + end + end +end diff --git a/spec/features/projects/features_visibility_spec.rb b/spec/features/projects/features_visibility_spec.rb index 09aa6758b5c..3bb33394be7 100644 --- a/spec/features/projects/features_visibility_spec.rb +++ b/spec/features/projects/features_visibility_spec.rb @@ -182,6 +182,44 @@ describe 'Edit Project Settings', feature: true do expect(page).not_to have_content("Comments") end end + + # Regression spec for https://gitlab.com/gitlab-org/gitlab-ce/issues/25272 + it "hides comments activity tab only on disabled issues, merge requests and repository" do + select "Disabled", from: "project_project_feature_attributes_issues_access_level" + + save_changes_and_check_activity_tab do + expect(page).to have_content("Comments") + end + + visit edit_namespace_project_path(project.namespace, project) + + select "Disabled", from: "project_project_feature_attributes_merge_requests_access_level" + + save_changes_and_check_activity_tab do + expect(page).to have_content("Comments") + end + + visit edit_namespace_project_path(project.namespace, project) + + select "Disabled", from: "project_project_feature_attributes_repository_access_level" + + save_changes_and_check_activity_tab do + expect(page).not_to have_content("Comments") + end + + visit edit_namespace_project_path(project.namespace, project) + end + + def save_changes_and_check_activity_tab + click_button "Save changes" + wait_for_ajax + + visit activity_namespace_project_path(project.namespace, project) + + page.within(".event-filter") do + yield + end + end end # Regression spec for https://gitlab.com/gitlab-org/gitlab-ce/issues/24056 diff --git a/spec/features/projects/members/group_members_spec.rb b/spec/features/projects/members/group_members_spec.rb new file mode 100644 index 00000000000..7d0065ee2c4 --- /dev/null +++ b/spec/features/projects/members/group_members_spec.rb @@ -0,0 +1,90 @@ +require 'spec_helper' + +feature 'Projects members', feature: true do + let(:user) { create(:user) } + let(:developer) { create(:user) } + let(:group) { create(:group, :public, :access_requestable) } + let(:project) { create(:empty_project, :public, :access_requestable, creator: user, group: group) } + let(:project_invitee) { create(:project_member, project: project, invite_token: '123', invite_email: 'test1@abc.com', user: nil) } + let(:group_invitee) { create(:group_member, group: group, invite_token: '123', invite_email: 'test2@abc.com', user: nil) } + let(:project_requester) { create(:user) } + let(:group_requester) { create(:user) } + + background do + project.team << [developer, :developer] + group.add_owner(user) + login_as(user) + end + + context 'with a group invitee' do + before do + group_invitee + visit namespace_project_project_members_path(project.namespace, project) + end + + scenario 'does not appear in the project members page' do + page.within first('.content-list') do + expect(page).not_to have_content('test2@abc.com') + end + end + end + + context 'with a group and a project invitee' do + before do + group_invitee + project_invitee + visit namespace_project_project_members_path(project.namespace, project) + end + + scenario 'shows the project invitee, the project developer, and the group owner' do + page.within first('.content-list') do + expect(page).to have_content('test1@abc.com') + expect(page).not_to have_content('test2@abc.com') + + # Project developer + expect(page).to have_content(developer.name) + + # Group owner + expect(page).to have_content(user.name) + expect(page).to have_content(group.name) + end + end + end + + context 'with a group requester' do + before do + group.request_access(group_requester) + visit namespace_project_project_members_path(project.namespace, project) + end + + scenario 'does not appear in the project members page' do + page.within first('.content-list') do + expect(page).not_to have_content(group_requester.name) + end + end + end + + context 'with a group and a project requesters' do + before do + group.request_access(group_requester) + project.request_access(project_requester) + visit namespace_project_project_members_path(project.namespace, project) + end + + scenario 'shows the project requester, the project developer, and the group owner' do + page.within first('.content-list') do + expect(page).to have_content(project_requester.name) + expect(page).not_to have_content(group_requester.name) + end + + page.within all('.content-list').last do + # Project developer + expect(page).to have_content(developer.name) + + # Group owner + expect(page).to have_content(user.name) + expect(page).to have_content(group.name) + end + end + end +end diff --git a/spec/features/projects/settings/merge_requests_settings_spec.rb b/spec/features/projects/settings/merge_requests_settings_spec.rb new file mode 100644 index 00000000000..4bfaa499272 --- /dev/null +++ b/spec/features/projects/settings/merge_requests_settings_spec.rb @@ -0,0 +1,70 @@ +require 'spec_helper' + +feature 'Project settings > Merge Requests', feature: true, js: true do + include GitlabRoutingHelper + + let(:project) { create(:empty_project, :public) } + let(:user) { create(:user) } + + background do + project.team << [user, :master] + login_as(user) + end + + context 'when Merge Request and Builds are initially enabled' do + before do + project.project_feature.update_attribute('merge_requests_access_level', ProjectFeature::ENABLED) + end + + context 'when Builds are initially enabled' do + before do + project.project_feature.update_attribute('builds_access_level', ProjectFeature::ENABLED) + visit edit_project_path(project) + end + + scenario 'shows the Merge Requests settings' do + expect(page).to have_content('Only allow merge requests to be merged if the build succeeds') + expect(page).to have_content('Only allow merge requests to be merged if all discussions are resolved') + + select 'Disabled', from: "project_project_feature_attributes_merge_requests_access_level" + + expect(page).not_to have_content('Only allow merge requests to be merged if the build succeeds') + expect(page).not_to have_content('Only allow merge requests to be merged if all discussions are resolved') + end + end + + context 'when Builds are initially disabled' do + before do + project.project_feature.update_attribute('builds_access_level', ProjectFeature::DISABLED) + visit edit_project_path(project) + end + + scenario 'shows the Merge Requests settings that do not depend on Builds feature' do + expect(page).not_to have_content('Only allow merge requests to be merged if the build succeeds') + expect(page).to have_content('Only allow merge requests to be merged if all discussions are resolved') + + select 'Everyone with access', from: "project_project_feature_attributes_builds_access_level" + + expect(page).to have_content('Only allow merge requests to be merged if the build succeeds') + expect(page).to have_content('Only allow merge requests to be merged if all discussions are resolved') + end + end + end + + context 'when Merge Request are initially disabled' do + before do + project.project_feature.update_attribute('merge_requests_access_level', ProjectFeature::DISABLED) + visit edit_project_path(project) + end + + scenario 'does not show the Merge Requests settings' do + expect(page).not_to have_content('Only allow merge requests to be merged if the build succeeds') + expect(page).not_to have_content('Only allow merge requests to be merged if all discussions are resolved') + + select 'Everyone with access', from: "project_project_feature_attributes_merge_requests_access_level" + + expect(page).to have_content('Only allow merge requests to be merged if the build succeeds') + expect(page).to have_content('Only allow merge requests to be merged if all discussions are resolved') + end + end +end diff --git a/spec/features/u2f_spec.rb b/spec/features/u2f_spec.rb index b750f27ea72..be21b403084 100644 --- a/spec/features/u2f_spec.rb +++ b/spec/features/u2f_spec.rb @@ -163,8 +163,7 @@ feature 'Using U2F (Universal 2nd Factor) Devices for Authentication', feature: click_on "Sign in via U2F device" expect(page.body).to match('We heard back from your U2F device') click_on "Authenticate via U2F Device" - - expect(page.body).to match('Signed in successfully') + expect(page.body).to match('href="/users/sign_out"') end end @@ -178,7 +177,7 @@ feature 'Using U2F (Universal 2nd Factor) Devices for Authentication', feature: expect(page.body).to match('We heard back from your U2F device') click_on "Authenticate via U2F Device" - expect(page.body).to match('Signed in successfully') + expect(page.body).to match('href="/users/sign_out"') end end @@ -234,7 +233,7 @@ feature 'Using U2F (Universal 2nd Factor) Devices for Authentication', feature: expect(page.body).to match('We heard back from your U2F device') click_on "Authenticate via U2F Device" - expect(page.body).to match('Signed in successfully') + expect(page.body).to match('href="/users/sign_out"') end end end @@ -275,7 +274,7 @@ feature 'Using U2F (Universal 2nd Factor) Devices for Authentication', feature: expect(page.body).to match('We heard back from your U2F device') click_on "Authenticate via U2F Device" - expect(page.body).to match('Signed in successfully') + expect(page.body).to match('href="/users/sign_out"') logout end diff --git a/spec/helpers/diff_helper_spec.rb b/spec/helpers/diff_helper_spec.rb index 837e7afa7e8..468bcc7badc 100644 --- a/spec/helpers/diff_helper_spec.rb +++ b/spec/helpers/diff_helper_spec.rb @@ -60,15 +60,58 @@ describe DiffHelper do end describe '#diff_line_content' do - it 'returns non breaking space when line is empty' do - expect(diff_line_content(nil)).to eq(' ') - end - - it 'returns the line itself' do - expect(diff_line_content(diff_file.diff_lines.first.text)). - to eq('@@ -6,12 +6,18 @@ module Popen') - expect(diff_line_content(diff_file.diff_lines.first.type)).to eq('match') - expect(diff_file.diff_lines.first.new_pos).to eq(6) + context 'when the line is empty' do + it 'returns a non breaking space' do + expect(diff_line_content(nil)).to eq(' ') + end + + it 'returns an HTML-safe string' do + expect(diff_line_content(nil)).to be_html_safe + end + end + + context 'when the line is not empty' do + context 'when the line starts with +, -, or a space' do + it 'strips the first character' do + expect(diff_line_content('+new line')).to eq('new line') + expect(diff_line_content('-new line')).to eq('new line') + expect(diff_line_content(' new line')).to eq('new line') + end + + context 'when the line is HTML-safe' do + it 'returns an HTML-safe string' do + expect(diff_line_content('+new line'.html_safe)).to be_html_safe + expect(diff_line_content('-new line'.html_safe)).to be_html_safe + expect(diff_line_content(' new line'.html_safe)).to be_html_safe + end + end + + context 'when the line is not HTML-safe' do + it 'returns a non-HTML-safe string' do + expect(diff_line_content('+new line')).not_to be_html_safe + expect(diff_line_content('-new line')).not_to be_html_safe + expect(diff_line_content(' new line')).not_to be_html_safe + end + end + end + + context 'when the line does not start with a +, -, or a space' do + it 'returns the string' do + expect(diff_line_content('@@ -6,12 +6,18 @@ module Popen')).to eq('@@ -6,12 +6,18 @@ module Popen') + end + + context 'when the line is HTML-safe' do + it 'returns an HTML-safe string' do + expect(diff_line_content('@@ -6,12 +6,18 @@ module Popen'.html_safe)).to be_html_safe + end + end + + context 'when the line is not HTML-safe' do + it 'returns a non-HTML-safe string' do + expect(diff_line_content('@@ -6,12 +6,18 @@ module Popen')).not_to be_html_safe + end + end + end end end diff --git a/spec/javascripts/environments/environment_actions_spec.js.es6 b/spec/javascripts/environments/environment_actions_spec.js.es6 index 76e81233e89..4bae3f30bb5 100644 --- a/spec/javascripts/environments/environment_actions_spec.js.es6 +++ b/spec/javascripts/environments/environment_actions_spec.js.es6 @@ -8,7 +8,7 @@ describe('Actions Component', () => { fixture.load('environments/element.html'); }); - it('Should render a dropdown with the provided actions', () => { + it('should render a dropdown with the provided actions', () => { const actionsMock = [ { name: 'bar', @@ -24,6 +24,7 @@ describe('Actions Component', () => { el: document.querySelector('.test-dom-element'), propsData: { actions: actionsMock, + playIconSvg: '<svg></svg>', }, }); @@ -34,4 +35,33 @@ describe('Actions Component', () => { component.$el.querySelector('.dropdown-menu li a').getAttribute('href'), ).toEqual(actionsMock[0].play_path); }); + + it('should render a dropdown with the provided svg', () => { + const actionsMock = [ + { + name: 'bar', + play_path: 'https://gitlab.com/play', + }, + { + name: 'foo', + play_path: '#', + }, + ]; + + const component = new window.gl.environmentsList.ActionsComponent({ + el: document.querySelector('.test-dom-element'), + propsData: { + actions: actionsMock, + playIconSvg: '<svg></svg>', + }, + }); + + expect( + component.$el.querySelector('.js-dropdown-play-icon-container').children, + ).toContain('svg'); + + expect( + component.$el.querySelector('.js-action-play-icon-container').children, + ).toContain('svg'); + }); }); diff --git a/spec/javascripts/environments/environment_external_url_spec.js.es6 b/spec/javascripts/environments/environment_external_url_spec.js.es6 index 156506ef28f..9f82567c35b 100644 --- a/spec/javascripts/environments/environment_external_url_spec.js.es6 +++ b/spec/javascripts/environments/environment_external_url_spec.js.es6 @@ -7,12 +7,12 @@ describe('External URL Component', () => { fixture.load('environments/element.html'); }); - it('should link to the provided external_url', () => { + it('should link to the provided externalUrl prop', () => { const externalURL = 'https://gitlab.com'; const component = new window.gl.environmentsList.ExternalUrlComponent({ el: document.querySelector('.test-dom-element'), propsData: { - external_url: externalURL, + externalUrl: externalURL, }, }); diff --git a/spec/javascripts/environments/environment_rollback_spec.js.es6 b/spec/javascripts/environments/environment_rollback_spec.js.es6 index 29449bbbd9e..77ba0ab38ec 100644 --- a/spec/javascripts/environments/environment_rollback_spec.js.es6 +++ b/spec/javascripts/environments/environment_rollback_spec.js.es6 @@ -9,24 +9,24 @@ describe('Rollback Component', () => { fixture.load('environments/element.html'); }); - it('Should link to the provided retry_url', () => { + it('Should link to the provided retryUrl', () => { const component = new window.gl.environmentsList.RollbackComponent({ el: document.querySelector('.test-dom-element'), propsData: { - retry_url: retryURL, - is_last_deployment: true, + retryUrl: retryURL, + isLastDeployment: true, }, }); expect(component.$el.getAttribute('href')).toEqual(retryURL); }); - it('Should render Re-deploy label when is_last_deployment is true', () => { + it('Should render Re-deploy label when isLastDeployment is true', () => { const component = new window.gl.environmentsList.RollbackComponent({ el: document.querySelector('.test-dom-element'), propsData: { - retry_url: retryURL, - is_last_deployment: true, + retryUrl: retryURL, + isLastDeployment: true, }, }); @@ -34,12 +34,12 @@ describe('Rollback Component', () => { }); - it('Should render Rollback label when is_last_deployment is false', () => { + it('Should render Rollback label when isLastDeployment is false', () => { const component = new window.gl.environmentsList.RollbackComponent({ el: document.querySelector('.test-dom-element'), propsData: { - retry_url: retryURL, - is_last_deployment: false, + retryUrl: retryURL, + isLastDeployment: false, }, }); diff --git a/spec/javascripts/environments/environment_stop_spec.js.es6 b/spec/javascripts/environments/environment_stop_spec.js.es6 index b842be4da61..84a41b2bf46 100644 --- a/spec/javascripts/environments/environment_stop_spec.js.es6 +++ b/spec/javascripts/environments/environment_stop_spec.js.es6 @@ -13,7 +13,7 @@ describe('Stop Component', () => { component = new window.gl.environmentsList.StopComponent({ el: document.querySelector('.test-dom-element'), propsData: { - stop_url: stopURL, + stopUrl: stopURL, }, }); }); diff --git a/spec/javascripts/fixtures/event_filter.html.haml b/spec/javascripts/fixtures/event_filter.html.haml index 95e248cadf8..5477c6075f0 100644 --- a/spec/javascripts/fixtures/event_filter.html.haml +++ b/spec/javascripts/fixtures/event_filter.html.haml @@ -12,6 +12,10 @@ %span Merge events %li + %a.event-filter-link{ id: "issue_event_filter", title: "Filter by issue events", href: "/dashboard/activity"} + %span + Issue events + %li %a.event-filter-link{ id: "comments_event_filter", title: "Filter by comments", href: "/dashboard/activity"} %span Comments diff --git a/spec/javascripts/fixtures/signin_tabs.html.haml b/spec/javascripts/fixtures/signin_tabs.html.haml new file mode 100644 index 00000000000..12b8d423cbe --- /dev/null +++ b/spec/javascripts/fixtures/signin_tabs.html.haml @@ -0,0 +1,5 @@ +%ul.nav-tabs + %li + %a.active{ id: 'standard', href: '#standard'} Standard + %li + %a{ id: 'ldap', href: '#ldap'} Ldap diff --git a/spec/javascripts/signin_tabs_memoizer_spec.js.es6 b/spec/javascripts/signin_tabs_memoizer_spec.js.es6 new file mode 100644 index 00000000000..9a9fb22255b --- /dev/null +++ b/spec/javascripts/signin_tabs_memoizer_spec.js.es6 @@ -0,0 +1,53 @@ +/*= require signin_tabs_memoizer */ + +((global) => { + describe('SigninTabsMemoizer', () => { + const fixtureTemplate = 'signin_tabs.html'; + const tabSelector = 'ul.nav-tabs'; + const currentTabKey = 'current_signin_tab'; + let memo; + + function createMemoizer() { + memo = new global.ActiveTabMemoizer({ + currentTabKey, + tabSelector, + }); + return memo; + } + + fixture.preload(fixtureTemplate); + + beforeEach(() => { + fixture.load(fixtureTemplate); + }); + + it('does nothing if no tab was previously selected', () => { + createMemoizer(); + + expect(document.querySelector('li a.active').getAttribute('id')).toEqual('standard'); + }); + + it('shows last selected tab on boot', () => { + createMemoizer().saveData('#ldap'); + const fakeTab = { + click: () => {}, + }; + spyOn(document, 'querySelector').and.returnValue(fakeTab); + spyOn(fakeTab, 'click'); + + memo.bootstrap(); + + // verify that triggers click on the last selected tab + expect(document.querySelector).toHaveBeenCalledWith(`${tabSelector} a[href="#ldap"]`); + expect(fakeTab.click).toHaveBeenCalled(); + }); + + it('saves last selected tab on change', () => { + createMemoizer(); + + document.getElementById('standard').click(); + + expect(memo.readData()).toEqual('#standard'); + }); + }); +})(window); diff --git a/spec/javascripts/vue_common_components/commit_spec.js.es6 b/spec/javascripts/vue_common_components/commit_spec.js.es6 index d170517dd9b..26dfdb94aae 100644 --- a/spec/javascripts/vue_common_components/commit_spec.js.es6 +++ b/spec/javascripts/vue_common_components/commit_spec.js.es6 @@ -10,12 +10,12 @@ describe('Commit component', () => { el: document.querySelector('.test-commit-container'), propsData: { tag: false, - commit_ref: { + commitRef: { name: 'master', ref_url: 'http://localhost/namespace2/gitlabhq/tree/master', }, - commit_url: 'https://gitlab.com/gitlab-org/gitlab-ce/commit/b7836eddf62d663c665769e1b0960197fd215067', - short_sha: 'b7836edd', + commitUrl: 'https://gitlab.com/gitlab-org/gitlab-ce/commit/b7836eddf62d663c665769e1b0960197fd215067', + shortSha: 'b7836edd', title: 'Commit message', author: { avatar_url: 'https://gitlab.com/uploads/user/avatar/300478/avatar.png', @@ -34,18 +34,19 @@ describe('Commit component', () => { props = { tag: true, - commit_ref: { + commitRef: { name: 'master', ref_url: 'http://localhost/namespace2/gitlabhq/tree/master', }, - commit_url: 'https://gitlab.com/gitlab-org/gitlab-ce/commit/b7836eddf62d663c665769e1b0960197fd215067', - short_sha: 'b7836edd', + commitUrl: 'https://gitlab.com/gitlab-org/gitlab-ce/commit/b7836eddf62d663c665769e1b0960197fd215067', + shortSha: 'b7836edd', title: 'Commit message', author: { avatar_url: 'https://gitlab.com/uploads/user/avatar/300478/avatar.png', web_url: 'https://gitlab.com/jschatz1', username: 'jschatz1', }, + commitIconSvg: '<svg></svg>', }; component = new window.gl.CommitComponent({ @@ -59,20 +60,24 @@ describe('Commit component', () => { }); it('should render a link to the ref url', () => { - expect(component.$el.querySelector('.branch-name').getAttribute('href')).toEqual(props.commit_ref.ref_url); + expect(component.$el.querySelector('.branch-name').getAttribute('href')).toEqual(props.commitRef.ref_url); }); it('should render the ref name', () => { - expect(component.$el.querySelector('.branch-name').textContent).toContain(props.commit_ref.name); + expect(component.$el.querySelector('.branch-name').textContent).toContain(props.commitRef.name); }); it('should render the commit short sha with a link to the commit url', () => { - expect(component.$el.querySelector('.commit-id').getAttribute('href')).toEqual(props.commit_url); - expect(component.$el.querySelector('.commit-id').textContent).toContain(props.short_sha); + expect(component.$el.querySelector('.commit-id').getAttribute('href')).toEqual(props.commitUrl); + expect(component.$el.querySelector('.commit-id').textContent).toContain(props.shortSha); + }); + + it('should render the given commitIconSvg', () => { + expect(component.$el.querySelector('.js-commit-icon').children).toContain('svg'); }); describe('Given commit title and author props', () => { - it('Should render a link to the author profile', () => { + it('should render a link to the author profile', () => { expect( component.$el.querySelector('.commit-title .avatar-image-container').getAttribute('href'), ).toEqual(props.author.web_url); @@ -91,7 +96,7 @@ describe('Commit component', () => { it('should render the commit title', () => { expect( component.$el.querySelector('a.commit-row-message').getAttribute('href'), - ).toEqual(props.commit_url); + ).toEqual(props.commitUrl); expect( component.$el.querySelector('a.commit-row-message').textContent, ).toContain(props.title); @@ -99,16 +104,16 @@ describe('Commit component', () => { }); describe('When commit title is not provided', () => { - it('Should render default message', () => { + it('should render default message', () => { fixture.set('<div class="test-commit-container"></div>'); props = { tag: false, - commit_ref: { + commitRef: { name: 'master', ref_url: 'http://localhost/namespace2/gitlabhq/tree/master', }, - commit_url: 'https://gitlab.com/gitlab-org/gitlab-ce/commit/b7836eddf62d663c665769e1b0960197fd215067', - short_sha: 'b7836edd', + commitUrl: 'https://gitlab.com/gitlab-org/gitlab-ce/commit/b7836eddf62d663c665769e1b0960197fd215067', + shortSha: 'b7836edd', title: null, author: {}, }; diff --git a/spec/lib/constraints/group_url_constrainer_spec.rb b/spec/lib/constraints/group_url_constrainer_spec.rb index 892554f2870..96dacdc5cd2 100644 --- a/spec/lib/constraints/group_url_constrainer_spec.rb +++ b/spec/lib/constraints/group_url_constrainer_spec.rb @@ -10,6 +10,13 @@ describe GroupUrlConstrainer, lib: true do it { expect(subject.matches?(request)).to be_truthy } end + context 'valid request for nested group' do + let!(:nested_group) { create(:group, path: 'nested', parent: group) } + let!(:request) { build_request('gitlab/nested') } + + it { expect(subject.matches?(request)).to be_truthy } + end + context 'invalid request' do let(:request) { build_request('foo') } diff --git a/spec/lib/event_filter_spec.rb b/spec/lib/event_filter_spec.rb index a6d8e6927e0..ec2f66b1136 100644 --- a/spec/lib/event_filter_spec.rb +++ b/spec/lib/event_filter_spec.rb @@ -7,6 +7,10 @@ describe EventFilter, lib: true do let!(:push_event) { create(:event, action: Event::PUSHED, project: public_project, target: public_project, author: source_user) } let!(:merged_event) { create(:event, action: Event::MERGED, project: public_project, target: public_project, author: source_user) } + let!(:created_event) { create(:event, action: Event::CREATED, project: public_project, target: public_project, author: source_user) } + let!(:updated_event) { create(:event, action: Event::UPDATED, project: public_project, target: public_project, author: source_user) } + let!(:closed_event) { create(:event, action: Event::CLOSED, project: public_project, target: public_project, author: source_user) } + let!(:reopened_event) { create(:event, action: Event::REOPENED, project: public_project, target: public_project, author: source_user) } let!(:comments_event) { create(:event, action: Event::COMMENTED, project: public_project, target: public_project, author: source_user) } let!(:joined_event) { create(:event, action: Event::JOINED, project: public_project, target: public_project, author: source_user) } let!(:left_event) { create(:event, action: Event::LEFT, project: public_project, target: public_project, author: source_user) } @@ -21,6 +25,11 @@ describe EventFilter, lib: true do expect(events).to contain_exactly(merged_event) end + it 'applies issue filter' do + events = EventFilter.new(EventFilter.issue).apply_filter(Event.all) + expect(events).to contain_exactly(created_event, updated_event, closed_event, reopened_event) + end + it 'applies comments filter' do events = EventFilter.new(EventFilter.comments).apply_filter(Event.all) expect(events).to contain_exactly(comments_event) @@ -33,17 +42,17 @@ describe EventFilter, lib: true do it 'applies all filter' do events = EventFilter.new(EventFilter.all).apply_filter(Event.all) - expect(events).to contain_exactly(push_event, merged_event, comments_event, joined_event, left_event) + expect(events).to contain_exactly(push_event, merged_event, created_event, updated_event, closed_event, reopened_event, comments_event, joined_event, left_event) end it 'applies no filter' do events = EventFilter.new(nil).apply_filter(Event.all) - expect(events).to contain_exactly(push_event, merged_event, comments_event, joined_event, left_event) + expect(events).to contain_exactly(push_event, merged_event, created_event, updated_event, closed_event, reopened_event, comments_event, joined_event, left_event) end it 'applies unknown filter' do events = EventFilter.new('').apply_filter(Event.all) - expect(events).to contain_exactly(push_event, merged_event, comments_event, joined_event, left_event) + expect(events).to contain_exactly(push_event, merged_event, created_event, updated_event, closed_event, reopened_event, comments_event, joined_event, left_event) end end end diff --git a/spec/lib/gitlab/ci/status/factory_spec.rb b/spec/lib/gitlab/ci/status/factory_spec.rb new file mode 100644 index 00000000000..d5bd7f7102b --- /dev/null +++ b/spec/lib/gitlab/ci/status/factory_spec.rb @@ -0,0 +1,22 @@ +require 'spec_helper' + +describe Gitlab::Ci::Status::Factory do + subject do + described_class.new(object) + end + + let(:status) { subject.fabricate! } + + context 'when object has a core status' do + HasStatus::AVAILABLE_STATUSES.each do |core_status| + context "when core status is #{core_status}" do + let(:object) { double(status: core_status) } + + it "fabricates a core status #{core_status}" do + expect(status).to be_a( + Gitlab::Ci::Status.const_get(core_status.capitalize)) + end + end + end + end +end diff --git a/spec/lib/gitlab/ci/status/stage/common_spec.rb b/spec/lib/gitlab/ci/status/stage/common_spec.rb new file mode 100644 index 00000000000..f3259c6f23e --- /dev/null +++ b/spec/lib/gitlab/ci/status/stage/common_spec.rb @@ -0,0 +1,26 @@ +require 'spec_helper' + +describe Gitlab::Ci::Status::Stage::Common do + let(:pipeline) { create(:ci_empty_pipeline) } + let(:stage) { build(:ci_stage, pipeline: pipeline, name: 'test') } + + subject do + Class.new(Gitlab::Ci::Status::Core) + .new(stage).extend(described_class) + end + + it 'does not have action' do + expect(subject).not_to have_action + end + + it 'has details' do + expect(subject).to have_details + end + + it 'links to the pipeline details page' do + expect(subject.details_path) + .to include "pipelines/#{pipeline.id}" + expect(subject.details_path) + .to include "##{stage.name}" + end +end diff --git a/spec/lib/gitlab/ci/status/stage/factory_spec.rb b/spec/lib/gitlab/ci/status/stage/factory_spec.rb new file mode 100644 index 00000000000..17929665c83 --- /dev/null +++ b/spec/lib/gitlab/ci/status/stage/factory_spec.rb @@ -0,0 +1,37 @@ +require 'spec_helper' + +describe Gitlab::Ci::Status::Stage::Factory do + let(:pipeline) { create(:ci_empty_pipeline) } + let(:stage) { build(:ci_stage, pipeline: pipeline, name: 'test') } + + subject do + described_class.new(stage) + end + + let(:status) do + subject.fabricate! + end + + context 'when stage has a core status' do + HasStatus::AVAILABLE_STATUSES.each do |core_status| + context "when core status is #{core_status}" do + before do + create(:ci_build, pipeline: pipeline, stage: 'test', status: core_status) + create(:commit_status, pipeline: pipeline, stage: 'test', status: core_status) + create(:ci_build, pipeline: pipeline, stage: 'build', status: :failed) + end + + it "fabricates a core status #{core_status}" do + expect(status).to be_a( + Gitlab::Ci::Status.const_get(core_status.capitalize)) + end + + it 'extends core status with common stage methods' do + expect(status).to have_details + expect(status.details_path).to include "pipelines/#{pipeline.id}" + expect(status.details_path).to include "##{stage.name}" + end + end + end + end +end diff --git a/spec/lib/gitlab/import_export/all_models.yml b/spec/lib/gitlab/import_export/all_models.yml index 7e00e214c6e..8e1a28f2723 100644 --- a/spec/lib/gitlab/import_export/all_models.yml +++ b/spec/lib/gitlab/import_export/all_models.yml @@ -188,6 +188,7 @@ project: - project_feature - authorized_users - project_authorizations +- route award_emoji: - awardable - user diff --git a/spec/lib/gitlab/search_results_spec.rb b/spec/lib/gitlab/search_results_spec.rb index f23e3522625..9614aad3e73 100644 --- a/spec/lib/gitlab/search_results_spec.rb +++ b/spec/lib/gitlab/search_results_spec.rb @@ -40,6 +40,15 @@ describe Gitlab::SearchResults do expect(results.milestones_count).to eq(1) end end + + it 'includes merge requests from source and target projects' do + forked_project = create(:empty_project, forked_from_project: project) + merge_request_2 = create(:merge_request, target_project: project, source_project: forked_project, title: 'foo') + + results = described_class.new(user, Project.where(id: forked_project.id), 'foo') + + expect(results.objects('merge_requests')).to include merge_request_2 + end end it 'does not list issues on private projects' do @@ -152,4 +161,11 @@ describe Gitlab::SearchResults do expect(results.issues_count).to eq 5 end end + + it 'does not list merge requests on projects with limited access' do + project.update!(visibility_level: Gitlab::VisibilityLevel::PUBLIC) + project.project_feature.update!(merge_requests_access_level: ProjectFeature::PRIVATE) + + expect(results.objects('merge_requests')).not_to include merge_request + end end diff --git a/spec/models/ci/pipeline_spec.rb b/spec/models/ci/pipeline_spec.rb index 3f93d9ddf19..8158e71dd55 100644 --- a/spec/models/ci/pipeline_spec.rb +++ b/spec/models/ci/pipeline_spec.rb @@ -20,8 +20,6 @@ describe Ci::Pipeline, models: true do it { is_expected.to respond_to :git_author_email } it { is_expected.to respond_to :short_sha } - it { is_expected.to delegate_method(:stages).to(:statuses) } - describe '#valid_commit_sha' do context 'commit.sha can not start with 00000000' do before do @@ -125,16 +123,55 @@ describe Ci::Pipeline, models: true do end describe '#stages' do - let(:pipeline2) { FactoryGirl.create :ci_pipeline, project: project } - subject { CommitStatus.where(pipeline: [pipeline, pipeline2]).stages } - before do - FactoryGirl.create :ci_build, pipeline: pipeline2, stage: 'test', stage_idx: 1 - FactoryGirl.create :ci_build, pipeline: pipeline, stage: 'build', stage_idx: 0 + create(:commit_status, pipeline: pipeline, stage: 'build', name: 'linux', stage_idx: 0, status: 'success') + create(:commit_status, pipeline: pipeline, stage: 'build', name: 'mac', stage_idx: 0, status: 'failed') + create(:commit_status, pipeline: pipeline, stage: 'deploy', name: 'staging', stage_idx: 2, status: 'running') + create(:commit_status, pipeline: pipeline, stage: 'test', name: 'rspec', stage_idx: 1, status: 'success') + end + + subject { pipeline.stages } + + context 'stages list' do + it 'returns ordered list of stages' do + expect(subject.map(&:name)).to eq(%w[build test deploy]) + end + end + + it 'returns a valid number of stages' do + expect(pipeline.stages_count).to eq(3) + end + + it 'returns a valid names of stages' do + expect(pipeline.stages_name).to eq(['build', 'test', 'deploy']) end - it 'return all stages' do - is_expected.to eq(%w(build test)) + context 'stages with statuses' do + let(:statuses) do + subject.map do |stage| + [stage.name, stage.status] + end + end + + it 'returns list of stages with statuses' do + expect(statuses).to eq([['build', 'failed'], + ['test', 'success'], + ['deploy', 'running'] + ]) + end + + context 'when build is retried' do + before do + create(:commit_status, pipeline: pipeline, stage: 'build', name: 'mac', stage_idx: 0, status: 'success') + end + + it 'ignores the previous state' do + expect(statuses).to eq([['build', 'success'], + ['test', 'success'], + ['deploy', 'running'] + ]) + end + end end end diff --git a/spec/models/ci/stage_spec.rb b/spec/models/ci/stage_spec.rb new file mode 100644 index 00000000000..f232761dba2 --- /dev/null +++ b/spec/models/ci/stage_spec.rb @@ -0,0 +1,133 @@ +require 'spec_helper' + +describe Ci::Stage, models: true do + let(:stage) { build(:ci_stage) } + let(:pipeline) { stage.pipeline } + let(:stage_name) { stage.name } + + describe '#expectations' do + subject { stage } + + it { is_expected.to include_module(StaticModel) } + + it { is_expected.to respond_to(:pipeline) } + it { is_expected.to respond_to(:name) } + + it { is_expected.to delegate_method(:project).to(:pipeline) } + end + + describe '#statuses' do + let!(:stage_build) { create_job(:ci_build) } + let!(:commit_status) { create_job(:commit_status) } + let!(:other_build) { create_job(:ci_build, stage: 'other stage') } + + subject { stage.statuses } + + it "returns only matching statuses" do + is_expected.to contain_exactly(stage_build, commit_status) + end + end + + describe '#builds' do + let!(:stage_build) { create_job(:ci_build) } + let!(:commit_status) { create_job(:commit_status) } + + subject { stage.builds } + + it "returns only builds" do + is_expected.to contain_exactly(stage_build) + end + end + + describe '#status' do + subject { stage.status } + + context 'if status is already defined' do + let(:stage) { build(:ci_stage, status: 'success') } + + it "returns defined status" do + is_expected.to eq('success') + end + end + + context 'if status has to be calculated' do + let!(:stage_build) { create_job(:ci_build, status: :failed) } + + it "returns status of a build" do + is_expected.to eq('failed') + end + + context 'and builds are retried' do + let!(:new_build) { create_job(:ci_build, status: :success) } + + it "returns status of latest build" do + is_expected.to eq('success') + end + end + end + end + + describe '#detailed_status' do + subject { stage.detailed_status } + + context 'when build is created' do + let!(:stage_build) { create_job(:ci_build, status: :created) } + + it 'returns detailed status for created stage' do + expect(subject.text).to eq 'created' + end + end + + context 'when build is pending' do + let!(:stage_build) { create_job(:ci_build, status: :pending) } + + it 'returns detailed status for pending stage' do + expect(subject.text).to eq 'pending' + end + end + + context 'when build is running' do + let!(:stage_build) { create_job(:ci_build, status: :running) } + + it 'returns detailed status for running stage' do + expect(subject.text).to eq 'running' + end + end + + context 'when build is successful' do + let!(:stage_build) { create_job(:ci_build, status: :success) } + + it 'returns detailed status for successful stage' do + expect(subject.text).to eq 'passed' + end + end + + context 'when build is failed' do + let!(:stage_build) { create_job(:ci_build, status: :failed) } + + it 'returns detailed status for failed stage' do + expect(subject.text).to eq 'failed' + end + end + + context 'when build is canceled' do + let!(:stage_build) { create_job(:ci_build, status: :canceled) } + + it 'returns detailed status for canceled stage' do + expect(subject.text).to eq 'canceled' + end + end + + context 'when build is skipped' do + let!(:stage_build) { create_job(:ci_build, status: :skipped) } + + it 'returns detailed status for skipped stage' do + expect(subject.text).to eq 'skipped' + end + end + end + + def create_job(type, status: 'success', stage: stage_name) + create(type, pipeline: pipeline, stage: stage, status: status) + end +end diff --git a/spec/models/commit_range_spec.rb b/spec/models/commit_range_spec.rb index d89d4342dea..30782ca75a0 100644 --- a/spec/models/commit_range_spec.rb +++ b/spec/models/commit_range_spec.rb @@ -137,26 +137,25 @@ describe CommitRange, models: true do end describe '#has_been_reverted?' do - it 'returns true if the commit has been reverted' do - issue = create(:issue) + let(:issue) { create(:issue) } + let(:user) { issue.author } + it 'returns true if the commit has been reverted' do create(:note_on_issue, noteable: issue, system: true, - note: commit1.revert_description, + note: commit1.revert_description(user), project: issue.project) expect_any_instance_of(Commit).to receive(:reverts_commit?). - with(commit1). + with(commit1, user). and_return(true) - expect(commit1.has_been_reverted?(nil, issue)).to eq(true) + expect(commit1.has_been_reverted?(user, issue)).to eq(true) end it 'returns false a commit has not been reverted' do - issue = create(:issue) - - expect(commit1.has_been_reverted?(nil, issue)).to eq(false) + expect(commit1.has_been_reverted?(user, issue)).to eq(false) end end end diff --git a/spec/models/commit_spec.rb b/spec/models/commit_spec.rb index eb482c7f913..0935fc0561c 100644 --- a/spec/models/commit_spec.rb +++ b/spec/models/commit_spec.rb @@ -179,25 +179,26 @@ eos describe '#reverts_commit?' do let(:another_commit) { double(:commit, revert_description: "This reverts commit #{commit.sha}") } + let(:user) { commit.author } - it { expect(commit.reverts_commit?(another_commit)).to be_falsy } + it { expect(commit.reverts_commit?(another_commit, user)).to be_falsy } context 'commit has no description' do before { allow(commit).to receive(:description?).and_return(false) } - it { expect(commit.reverts_commit?(another_commit)).to be_falsy } + it { expect(commit.reverts_commit?(another_commit, user)).to be_falsy } end context "another_commit's description does not revert commit" do before { allow(commit).to receive(:description).and_return("Foo Bar") } - it { expect(commit.reverts_commit?(another_commit)).to be_falsy } + it { expect(commit.reverts_commit?(another_commit, user)).to be_falsy } end context "another_commit's description reverts commit" do before { allow(commit).to receive(:description).and_return("Foo #{another_commit.revert_description} Bar") } - it { expect(commit.reverts_commit?(another_commit)).to be_truthy } + it { expect(commit.reverts_commit?(another_commit, user)).to be_truthy } end context "another_commit's description reverts merged merge request" do @@ -207,7 +208,7 @@ eos allow(commit).to receive(:description).and_return("Foo #{another_commit.revert_description} Bar") end - it { expect(commit.reverts_commit?(another_commit)).to be_truthy } + it { expect(commit.reverts_commit?(another_commit, user)).to be_truthy } end end diff --git a/spec/models/commit_status_spec.rb b/spec/models/commit_status_spec.rb index 80c2a1bc7a9..1ec08c2a9d0 100644 --- a/spec/models/commit_status_spec.rb +++ b/spec/models/commit_status_spec.rb @@ -175,7 +175,7 @@ describe CommitStatus, models: true do end it 'returns statuses without what we want to ignore' do - is_expected.to eq(statuses.values_at(1, 2, 4, 5, 6, 8, 9)) + is_expected.to eq(statuses.values_at(0, 1, 2, 3, 4, 5, 6, 8, 9)) end end @@ -200,49 +200,6 @@ describe CommitStatus, models: true do end end - describe '#stages' do - before do - create :commit_status, pipeline: pipeline, stage: 'build', name: 'linux', stage_idx: 0, status: 'success' - create :commit_status, pipeline: pipeline, stage: 'build', name: 'mac', stage_idx: 0, status: 'failed' - create :commit_status, pipeline: pipeline, stage: 'deploy', name: 'staging', stage_idx: 2, status: 'running' - create :commit_status, pipeline: pipeline, stage: 'test', name: 'rspec', stage_idx: 1, status: 'success' - end - - context 'stages list' do - subject { CommitStatus.where(pipeline: pipeline).stages } - - it 'returns ordered list of stages' do - is_expected.to eq(%w[build test deploy]) - end - end - - context 'stages with statuses' do - subject { CommitStatus.where(pipeline: pipeline).latest.stages_status } - - it 'returns list of stages with statuses' do - is_expected.to eq({ - 'build' => 'failed', - 'test' => 'success', - 'deploy' => 'running' - }) - end - - context 'when build is retried' do - before do - create :commit_status, pipeline: pipeline, stage: 'build', name: 'mac', stage_idx: 0, status: 'success' - end - - it 'ignores a previous state' do - is_expected.to eq({ - 'build' => 'success', - 'test' => 'success', - 'deploy' => 'running' - }) - end - end - end - end - describe '#commit' do it 'returns commit pipeline has been created for' do expect(commit_status.commit).to eq project.commit diff --git a/spec/models/concerns/has_status_spec.rb b/spec/models/concerns/has_status_spec.rb index 9defb17dc92..4d0f51fe82a 100644 --- a/spec/models/concerns/has_status_spec.rb +++ b/spec/models/concerns/has_status_spec.rb @@ -48,7 +48,7 @@ describe HasStatus do [create(type, status: :failed, allow_failure: true)] end - it { is_expected.to eq 'success' } + it { is_expected.to eq 'skipped' } end context 'success and canceled' do diff --git a/spec/models/concerns/routable_spec.rb b/spec/models/concerns/routable_spec.rb new file mode 100644 index 00000000000..0acefc0c1d5 --- /dev/null +++ b/spec/models/concerns/routable_spec.rb @@ -0,0 +1,67 @@ +require 'spec_helper' + +describe Group, 'Routable' do + let!(:group) { create(:group) } + + describe 'Associations' do + it { is_expected.to have_one(:route).dependent(:destroy) } + end + + describe 'Callbacks' do + it 'creates route record on create' do + expect(group.route.path).to eq(group.path) + end + + it 'updates route record on path change' do + group.update_attributes(path: 'wow') + + expect(group.route.path).to eq('wow') + end + + it 'ensure route path uniqueness across different objects' do + create(:group, parent: group, path: 'xyz') + duplicate = build(:project, namespace: group, path: 'xyz') + + expect { duplicate.save! }.to raise_error(ActiveRecord::RecordInvalid, 'Validation failed: Route path has already been taken, Route is invalid') + end + end + + describe '.find_by_full_path' do + let!(:nested_group) { create(:group, parent: group) } + + it { expect(described_class.find_by_full_path(group.to_param)).to eq(group) } + it { expect(described_class.find_by_full_path(group.to_param.upcase)).to eq(group) } + it { expect(described_class.find_by_full_path(nested_group.to_param)).to eq(nested_group) } + it { expect(described_class.find_by_full_path('unknown')).to eq(nil) } + end + + describe '.where_paths_in' do + context 'without any paths' do + it 'returns an empty relation' do + expect(described_class.where_paths_in([])).to eq([]) + end + end + + context 'without any valid paths' do + it 'returns an empty relation' do + expect(described_class.where_paths_in(%w[unknown])).to eq([]) + end + end + + context 'with valid paths' do + let!(:nested_group) { create(:group, parent: group) } + + it 'returns the projects matching the paths' do + result = described_class.where_paths_in([group.to_param, nested_group.to_param]) + + expect(result).to contain_exactly(group, nested_group) + end + + it 'returns projects regardless of the casing of paths' do + result = described_class.where_paths_in([group.to_param.upcase, nested_group.to_param.upcase]) + + expect(result).to contain_exactly(group, nested_group) + end + end + end +end diff --git a/spec/models/namespace_spec.rb b/spec/models/namespace_spec.rb index ba0ed4a3603..7f82e85563b 100644 --- a/spec/models/namespace_spec.rb +++ b/spec/models/namespace_spec.rb @@ -124,4 +124,12 @@ describe Namespace, models: true do expect(Namespace.clean_path("--%+--valid_*&%name=.git.%.atom.atom.@email.com")).to eq("valid_name") end end + + describe '#full_path' do + let(:group) { create(:group) } + let(:nested_group) { create(:group, parent: group) } + + it { expect(group.full_path).to eq(group.path) } + it { expect(nested_group.full_path).to eq("#{group.path}/#{nested_group.path}") } + end end diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb index 4d57330ed1c..21ff238841e 100644 --- a/spec/models/project_spec.rb +++ b/spec/models/project_spec.rb @@ -478,35 +478,6 @@ describe Project, models: true do end end - describe '.find_with_namespace' do - context 'with namespace' do - before do - @group = create :group, name: 'gitlab' - @project = create(:project, name: 'gitlabhq', namespace: @group) - end - - it { expect(Project.find_with_namespace('gitlab/gitlabhq')).to eq(@project) } - it { expect(Project.find_with_namespace('GitLab/GitlabHQ')).to eq(@project) } - it { expect(Project.find_with_namespace('gitlab-ci')).to be_nil } - end - - context 'when multiple projects using a similar name exist' do - let(:group) { create(:group, name: 'gitlab') } - - let!(:project1) do - create(:empty_project, name: 'gitlab1', path: 'gitlab', namespace: group) - end - - let!(:project2) do - create(:empty_project, name: 'gitlab2', path: 'GITLAB', namespace: group) - end - - it 'returns the row where the path matches literally' do - expect(Project.find_with_namespace('gitlab/GITLAB')).to eq(project2) - end - end - end - describe '#to_param' do context 'with namespace' do before do @@ -1548,39 +1519,6 @@ describe Project, models: true do end end - describe '.where_paths_in' do - context 'without any paths' do - it 'returns an empty relation' do - expect(Project.where_paths_in([])).to eq([]) - end - end - - context 'without any valid paths' do - it 'returns an empty relation' do - expect(Project.where_paths_in(%w[foo])).to eq([]) - end - end - - context 'with valid paths' do - let!(:project1) { create(:project) } - let!(:project2) { create(:project) } - - it 'returns the projects matching the paths' do - projects = Project.where_paths_in([project1.path_with_namespace, - project2.path_with_namespace]) - - expect(projects).to contain_exactly(project1, project2) - end - - it 'returns projects regardless of the casing of paths' do - projects = Project.where_paths_in([project1.path_with_namespace.upcase, - project2.path_with_namespace.upcase]) - - expect(projects).to contain_exactly(project1, project2) - end - end - end - describe 'change_head' do let(:project) { create(:project) } diff --git a/spec/models/route_spec.rb b/spec/models/route_spec.rb new file mode 100644 index 00000000000..6f491fdf9a0 --- /dev/null +++ b/spec/models/route_spec.rb @@ -0,0 +1,29 @@ +require 'spec_helper' + +describe Route, models: true do + let!(:group) { create(:group) } + let!(:route) { group.route } + + describe 'relationships' do + it { is_expected.to belong_to(:source) } + end + + describe 'validations' do + it { is_expected.to validate_presence_of(:source) } + it { is_expected.to validate_presence_of(:path) } + it { is_expected.to validate_uniqueness_of(:path) } + end + + describe '#rename_children' do + let!(:nested_group) { create(:group, path: "test", parent: group) } + let!(:deep_nested_group) { create(:group, path: "foo", parent: nested_group) } + + it "updates children routes with new path" do + route.update_attributes(path: 'bar') + + expect(described_class.exists?(path: 'bar')).to be_truthy + expect(described_class.exists?(path: 'bar/test')).to be_truthy + expect(described_class.exists?(path: 'bar/test/foo')).to be_truthy + end + end +end diff --git a/spec/requests/api/merge_requests_spec.rb b/spec/requests/api/merge_requests_spec.rb index 0b89ac7960e..75b270aa93c 100644 --- a/spec/requests/api/merge_requests_spec.rb +++ b/spec/requests/api/merge_requests_spec.rb @@ -234,11 +234,14 @@ describe API::MergeRequests, api: true do target_branch: 'master', author: user, labels: 'label, label2', - milestone_id: milestone.id + milestone_id: milestone.id, + remove_source_branch: true + expect(response).to have_http_status(201) expect(json_response['title']).to eq('Test merge_request') expect(json_response['labels']).to eq(['label', 'label2']) expect(json_response['milestone']['id']).to eq(milestone.id) + expect(json_response['force_remove_source_branch']).to be_truthy end it "returns 422 when source_branch equals target_branch" do @@ -511,6 +514,13 @@ describe API::MergeRequests, api: true do expect(json_response['target_branch']).to eq('wiki') end + it "returns merge_request that removes the source branch" do + put api("/projects/#{project.id}/merge_requests/#{merge_request.id}", user), remove_source_branch: true + + expect(response).to have_http_status(200) + expect(json_response['force_remove_source_branch']).to be_truthy + end + it 'allows special label names' do put api("/projects/#{project.id}/merge_requests/#{merge_request.id}", user), title: 'new issue', diff --git a/spec/services/merge_requests/refresh_service_spec.rb b/spec/services/merge_requests/refresh_service_spec.rb index bc340ff9d3c..7e3705983fb 100644 --- a/spec/services/merge_requests/refresh_service_spec.rb +++ b/spec/services/merge_requests/refresh_service_spec.rb @@ -126,17 +126,27 @@ describe MergeRequests::RefreshService, services: true do end context 'push to fork repo target branch' do - before do - service.new(@fork_project, @user).execute(@oldrev, @newrev, 'refs/heads/feature') - reload_mrs + describe 'changes to merge requests' do + before do + service.new(@fork_project, @user).execute(@oldrev, @newrev, 'refs/heads/feature') + reload_mrs + end + + it { expect(@merge_request.notes).to be_empty } + it { expect(@merge_request).to be_open } + it { expect(@fork_merge_request.notes).to be_empty } + it { expect(@fork_merge_request).to be_open } + it { expect(@build_failed_todo).to be_pending } + it { expect(@fork_build_failed_todo).to be_pending } end - it { expect(@merge_request.notes).to be_empty } - it { expect(@merge_request).to be_open } - it { expect(@fork_merge_request.notes).to be_empty } - it { expect(@fork_merge_request).to be_open } - it { expect(@build_failed_todo).to be_pending } - it { expect(@fork_build_failed_todo).to be_pending } + describe 'merge request diff' do + it 'does not reload the diff of the merge request made from fork' do + expect do + service.new(@fork_project, @user).execute(@oldrev, @newrev, 'refs/heads/feature') + end.not_to change { @fork_merge_request.reload.merge_request_diff } + end + end end context 'push to origin repo target branch after fork project was removed' do |