diff options
author | Tim Zallmann <tzallmann@gitlab.com> | 2017-06-21 09:39:30 +0200 |
---|---|---|
committer | Tim Zallmann <tzallmann@gitlab.com> | 2017-06-21 09:39:30 +0200 |
commit | 1578ee93475c0ebb62399403d971e99aa45e7276 (patch) | |
tree | 9664baf0f488e2f6dcf2e6d20305331c5efd3966 /spec | |
parent | 278bf413bf4e395e268aa6bacade828e9f58d064 (diff) | |
parent | 6d859009efb4c9c865b63f074d07461d0558fd2a (diff) | |
download | gitlab-ce-1578ee93475c0ebb62399403d971e99aa45e7276.tar.gz |
Merge branch 'master' of gitlab.com:gitlab-org/gitlab-ce
Diffstat (limited to 'spec')
25 files changed, 490 insertions, 180 deletions
diff --git a/spec/features/issues/bulk_assignment_labels_spec.rb b/spec/features/issues/bulk_assignment_labels_spec.rb index 2eb04df3cb3..a99c19cb787 100644 --- a/spec/features/issues/bulk_assignment_labels_spec.rb +++ b/spec/features/issues/bulk_assignment_labels_spec.rb @@ -16,6 +16,21 @@ feature 'Issues > Labels bulk assignment', feature: true do gitlab_sign_in user end + context 'sidebar' do + before do + enable_bulk_update + end + + it 'is present when bulk edit is enabled' do + expect(page).to have_css('.issuable-sidebar') + end + + it 'is not present when bulk edit is disabled' do + disable_bulk_update + expect(page).not_to have_css('.issuable-sidebar') + end + end + context 'can bulk assign' do before do enable_bulk_update @@ -398,4 +413,8 @@ feature 'Issues > Labels bulk assignment', feature: true do visit namespace_project_issues_path(project.namespace, project) click_button 'Edit Issues' end + + def disable_bulk_update + click_button 'Cancel' + end end diff --git a/spec/features/reportable_note/snippets_spec.rb b/spec/features/reportable_note/snippets_spec.rb index a88990eada0..5bee4a31379 100644 --- a/spec/features/reportable_note/snippets_spec.rb +++ b/spec/features/reportable_note/snippets_spec.rb @@ -19,15 +19,4 @@ describe 'Reportable note on snippets', :feature, :js do it_behaves_like 'reportable note' end - - describe 'on personal snippet' do - let(:snippet) { create(:personal_snippet, :public, author: user) } - let!(:note) { create(:note_on_personal_snippet, noteable: snippet, author: user) } - - before do - visit snippet_path(snippet) - end - - it_behaves_like 'reportable note' - end end diff --git a/spec/fixtures/emails/html_empty_link.eml b/spec/fixtures/emails/html_empty_link.eml new file mode 100644 index 00000000000..1672b98b925 --- /dev/null +++ b/spec/fixtures/emails/html_empty_link.eml @@ -0,0 +1,26 @@ + +MIME-Version: 1.0 +Received: by 10.25.161.144 with HTTP; Tue, 7 Oct 2014 22:17:17 -0700 (PDT) +X-Originating-IP: [117.207.85.84] +In-Reply-To: <5434c8b52bb3a_623ff09fec70f049749@discourse-app.mail> +References: <topic/35@discourse.techapj.com> + <5434c8b52bb3a_623ff09fec70f049749@discourse-app.mail> +Date: Wed, 8 Oct 2014 10:47:17 +0530 +Delivered-To: arpit@techapj.com +Message-ID: <CAOJeqne=SJ_LwN4sb-0Y95ejc2OpreVhdmcPn0TnmwSvTCYzzQ@mail.gmail.com> +Subject: Re: [Discourse] [Meta] Welcome to techAPJ's Discourse! +From: Arpit Jalan <arpit@techapj.com> +To: Discourse <mail+e1c7f2a380e33840aeb654f075490bad@arpitjalan.com>Accept-Language: en-US +Content-Language: en-US +X-MS-Has-Attach: +X-MS-TNEF-Correlator: +x-originating-ip: [134.68.31.227] +Content-Type: multipart/alternative; + boundary="_000_B0DFE1BEB3739743BC9B639D0E6BC8FF217A6341IUMSSGMBX104ads_" +MIME-Version: 1.0 + +--_000_B0DFE1BEB3739743BC9B639D0E6BC8FF217A6341IUMSSGMBX104ads_ +Content-Type: text/html; charset="utf-8" + +<a name="_MailEndCompose">no brackets!</a> +--_000_B0DFE1BEB3739743BC9B639D0E6BC8FF217A6341IUMSSGMBX104ads_-- diff --git a/spec/helpers/application_helper_spec.rb b/spec/helpers/application_helper_spec.rb index cc7f889b927..c1966c273db 100644 --- a/spec/helpers/application_helper_spec.rb +++ b/spec/helpers/application_helper_spec.rb @@ -82,42 +82,71 @@ describe ApplicationHelper do end describe 'avatar_icon' do - it 'returns an url for the avatar' do - user = create(:user, avatar: File.open(uploaded_image_temp_path)) - - avatar_url = "/uploads/system/user/avatar/#{user.id}/banana_sample.gif" - - expect(helper.avatar_icon(user.email).to_s).to match(avatar_url) - - allow(ActionController::Base).to receive(:asset_host).and_return(gitlab_host) - avatar_url = "#{gitlab_host}/uploads/system/user/avatar/#{user.id}/banana_sample.gif" - - expect(helper.avatar_icon(user.email).to_s).to match(avatar_url) - end - - it 'returns an url for the avatar with relative url' do - stub_config_setting(relative_url_root: '/gitlab') - # Must be stubbed after the stub above, and separately - stub_config_setting(url: Settings.send(:build_gitlab_url)) - - user = create(:user, avatar: File.open(uploaded_image_temp_path)) - - expect(helper.avatar_icon(user.email).to_s). - to match("/gitlab/uploads/system/user/avatar/#{user.id}/banana_sample.gif") - end + let(:user) { create(:user, avatar: File.open(uploaded_image_temp_path)) } + + context 'using an email' do + context 'when there is a matching user' do + it 'returns a relative URL for the avatar' do + expect(helper.avatar_icon(user.email).to_s). + to eq("/uploads/system/user/avatar/#{user.id}/banana_sample.gif") + end + + context 'when an asset_host is set in the config' do + let(:asset_host) { 'http://assets' } + + before do + allow(ActionController::Base).to receive(:asset_host).and_return(asset_host) + end + + it 'returns an absolute URL on that asset host' do + expect(helper.avatar_icon(user.email, only_path: false).to_s). + to eq("#{asset_host}/uploads/system/user/avatar/#{user.id}/banana_sample.gif") + end + end + + context 'when only_path is set to false' do + it 'returns an absolute URL for the avatar' do + expect(helper.avatar_icon(user.email, only_path: false).to_s). + to eq("#{gitlab_host}/uploads/system/user/avatar/#{user.id}/banana_sample.gif") + end + end + + context 'when the GitLab instance is at a relative URL' do + before do + stub_config_setting(relative_url_root: '/gitlab') + # Must be stubbed after the stub above, and separately + stub_config_setting(url: Settings.send(:build_gitlab_url)) + end + + it 'returns a relative URL with the correct prefix' do + expect(helper.avatar_icon(user.email).to_s). + to eq("/gitlab/uploads/system/user/avatar/#{user.id}/banana_sample.gif") + end + end + end - it 'calls gravatar_icon when no User exists with the given email' do - expect(helper).to receive(:gravatar_icon).with('foo@example.com', 20, 2) + context 'when no user exists for the email' do + it 'calls gravatar_icon' do + expect(helper).to receive(:gravatar_icon).with('foo@example.com', 20, 2) - helper.avatar_icon('foo@example.com', 20, 2) + helper.avatar_icon('foo@example.com', 20, 2) + end + end end - describe 'using a User' do - it 'returns an URL for the avatar' do - user = create(:user, avatar: File.open(uploaded_image_temp_path)) + describe 'using a user' do + context 'when only_path is true' do + it 'returns a relative URL for the avatar' do + expect(helper.avatar_icon(user, only_path: true).to_s). + to eq("/uploads/system/user/avatar/#{user.id}/banana_sample.gif") + end + end - expect(helper.avatar_icon(user).to_s). - to match("/uploads/system/user/avatar/#{user.id}/banana_sample.gif") + context 'when only_path is false' do + it 'returns an absolute URL for the avatar' do + expect(helper.avatar_icon(user, only_path: false).to_s). + to eq("#{gitlab_host}/uploads/system/user/avatar/#{user.id}/banana_sample.gif") + end end end end diff --git a/spec/javascripts/commit/pipelines/pipelines_spec.js b/spec/javascripts/commit/pipelines/pipelines_spec.js index ebfd60198b2..694f94efcff 100644 --- a/spec/javascripts/commit/pipelines/pipelines_spec.js +++ b/spec/javascripts/commit/pipelines/pipelines_spec.js @@ -1,15 +1,15 @@ import Vue from 'vue'; -import PipelinesTable from '~/commit/pipelines/pipelines_table'; +import pipelinesTable from '~/commit/pipelines/pipelines_table.vue'; describe('Pipelines table in Commits and Merge requests', () => { const jsonFixtureName = 'pipelines/pipelines.json'; let pipeline; + let PipelinesTable; - preloadFixtures('static/pipelines_table.html.raw'); preloadFixtures(jsonFixtureName); beforeEach(() => { - loadFixtures('static/pipelines_table.html.raw'); + PipelinesTable = Vue.extend(pipelinesTable); const pipelines = getJSONFixture(jsonFixtureName).pipelines; pipeline = pipelines.find(p => p.id === 1); }); @@ -26,8 +26,11 @@ describe('Pipelines table in Commits and Merge requests', () => { Vue.http.interceptors.push(pipelinesEmptyResponse); this.component = new PipelinesTable({ - el: document.querySelector('#commit-pipeline-table-view'), - }); + propsData: { + endpoint: 'endpoint', + helpPagePath: 'foo', + }, + }).$mount(); }); afterEach(function () { @@ -58,8 +61,11 @@ describe('Pipelines table in Commits and Merge requests', () => { Vue.http.interceptors.push(pipelinesResponse); this.component = new PipelinesTable({ - el: document.querySelector('#commit-pipeline-table-view'), - }); + propsData: { + endpoint: 'endpoint', + helpPagePath: 'foo', + }, + }).$mount(); }); afterEach(() => { @@ -92,8 +98,11 @@ describe('Pipelines table in Commits and Merge requests', () => { Vue.http.interceptors.push(pipelinesErrorResponse); this.component = new PipelinesTable({ - el: document.querySelector('#commit-pipeline-table-view'), - }); + propsData: { + endpoint: 'endpoint', + helpPagePath: 'foo', + }, + }).$mount(); }); afterEach(function () { diff --git a/spec/javascripts/fixtures/merge_requests.rb b/spec/javascripts/fixtures/merge_requests.rb index a746a776548..0715f4d5f6b 100644 --- a/spec/javascripts/fixtures/merge_requests.rb +++ b/spec/javascripts/fixtures/merge_requests.rb @@ -55,13 +55,20 @@ describe Projects::MergeRequestsController, '(JavaScript fixtures)', type: :cont render_merge_request(example.description, merge_request) end + it 'merge_requests/changes_tab_with_comments.json' do |example| + create(:diff_note_on_merge_request, project: project, author: admin, position: position, noteable: merge_request) + create(:note_on_merge_request, author: admin, project: project, noteable: merge_request) + render_merge_request(example.description, merge_request, action: :diffs, format: :json) + end + private - def render_merge_request(fixture_file_name, merge_request) - get :show, + def render_merge_request(fixture_file_name, merge_request, action: :show, format: :html) + get action, namespace_id: project.namespace.to_param, project_id: project, - id: merge_request.to_param + id: merge_request.to_param, + format: format expect(response).to be_success store_frontend_fixture(response, fixture_file_name) diff --git a/spec/javascripts/fixtures/pipelines_table.html.haml b/spec/javascripts/fixtures/pipelines_table.html.haml deleted file mode 100644 index ad1682704bb..00000000000 --- a/spec/javascripts/fixtures/pipelines_table.html.haml +++ /dev/null @@ -1 +0,0 @@ -#commit-pipeline-table-view{ data: { endpoint: "endpoint", "help-page-path": "foo" } } diff --git a/spec/javascripts/issue_show/components/description_spec.js b/spec/javascripts/issue_show/components/description_spec.js index 886462c4b9a..f3fdbff01a6 100644 --- a/spec/javascripts/issue_show/components/description_spec.js +++ b/spec/javascripts/issue_show/components/description_spec.js @@ -95,6 +95,18 @@ describe('Description component', () => { done(); }); }); + + it('clears task status text when no tasks are present', (done) => { + vm.taskStatus = '0 of 0'; + + setTimeout(() => { + expect( + document.querySelector('.issuable-meta #task_status').textContent.trim(), + ).toBe(''); + + done(); + }); + }); }); it('applies syntax highlighting and math when description changed', (done) => { diff --git a/spec/javascripts/merge_request_notes_spec.js b/spec/javascripts/merge_request_notes_spec.js index e54acfa8e44..b6d0ce02c4f 100644 --- a/spec/javascripts/merge_request_notes_spec.js +++ b/spec/javascripts/merge_request_notes_spec.js @@ -7,54 +7,92 @@ import '~/render_gfm'; import '~/render_math'; import '~/notes'; +const upArrowKeyCode = 38; + describe('Merge request notes', () => { window.gon = window.gon || {}; window.gl = window.gl || {}; gl.utils = gl.utils || {}; - const fixture = 'merge_requests/diff_comment.html.raw'; - preloadFixtures(fixture); + const discussionTabFixture = 'merge_requests/diff_comment.html.raw'; + const changesTabJsonFixture = 'merge_requests/changes_tab_with_comments.json'; + preloadFixtures(discussionTabFixture, changesTabJsonFixture); - beforeEach(() => { - loadFixtures(fixture); - gl.utils.disableButtonIfEmptyField = _.noop; - window.project_uploads_path = 'http://test.host/uploads'; - $('body').data('page', 'projects:merge_requests:show'); - window.gon.current_user_id = $('.note:last').data('author-id'); + describe('Discussion tab with diff comments', () => { + beforeEach(() => { + loadFixtures(discussionTabFixture); + gl.utils.disableButtonIfEmptyField = _.noop; + window.project_uploads_path = 'http://test.host/uploads'; + $('body').data('page', 'projects:merge_requests:show'); + window.gon.current_user_id = $('.note:last').data('author-id'); - return new Notes('', []); - }); + return new Notes('', []); + }); + + describe('up arrow', () => { + it('edits last comment when triggered in main form', () => { + const upArrowEvent = $.Event('keydown'); + upArrowEvent.which = upArrowKeyCode; + + spyOnEvent('.note:last .js-note-edit', 'click'); + + $('.js-note-text').trigger(upArrowEvent); + + expect('click').toHaveBeenTriggeredOn('.note:last .js-note-edit'); + }); + + it('edits last comment in discussion when triggered in discussion form', (done) => { + const upArrowEvent = $.Event('keydown'); + upArrowEvent.which = upArrowKeyCode; + + spyOnEvent('.note-discussion .js-note-edit', 'click'); + + $('.js-discussion-reply-button').click(); - describe('up arrow', () => { - it('edits last comment when triggered in main form', () => { - const upArrowEvent = $.Event('keydown'); - upArrowEvent.which = 38; + setTimeout(() => { + expect( + $('.note-discussion .js-note-text'), + ).toExist(); - spyOnEvent('.note:last .js-note-edit', 'click'); + $('.note-discussion .js-note-text').trigger(upArrowEvent); - $('.js-note-text').trigger(upArrowEvent); + expect('click').toHaveBeenTriggeredOn('.note-discussion .js-note-edit'); - expect('click').toHaveBeenTriggeredOn('.note:last .js-note-edit'); + done(); + }); + }); }); + }); - it('edits last comment in discussion when triggered in discussion form', (done) => { - const upArrowEvent = $.Event('keydown'); - upArrowEvent.which = 38; + describe('Changes tab with diff comments', () => { + beforeEach(() => { + const diffsResponse = getJSONFixture(changesTabJsonFixture); + const noteFormHtml = `<form class="js-new-note-form"> + <textarea class="js-note-text"></textarea> + </form>`; + setFixtures(diffsResponse.html + noteFormHtml); + $('body').data('page', 'projects:merge_requests:show'); + window.gon.current_user_id = $('.note:last').data('author-id'); + + return new Notes('', []); + }); - spyOnEvent('.note-discussion .js-note-edit', 'click'); + describe('up arrow', () => { + it('edits last comment in discussion when triggered in discussion form', (done) => { + const upArrowEvent = $.Event('keydown'); + upArrowEvent.which = upArrowKeyCode; - $('.js-discussion-reply-button').click(); + spyOnEvent('.note:last .js-note-edit', 'click'); - setTimeout(() => { - expect( - $('.note-discussion .js-note-text'), - ).toExist(); + $('.js-discussion-reply-button').trigger('click'); - $('.note-discussion .js-note-text').trigger(upArrowEvent); + setTimeout(() => { + $('.js-note-text').trigger(upArrowEvent); - expect('click').toHaveBeenTriggeredOn('.note-discussion .js-note-edit'); + expect('click').toHaveBeenTriggeredOn('.note:last .js-note-edit'); - done(); + done(); + }); }); }); }); diff --git a/spec/javascripts/pipelines/async_button_spec.js b/spec/javascripts/pipelines/async_button_spec.js index 28c9c7ab282..48620898357 100644 --- a/spec/javascripts/pipelines/async_button_spec.js +++ b/spec/javascripts/pipelines/async_button_spec.js @@ -1,25 +1,20 @@ import Vue from 'vue'; import asyncButtonComp from '~/pipelines/components/async_button.vue'; +import eventHub from '~/pipelines/event_hub'; describe('Pipelines Async Button', () => { let component; - let spy; let AsyncButtonComponent; beforeEach(() => { AsyncButtonComponent = Vue.extend(asyncButtonComp); - spy = jasmine.createSpy('spy').and.returnValue(Promise.resolve()); - component = new AsyncButtonComponent({ propsData: { endpoint: '/foo', title: 'Foo', icon: 'fa fa-foo', cssClass: 'bar', - service: { - postAction: spy, - }, }, }).$mount(); }); @@ -33,7 +28,7 @@ describe('Pipelines Async Button', () => { }); it('should render the provided title', () => { - expect(component.$el.getAttribute('title')).toContain('Foo'); + expect(component.$el.getAttribute('data-original-title')).toContain('Foo'); expect(component.$el.getAttribute('aria-label')).toContain('Foo'); }); @@ -41,37 +36,12 @@ describe('Pipelines Async Button', () => { expect(component.$el.getAttribute('class')).toContain('bar'); }); - it('should call the service when it is clicked with the provided endpoint', () => { - component.$el.click(); - expect(spy).toHaveBeenCalledWith('/foo'); - }); - - it('should hide loading if request fails', () => { - spy = jasmine.createSpy('spy').and.returnValue(Promise.reject()); - - component = new AsyncButtonComponent({ - propsData: { - endpoint: '/foo', - title: 'Foo', - icon: 'fa fa-foo', - cssClass: 'bar', - dataAttributes: { - 'data-foo': 'foo', - }, - service: { - postAction: spy, - }, - }, - }).$mount(); - - component.$el.click(); - expect(component.$el.querySelector('.fa-spinner')).toBe(null); - }); - describe('With confirm dialog', () => { it('should call the service when confimation is positive', () => { spyOn(window, 'confirm').and.returnValue(true); - spy = jasmine.createSpy('spy').and.returnValue(Promise.resolve()); + eventHub.$on('postAction', (endpoint) => { + expect(endpoint).toEqual('/foo'); + }); component = new AsyncButtonComponent({ propsData: { @@ -79,15 +49,11 @@ describe('Pipelines Async Button', () => { title: 'Foo', icon: 'fa fa-foo', cssClass: 'bar', - service: { - postAction: spy, - }, confirmActionMessage: 'bar', }, }).$mount(); component.$el.click(); - expect(spy).toHaveBeenCalledWith('/foo'); }); }); }); diff --git a/spec/javascripts/pipelines/pipelines_actions_spec.js b/spec/javascripts/pipelines/pipelines_actions_spec.js index 8a58b77f1e3..72fb0a8f9ef 100644 --- a/spec/javascripts/pipelines/pipelines_actions_spec.js +++ b/spec/javascripts/pipelines/pipelines_actions_spec.js @@ -3,7 +3,6 @@ import pipelinesActionsComp from '~/pipelines/components/pipelines_actions.vue'; describe('Pipelines Actions dropdown', () => { let component; - let spy; let actions; let ActionsComponent; @@ -22,14 +21,9 @@ describe('Pipelines Actions dropdown', () => { }, ]; - spy = jasmine.createSpy('spy').and.returnValue(Promise.resolve()); - component = new ActionsComponent({ propsData: { actions, - service: { - postAction: spy, - }, }, }).$mount(); }); @@ -40,31 +34,6 @@ describe('Pipelines Actions dropdown', () => { ).toEqual(actions.length); }); - it('should call the service when an action is clicked', () => { - component.$el.querySelector('.js-pipeline-dropdown-manual-actions').click(); - component.$el.querySelector('.js-pipeline-action-link').click(); - - expect(spy).toHaveBeenCalledWith(actions[0].path); - }); - - it('should hide loading if request fails', () => { - spy = jasmine.createSpy('spy').and.returnValue(Promise.reject()); - - component = new ActionsComponent({ - propsData: { - actions, - service: { - postAction: spy, - }, - }, - }).$mount(); - - component.$el.querySelector('.js-pipeline-dropdown-manual-actions').click(); - component.$el.querySelector('.js-pipeline-action-link').click(); - - expect(component.$el.querySelector('.fa-spinner')).toEqual(null); - }); - it('should render a disabled action when it\'s not playable', () => { expect( component.$el.querySelector('.dropdown-menu li:last-child button').getAttribute('disabled'), diff --git a/spec/javascripts/vue_shared/components/pipelines_table_row_spec.js b/spec/javascripts/pipelines/pipelines_table_row_spec.js index 9475ee28a03..7ce39dca112 100644 --- a/spec/javascripts/vue_shared/components/pipelines_table_row_spec.js +++ b/spec/javascripts/pipelines/pipelines_table_row_spec.js @@ -1,5 +1,5 @@ import Vue from 'vue'; -import tableRowComp from '~/vue_shared/components/pipelines_table_row.vue'; +import tableRowComp from '~/pipelines/components/pipelines_table_row.vue'; describe('Pipelines Table Row', () => { const jsonFixtureName = 'pipelines/pipelines.json'; diff --git a/spec/javascripts/vue_shared/components/pipelines_table_spec.js b/spec/javascripts/pipelines/pipelines_table_spec.js index 4c35d702004..3afe89c8db4 100644 --- a/spec/javascripts/vue_shared/components/pipelines_table_spec.js +++ b/spec/javascripts/pipelines/pipelines_table_spec.js @@ -1,5 +1,5 @@ import Vue from 'vue'; -import pipelinesTableComp from '~/vue_shared/components/pipelines_table.vue'; +import pipelinesTableComp from '~/pipelines/components/pipelines_table.vue'; import '~/lib/utils/datetime_utility'; describe('Pipelines Table', () => { @@ -22,7 +22,6 @@ describe('Pipelines Table', () => { component = new PipelinesTableComponent({ propsData: { pipelines: [], - service: {}, }, }).$mount(); }); @@ -48,7 +47,6 @@ describe('Pipelines Table', () => { const component = new PipelinesTableComponent({ propsData: { pipelines: [], - service: {}, }, }).$mount(); expect(component.$el.querySelectorAll('.commit.gl-responsive-table-row').length).toEqual(0); @@ -58,10 +56,8 @@ describe('Pipelines Table', () => { describe('with data', () => { it('should render rows', () => { const component = new PipelinesTableComponent({ - el: document.querySelector('.test-dom-element'), propsData: { pipelines: [pipeline], - service: {}, }, }).$mount(); diff --git a/spec/lib/gitlab/email/reply_parser_spec.rb b/spec/lib/gitlab/email/reply_parser_spec.rb index 28698e89c33..71659d5e8b0 100644 --- a/spec/lib/gitlab/email/reply_parser_spec.rb +++ b/spec/lib/gitlab/email/reply_parser_spec.rb @@ -208,5 +208,9 @@ describe Gitlab::Email::ReplyParser, lib: true do it "properly renders html-only email from MS Outlook" do expect(test_parse_body(fixture_file("emails/outlook_html.eml"))).to eq("Microsoft Outlook 2010") end + + it "does not wrap links with no href in unnecessary brackets" do + expect(test_parse_body(fixture_file("emails/html_empty_link.eml"))).to eq("no brackets!") + end end end diff --git a/spec/lib/gitlab/metrics_spec.rb b/spec/lib/gitlab/metrics_spec.rb index 5a87b906609..58a84cd3fe1 100644 --- a/spec/lib/gitlab/metrics_spec.rb +++ b/spec/lib/gitlab/metrics_spec.rb @@ -15,6 +15,36 @@ describe Gitlab::Metrics do end end + describe '.prometheus_metrics_enabled_unmemoized' do + subject { described_class.send(:prometheus_metrics_enabled_unmemoized) } + + context 'prometheus metrics enabled in config' do + before do + allow(described_class).to receive(:current_application_settings).and_return(prometheus_metrics_enabled: true) + end + + context 'when metrics folder is present' do + before do + allow(described_class).to receive(:metrics_folder_present?).and_return(true) + end + + it 'metrics are enabled' do + expect(subject).to eq(true) + end + end + + context 'when metrics folder is missing' do + before do + allow(described_class).to receive(:metrics_folder_present?).and_return(false) + end + + it 'metrics are disabled' do + expect(subject).to eq(false) + end + end + end + end + describe '.prometheus_metrics_enabled?' do it 'returns a boolean' do expect(described_class.prometheus_metrics_enabled?).to be_in([true, false]) diff --git a/spec/lib/gitlab/visibility_level_spec.rb b/spec/lib/gitlab/visibility_level_spec.rb index 3255c6f1ef7..a8f21803ec7 100644 --- a/spec/lib/gitlab/visibility_level_spec.rb +++ b/spec/lib/gitlab/visibility_level_spec.rb @@ -18,4 +18,35 @@ describe Gitlab::VisibilityLevel, lib: true do expect(described_class.level_value(100)).to eq(Gitlab::VisibilityLevel::PRIVATE) end end + + describe '.levels_for_user' do + it 'returns all levels for an admin' do + user = double(:user, admin?: true) + + expect(described_class.levels_for_user(user)). + to eq([Gitlab::VisibilityLevel::PRIVATE, + Gitlab::VisibilityLevel::INTERNAL, + Gitlab::VisibilityLevel::PUBLIC]) + end + + it 'returns INTERNAL and PUBLIC for internal users' do + user = double(:user, admin?: false, external?: false) + + expect(described_class.levels_for_user(user)). + to eq([Gitlab::VisibilityLevel::INTERNAL, + Gitlab::VisibilityLevel::PUBLIC]) + end + + it 'returns PUBLIC for external users' do + user = double(:user, admin?: false, external?: true) + + expect(described_class.levels_for_user(user)). + to eq([Gitlab::VisibilityLevel::PUBLIC]) + end + + it 'returns PUBLIC when no user is given' do + expect(described_class.levels_for_user). + to eq([Gitlab::VisibilityLevel::PUBLIC]) + end + end end diff --git a/spec/models/project_feature_spec.rb b/spec/models/project_feature_spec.rb index 09a4448d387..580c83c12c0 100644 --- a/spec/models/project_feature_spec.rb +++ b/spec/models/project_feature_spec.rb @@ -4,6 +4,18 @@ describe ProjectFeature do let(:project) { create(:empty_project) } let(:user) { create(:user) } + describe '.quoted_access_level_column' do + it 'returns the table name and quoted column name for a feature' do + expected = if Gitlab::Database.postgresql? + '"project_features"."issues_access_level"' + else + '`project_features`.`issues_access_level`' + end + + expect(described_class.quoted_access_level_column(:issues)).to eq(expected) + end + end + describe '#feature_available?' do let(:features) { %w(issues wiki builds merge_requests snippets repository) } diff --git a/spec/models/project_services/chat_message/pipeline_message_spec.rb b/spec/models/project_services/chat_message/pipeline_message_spec.rb index 7d2599dc703..43b02568cb9 100644 --- a/spec/models/project_services/chat_message/pipeline_message_spec.rb +++ b/spec/models/project_services/chat_message/pipeline_message_spec.rb @@ -62,7 +62,7 @@ describe ChatMessage::PipelineMessage do def build_message(status_text = status, name = user[:name]) "<http://example.gitlab.com|project_name>:" \ " Pipeline <http://example.gitlab.com/pipelines/123|#123>" \ - " of branch `<http://example.gitlab.com/commits/develop|develop>`" \ + " of branch <http://example.gitlab.com/commits/develop|develop>" \ " by #{name} #{status_text} in 02:00:10" end end @@ -81,7 +81,7 @@ describe ChatMessage::PipelineMessage do expect(subject.pretext).to be_empty expect(subject.attachments).to eq(message) expect(subject.activity).to eq({ - title: 'Pipeline [#123](http://example.gitlab.com/pipelines/123) of branch `[develop](http://example.gitlab.com/commits/develop)` by hacker passed', + title: 'Pipeline [#123](http://example.gitlab.com/pipelines/123) of branch [develop](http://example.gitlab.com/commits/develop) by hacker passed', subtitle: 'in [project_name](http://example.gitlab.com)', text: 'in 02:00:10', image: '' @@ -98,7 +98,7 @@ describe ChatMessage::PipelineMessage do expect(subject.pretext).to be_empty expect(subject.attachments).to eq(message) expect(subject.activity).to eq({ - title: 'Pipeline [#123](http://example.gitlab.com/pipelines/123) of branch `[develop](http://example.gitlab.com/commits/develop)` by hacker failed', + title: 'Pipeline [#123](http://example.gitlab.com/pipelines/123) of branch [develop](http://example.gitlab.com/commits/develop) by hacker failed', subtitle: 'in [project_name](http://example.gitlab.com)', text: 'in 02:00:10', image: '' @@ -113,7 +113,7 @@ describe ChatMessage::PipelineMessage do expect(subject.pretext).to be_empty expect(subject.attachments).to eq(message) expect(subject.activity).to eq({ - title: 'Pipeline [#123](http://example.gitlab.com/pipelines/123) of branch `[develop](http://example.gitlab.com/commits/develop)` by API failed', + title: 'Pipeline [#123](http://example.gitlab.com/pipelines/123) of branch [develop](http://example.gitlab.com/commits/develop) by API failed', subtitle: 'in [project_name](http://example.gitlab.com)', text: 'in 02:00:10', image: '' @@ -125,7 +125,7 @@ describe ChatMessage::PipelineMessage do def build_markdown_message(status_text = status, name = user[:name]) "[project_name](http://example.gitlab.com):" \ " Pipeline [#123](http://example.gitlab.com/pipelines/123)" \ - " of branch `[develop](http://example.gitlab.com/commits/develop)`" \ + " of branch [develop](http://example.gitlab.com/commits/develop)" \ " by #{name} #{status_text} in 02:00:10" end end diff --git a/spec/models/project_services/chat_message/push_message_spec.rb b/spec/models/project_services/chat_message/push_message_spec.rb index e38117b75f6..c794f659c41 100644 --- a/spec/models/project_services/chat_message/push_message_spec.rb +++ b/spec/models/project_services/chat_message/push_message_spec.rb @@ -28,7 +28,7 @@ describe ChatMessage::PushMessage, models: true do context 'without markdown' do it 'returns a message regarding pushes' do expect(subject.pretext).to eq( - 'test.user pushed to branch `<http://url.com/commits/master|master>` of '\ + 'test.user pushed to branch <http://url.com/commits/master|master> of '\ '<http://url.com|project_name> (<http://url.com/compare/before...after|Compare changes>)') expect(subject.attachments).to eq([{ text: "<http://url1.com|abcdefgh>: message1 - author1\n\n"\ @@ -45,7 +45,7 @@ describe ChatMessage::PushMessage, models: true do it 'returns a message regarding pushes' do expect(subject.pretext).to eq( - 'test.user pushed to branch `[master](http://url.com/commits/master)` of [project_name](http://url.com) ([Compare changes](http://url.com/compare/before...after))') + 'test.user pushed to branch [master](http://url.com/commits/master) of [project_name](http://url.com) ([Compare changes](http://url.com/compare/before...after))') expect(subject.attachments).to eq( "[abcdefgh](http://url1.com): message1 - author1\n\n[12345678](http://url2.com): message2 - author2") expect(subject.activity).to eq({ @@ -74,7 +74,7 @@ describe ChatMessage::PushMessage, models: true do context 'without markdown' do it 'returns a message regarding pushes' do expect(subject.pretext).to eq('test.user pushed new tag ' \ - '`<http://url.com/commits/new_tag|new_tag>` to ' \ + '<http://url.com/commits/new_tag|new_tag> to ' \ '<http://url.com|project_name>') expect(subject.attachments).to be_empty end @@ -87,7 +87,7 @@ describe ChatMessage::PushMessage, models: true do it 'returns a message regarding pushes' do expect(subject.pretext).to eq( - 'test.user pushed new tag `[new_tag](http://url.com/commits/new_tag)` to [project_name](http://url.com)') + 'test.user pushed new tag [new_tag](http://url.com/commits/new_tag) to [project_name](http://url.com)') expect(subject.attachments).to be_empty expect(subject.activity).to eq({ title: 'test.user created tag', @@ -107,7 +107,7 @@ describe ChatMessage::PushMessage, models: true do context 'without markdown' do it 'returns a message regarding a new branch' do expect(subject.pretext).to eq( - 'test.user pushed new branch `<http://url.com/commits/master|master>` to '\ + 'test.user pushed new branch <http://url.com/commits/master|master> to '\ '<http://url.com|project_name>') expect(subject.attachments).to be_empty end @@ -120,7 +120,7 @@ describe ChatMessage::PushMessage, models: true do it 'returns a message regarding a new branch' do expect(subject.pretext).to eq( - 'test.user pushed new branch `[master](http://url.com/commits/master)` to [project_name](http://url.com)') + 'test.user pushed new branch [master](http://url.com/commits/master) to [project_name](http://url.com)') expect(subject.attachments).to be_empty expect(subject.activity).to eq({ title: 'test.user created branch', @@ -140,7 +140,7 @@ describe ChatMessage::PushMessage, models: true do context 'without markdown' do it 'returns a message regarding a removed branch' do expect(subject.pretext).to eq( - 'test.user removed branch `master` from <http://url.com|project_name>') + 'test.user removed branch master from <http://url.com|project_name>') expect(subject.attachments).to be_empty end end @@ -152,7 +152,7 @@ describe ChatMessage::PushMessage, models: true do it 'returns a message regarding a removed branch' do expect(subject.pretext).to eq( - 'test.user removed branch `master` from [project_name](http://url.com)') + 'test.user removed branch master from [project_name](http://url.com)') expect(subject.attachments).to be_empty expect(subject.activity).to eq({ title: 'test.user removed branch', diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb index 63333b7af1f..c7ba3ae903d 100644 --- a/spec/models/project_spec.rb +++ b/spec/models/project_spec.rb @@ -2060,4 +2060,36 @@ describe Project, models: true do expect(project.last_repository_updated_at.to_i).to eq(project.created_at.to_i) end end + + describe '.public_or_visible_to_user' do + let!(:user) { create(:user) } + + let!(:private_project) do + create(:empty_project, :private, creator: user, namespace: user.namespace) + end + + let!(:public_project) { create(:empty_project, :public) } + + context 'with a user' do + let(:projects) do + Project.all.public_or_visible_to_user(user) + end + + it 'includes projects the user has access to' do + expect(projects).to include(private_project) + end + + it 'includes projects the user can see' do + expect(projects).to include(public_project) + end + end + + context 'without a user' do + it 'only includes public projects' do + projects = Project.all.public_or_visible_to_user + + expect(projects).to eq([public_project]) + end + end + end end diff --git a/spec/requests/api/users_spec.rb b/spec/requests/api/users_spec.rb index bc869ea1108..750682bde52 100644 --- a/spec/requests/api/users_spec.rb +++ b/spec/requests/api/users_spec.rb @@ -11,7 +11,7 @@ describe API::Users do let(:not_existing_user_id) { (User.maximum('id') || 0 ) + 10 } let(:not_existing_pat_id) { (PersonalAccessToken.maximum('id') || 0 ) + 10 } - describe "GET /users" do + describe 'GET /users' do context "when unauthenticated" do it "returns authentication error" do get api("/users") @@ -76,6 +76,12 @@ describe API::Users do expect(response).to have_http_status(403) end + + it 'does not reveal the `is_admin` flag of the user' do + get api('/users', user) + + expect(json_response.first.keys).not_to include 'is_admin' + end end context "when admin" do @@ -92,6 +98,7 @@ describe API::Users do expect(json_response.first.keys).to include 'two_factor_enabled' expect(json_response.first.keys).to include 'last_sign_in_at' expect(json_response.first.keys).to include 'confirmed_at' + expect(json_response.first.keys).to include 'is_admin' end it "returns an array of external users" do diff --git a/spec/requests/api/v3/users_spec.rb b/spec/requests/api/v3/users_spec.rb index e9c57f7c6c3..6d7401f9764 100644 --- a/spec/requests/api/v3/users_spec.rb +++ b/spec/requests/api/v3/users_spec.rb @@ -7,6 +7,38 @@ describe API::V3::Users do let(:email) { create(:email, user: user) } let(:ldap_blocked_user) { create(:omniauth_user, provider: 'ldapmain', state: 'ldap_blocked') } + describe 'GET /users' do + context 'when authenticated' do + it 'returns an array of users' do + get v3_api('/users', user) + + expect(response).to have_http_status(200) + expect(response).to include_pagination_headers + expect(json_response).to be_an Array + username = user.username + expect(json_response.detect do |user| + user['username'] == username + end['username']).to eq(username) + end + end + + context 'when authenticated as user' do + it 'does not reveal the `is_admin` flag of the user' do + get v3_api('/users', user) + + expect(json_response.first.keys).not_to include 'is_admin' + end + end + + context 'when authenticated as admin' do + it 'reveals the `is_admin` flag of the user' do + get v3_api('/users', admin) + + expect(json_response.first.keys).to include 'is_admin' + end + end + end + describe 'GET /user/:id/keys' do before { admin } diff --git a/spec/services/projects/transfer_service_spec.rb b/spec/services/projects/transfer_service_spec.rb index 5d2f4cf17fb..63bd0353d50 100644 --- a/spec/services/projects/transfer_service_spec.rb +++ b/spec/services/projects/transfer_service_spec.rb @@ -19,6 +19,67 @@ describe Projects::TransferService, services: true do it { expect(project.namespace).to eq(group) } end + context 'when transfer succeeds' do + before do + group.add_owner(user) + end + + it 'sends notifications' do + expect_any_instance_of(NotificationService).to receive(:project_was_moved) + + transfer_project(project, user, group) + end + + it 'executes system hooks' do + expect_any_instance_of(Projects::TransferService).to receive(:execute_system_hooks) + + transfer_project(project, user, group) + end + end + + context 'when transfer fails' do + let!(:original_path) { project_path(project) } + + def attempt_project_transfer + expect do + transfer_project(project, user, group) + end.to raise_error(ActiveRecord::ActiveRecordError) + end + + before do + group.add_owner(user) + + expect_any_instance_of(Labels::TransferService).to receive(:execute).and_raise(ActiveRecord::StatementInvalid, "PG ERROR") + end + + def project_path(project) + File.join(project.repository_storage_path, "#{project.path_with_namespace}.git") + end + + def current_path + project_path(project) + end + + it 'rolls back repo location' do + attempt_project_transfer + + expect(Dir.exist?(original_path)).to be_truthy + expect(original_path).to eq current_path + end + + it "doesn't send move notifications" do + expect_any_instance_of(NotificationService).not_to receive(:project_was_moved) + + attempt_project_transfer + end + + it "doesn't run system hooks" do + expect_any_instance_of(Projects::TransferService).not_to receive(:execute_system_hooks) + + attempt_project_transfer + end + end + context 'namespace -> no namespace' do before do @result = transfer_project(project, user, nil) diff --git a/spec/support/features/reportable_note_shared_examples.rb b/spec/support/features/reportable_note_shared_examples.rb index 0d80c95e826..27e079c01dd 100644 --- a/spec/support/features/reportable_note_shared_examples.rb +++ b/spec/support/features/reportable_note_shared_examples.rb @@ -13,9 +13,7 @@ shared_examples 'reportable note' do it 'dropdown has Edit, Report and Delete links' do dropdown = comment.find(more_actions_selector) - - dropdown.click - dropdown.find('.dropdown-menu li', match: :first) + open_dropdown(dropdown) expect(dropdown).to have_button('Edit comment') expect(dropdown).to have_link('Report as abuse', href: abuse_report_path) @@ -24,13 +22,16 @@ shared_examples 'reportable note' do it 'Report button links to a report page' do dropdown = comment.find(more_actions_selector) - - dropdown.click - dropdown.find('.dropdown-menu li', match: :first) + open_dropdown(dropdown) dropdown.click_link('Report as abuse') expect(find('#user_name')['value']).to match(note.author.username) expect(find('#abuse_report_message')['value']).to match(noteable_note_url(note)) end + + def open_dropdown(dropdown) + dropdown.click + dropdown.find('.dropdown-menu li', match: :first) + end end diff --git a/spec/views/projects/notes/_more_actions_dropdown.html.haml_spec.rb b/spec/views/projects/notes/_more_actions_dropdown.html.haml_spec.rb new file mode 100644 index 00000000000..e56c0f6be03 --- /dev/null +++ b/spec/views/projects/notes/_more_actions_dropdown.html.haml_spec.rb @@ -0,0 +1,41 @@ +require 'spec_helper' + +describe 'projects/notes/_more_actions_dropdown', :view do + let(:author_user) { create(:user) } + let(:not_author_user) { create(:user) } + + let(:project) { create(:empty_project) } + let(:issue) { create(:issue, project: project) } + let!(:note) { create(:note_on_issue, author: author_user, noteable: issue, project: project) } + + before do + assign(:project, project) + end + + it 'shows Report as abuse button if not editable and not current users comment' do + render 'projects/notes/more_actions_dropdown', current_user: not_author_user, note_editable: false, note: note + + expect(rendered).to have_link('Report as abuse') + end + + it 'does not show the More actions button if not editable and current users comment' do + render 'projects/notes/more_actions_dropdown', current_user: author_user, note_editable: false, note: note + + expect(rendered).not_to have_selector('.dropdown.more-actions') + end + + it 'shows Report as abuse, Edit and Delete buttons if editable and not current users comment' do + render 'projects/notes/more_actions_dropdown', current_user: not_author_user, note_editable: true, note: note + + expect(rendered).to have_link('Report as abuse') + expect(rendered).to have_button('Edit comment') + expect(rendered).to have_link('Delete comment') + end + + it 'shows Edit and Delete buttons if editable and current users comment' do + render 'projects/notes/more_actions_dropdown', current_user: author_user, note_editable: true, note: note + + expect(rendered).to have_button('Edit comment') + expect(rendered).to have_link('Delete comment') + end +end |