diff options
Diffstat (limited to 'spec')
40 files changed, 1194 insertions, 100 deletions
diff --git a/spec/controllers/projects/tree_controller_spec.rb b/spec/controllers/projects/tree_controller_spec.rb index a474574c6e5..e74731c9ed8 100644 --- a/spec/controllers/projects/tree_controller_spec.rb +++ b/spec/controllers/projects/tree_controller_spec.rb @@ -98,7 +98,7 @@ describe Projects::TreeController do project_id: project.to_param, id: 'master', dir_name: path, - new_branch: target_branch, + target_branch: target_branch, commit_message: 'Test commit message') end @@ -108,8 +108,8 @@ describe Projects::TreeController do it 'redirects to the new directory' do expect(subject). - to redirect_to("/#{project.path_with_namespace}/blob/#{target_branch}/#{path}") - expect(flash[:notice]).to eq('The directory has been successfully created') + to redirect_to("/#{project.path_with_namespace}/tree/#{target_branch}/#{path}") + expect(flash[:notice]).to eq('The directory has been successfully created.') end end @@ -119,7 +119,7 @@ describe Projects::TreeController do it 'does not allow overwriting of existing files' do expect(subject). - to redirect_to("/#{project.path_with_namespace}/blob/master") + to redirect_to("/#{project.path_with_namespace}/tree/master") expect(flash[:alert]).to eq('Directory already exists as a file') end end diff --git a/spec/features/admin/admin_runners_spec.rb b/spec/features/admin/admin_runners_spec.rb index 66a2cc0c157..26d03944b8a 100644 --- a/spec/features/admin/admin_runners_spec.rb +++ b/spec/features/admin/admin_runners_spec.rb @@ -63,7 +63,7 @@ describe "Admin Runners" do end describe 'runners registration token' do - let!(:token) { current_application_settings.ensure_runners_registration_token } + let!(:token) { current_application_settings.runners_registration_token } before { visit admin_runners_path } it 'has a registration token' do diff --git a/spec/features/ci_lint_spec.rb b/spec/features/ci_lint_spec.rb new file mode 100644 index 00000000000..e6e73e5e67c --- /dev/null +++ b/spec/features/ci_lint_spec.rb @@ -0,0 +1,39 @@ +require 'spec_helper' + +describe 'CI Lint' do + before do + login_as :user + end + + describe 'YAML parsing' do + before do + visit ci_lint_path + fill_in 'content', with: yaml_content + click_on 'Validate' + end + + context 'YAML is correct' do + let(:yaml_content) do + File.read(Rails.root.join('spec/support/gitlab_stubs/gitlab_ci.yml')) + end + + it 'Yaml parsing' do + within "table" do + expect(page).to have_content('Job - rspec') + expect(page).to have_content('Job - spinach') + expect(page).to have_content('Deploy Job - staging') + expect(page).to have_content('Deploy Job - production') + end + end + end + + context 'YAML is incorrect' do + let(:yaml_content) { '' } + + it 'displays information about an error' do + expect(page).to have_content('Status: syntax is incorrect') + expect(page).to have_content('Error: Please provide content of .gitlab-ci.yml') + end + end + end +end diff --git a/spec/features/commits_spec.rb b/spec/features/commits_spec.rb index ecc85376ffc..fe7f07f5b75 100644 --- a/spec/features/commits_spec.rb +++ b/spec/features/commits_spec.rb @@ -19,30 +19,13 @@ describe 'Commits' do let!(:build) { FactoryGirl.create :ci_build, commit: commit } describe 'Project commits' do - context 'builds enabled' do - context '.gitlab-ci.yml found' do - before do - visit namespace_project_commits_path(project.namespace, project, :master) - end - - it 'should show build status' do - page.within("//li[@id='commit-#{commit.short_sha}']") do - expect(page).to have_css(".ci-status-link") - end - end - end + before do + visit namespace_project_commits_path(project.namespace, project, :master) + end - context 'no .gitlab-ci.yml found' do - before do - stub_ci_commit_yaml_file(nil) - visit namespace_project_commits_path(project.namespace, project, :master) - end - - it 'should not show build status' do - page.within("//li[@id='commit-#{commit.short_sha}']") do - expect(page).to have_no_css(".ci-status-link") - end - end + it 'should show build status' do + page.within("//li[@id='commit-#{commit.short_sha}']") do + expect(page).to have_css(".ci-status-link") end end end diff --git a/spec/features/lint_spec.rb b/spec/features/lint_spec.rb deleted file mode 100644 index 5d8f56e2cfb..00000000000 --- a/spec/features/lint_spec.rb +++ /dev/null @@ -1,28 +0,0 @@ -require 'spec_helper' - -describe "Lint" do - before do - login_as :user - end - - it "Yaml parsing", js: true do - content = File.read(Rails.root.join('spec/support/gitlab_stubs/gitlab_ci.yml')) - visit ci_lint_path - fill_in "content", with: content - click_on "Validate" - within "table" do - expect(page).to have_content("Job - rspec") - expect(page).to have_content("Job - spinach") - expect(page).to have_content("Deploy Job - staging") - expect(page).to have_content("Deploy Job - production") - end - end - - it "Yaml parsing with error", js: true do - visit ci_lint_path - fill_in "content", with: "" - click_on "Validate" - expect(page).to have_content("Status: syntax is incorrect") - expect(page).to have_content("Error: Please provide content of .gitlab-ci.yml") - end -end diff --git a/spec/features/login_spec.rb b/spec/features/login_spec.rb index 922c76285d1..2451e56fe7c 100644 --- a/spec/features/login_spec.rb +++ b/spec/features/login_spec.rb @@ -98,4 +98,56 @@ feature 'Login', feature: true do expect(page).to have_content('Invalid login or password.') end end + + describe 'with required two-factor authentication enabled' do + let(:user) { create(:user) } + before(:each) { stub_application_setting(require_two_factor_authentication: true) } + + context 'with grace period defined' do + before(:each) do + stub_application_setting(two_factor_grace_period: 48) + login_with(user) + end + + context 'within the grace period' do + it 'redirects to two-factor configuration page' do + expect(current_path).to eq new_profile_two_factor_auth_path + expect(page).to have_content('You must configure Two-Factor Authentication in your account until') + end + + it 'two-factor configuration is skippable' do + expect(current_path).to eq new_profile_two_factor_auth_path + + click_link 'Configure it later' + expect(current_path).to eq root_path + end + end + + context 'after the grace period' do + let(:user) { create(:user, otp_grace_period_started_at: 9999.hours.ago) } + + it 'redirects to two-factor configuration page' do + expect(current_path).to eq new_profile_two_factor_auth_path + expect(page).to have_content('You must configure Two-Factor Authentication in your account.') + end + + it 'two-factor configuration is not skippable' do + expect(current_path).to eq new_profile_two_factor_auth_path + expect(page).not_to have_link('Configure it later') + end + end + end + + context 'without grace pariod defined' do + before(:each) do + stub_application_setting(two_factor_grace_period: 0) + login_with(user) + end + + it 'redirects to two-factor configuration page' do + expect(current_path).to eq new_profile_two_factor_auth_path + expect(page).to have_content('You must configure Two-Factor Authentication in your account.') + end + end + end end diff --git a/spec/features/projects_spec.rb b/spec/features/projects_spec.rb index 09fcff2444a..74b148f5d17 100644 --- a/spec/features/projects_spec.rb +++ b/spec/features/projects_spec.rb @@ -70,6 +70,20 @@ feature 'Project', feature: true do end end + describe 'leave project link' do + let(:user) { create(:user) } + let(:project) { create(:project, namespace: user.namespace) } + + before do + login_with(user) + project.team.add_user(user, Gitlab::Access::MASTER) + visit namespace_project_path(project.namespace, project) + end + + it { expect(page).to have_content('You have Master access to this project.') } + it { expect(page).to have_link('Leave this project') } + end + def remove_with_confirm(button_text, confirm_with) click_button button_text fill_in 'confirm_name_input', with: confirm_with diff --git a/spec/helpers/issues_helper_spec.rb b/spec/helpers/issues_helper_spec.rb index 1f2c4ee77b5..ffd8ebae029 100644 --- a/spec/helpers/issues_helper_spec.rb +++ b/spec/helpers/issues_helper_spec.rb @@ -127,18 +127,6 @@ describe IssuesHelper do it { is_expected.to eq("!1, !2, or !3") } end - describe "#url_to_emoji" do - it "returns url" do - expect(url_to_emoji("smile")).to include("emoji/1F604.png") - end - end - - describe "#emoji_list" do - it "returns url" do - expect(emoji_list).to be_kind_of(Array) - end - end - describe "#note_active_class" do before do @note = create :note @@ -153,4 +141,11 @@ describe IssuesHelper do expect(note_active_class(Note.all, @note.author)).to eq("active") end end + + describe "#awards_sort" do + it "sorts a hash so thumbsup and thumbsdown are always on top" do + data = { "thumbsdown" => "some value", "lifter" => "some value", "thumbsup" => "some value" } + expect(awards_sort(data).keys).to eq(["thumbsup", "thumbsdown", "lifter"]) + end + end end diff --git a/spec/helpers/merge_requests_helper_spec.rb b/spec/helpers/merge_requests_helper_spec.rb index 0ef1efb8bce..600e1c4e9ec 100644 --- a/spec/helpers/merge_requests_helper_spec.rb +++ b/spec/helpers/merge_requests_helper_spec.rb @@ -1,24 +1,57 @@ require 'spec_helper' describe MergeRequestsHelper do - describe "#issues_sentence" do + describe 'ci_build_details_path' do + let(:project) { create :project } + let(:merge_request) { MergeRequest.new } + let(:ci_service) { CiService.new } + let(:last_commit) { Ci::Commit.new({}) } + + before do + allow(merge_request).to receive(:source_project).and_return(project) + allow(merge_request).to receive(:last_commit).and_return(last_commit) + allow(project).to receive(:ci_service).and_return(ci_service) + allow(last_commit).to receive(:sha).and_return('12d65c') + end + + it 'does not include api credentials in a link' do + allow(ci_service). + to receive(:build_page).and_return("http://secretuser:secretpass@jenkins.example.com:8888/job/test1/scm/bySHA1/12d65c") + expect(helper.ci_build_details_path(merge_request)).to_not match("secret") + end + end + + describe '#issues_sentence' do subject { issues_sentence(issues) } let(:issues) do [build(:issue, iid: 1), build(:issue, iid: 2), build(:issue, iid: 3)] end it { is_expected.to eq('#1, #2, and #3') } + + context 'for JIRA issues' do + let(:project) { create(:project) } + let(:issues) do + [ + JiraIssue.new('JIRA-123', project), + JiraIssue.new('JIRA-456', project), + JiraIssue.new('FOOBAR-7890', project) + ] + end + + it { is_expected.to eq('FOOBAR-7890, JIRA-123, and JIRA-456') } + end end - describe "#format_mr_branch_names" do - describe "within the same project" do + describe '#format_mr_branch_names' do + describe 'within the same project' do let(:merge_request) { create(:merge_request) } subject { format_mr_branch_names(merge_request) } it { is_expected.to eq([merge_request.source_branch, merge_request.target_branch]) } end - describe "within different projects" do + describe 'within different projects' do let(:project) { create(:project) } let(:fork_project) { create(:project, forked_from_project: project) } let(:merge_request) { create(:merge_request, source_project: fork_project, target_project: project) } diff --git a/spec/helpers/page_layout_helper_spec.rb b/spec/helpers/page_layout_helper_spec.rb new file mode 100644 index 00000000000..fd7107779f6 --- /dev/null +++ b/spec/helpers/page_layout_helper_spec.rb @@ -0,0 +1,129 @@ +require 'rails_helper' + +describe PageLayoutHelper do + describe 'page_description' do + it 'defaults to value returned by page_description_default helper' do + allow(helper).to receive(:page_description_default).and_return('Foo') + + expect(helper.page_description).to eq 'Foo' + end + + it 'returns the last-pushed description' do + helper.page_description('Foo') + helper.page_description('Bar') + helper.page_description('Baz') + + expect(helper.page_description).to eq 'Baz' + end + + it 'squishes multiple newlines' do + helper.page_description("Foo\nBar\nBaz") + + expect(helper.page_description).to eq 'Foo Bar Baz' + end + + it 'truncates' do + helper.page_description <<-LOREM.strip_heredoc + Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo + ligula eget dolor. Aenean massa. Cum sociis natoque penatibus et magnis + dis parturient montes, nascetur ridiculus mus. Donec quam felis, + ultricies nec, pellentesque eu, pretium quis, sem. Nulla consequat massa + quis enim. Donec pede justo, fringilla vel, aliquet nec, vulputate eget, + arcu. + LOREM + + expect(helper.page_description).to end_with 'quam felis,...' + end + + it 'sanitizes all HTML' do + helper.page_description("<b>Bold</b> <h1>Header</h1>") + + expect(helper.page_description).to eq 'Bold Header' + end + end + + describe 'page_description_default' do + it 'uses Project description when available' do + project = double(description: 'Project Description') + helper.instance_variable_set(:@project, project) + + expect(helper.page_description_default).to eq 'Project Description' + end + + it 'uses brand_title when Project description is nil' do + project = double(description: nil) + helper.instance_variable_set(:@project, project) + + expect(helper).to receive(:brand_title).and_return('Brand Title') + expect(helper.page_description_default).to eq 'Brand Title' + end + + it 'falls back to brand_title' do + allow(helper).to receive(:brand_title).and_return('Brand Title') + + expect(helper.page_description_default).to eq 'Brand Title' + end + end + + describe 'page_image' do + it 'defaults to the GitLab logo' do + expect(helper.page_image).to end_with 'assets/gitlab_logo.png' + end + + context 'with @project' do + it 'uses Project avatar if available' do + project = double(avatar_url: 'http://example.com/uploads/avatar.png') + helper.instance_variable_set(:@project, project) + + expect(helper.page_image).to eq project.avatar_url + end + + it 'falls back to the default' do + project = double(avatar_url: nil) + helper.instance_variable_set(:@project, project) + + expect(helper.page_image).to end_with 'assets/gitlab_logo.png' + end + end + + context 'with @user' do + it 'delegates to avatar_icon helper' do + user = double('User') + helper.instance_variable_set(:@user, user) + + expect(helper).to receive(:avatar_icon).with(user) + + helper.page_image + end + end + end + + describe 'page_card_attributes' do + it 'raises ArgumentError when given more than two attributes' do + map = { foo: 'foo', bar: 'bar', baz: 'baz' } + + expect { helper.page_card_attributes(map) }. + to raise_error(ArgumentError, /more than two attributes/) + end + + it 'rejects blank values' do + map = { foo: 'foo', bar: '' } + helper.page_card_attributes(map) + + expect(helper.page_card_attributes).to eq({ foo: 'foo' }) + end + end + + describe 'page_card_meta_tags' do + it 'returns the twitter:label and twitter:data tags' do + allow(helper).to receive(:page_card_attributes).and_return(foo: 'bar') + + tags = helper.page_card_meta_tags + + aggregate_failures do + expect(tags).to include %q(<meta property="twitter:label1" content="foo" />) + expect(tags).to include %q(<meta property="twitter:data1" content="bar" />) + end + end + end +end diff --git a/spec/helpers/projects_helper_spec.rb b/spec/helpers/projects_helper_spec.rb index f2efb528aeb..53207767581 100644 --- a/spec/helpers/projects_helper_spec.rb +++ b/spec/helpers/projects_helper_spec.rb @@ -53,6 +53,16 @@ describe ProjectsHelper do end end + describe 'user_max_access_in_project' do + let(:project) { create(:project) } + let(:user) { create(:user) } + before do + project.team.add_user(user, Gitlab::Access::MASTER) + end + + it { expect(helper.user_max_access_in_project(user.id, project)).to eq('Master') } + end + describe "readme_cache_key" do let(:project) { create(:project) } diff --git a/spec/javascripts/fixtures/issues_show.html.haml b/spec/javascripts/fixtures/issues_show.html.haml index 8447dfdda32..470cabeafbb 100644 --- a/spec/javascripts/fixtures/issues_show.html.haml +++ b/spec/javascripts/fixtures/issues_show.html.haml @@ -1,4 +1,14 @@ -%a.btn-close +:css + .hidden { display: none !important; } + +.flash-container + .flash-alert + .flash-notice + +.status-box.status-box-open Open +.status-box.status-box-closed.hidden Closed +%a.btn-close{"href" => "http://gitlab.com/issues/6/close"} Close +%a.btn-reopen.hidden{"href" => "http://gitlab.com/issues/6/reopen"} Reopen .detail-page-description .description.js-task-list-container diff --git a/spec/javascripts/fixtures/new_branch.html.haml b/spec/javascripts/fixtures/new_branch.html.haml new file mode 100644 index 00000000000..f06629e5ecc --- /dev/null +++ b/spec/javascripts/fixtures/new_branch.html.haml @@ -0,0 +1,4 @@ +%form.js-create-branch-form + %input.js-branch-name + .js-branch-name-error + %input{id: "ref"} diff --git a/spec/javascripts/issue_spec.js.coffee b/spec/javascripts/issue_spec.js.coffee index 268e4c68c31..7e67c778861 100644 --- a/spec/javascripts/issue_spec.js.coffee +++ b/spec/javascripts/issue_spec.js.coffee @@ -20,3 +20,89 @@ describe 'Issue', -> expect(req.data.issue.description).not.toBe(null) $('.js-task-list-field').trigger('tasklist:changed') +describe 'reopen/close issue', -> + fixture.preload('issues_show.html') + beforeEach -> + fixture.load('issues_show.html') + @issue = new Issue() + it 'closes an issue', -> + $.ajax = (obj) -> + expect(obj.type).toBe('PUT') + expect(obj.url).toBe('http://gitlab.com/issues/6/close') + obj.success saved: true + + $btnClose = $('a.btn-close') + $btnReopen = $('a.btn-reopen') + expect($btnReopen).toBeHidden() + expect($btnClose.text()).toBe('Close') + expect(typeof $btnClose.prop('disabled')).toBe('undefined') + + $btnClose.trigger('click') + + expect($btnReopen).toBeVisible() + expect($btnClose).toBeHidden() + expect($('div.status-box-closed')).toBeVisible() + expect($('div.status-box-open')).toBeHidden() + + it 'fails to closes an issue with success:false', -> + + $.ajax = (obj) -> + expect(obj.type).toBe('PUT') + expect(obj.url).toBe('http://goesnowhere.nothing/whereami') + obj.success saved: false + + $btnClose = $('a.btn-close') + $btnReopen = $('a.btn-reopen') + $btnClose.attr('href','http://goesnowhere.nothing/whereami') + expect($btnReopen).toBeHidden() + expect($btnClose.text()).toBe('Close') + expect(typeof $btnClose.prop('disabled')).toBe('undefined') + + $btnClose.trigger('click') + + expect($btnReopen).toBeHidden() + expect($btnClose).toBeVisible() + expect($('div.status-box-closed')).toBeHidden() + expect($('div.status-box-open')).toBeVisible() + expect($('div.flash-alert')).toBeVisible() + expect($('div.flash-alert').text()).toBe('Unable to update this issue at this time.') + + it 'fails to closes an issue with HTTP error', -> + + $.ajax = (obj) -> + expect(obj.type).toBe('PUT') + expect(obj.url).toBe('http://goesnowhere.nothing/whereami') + obj.error() + + $btnClose = $('a.btn-close') + $btnReopen = $('a.btn-reopen') + $btnClose.attr('href','http://goesnowhere.nothing/whereami') + expect($btnReopen).toBeHidden() + expect($btnClose.text()).toBe('Close') + expect(typeof $btnClose.prop('disabled')).toBe('undefined') + + $btnClose.trigger('click') + + expect($btnReopen).toBeHidden() + expect($btnClose).toBeVisible() + expect($('div.status-box-closed')).toBeHidden() + expect($('div.status-box-open')).toBeVisible() + expect($('div.flash-alert')).toBeVisible() + expect($('div.flash-alert').text()).toBe('Unable to update this issue at this time.') + + it 'reopens an issue', -> + $.ajax = (obj) -> + expect(obj.type).toBe('PUT') + expect(obj.url).toBe('http://gitlab.com/issues/6/reopen') + obj.success saved: true + + $btnClose = $('a.btn-close') + $btnReopen = $('a.btn-reopen') + expect($btnReopen.text()).toBe('Reopen') + + $btnReopen.trigger('click') + + expect($btnReopen).toBeHidden() + expect($btnClose).toBeVisible() + expect($('div.status-box-open')).toBeVisible() + expect($('div.status-box-closed')).toBeHidden()
\ No newline at end of file diff --git a/spec/javascripts/new_branch_spec.js.coffee b/spec/javascripts/new_branch_spec.js.coffee new file mode 100644 index 00000000000..f2ce85efcdc --- /dev/null +++ b/spec/javascripts/new_branch_spec.js.coffee @@ -0,0 +1,160 @@ +#= require jquery-ui +#= require new_branch_form + +describe 'Branch', -> + describe 'create a new branch', -> + fixture.preload('new_branch.html') + + fillNameWith = (value) -> + $('.js-branch-name').val(value).trigger('blur') + + expectToHaveError = (error) -> + expect($('.js-branch-name-error span').text()).toEqual(error) + + beforeEach -> + fixture.load('new_branch.html') + $('form').on 'submit', (e) -> e.preventDefault() + + @form = new NewBranchForm($('.js-create-branch-form'), []) + + it "can't start with a dot", -> + fillNameWith '.foo' + expectToHaveError "can't start with '.'" + + it "can't start with a slash", -> + fillNameWith '/foo' + expectToHaveError "can't start with '/'" + + it "can't have two consecutive dots", -> + fillNameWith 'foo..bar' + expectToHaveError "can't contain '..'" + + it "can't have spaces anywhere", -> + fillNameWith ' foo' + expectToHaveError "can't contain spaces" + fillNameWith 'foo bar' + expectToHaveError "can't contain spaces" + fillNameWith 'foo ' + expectToHaveError "can't contain spaces" + + it "can't have ~ anywhere", -> + fillNameWith '~foo' + expectToHaveError "can't contain '~'" + fillNameWith 'foo~bar' + expectToHaveError "can't contain '~'" + fillNameWith 'foo~' + expectToHaveError "can't contain '~'" + + it "can't have tilde anwhere", -> + fillNameWith '~foo' + expectToHaveError "can't contain '~'" + fillNameWith 'foo~bar' + expectToHaveError "can't contain '~'" + fillNameWith 'foo~' + expectToHaveError "can't contain '~'" + + it "can't have caret anywhere", -> + fillNameWith '^foo' + expectToHaveError "can't contain '^'" + fillNameWith 'foo^bar' + expectToHaveError "can't contain '^'" + fillNameWith 'foo^' + expectToHaveError "can't contain '^'" + + it "can't have : anywhere", -> + fillNameWith ':foo' + expectToHaveError "can't contain ':'" + fillNameWith 'foo:bar' + expectToHaveError "can't contain ':'" + fillNameWith ':foo' + expectToHaveError "can't contain ':'" + + it "can't have question mark anywhere", -> + fillNameWith '?foo' + expectToHaveError "can't contain '?'" + fillNameWith 'foo?bar' + expectToHaveError "can't contain '?'" + fillNameWith 'foo?' + expectToHaveError "can't contain '?'" + + it "can't have asterisk anywhere", -> + fillNameWith '*foo' + expectToHaveError "can't contain '*'" + fillNameWith 'foo*bar' + expectToHaveError "can't contain '*'" + fillNameWith 'foo*' + expectToHaveError "can't contain '*'" + + it "can't have open bracket anywhere", -> + fillNameWith '[foo' + expectToHaveError "can't contain '['" + fillNameWith 'foo[bar' + expectToHaveError "can't contain '['" + fillNameWith 'foo[' + expectToHaveError "can't contain '['" + + it "can't have a backslash anywhere", -> + fillNameWith '\\foo' + expectToHaveError "can't contain '\\'" + fillNameWith 'foo\\bar' + expectToHaveError "can't contain '\\'" + fillNameWith 'foo\\' + expectToHaveError "can't contain '\\'" + + it "can't contain a sequence @{ anywhere", -> + fillNameWith '@{foo' + expectToHaveError "can't contain '@{'" + fillNameWith 'foo@{bar' + expectToHaveError "can't contain '@{'" + fillNameWith 'foo@{' + expectToHaveError "can't contain '@{'" + + it "can't have consecutive slashes", -> + fillNameWith 'foo//bar' + expectToHaveError "can't contain consecutive slashes" + + it "can't end with a slash", -> + fillNameWith 'foo/' + expectToHaveError "can't end in '/'" + + it "can't end with a dot", -> + fillNameWith 'foo.' + expectToHaveError "can't end in '.'" + + it "can't end with .lock", -> + fillNameWith 'foo.lock' + expectToHaveError "can't end in '.lock'" + + it "can't be the single character @", -> + fillNameWith '@' + expectToHaveError "can't be '@'" + + it "concatenates all error messages", -> + fillNameWith '/foo bar?~.' + expectToHaveError "can't start with '/', can't contain spaces, '?', '~', can't end in '.'" + + it "doesn't duplicate error messages", -> + fillNameWith '?foo?bar?zoo?' + expectToHaveError "can't contain '?'" + + it "removes the error message when is a valid name", -> + fillNameWith 'foo?bar' + expect($('.js-branch-name-error span').length).toEqual(1) + fillNameWith 'foobar' + expect($('.js-branch-name-error span').length).toEqual(0) + + it "can have dashes anywhere", -> + fillNameWith '-foo-bar-zoo-' + expect($('.js-branch-name-error span').length).toEqual(0) + + it "can have underscores anywhere", -> + fillNameWith '_foo_bar_zoo_' + expect($('.js-branch-name-error span').length).toEqual(0) + + it "can have numbers anywhere", -> + fillNameWith '1foo2bar3zoo4' + expect($('.js-branch-name-error span').length).toEqual(0) + + it "can be only letters", -> + fillNameWith 'foo' + expect($('.js-branch-name-error span').length).toEqual(0) diff --git a/spec/lib/banzai/filter/user_reference_filter_spec.rb b/spec/lib/banzai/filter/user_reference_filter_spec.rb index 3534bf97784..8bdebae1841 100644 --- a/spec/lib/banzai/filter/user_reference_filter_spec.rb +++ b/spec/lib/banzai/filter/user_reference_filter_spec.rb @@ -37,9 +37,22 @@ describe Banzai::Filter::UserReferenceFilter, lib: true do .to eq urls.namespace_project_url(project.namespace, project) end - it 'adds to the results hash' do - result = reference_pipeline_result("Hey #{reference}") - expect(result[:references][:user]).to eq [project.creator] + context "when the author is a member of the project" do + + it 'adds to the results hash' do + result = reference_pipeline_result("Hey #{reference}", author: project.creator) + expect(result[:references][:user]).to eq [project.creator] + end + end + + context "when the author is not a member of the project" do + + let(:other_user) { create(:user) } + + it "doesn't add to the results hash" do + result = reference_pipeline_result("Hey #{reference}", author: other_user) + expect(result[:references][:user]).to eq [] + end end end diff --git a/spec/lib/gitlab/ldap/user_spec.rb b/spec/lib/gitlab/ldap/user_spec.rb index 3bba5e2efa2..1e755259dae 100644 --- a/spec/lib/gitlab/ldap/user_spec.rb +++ b/spec/lib/gitlab/ldap/user_spec.rb @@ -42,6 +42,21 @@ describe Gitlab::LDAP::User, lib: true do end end + describe '.find_by_uid_and_provider' do + it 'retrieves the correct user' do + special_info = { + name: 'John Åström', + email: 'john@example.com', + nickname: 'jastrom' + } + special_hash = OmniAuth::AuthHash.new(uid: 'CN=John Åström,CN=Users,DC=Example,DC=com', provider: 'ldapmain', info: special_info) + special_chars_user = described_class.new(special_hash) + user = special_chars_user.save + + expect(described_class.find_by_uid_and_provider(special_hash.uid, special_hash.provider)).to eq user + end + end + describe :find_or_create do it "finds the user if already existing" do create(:omniauth_user, extern_uid: 'my-uid', provider: 'ldapmain') diff --git a/spec/lib/gitlab/reference_extractor_spec.rb b/spec/lib/gitlab/reference_extractor_spec.rb index 66dc5d4911d..7d963795e17 100644 --- a/spec/lib/gitlab/reference_extractor_spec.rb +++ b/spec/lib/gitlab/reference_extractor_spec.rb @@ -97,6 +97,16 @@ describe Gitlab::ReferenceExtractor, lib: true do expect(extracted.first.commit_to).to eq commit end + context 'with an external issue tracker' do + let(:project) { create(:jira_project) } + subject { described_class.new(project, project.creator) } + + it 'returns JIRA issues for a JIRA-integrated project' do + subject.analyze('JIRA-123 and FOOBAR-4567') + expect(subject.issues).to eq [JiraIssue.new('JIRA-123', project), JiraIssue.new('FOOBAR-4567', project)] + end + end + context 'with a project with an underscore' do let(:other_project) { create(:project, path: 'test_project') } let(:issue) { create(:issue, project: other_project) } diff --git a/spec/models/application_setting_spec.rb b/spec/models/application_setting_spec.rb index 5f64453a35f..35d8220ae54 100644 --- a/spec/models/application_setting_spec.rb +++ b/spec/models/application_setting_spec.rb @@ -27,6 +27,7 @@ # admin_notification_email :string(255) # shared_runners_enabled :boolean default(TRUE), not null # max_artifacts_size :integer default(100), not null +# runners_registration_token :string(255) # require 'spec_helper' diff --git a/spec/models/build_spec.rb b/spec/models/build_spec.rb index 96b6f1dbca6..1c22e3cb7c4 100644 --- a/spec/models/build_spec.rb +++ b/spec/models/build_spec.rb @@ -189,6 +189,12 @@ describe Ci::Build, models: true do it { is_expected.to eq(98.29) } end + + context 'using a regex capture' do + subject { build.extract_coverage('TOTAL 9926 3489 65%', 'TOTAL\s+\d+\s+\d+\s+(\d{1,3}\%)') } + + it { is_expected.to eq(65) } + end end describe :variables do @@ -390,4 +396,68 @@ describe Ci::Build, models: true do it { is_expected.to include('gitlab-ci-token') } it { is_expected.to include(project.web_url[7..-1]) } end + + def create_mr(build, commit, factory: :merge_request, created_at: Time.now) + FactoryGirl.create(factory, + source_project_id: commit.gl_project_id, + target_project_id: commit.gl_project_id, + source_branch: build.ref, + created_at: created_at) + end + + describe :merge_request do + context 'when a MR has a reference to the commit' do + before do + @merge_request = create_mr(build, commit, factory: :merge_request) + + commits = [double(id: commit.sha)] + allow(@merge_request).to receive(:commits).and_return(commits) + allow(MergeRequest).to receive_message_chain(:includes, :where, :reorder).and_return([@merge_request]) + end + + it 'returns the single associated MR' do + expect(build.merge_request.id).to eq(@merge_request.id) + end + end + + context 'when there is not a MR referencing the commit' do + it 'returns nil' do + expect(build.merge_request).to be_nil + end + end + + context 'when more than one MR have a reference to the commit' do + before do + @merge_request = create_mr(build, commit, factory: :merge_request) + @merge_request.close! + @merge_request2 = create_mr(build, commit, factory: :merge_request) + + commits = [double(id: commit.sha)] + allow(@merge_request).to receive(:commits).and_return(commits) + allow(@merge_request2).to receive(:commits).and_return(commits) + allow(MergeRequest).to receive_message_chain(:includes, :where, :reorder).and_return([@merge_request, @merge_request2]) + end + + it 'returns the first MR' do + expect(build.merge_request.id).to eq(@merge_request.id) + end + end + + context 'when a Build is created after the MR' do + before do + @merge_request = create_mr(build, commit, factory: :merge_request_with_diffs) + commit2 = FactoryGirl.create :ci_commit, project: project + @build2 = FactoryGirl.create :ci_build, commit: commit2 + + commits = [double(id: commit.sha), double(id: commit2.sha)] + allow(@merge_request).to receive(:commits).and_return(commits) + allow(MergeRequest).to receive_message_chain(:includes, :where, :reorder).and_return([@merge_request]) + end + + it 'returns the current MR' do + expect(@build2.merge_request.id).to eq(@merge_request.id) + end + end + + end end diff --git a/spec/models/concerns/issuable_spec.rb b/spec/models/concerns/issuable_spec.rb index 0f13c4410cd..f9d3c56750f 100644 --- a/spec/models/concerns/issuable_spec.rb +++ b/spec/models/concerns/issuable_spec.rb @@ -81,4 +81,22 @@ describe Issue, "Issuable" do expect(hook_data[:object_attributes]).to eq(issue.hook_attrs) end end + + describe '#card_attributes' do + it 'includes the author name' do + allow(issue).to receive(:author).and_return(double(name: 'Robert')) + allow(issue).to receive(:assignee).and_return(nil) + + expect(issue.card_attributes). + to eq({ 'Author' => 'Robert', 'Assignee' => nil }) + end + + it 'includes the assignee name' do + allow(issue).to receive(:author).and_return(double(name: 'Robert')) + allow(issue).to receive(:assignee).and_return(double(name: 'Douwe')) + + expect(issue.card_attributes). + to eq({ 'Author' => 'Robert', 'Assignee' => 'Douwe' }) + end + end end diff --git a/spec/models/concerns/mentionable_spec.rb b/spec/models/concerns/mentionable_spec.rb index 6179882e935..20f0c561e44 100644 --- a/spec/models/concerns/mentionable_spec.rb +++ b/spec/models/concerns/mentionable_spec.rb @@ -1,5 +1,22 @@ require 'spec_helper' +describe Mentionable do + include Mentionable + + def author + nil + end + + describe :references do + let(:project) { create(:project) } + + it 'excludes JIRA references' do + allow(project).to receive_messages(jira_tracker?: true) + expect(referenced_mentionables(project, 'JIRA-123')).to be_empty + end + end +end + describe Issue, "Mentionable" do describe '#mentioned_users' do let!(:user) { create(:user, username: 'stranger') } diff --git a/spec/models/concerns/token_authenticatable_spec.rb b/spec/models/concerns/token_authenticatable_spec.rb index a9b0b64e5de..30c0a04b840 100644 --- a/spec/models/concerns/token_authenticatable_spec.rb +++ b/spec/models/concerns/token_authenticatable_spec.rb @@ -2,7 +2,8 @@ require 'spec_helper' shared_examples 'TokenAuthenticatable' do describe 'dynamically defined methods' do - it { expect(described_class).to be_private_method_defined(:generate_token_for) } + it { expect(described_class).to be_private_method_defined(:generate_token) } + it { expect(described_class).to be_private_method_defined(:write_new_token) } it { expect(described_class).to respond_to("find_by_#{token_field}") } it { is_expected.to respond_to("ensure_#{token_field}") } it { is_expected.to respond_to("reset_#{token_field}!") } @@ -24,11 +25,11 @@ describe ApplicationSetting, 'TokenAuthenticatable' do it_behaves_like 'TokenAuthenticatable' describe 'generating new token' do - subject { described_class.new } - let(:token) { subject.send(token_field) } - context 'token is not generated yet' do - it { expect(token).to be nil } + describe 'token field accessor' do + subject { described_class.new.send(token_field) } + it { is_expected.to_not be_blank } + end describe 'ensured token' do subject { described_class.new.send("ensure_#{token_field}") } @@ -36,11 +37,21 @@ describe ApplicationSetting, 'TokenAuthenticatable' do it { is_expected.to be_a String } it { is_expected.to_not be_blank } end + + describe 'ensured! token' do + subject { described_class.new.send("ensure_#{token_field}!") } + + it 'should persist new token' do + expect(subject).to eq described_class.current[token_field] + end + end end context 'token is generated' do before { subject.send("reset_#{token_field}!") } - it { expect(token).to be_a String } + it 'persists a new token 'do + expect(subject.send(:read_attribute, token_field)).to be_a String + end end end diff --git a/spec/models/global_milestone_spec.rb b/spec/models/global_milestone_spec.rb index ba03e6aabd0..197c99cd007 100644 --- a/spec/models/global_milestone_spec.rb +++ b/spec/models/global_milestone_spec.rb @@ -62,4 +62,14 @@ describe GlobalMilestone, models: true do expect(@global_milestone.milestones.count).to eq(3) end end + + describe :safe_title do + let(:milestone) { create(:milestone, title: "git / test", project: project1) } + + it 'should strip out slashes and spaces' do + global_milestone = GlobalMilestone.new(milestone.title, [milestone]) + + expect(global_milestone.safe_title).to eq('git-test') + end + end end diff --git a/spec/models/jira_issue_spec.rb b/spec/models/jira_issue_spec.rb new file mode 100644 index 00000000000..1634265b439 --- /dev/null +++ b/spec/models/jira_issue_spec.rb @@ -0,0 +1,30 @@ +require 'spec_helper' + +describe JiraIssue do + let(:project) { create(:project) } + subject { JiraIssue.new('JIRA-123', project) } + + describe 'id' do + subject { super().id } + it { is_expected.to eq('JIRA-123') } + end + + describe 'iid' do + subject { super().iid } + it { is_expected.to eq('JIRA-123') } + end + + describe 'to_s' do + subject { super().to_s } + it { is_expected.to eq('JIRA-123') } + end + + describe :== do + specify { expect(subject).to eq(JiraIssue.new('JIRA-123', project)) } + specify { expect(subject).not_to eq(JiraIssue.new('JIRA-124', project)) } + + it 'only compares with JiraIssues' do + expect(subject).not_to eq('JIRA-123') + end + end +end diff --git a/spec/models/merge_request_spec.rb b/spec/models/merge_request_spec.rb index 1aeba9b2b3b..e0653a8327d 100644 --- a/spec/models/merge_request_spec.rb +++ b/spec/models/merge_request_spec.rb @@ -164,6 +164,17 @@ describe MergeRequest, models: true do expect(subject.closes_issues).to include(issue2) end + + context 'for a project with JIRA integration' do + let(:issue0) { JiraIssue.new('JIRA-123', subject.project) } + let(:issue1) { JiraIssue.new('FOOBAR-4567', subject.project) } + + it 'returns sorted JiraIssues' do + allow(subject.project).to receive_messages(default_branch: subject.target_branch) + + expect(subject.closes_issues).to eq([issue0, issue1]) + end + end end describe "#work_in_progress?" do diff --git a/spec/models/note_spec.rb b/spec/models/note_spec.rb index 216c7dabae0..593d8f76215 100644 --- a/spec/models/note_spec.rb +++ b/spec/models/note_spec.rb @@ -137,9 +137,14 @@ describe Note, models: true do create :note, note: "smile", is_award: true end - it "returns grouped array of notes" do - expect(Note.grouped_awards.first.first).to eq("smile") - expect(Note.grouped_awards.first.last).to match_array(Note.all) + it "returns grouped hash of notes" do + expect(Note.grouped_awards.keys.size).to eq(3) + expect(Note.grouped_awards["smile"]).to match_array(Note.all) + end + + it "returns thumbsup and thumbsdown always" do + expect(Note.grouped_awards["thumbsup"]).to match_array(Note.none) + expect(Note.grouped_awards["thumbsdown"]).to match_array(Note.none) end end @@ -164,8 +169,8 @@ describe Note, models: true do let(:issue) { create :issue } it "converts aliases to actual name" do - note = create :note, note: ":thumbsup:", noteable: issue - expect(note.reload.note).to eq("+1") + note = create :note, note: ":+1:", noteable: issue + expect(note.reload.note).to eq("thumbsup") end end end diff --git a/spec/models/project_services/jira_service_spec.rb b/spec/models/project_services/jira_service_spec.rb index 7d91ebe9ce6..2f8193170ae 100644 --- a/spec/models/project_services/jira_service_spec.rb +++ b/spec/models/project_services/jira_service_spec.rb @@ -26,6 +26,113 @@ describe JiraService, models: true do it { is_expected.to have_one :service_hook } end + describe "Execute" do + let(:user) { create(:user) } + let(:project) { create(:project) } + let(:merge_request) { create(:merge_request) } + + before do + @jira_service = JiraService.new + allow(@jira_service).to receive_messages( + project_id: project.id, + project: project, + service_hook: true, + project_url: 'http://jira.example.com', + username: 'gitlab_jira_username', + password: 'gitlab_jira_password' + ) + @jira_service.save # will build API URL, as api_url was not specified above + @sample_data = Gitlab::PushDataBuilder.build_sample(project, user) + # https://github.com/bblimke/webmock#request-with-basic-authentication + @api_url = 'http://gitlab_jira_username:gitlab_jira_password@jira.example.com/rest/api/2/issue/JIRA-123/transitions' + @comment_url = 'http://gitlab_jira_username:gitlab_jira_password@jira.example.com/rest/api/2/issue/JIRA-123/comment' + + WebMock.stub_request(:post, @api_url) + WebMock.stub_request(:post, @comment_url) + end + + it "should call JIRA API" do + @jira_service.execute(merge_request, JiraIssue.new("JIRA-123", project)) + expect(WebMock).to have_requested(:post, @comment_url).with( + body: /Issue solved with/ + ).once + end + + it "calls the api with jira_issue_transition_id" do + @jira_service.jira_issue_transition_id = 'this-is-a-custom-id' + @jira_service.execute(merge_request, JiraIssue.new("JIRA-123", project)) + expect(WebMock).to have_requested(:post, @api_url).with( + body: /this-is-a-custom-id/ + ).once + end + end + + describe "Stored password invalidation" do + let(:project) { create(:project) } + + context "when a password was previously set" do + before do + @jira_service = JiraService.create( + project: create(:project), + properties: { + api_url: 'http://jira.example.com/rest/api/2', + username: 'mic', + password: "password" + } + ) + end + + it "reset password if url changed" do + @jira_service.api_url = 'http://jira_edited.example.com/rest/api/2' + @jira_service.save + expect(@jira_service.password).to be_nil + end + + it "does not reset password if username changed" do + @jira_service.username = "some_name" + @jira_service.save + expect(@jira_service.password).to eq("password") + end + + it "does not reset password if new url is set together with password, even if it's the same password" do + @jira_service.api_url = 'http://jira_edited.example.com/rest/api/2' + @jira_service.password = 'password' + @jira_service.save + expect(@jira_service.password).to eq("password") + expect(@jira_service.api_url).to eq("http://jira_edited.example.com/rest/api/2") + end + + it "should reset password if url changed, even if setter called multiple times" do + @jira_service.api_url = 'http://jira1.example.com/rest/api/2' + @jira_service.api_url = 'http://jira1.example.com/rest/api/2' + @jira_service.save + expect(@jira_service.password).to be_nil + end + end + + context "when no password was previously set" do + before do + @jira_service = JiraService.create( + project: create(:project), + properties: { + api_url: 'http://jira.example.com/rest/api/2', + username: 'mic' + } + ) + end + + it "saves password if new url is set together with password" do + @jira_service.api_url = 'http://jira_edited.example.com/rest/api/2' + @jira_service.password = 'password' + @jira_service.save + expect(@jira_service.password).to eq("password") + expect(@jira_service.api_url).to eq("http://jira_edited.example.com/rest/api/2") + end + + end + end + + describe "Validations" do context "active" do before do @@ -78,11 +185,12 @@ describe JiraService, models: true do context 'when gitlab.yml was initialized' do before do - settings = { "jira" => { - "title" => "Jira", - "project_url" => "http://jira.sample/projects/project_a", - "issues_url" => "http://jira.sample/issues/:id", - "new_issue_url" => "http://jira.sample/projects/project_a/issues/new" + settings = { + "jira" => { + "title" => "Jira", + "project_url" => "http://jira.sample/projects/project_a", + "issues_url" => "http://jira.sample/issues/:id", + "new_issue_url" => "http://jira.sample/projects/project_a/issues/new" } } allow(Gitlab.config).to receive(:issues_tracker).and_return(settings) diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb index c4d3813e9c9..400bdf2d962 100644 --- a/spec/models/project_spec.rb +++ b/spec/models/project_spec.rb @@ -552,4 +552,28 @@ describe Project, models: true do end end end + + describe '#visibility_level_allowed?' do + let(:project) { create :project, visibility_level: Gitlab::VisibilityLevel::INTERNAL } + + context 'when checking on non-forked project' do + it { expect(project.visibility_level_allowed?(Gitlab::VisibilityLevel::PRIVATE)).to be_truthy } + it { expect(project.visibility_level_allowed?(Gitlab::VisibilityLevel::INTERNAL)).to be_truthy } + it { expect(project.visibility_level_allowed?(Gitlab::VisibilityLevel::PUBLIC)).to be_truthy } + end + + context 'when checking on forked project' do + let(:forked_project) { create :forked_project_with_submodules } + + before do + forked_project.build_forked_project_link(forked_to_project_id: forked_project.id, forked_from_project_id: project.id) + forked_project.save + end + + it { expect(forked_project.visibility_level_allowed?(Gitlab::VisibilityLevel::PRIVATE)).to be_truthy } + it { expect(forked_project.visibility_level_allowed?(Gitlab::VisibilityLevel::INTERNAL)).to be_truthy } + it { expect(forked_project.visibility_level_allowed?(Gitlab::VisibilityLevel::PUBLIC)).to be_falsey } + end + + end end diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb index 376266c0955..2f184bbaf92 100644 --- a/spec/models/user_spec.rb +++ b/spec/models/user_spec.rb @@ -26,6 +26,7 @@ # bio :string(255) # failed_attempts :integer default(0) # locked_at :datetime +# unlock_token :string(255) # username :string(255) # can_create_group :boolean default(TRUE), not null # can_create_team :boolean default(TRUE), not null diff --git a/spec/requests/api/branches_spec.rb b/spec/requests/api/branches_spec.rb index 5c1b58535cc..36461e84c3a 100644 --- a/spec/requests/api/branches_spec.rb +++ b/spec/requests/api/branches_spec.rb @@ -118,7 +118,7 @@ describe API::API, api: true do branch_name: 'new design', ref: branch_sha expect(response.status).to eq(400) - expect(json_response['message']).to eq('Branch name invalid') + expect(json_response['message']).to eq('Branch name is invalid') end it 'should return 400 if branch already exists' do diff --git a/spec/requests/api/projects_spec.rb b/spec/requests/api/projects_spec.rb index 01d2ec79482..7f0f9454b10 100644 --- a/spec/requests/api/projects_spec.rb +++ b/spec/requests/api/projects_spec.rb @@ -131,6 +131,7 @@ describe API::API, api: true do expect(json_response).to satisfy do |response| response.one? do |entry| + entry.has_key?('permissions') && entry['name'] == project.name && entry['owner']['username'] == user.username end @@ -382,6 +383,18 @@ describe API::API, api: true do end describe 'permissions' do + context 'all projects' do + it 'Contains permission information' do + project.team << [user, :master] + get api("/projects", user) + + expect(response.status).to eq(200) + expect(json_response.first['permissions']['project_access']['access_level']). + to eq(Gitlab::Access::MASTER) + expect(json_response.first['permissions']['group_access']).to be_nil + end + end + context 'personal project' do it 'Sets project access and returns 200' do project.team << [user, :master] diff --git a/spec/requests/api/users_spec.rb b/spec/requests/api/users_spec.rb index 2f609c63330..4f278551d07 100644 --- a/spec/requests/api/users_spec.rb +++ b/spec/requests/api/users_spec.rb @@ -27,6 +27,13 @@ describe API::API, api: true do user['username'] == username end['username']).to eq(username) end + + it "should return one user" do + get api("/users?username=#{omniauth_user.username}", user) + expect(response.status).to eq(200) + expect(json_response).to be_an Array + expect(json_response.first['username']).to eq(omniauth_user.username) + end end context "when admin" do diff --git a/spec/requests/ci/api/runners_spec.rb b/spec/requests/ci/api/runners_spec.rb index 567da013e6f..5942aa7a1b5 100644 --- a/spec/requests/ci/api/runners_spec.rb +++ b/spec/requests/ci/api/runners_spec.rb @@ -8,7 +8,6 @@ describe Ci::API::API do before do stub_gitlab_calls - stub_application_setting(ensure_runners_registration_token: registration_token) stub_application_setting(runners_registration_token: registration_token) end diff --git a/spec/services/create_commit_builds_service_spec.rb b/spec/services/create_commit_builds_service_spec.rb index 40ba6549e1f..ea5dcfa068a 100644 --- a/spec/services/create_commit_builds_service_spec.rb +++ b/spec/services/create_commit_builds_service_spec.rb @@ -52,7 +52,7 @@ describe CreateCommitBuildsService, services: true do end end - it 'skips commits without .gitlab-ci.yml' do + it 'skips creating ci_commit for refs without .gitlab-ci.yml' do stub_ci_commit_yaml_file(nil) result = service.execute(project, user, ref: 'refs/heads/0_1', @@ -60,13 +60,11 @@ describe CreateCommitBuildsService, services: true do after: '31das312', commits: [{ message: 'Message' }] ) - expect(result).to be_persisted - expect(result.builds.any?).to be_falsey - expect(result.status).to eq('skipped') - expect(result.yaml_errors).to be_nil + expect(result).to be_falsey + expect(Ci::Commit.count).to eq(0) end - it 'skips commits if yaml is invalid' do + it 'fails commits if yaml is invalid' do message = 'message' allow_any_instance_of(Ci::Commit).to receive(:git_commit_message) { message } stub_ci_commit_yaml_file('invalid: file: file') @@ -77,6 +75,7 @@ describe CreateCommitBuildsService, services: true do after: '31das312', commits: commits ) + expect(commit).to be_persisted expect(commit.builds.any?).to be false expect(commit.status).to eq('failed') expect(commit.yaml_errors).to_not be_nil @@ -97,6 +96,7 @@ describe CreateCommitBuildsService, services: true do after: '31das312', commits: commits ) + expect(commit).to be_persisted expect(commit.builds.any?).to be false expect(commit.status).to eq("skipped") end @@ -112,6 +112,7 @@ describe CreateCommitBuildsService, services: true do commits: commits ) + expect(commit).to be_persisted expect(commit.builds.first.name).to eq("staging") end @@ -124,6 +125,7 @@ describe CreateCommitBuildsService, services: true do after: '31das312', commits: commits ) + expect(commit).to be_persisted expect(commit.builds.any?).to be false expect(commit.status).to eq("skipped") expect(commit.yaml_errors).to be_nil @@ -140,6 +142,7 @@ describe CreateCommitBuildsService, services: true do after: '31das312', commits: commits ) + expect(commit).to be_persisted expect(commit.builds.count(:all)).to eq(2) commit = service.execute(project, user, @@ -148,6 +151,7 @@ describe CreateCommitBuildsService, services: true do after: '31das312', commits: commits ) + expect(commit).to be_persisted expect(commit.builds.count(:all)).to eq(2) end @@ -163,6 +167,7 @@ describe CreateCommitBuildsService, services: true do commits: commits ) + expect(commit).to be_persisted expect(commit.status).to eq("failed") expect(commit.builds.any?).to be false end diff --git a/spec/services/git_push_service_spec.rb b/spec/services/git_push_service_spec.rb index a04c242cf0e..c1080ef190a 100644 --- a/spec/services/git_push_service_spec.rb +++ b/spec/services/git_push_service_spec.rb @@ -265,6 +265,75 @@ describe GitPushService, services: true do expect(Issue.find(issue.id)).to be_opened end end + + # EE-only tests + context "for jira issue tracker" do + include JiraServiceHelper + + let(:jira_tracker) { project.create_jira_service if project.jira_service.nil? } + + before do + jira_service_settings + + WebMock.stub_request(:post, jira_api_transition_url) + WebMock.stub_request(:post, jira_api_comment_url) + WebMock.stub_request(:get, jira_api_comment_url).to_return(body: jira_issue_comments) + WebMock.stub_request(:get, jira_api_test_url) + + allow(closing_commit).to receive_messages({ + issue_closing_regex: Regexp.new(Gitlab.config.gitlab.issue_closing_pattern), + safe_message: message, + author_name: commit_author.name, + author_email: commit_author.email + }) + + allow(project.repository).to receive_messages(commits_between: [closing_commit]) + end + + after do + jira_tracker.destroy! + end + + context "mentioning an issue" do + let(:message) { "this is some work.\n\nrelated to JIRA-1" } + + it "should initiate one api call to jira server to mention the issue" do + service.execute(project, user, @oldrev, @newrev, @ref) + + expect(WebMock).to have_requested(:post, jira_api_comment_url).with( + body: /mentioned this issue in/ + ).once + end + end + + context "closing an issue" do + let(:message) { "this is some work.\n\ncloses JIRA-1" } + + it "should initiate one api call to jira server to close the issue" do + transition_body = { + transition: { + id: '2' + } + }.to_json + + service.execute(project, user, @oldrev, @newrev, @ref) + expect(WebMock).to have_requested(:post, jira_api_transition_url).with( + body: transition_body + ).once + end + + it "should initiate one api call to jira server to comment on the issue" do + comment_body = { + body: "Issue solved with [#{closing_commit.id}|http://localhost/#{project.path_with_namespace}/commit/#{closing_commit.id}]." + }.to_json + + service.execute(project, user, @oldrev, @newrev, @ref) + expect(WebMock).to have_requested(:post, jira_api_comment_url).with( + body: comment_body + ).once + end + end + end end describe "empty project" do diff --git a/spec/services/notification_service_spec.rb b/spec/services/notification_service_spec.rb index d7a898e85ff..c103752198d 100644 --- a/spec/services/notification_service_spec.rb +++ b/spec/services/notification_service_spec.rb @@ -115,6 +115,7 @@ describe NotificationService, services: true do before do build_team(note.project) + note.project.team << [note.author, :master] ActionMailer::Base.deliveries.clear end @@ -126,6 +127,8 @@ describe NotificationService, services: true do note.project.team.members.each do |member| # User with disabled notification should not be notified next if member.id == @u_disabled.id + # Author should not be notified + next if member.id == note.author.id should_email(member) end diff --git a/spec/services/projects/update_service_spec.rb b/spec/services/projects/update_service_spec.rb index c36d4581989..3c06a890163 100644 --- a/spec/services/projects/update_service_spec.rb +++ b/spec/services/projects/update_service_spec.rb @@ -100,6 +100,45 @@ describe Projects::UpdateService, services: true do end end + describe :visibility_level do + let(:user) { create :user, admin: true } + let(:project) { create :project, visibility_level: Gitlab::VisibilityLevel::INTERNAL } + let(:forked_project) { create :forked_project_with_submodules, visibility_level: Gitlab::VisibilityLevel::INTERNAL } + let(:opts) { {} } + + before do + forked_project.build_forked_project_link(forked_to_project_id: forked_project.id, forked_from_project_id: project.id) + forked_project.save + + @created_internal = project.internal? + @fork_created_internal = forked_project.internal? + end + + context 'should update forks visibility level when parent set to more restrictive' do + before do + opts.merge!(visibility_level: Gitlab::VisibilityLevel::PRIVATE) + update_project(project, user, opts).inspect + end + + it { expect(@created_internal).to be_truthy } + it { expect(@fork_created_internal).to be_truthy } + it { expect(project.private?).to be_truthy } + it { expect(project.forks.first.private?).to be_truthy } + end + + context 'should not update forks visibility level when parent set to less restrictive' do + before do + opts.merge!(visibility_level: Gitlab::VisibilityLevel::PUBLIC) + update_project(project, user, opts).inspect + end + + it { expect(@created_internal).to be_truthy } + it { expect(@fork_created_internal).to be_truthy } + it { expect(project.public?).to be_truthy } + it { expect(project.forks.first.internal?).to be_truthy } + end + end + def update_project(project, user, opts) Projects::UpdateService.new(project, user, opts).execute end diff --git a/spec/services/system_note_service_spec.rb b/spec/services/system_note_service_spec.rb index 0a4f9b230e8..c9f828ae2f7 100644 --- a/spec/services/system_note_service_spec.rb +++ b/spec/services/system_note_service_spec.rb @@ -425,4 +425,65 @@ describe SystemNoteService, services: true do end end end + + include JiraServiceHelper + + describe 'JIRA integration' do + let(:project) { create(:project) } + let(:author) { create(:user) } + let(:issue) { create(:issue, project: project) } + let(:mergereq) { create(:merge_request, :simple, target_project: project, source_project: project) } + let(:jira_issue) { JiraIssue.new("JIRA-1", project)} + let(:jira_tracker) { project.create_jira_service if project.jira_service.nil? } + let(:commit) { project.commit } + + context 'in JIRA issue tracker' do + before do + jira_service_settings + WebMock.stub_request(:post, jira_api_comment_url) + end + + after do + jira_tracker.destroy! + end + + describe "new reference" do + before do + WebMock.stub_request(:get, jira_api_comment_url).to_return(body: jira_issue_comments) + end + + subject { described_class.cross_reference(jira_issue, commit, author) } + + it { is_expected.to eq(jira_status_message) } + end + + describe "existing reference" do + before do + message = "[#{author.name}|http://localhost/u/#{author.username}] mentioned this issue in [a commit of #{project.path_with_namespace}|http://localhost/#{project.path_with_namespace}/commit/#{commit.id}]." + WebMock.stub_request(:get, jira_api_comment_url).to_return(body: "{\"comments\":[{\"body\":\"#{message}\"}]}") + end + + subject { described_class.cross_reference(jira_issue, commit, author) } + it { is_expected.not_to eq(jira_status_message) } + end + end + + context 'issue from an issue' do + context 'in JIRA issue tracker' do + before do + jira_service_settings + WebMock.stub_request(:post, jira_api_comment_url) + WebMock.stub_request(:get, jira_api_comment_url).to_return(body: jira_issue_comments) + end + + after do + jira_tracker.destroy! + end + + subject { described_class.cross_reference(jira_issue, issue, author) } + + it { is_expected.to eq(jira_status_message) } + end + end + end end diff --git a/spec/support/jira_service_helper.rb b/spec/support/jira_service_helper.rb new file mode 100644 index 00000000000..a3f496359b1 --- /dev/null +++ b/spec/support/jira_service_helper.rb @@ -0,0 +1,67 @@ +module JiraServiceHelper + + def jira_service_settings + properties = { + "title"=>"JIRA tracker", + "project_url"=>"http://jira.example/issues/?jql=project=A", + "issues_url"=>"http://jira.example/browse/JIRA-1", + "new_issue_url"=>"http://jira.example/secure/CreateIssue.jspa", + "api_url"=>"http://jira.example/rest/api/2" + } + + jira_tracker.update_attributes(properties: properties, active: true) + end + + def jira_status_message + "JiraService SUCCESS 200: Successfully posted to #{jira_api_comment_url}." + end + + def jira_issue_comments + "{\"startAt\":0,\"maxResults\":11,\"total\":11, + \"comments\":[{\"self\":\"http://0.0.0.0:4567/rest/api/2/issue/10002/comment/10609\", + \"id\":\"10609\",\"author\":{\"self\":\"http://0.0.0.0:4567/rest/api/2/user?username=gitlab\", + \"name\":\"gitlab\",\"emailAddress\":\"gitlab@example.com\", + \"avatarUrls\":{\"16x16\":\"http://0.0.0.0:4567/secure/useravatar?size=xsmall&avatarId=10122\", + \"24x24\":\"http://0.0.0.0:4567/secure/useravatar?size=small&avatarId=10122\", + \"32x32\":\"http://0.0.0.0:4567/secure/useravatar?size=medium&avatarId=10122\", + \"48x48\":\"http://0.0.0.0:4567/secure/useravatar?avatarId=10122\"}, + \"displayName\":\"GitLab\",\"active\":true}, + \"body\":\"[Administrator|http://localhost:3000/u/root] mentioned JIRA-1 in Merge request of [gitlab-org/gitlab-test|http://localhost:3000/gitlab-org/gitlab-test/merge_requests/2].\", + \"updateAuthor\":{\"self\":\"http://0.0.0.0:4567/rest/api/2/user?username=gitlab\",\"name\":\"gitlab\",\"emailAddress\":\"gitlab@example.com\", + \"avatarUrls\":{\"16x16\":\"http://0.0.0.0:4567/secure/useravatar?size=xsmall&avatarId=10122\", + \"24x24\":\"http://0.0.0.0:4567/secure/useravatar?size=small&avatarId=10122\", + \"32x32\":\"http://0.0.0.0:4567/secure/useravatar?size=medium&avatarId=10122\", + \"48x48\":\"http://0.0.0.0:4567/secure/useravatar?avatarId=10122\"},\"displayName\":\"GitLab\",\"active\":true}, + \"created\":\"2015-02-12T22:47:07.826+0100\", + \"updated\":\"2015-02-12T22:47:07.826+0100\"}, + {\"self\":\"http://0.0.0.0:4567/rest/api/2/issue/10002/comment/10700\", + \"id\":\"10700\",\"author\":{\"self\":\"http://0.0.0.0:4567/rest/api/2/user?username=gitlab\", + \"name\":\"gitlab\",\"emailAddress\":\"gitlab@example.com\", + \"avatarUrls\":{\"16x16\":\"http://0.0.0.0:4567/secure/useravatar?size=xsmall&avatarId=10122\", + \"24x24\":\"http://0.0.0.0:4567/secure/useravatar?size=small&avatarId=10122\", + \"32x32\":\"http://0.0.0.0:4567/secure/useravatar?size=medium&avatarId=10122\", + \"48x48\":\"http://0.0.0.0:4567/secure/useravatar?avatarId=10122\"},\"displayName\":\"GitLab\",\"active\":true}, + \"body\":\"[Administrator|http://localhost:3000/u/root] mentioned this issue in [a commit of h5bp/html5-boilerplate|http://localhost:3000/h5bp/html5-boilerplate/commit/2439f77897122fbeee3bfd9bb692d3608848433e].\", + \"updateAuthor\":{\"self\":\"http://0.0.0.0:4567/rest/api/2/user?username=gitlab\",\"name\":\"gitlab\",\"emailAddress\":\"gitlab@example.com\", + \"avatarUrls\":{\"16x16\":\"http://0.0.0.0:4567/secure/useravatar?size=xsmall&avatarId=10122\", + \"24x24\":\"http://0.0.0.0:4567/secure/useravatar?size=small&avatarId=10122\", + \"32x32\":\"http://0.0.0.0:4567/secure/useravatar?size=medium&avatarId=10122\", + \"48x48\":\"http://0.0.0.0:4567/secure/useravatar?avatarId=10122\"},\"displayName\":\"GitLab\",\"active\":true}, + \"created\":\"2015-04-01T03:45:55.667+0200\", + \"updated\":\"2015-04-01T03:45:55.667+0200\" + } + ]}" + end + + def jira_api_comment_url + 'http://jira.example/rest/api/2/issue/JIRA-1/comment' + end + + def jira_api_transition_url + 'http://jira.example/rest/api/2/issue/JIRA-1/transitions' + end + + def jira_api_test_url + 'http://jira.example/rest/api/2/myself' + end +end |