diff options
Diffstat (limited to 'spec')
90 files changed, 1192 insertions, 260 deletions
diff --git a/spec/controllers/projects/clusters/gcp_controller_spec.rb b/spec/controllers/projects/clusters/gcp_controller_spec.rb index be19fa93183..775f9db1c6e 100644 --- a/spec/controllers/projects/clusters/gcp_controller_spec.rb +++ b/spec/controllers/projects/clusters/gcp_controller_spec.rb @@ -137,11 +137,14 @@ describe Projects::Clusters::GcpController do context 'when access token is valid' do before do stub_google_api_validate_token + allow_any_instance_of(described_class).to receive(:authorize_google_project_billing) end context 'when google project billing is enabled' do before do - stub_google_project_billing_status + redis_double = double + allow(Gitlab::Redis::SharedState).to receive(:with).and_yield(redis_double) + allow(redis_double).to receive(:get).with(CheckGcpProjectBillingWorker.redis_shared_state_key_for('token')).and_return('true') end it 'creates a new cluster' do @@ -158,7 +161,7 @@ describe Projects::Clusters::GcpController do it 'renders the cluster form with an error' do go - expect(response).to set_flash[:error] + expect(response).to set_flash[:alert] expect(response).to render_template('new') end end diff --git a/spec/factories/protected_branches.rb b/spec/factories/protected_branches.rb index 39460834d06..60956511834 100644 --- a/spec/factories/protected_branches.rb +++ b/spec/factories/protected_branches.rb @@ -53,6 +53,7 @@ FactoryBot.define do if evaluator.default_access_level && evaluator.default_push_level protected_branch.push_access_levels.new(access_level: Gitlab::Access::MASTER) end + if evaluator.default_access_level && evaluator.default_merge_level protected_branch.merge_access_levels.new(access_level: Gitlab::Access::MASTER) end diff --git a/spec/factories/redirect_routes.rb b/spec/factories/redirect_routes.rb new file mode 100644 index 00000000000..c29c81c5df9 --- /dev/null +++ b/spec/factories/redirect_routes.rb @@ -0,0 +1,15 @@ +FactoryBot.define do + factory :redirect_route do + sequence(:path) { |n| "redirect#{n}" } + source factory: :group + permanent false + + trait :permanent do + permanent true + end + + trait :temporary do + permanent false + end + end +end diff --git a/spec/features/copy_as_gfm_spec.rb b/spec/features/copy_as_gfm_spec.rb index d8f1a919522..f82ed6300cc 100644 --- a/spec/features/copy_as_gfm_spec.rb +++ b/spec/features/copy_as_gfm_spec.rb @@ -750,7 +750,7 @@ describe 'Copy as GFM', :js do js = <<-JS.strip_heredoc (function(selector) { var els = document.querySelectorAll(selector); - var htmls = _.map(els, function(el) { return el.outerHTML; }); + var htmls = [].slice.call(els).map(function(el) { return el.outerHTML; }); return htmls.join("\\n"); })("#{escape_javascript(selector)}") JS diff --git a/spec/features/issues/bulk_assignment_labels_spec.rb b/spec/features/issues/bulk_assignment_labels_spec.rb index 587ece22ec7..cf283119f36 100644 --- a/spec/features/issues/bulk_assignment_labels_spec.rb +++ b/spec/features/issues/bulk_assignment_labels_spec.rb @@ -377,6 +377,7 @@ feature 'Issues > Labels bulk assignment' do items.map do |item| click_link item end + if unmark items.map do |item| # Make sure we are unmarking the item no matter the state it has currently diff --git a/spec/features/projects/clusters/gcp_spec.rb b/spec/features/projects/clusters/gcp_spec.rb index 523cc08496b..8953b30bebf 100644 --- a/spec/features/projects/clusters/gcp_spec.rb +++ b/spec/features/projects/clusters/gcp_spec.rb @@ -13,6 +13,8 @@ feature 'Gcp Cluster', :js do end context 'when user has signed with Google' do + let(:project_id) { 'test-project-1234' } + before do allow_any_instance_of(Projects::Clusters::GcpController) .to receive(:token_in_session).and_return('token') @@ -23,7 +25,7 @@ feature 'Gcp Cluster', :js do context 'when user has a GCP project with billing enabled' do before do allow_any_instance_of(Projects::Clusters::GcpController).to receive(:authorize_google_project_billing) - stub_google_project_billing_status + allow_any_instance_of(Projects::Clusters::GcpController).to receive(:google_project_billing_status).and_return('true') end context 'when user does not have a cluster and visits cluster index page' do @@ -131,15 +133,41 @@ feature 'Gcp Cluster', :js do context 'when user does not have a GCP project with billing enabled' do before do + allow_any_instance_of(Projects::Clusters::GcpController).to receive(:authorize_google_project_billing) + allow_any_instance_of(Projects::Clusters::GcpController).to receive(:google_project_billing_status).and_return('false') + visit project_clusters_path(project) click_link 'Add cluster' click_link 'Create on GKE' + + fill_in 'cluster_provider_gcp_attributes_gcp_project_id', with: 'gcp-project-123' + fill_in 'cluster_name', with: 'dev-cluster' + click_button 'Create cluster' + end + + it 'user sees form with error' do + expect(page).to have_content('Please enable billing for one of your projects to be able to create a cluster, then try again.') + end + end + + context 'when gcp billing status is not in redis' do + before do + allow_any_instance_of(Projects::Clusters::GcpController).to receive(:authorize_google_project_billing) + allow_any_instance_of(Projects::Clusters::GcpController).to receive(:google_project_billing_status).and_return(nil) + + visit project_clusters_path(project) + + click_link 'Add cluster' + click_link 'Create on GKE' + + fill_in 'cluster_provider_gcp_attributes_gcp_project_id', with: 'gcp-project-123' + fill_in 'cluster_name', with: 'dev-cluster' + click_button 'Create cluster' end - it 'user sees a check page' do - pending 'the frontend still has not been implemented' - expect(page).to have_link('Continue') + it 'user sees form with error' do + expect(page).to have_content('We could not verify that one of your projects on GCP has billing enabled. Please try again.') end end end diff --git a/spec/features/projects/commits/user_browses_commits_spec.rb b/spec/features/projects/commits/user_browses_commits_spec.rb index 41f3c15a94c..b650c1f4197 100644 --- a/spec/features/projects/commits/user_browses_commits_spec.rb +++ b/spec/features/projects/commits/user_browses_commits_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe 'User broweses commits' do +describe 'User browses commits' do let(:user) { create(:user) } let(:project) { create(:project, :repository, namespace: user.namespace) } @@ -31,6 +31,19 @@ describe 'User broweses commits' do check_author_link(RepoHelpers.sample_commit.author_email, user) end end + + context 'when the blob does not exist' do + let(:commit) { create(:commit, project: project) } + + it 'shows a blank label' do + allow_any_instance_of(Gitlab::Diff::File).to receive(:blob).and_return(nil) + allow_any_instance_of(Gitlab::Diff::File).to receive(:raw_binary?).and_return(true) + + visit(project_commit_path(project, commit)) + + expect(find('.diff-file-changes', visible: false)).to have_content('No file name available') + end + end end private diff --git a/spec/features/projects/tree/create_directory_spec.rb b/spec/features/projects/tree/create_directory_spec.rb index 3f6d16c8acf..0c67196f53e 100644 --- a/spec/features/projects/tree/create_directory_spec.rb +++ b/spec/features/projects/tree/create_directory_spec.rb @@ -14,7 +14,7 @@ feature 'Multi-file editor new directory', :js do wait_for_requests - click_link('Multi Edit') + click_link('Web IDE') wait_for_requests end diff --git a/spec/features/projects/tree/create_file_spec.rb b/spec/features/projects/tree/create_file_spec.rb index ba71eef07f4..85f7318c05d 100644 --- a/spec/features/projects/tree/create_file_spec.rb +++ b/spec/features/projects/tree/create_file_spec.rb @@ -14,7 +14,7 @@ feature 'Multi-file editor new file', :js do wait_for_requests - click_link('Multi Edit') + click_link('Web IDE') wait_for_requests end diff --git a/spec/features/projects/tree/upload_file_spec.rb b/spec/features/projects/tree/upload_file_spec.rb index 9fbb1dbd0e8..f81e8677e92 100644 --- a/spec/features/projects/tree/upload_file_spec.rb +++ b/spec/features/projects/tree/upload_file_spec.rb @@ -16,7 +16,7 @@ feature 'Multi-file editor upload file', :js do wait_for_requests - click_link('Multi Edit') + click_link('Web IDE') wait_for_requests end diff --git a/spec/fixtures/api/schemas/entities/issue.json b/spec/fixtures/api/schemas/entities/issue.json index 3d3329a3406..38467b4ca20 100644 --- a/spec/fixtures/api/schemas/entities/issue.json +++ b/spec/fixtures/api/schemas/entities/issue.json @@ -28,7 +28,6 @@ "confidential": { "type": "boolean" }, "discussion_locked": { "type": ["boolean", "null"] }, "updated_by_id": { "type": ["string", "null"] }, - "deleted_at": { "type": ["string", "null"] }, "time_estimate": { "type": "integer" }, "total_time_spent": { "type": "integer" }, "human_time_estimate": { "type": ["integer", "null"] }, diff --git a/spec/fixtures/api/schemas/entities/merge_request_widget.json b/spec/fixtures/api/schemas/entities/merge_request_widget.json index 7f662098216..05461787f06 100644 --- a/spec/fixtures/api/schemas/entities/merge_request_widget.json +++ b/spec/fixtures/api/schemas/entities/merge_request_widget.json @@ -13,7 +13,6 @@ "updated_by_id": { "type": ["string", "null"] }, "created_at": { "type": "string" }, "updated_at": { "type": "string" }, - "deleted_at": { "type": ["string", "null"] }, "time_estimate": { "type": "integer" }, "total_time_spent": { "type": "integer" }, "human_time_estimate": { "type": ["integer", "null"] }, diff --git a/spec/fixtures/api/schemas/public_api/v4/pipelines.json b/spec/fixtures/api/schemas/public_api/v4/pipelines.json new file mode 100644 index 00000000000..8b08a00f708 --- /dev/null +++ b/spec/fixtures/api/schemas/public_api/v4/pipelines.json @@ -0,0 +1,4 @@ +{ + "type": "array", + "items": { "$ref": "pipeline/basic.json" } +} diff --git a/spec/javascripts/boards/list_spec.js b/spec/javascripts/boards/list_spec.js index 645ce831b53..e5e7b48228b 100644 --- a/spec/javascripts/boards/list_spec.js +++ b/spec/javascripts/boards/list_spec.js @@ -5,7 +5,7 @@ import MockAdapter from 'axios-mock-adapter'; import axios from '~/lib/utils/axios_utils'; - +import _ from 'underscore'; import '~/boards/models/issue'; import '~/boards/models/label'; import '~/boards/models/list'; diff --git a/spec/javascripts/boards/mock_data.js b/spec/javascripts/boards/mock_data.js index 9ae2d535398..0671facb285 100644 --- a/spec/javascripts/boards/mock_data.js +++ b/spec/javascripts/boards/mock_data.js @@ -1,5 +1,6 @@ /* global BoardService */ /* eslint-disable comma-dangle, no-unused-vars, quote-props */ +import _ from 'underscore'; export const listObj = { id: 300, diff --git a/spec/javascripts/commit/pipelines/pipelines_spec.js b/spec/javascripts/commit/pipelines/pipelines_spec.js index 9fc047b1f5e..d62c2966a8b 100644 --- a/spec/javascripts/commit/pipelines/pipelines_spec.js +++ b/spec/javascripts/commit/pipelines/pipelines_spec.js @@ -1,3 +1,4 @@ +import _ from 'underscore'; import Vue from 'vue'; import pipelinesTable from '~/commit/pipelines/pipelines_table.vue'; diff --git a/spec/javascripts/deploy_keys/components/app_spec.js b/spec/javascripts/deploy_keys/components/app_spec.js index 0ca9290d3d2..b870f87eab9 100644 --- a/spec/javascripts/deploy_keys/components/app_spec.js +++ b/spec/javascripts/deploy_keys/components/app_spec.js @@ -1,3 +1,4 @@ +import _ from 'underscore'; import Vue from 'vue'; import eventHub from '~/deploy_keys/eventhub'; import deployKeysApp from '~/deploy_keys/components/app.vue'; diff --git a/spec/javascripts/environments/environments_app_spec.js b/spec/javascripts/environments/environments_app_spec.js index d02adb25b4e..a41a4e5a3f7 100644 --- a/spec/javascripts/environments/environments_app_spec.js +++ b/spec/javascripts/environments/environments_app_spec.js @@ -1,3 +1,4 @@ +import _ from 'underscore'; import Vue from 'vue'; import environmentsComponent from '~/environments/components/environments_app.vue'; import { environment, folder } from './mock_data'; diff --git a/spec/javascripts/environments/folder/environments_folder_view_spec.js b/spec/javascripts/environments/folder/environments_folder_view_spec.js index 4ea4d9d7499..a085074d312 100644 --- a/spec/javascripts/environments/folder/environments_folder_view_spec.js +++ b/spec/javascripts/environments/folder/environments_folder_view_spec.js @@ -1,3 +1,4 @@ +import _ from 'underscore'; import Vue from 'vue'; import environmentsFolderViewComponent from '~/environments/folder/environments_folder_view.vue'; import { environmentsList } from '../mock_data'; diff --git a/spec/javascripts/filtered_search/filtered_search_visual_tokens_spec.js b/spec/javascripts/filtered_search/filtered_search_visual_tokens_spec.js index 2ecb64d84b5..0684c3498a2 100644 --- a/spec/javascripts/filtered_search/filtered_search_visual_tokens_spec.js +++ b/spec/javascripts/filtered_search/filtered_search_visual_tokens_spec.js @@ -1,3 +1,4 @@ +import _ from 'underscore'; import AjaxCache from '~/lib/utils/ajax_cache'; import UsersCache from '~/lib/utils/users_cache'; diff --git a/spec/javascripts/groups/components/app_spec.js b/spec/javascripts/groups/components/app_spec.js index 97e39f6411b..8338efe915b 100644 --- a/spec/javascripts/groups/components/app_spec.js +++ b/spec/javascripts/groups/components/app_spec.js @@ -256,6 +256,36 @@ describe('AppComponent', () => { }); }); + describe('showLeaveGroupModal', () => { + it('caches candidate group (as props) which is to be left', () => { + const group = Object.assign({}, mockParentGroupItem); + expect(vm.targetGroup).toBe(null); + expect(vm.targetParentGroup).toBe(null); + vm.showLeaveGroupModal(group, mockParentGroupItem); + expect(vm.targetGroup).not.toBe(null); + expect(vm.targetParentGroup).not.toBe(null); + }); + + it('updates props which show modal confirmation dialog', () => { + const group = Object.assign({}, mockParentGroupItem); + expect(vm.showModal).toBeFalsy(); + expect(vm.groupLeaveConfirmationMessage).toBe(''); + vm.showLeaveGroupModal(group, mockParentGroupItem); + expect(vm.showModal).toBeTruthy(); + expect(vm.groupLeaveConfirmationMessage).toBe(`Are you sure you want to leave the "${group.fullName}" group?`); + }); + }); + + describe('hideLeaveGroupModal', () => { + it('hides modal confirmation which is shown before leaving the group', () => { + const group = Object.assign({}, mockParentGroupItem); + vm.showLeaveGroupModal(group, mockParentGroupItem); + expect(vm.showModal).toBeTruthy(); + vm.hideLeaveGroupModal(); + expect(vm.showModal).toBeFalsy(); + }); + }); + describe('leaveGroup', () => { let groupItem; let childGroupItem; @@ -265,21 +295,24 @@ describe('AppComponent', () => { groupItem.children = mockChildren; childGroupItem = groupItem.children[0]; groupItem.isChildrenLoading = false; + vm.targetGroup = childGroupItem; + vm.targetParentGroup = groupItem; }); - it('should leave group and remove group item from tree', (done) => { + it('hides modal confirmation leave group and remove group item from tree', (done) => { const notice = `You left the "${childGroupItem.fullName}" group.`; spyOn(vm.service, 'leaveGroup').and.returnValue(returnServicePromise({ notice })); spyOn(vm.store, 'removeGroup').and.callThrough(); spyOn(window, 'Flash'); spyOn($, 'scrollTo'); - vm.leaveGroup(childGroupItem, groupItem); - expect(childGroupItem.isBeingRemoved).toBeTruthy(); - expect(vm.service.leaveGroup).toHaveBeenCalledWith(childGroupItem.leavePath); + vm.leaveGroup(); + expect(vm.showModal).toBeFalsy(); + expect(vm.targetGroup.isBeingRemoved).toBeTruthy(); + expect(vm.service.leaveGroup).toHaveBeenCalledWith(vm.targetGroup.leavePath); setTimeout(() => { expect($.scrollTo).toHaveBeenCalledWith(0); - expect(vm.store.removeGroup).toHaveBeenCalledWith(childGroupItem, groupItem); + expect(vm.store.removeGroup).toHaveBeenCalledWith(vm.targetGroup, vm.targetParentGroup); expect(window.Flash).toHaveBeenCalledWith(notice, 'notice'); done(); }, 0); @@ -291,13 +324,13 @@ describe('AppComponent', () => { spyOn(vm.store, 'removeGroup').and.callThrough(); spyOn(window, 'Flash'); - vm.leaveGroup(childGroupItem, groupItem); - expect(childGroupItem.isBeingRemoved).toBeTruthy(); + vm.leaveGroup(); + expect(vm.targetGroup.isBeingRemoved).toBeTruthy(); expect(vm.service.leaveGroup).toHaveBeenCalledWith(childGroupItem.leavePath); setTimeout(() => { expect(vm.store.removeGroup).not.toHaveBeenCalled(); expect(window.Flash).toHaveBeenCalledWith(message); - expect(childGroupItem.isBeingRemoved).toBeFalsy(); + expect(vm.targetGroup.isBeingRemoved).toBeFalsy(); done(); }, 0); }); @@ -309,12 +342,12 @@ describe('AppComponent', () => { spyOn(window, 'Flash'); vm.leaveGroup(childGroupItem, groupItem); - expect(childGroupItem.isBeingRemoved).toBeTruthy(); + expect(vm.targetGroup.isBeingRemoved).toBeTruthy(); expect(vm.service.leaveGroup).toHaveBeenCalledWith(childGroupItem.leavePath); setTimeout(() => { expect(vm.store.removeGroup).not.toHaveBeenCalled(); expect(window.Flash).toHaveBeenCalledWith(message); - expect(childGroupItem.isBeingRemoved).toBeFalsy(); + expect(vm.targetGroup.isBeingRemoved).toBeFalsy(); done(); }, 0); }); @@ -364,7 +397,7 @@ describe('AppComponent', () => { Vue.nextTick(() => { expect(eventHub.$on).toHaveBeenCalledWith('fetchPage', jasmine.any(Function)); expect(eventHub.$on).toHaveBeenCalledWith('toggleChildren', jasmine.any(Function)); - expect(eventHub.$on).toHaveBeenCalledWith('leaveGroup', jasmine.any(Function)); + expect(eventHub.$on).toHaveBeenCalledWith('showLeaveGroupModal', jasmine.any(Function)); expect(eventHub.$on).toHaveBeenCalledWith('updatePagination', jasmine.any(Function)); expect(eventHub.$on).toHaveBeenCalledWith('updateGroups', jasmine.any(Function)); newVm.$destroy(); @@ -404,7 +437,7 @@ describe('AppComponent', () => { Vue.nextTick(() => { expect(eventHub.$off).toHaveBeenCalledWith('fetchPage', jasmine.any(Function)); expect(eventHub.$off).toHaveBeenCalledWith('toggleChildren', jasmine.any(Function)); - expect(eventHub.$off).toHaveBeenCalledWith('leaveGroup', jasmine.any(Function)); + expect(eventHub.$off).toHaveBeenCalledWith('showLeaveGroupModal', jasmine.any(Function)); expect(eventHub.$off).toHaveBeenCalledWith('updatePagination', jasmine.any(Function)); expect(eventHub.$off).toHaveBeenCalledWith('updateGroups', jasmine.any(Function)); done(); @@ -439,5 +472,14 @@ describe('AppComponent', () => { done(); }); }); + + it('renders modal confirmation dialog', () => { + vm.groupLeaveConfirmationMessage = 'Are you sure you want to leave the "foo" group?'; + vm.showModal = true; + const modalDialogEl = vm.$el.querySelector('.modal'); + expect(modalDialogEl).not.toBe(null); + expect(modalDialogEl.querySelector('.modal-title').innerText.trim()).toBe('Are you sure?'); + expect(modalDialogEl.querySelector('.btn.btn-warning').innerText.trim()).toBe('Leave'); + }); }); }); diff --git a/spec/javascripts/groups/components/item_actions_spec.js b/spec/javascripts/groups/components/item_actions_spec.js index 6d6fb410859..acccbe639c4 100644 --- a/spec/javascripts/groups/components/item_actions_spec.js +++ b/spec/javascripts/groups/components/item_actions_spec.js @@ -26,32 +26,12 @@ describe('ItemActionsComponent', () => { vm.$destroy(); }); - describe('computed', () => { - describe('leaveConfirmationMessage', () => { - it('should return appropriate string for leave group confirmation', () => { - expect(vm.leaveConfirmationMessage).toBe('Are you sure you want to leave the "platform / hardware" group?'); - }); - }); - }); - describe('methods', () => { describe('onLeaveGroup', () => { - it('should change `modalStatus` prop to `true` which shows confirmation dialog', () => { - expect(vm.modalStatus).toBeFalsy(); - vm.onLeaveGroup(); - expect(vm.modalStatus).toBeTruthy(); - }); - }); - - describe('leaveGroup', () => { - it('should change `modalStatus` prop to `false` and emit `leaveGroup` event with required params when called with `leaveConfirmed` as `true`', () => { + it('emits `showLeaveGroupModal` event with `group` and `parentGroup` props', () => { spyOn(eventHub, '$emit'); - vm.modalStatus = true; - - vm.leaveGroup(); - - expect(vm.modalStatus).toBeFalsy(); - expect(eventHub.$emit).toHaveBeenCalledWith('leaveGroup', vm.group, vm.parentGroup); + vm.onLeaveGroup(); + expect(eventHub.$emit).toHaveBeenCalledWith('showLeaveGroupModal', vm.group, vm.parentGroup); }); }); }); @@ -72,7 +52,8 @@ describe('ItemActionsComponent', () => { expect(editBtn.getAttribute('href')).toBe(group.editPath); expect(editBtn.getAttribute('aria-label')).toBe('Edit group'); expect(editBtn.dataset.originalTitle).toBe('Edit group'); - expect(editBtn.querySelector('i.fa.fa-cogs')).toBeDefined(); + expect(editBtn.querySelectorAll('svg use').length).not.toBe(0); + expect(editBtn.querySelector('svg use').getAttribute('xlink:href')).toContain('#settings'); newVm.$destroy(); }); @@ -88,17 +69,10 @@ describe('ItemActionsComponent', () => { expect(leaveBtn.getAttribute('href')).toBe(group.leavePath); expect(leaveBtn.getAttribute('aria-label')).toBe('Leave this group'); expect(leaveBtn.dataset.originalTitle).toBe('Leave this group'); - expect(leaveBtn.querySelector('i.fa.fa-sign-out')).toBeDefined(); + expect(leaveBtn.querySelectorAll('svg use').length).not.toBe(0); + expect(leaveBtn.querySelector('svg use').getAttribute('xlink:href')).toContain('#leave'); newVm.$destroy(); }); - - it('should show modal dialog when `modalStatus` is set to `true`', () => { - vm.modalStatus = true; - const modalDialogEl = vm.$el.querySelector('.modal'); - expect(modalDialogEl).toBeDefined(); - expect(modalDialogEl.querySelector('.modal-title').innerText.trim()).toBe('Are you sure?'); - expect(modalDialogEl.querySelector('.btn.btn-warning').innerText.trim()).toBe('Leave'); - }); }); }); diff --git a/spec/javascripts/issue_show/components/fields/description_template_spec.js b/spec/javascripts/issue_show/components/fields/description_template_spec.js index 2b7ee65094b..30441faf844 100644 --- a/spec/javascripts/issue_show/components/fields/description_template_spec.js +++ b/spec/javascripts/issue_show/components/fields/description_template_spec.js @@ -1,7 +1,5 @@ import Vue from 'vue'; import descriptionTemplate from '~/issue_show/components/fields/description_template.vue'; -import '~/templates/issuable_template_selector'; -import '~/templates/issuable_template_selectors'; describe('Issue description template component', () => { let vm; diff --git a/spec/javascripts/issue_show/components/form_spec.js b/spec/javascripts/issue_show/components/form_spec.js index 000b53af016..50ce019c32a 100644 --- a/spec/javascripts/issue_show/components/form_spec.js +++ b/spec/javascripts/issue_show/components/form_spec.js @@ -1,7 +1,5 @@ import Vue from 'vue'; import formComponent from '~/issue_show/components/form.vue'; -import '~/templates/issuable_template_selector'; -import '~/templates/issuable_template_selectors'; describe('Inline edit form component', () => { let vm; diff --git a/spec/javascripts/merge_request_notes_spec.js b/spec/javascripts/merge_request_notes_spec.js index e983e4de3fc..5d0ee91d977 100644 --- a/spec/javascripts/merge_request_notes_spec.js +++ b/spec/javascripts/merge_request_notes_spec.js @@ -1,3 +1,4 @@ +import _ from 'underscore'; import 'autosize'; import '~/gl_form'; import '~/lib/utils/text_utility'; diff --git a/spec/javascripts/notes/components/comment_form_spec.js b/spec/javascripts/notes/components/comment_form_spec.js index 20e352dd8bd..104d03377b6 100644 --- a/spec/javascripts/notes/components/comment_form_spec.js +++ b/spec/javascripts/notes/components/comment_form_spec.js @@ -139,13 +139,21 @@ describe('issue_comment_form component', () => { }); describe('event enter', () => { - it('should save note when cmd/ctrl+enter is pressed', () => { + it('should save note when cmd+enter is pressed', () => { spyOn(vm, 'handleSave').and.callThrough(); vm.$el.querySelector('.js-main-target-form textarea').value = 'Foo'; vm.$el.querySelector('.js-main-target-form textarea').dispatchEvent(keyboardDownEvent(13, true)); expect(vm.handleSave).toHaveBeenCalled(); }); + + it('should save note when ctrl+enter is pressed', () => { + spyOn(vm, 'handleSave').and.callThrough(); + vm.$el.querySelector('.js-main-target-form textarea').value = 'Foo'; + vm.$el.querySelector('.js-main-target-form textarea').dispatchEvent(keyboardDownEvent(13, false, true)); + + expect(vm.handleSave).toHaveBeenCalled(); + }); }); }); diff --git a/spec/javascripts/notes/components/note_app_spec.js b/spec/javascripts/notes/components/note_app_spec.js index 7c8d6685ee1..36c56cd3862 100644 --- a/spec/javascripts/notes/components/note_app_spec.js +++ b/spec/javascripts/notes/components/note_app_spec.js @@ -1,3 +1,4 @@ +import _ from 'underscore'; import Vue from 'vue'; import notesApp from '~/notes/components/notes_app.vue'; import service from '~/notes/services/notes_service'; diff --git a/spec/javascripts/notes/components/note_form_spec.js b/spec/javascripts/notes/components/note_form_spec.js index 86e9e2a32a9..f841a408d09 100644 --- a/spec/javascripts/notes/components/note_form_spec.js +++ b/spec/javascripts/notes/components/note_form_spec.js @@ -69,13 +69,20 @@ describe('issue_note_form component', () => { }); describe('enter', () => { - it('should submit note', () => { + it('should save note when cmd+enter is pressed', () => { spyOn(vm, 'handleUpdate').and.callThrough(); vm.$el.querySelector('textarea').value = 'Foo'; vm.$el.querySelector('textarea').dispatchEvent(keyboardDownEvent(13, true)); expect(vm.handleUpdate).toHaveBeenCalled(); }); + it('should save note when ctrl+enter is pressed', () => { + spyOn(vm, 'handleUpdate').and.callThrough(); + vm.$el.querySelector('textarea').value = 'Foo'; + vm.$el.querySelector('textarea').dispatchEvent(keyboardDownEvent(13, false, true)); + + expect(vm.handleUpdate).toHaveBeenCalled(); + }); }); }); diff --git a/spec/javascripts/notes/components/noteable_note_spec.js b/spec/javascripts/notes/components/noteable_note_spec.js index c8a6cb7e612..cb63b64724d 100644 --- a/spec/javascripts/notes/components/noteable_note_spec.js +++ b/spec/javascripts/notes/components/noteable_note_spec.js @@ -1,4 +1,4 @@ - +import _ from 'underscore'; import Vue from 'vue'; import store from '~/notes/stores'; import issueNote from '~/notes/components/noteable_note.vue'; diff --git a/spec/javascripts/notes/mock_data.js b/spec/javascripts/notes/mock_data.js index 6b608adff15..b020a1020df 100644 --- a/spec/javascripts/notes/mock_data.js +++ b/spec/javascripts/notes/mock_data.js @@ -29,7 +29,6 @@ export const noteableDataMock = { can_create_note: true, can_update: true, }, - deleted_at: null, description: '', due_date: null, human_time_estimate: null, @@ -283,7 +282,6 @@ export const loggedOutnoteableData = { "updated_by_id": 1, "created_at": "2017-02-07T10:11:18.395Z", "updated_at": "2017-08-08T10:22:51.564Z", - "deleted_at": null, "time_estimate": 0, "total_time_spent": 0, "human_time_estimate": null, diff --git a/spec/javascripts/notes_spec.js b/spec/javascripts/notes_spec.js index 167f074fb9b..a40821a5693 100644 --- a/spec/javascripts/notes_spec.js +++ b/spec/javascripts/notes_spec.js @@ -1,4 +1,5 @@ /* eslint-disable space-before-function-paren, no-unused-expressions, no-var, object-shorthand, comma-dangle, max-len */ +import _ from 'underscore'; import * as urlUtils from '~/lib/utils/url_utility'; import 'autosize'; import '~/gl_form'; diff --git a/spec/javascripts/oauth_remember_me_spec.js b/spec/javascripts/oauth_remember_me_spec.js index f90e0093d25..b24563f738b 100644 --- a/spec/javascripts/oauth_remember_me_spec.js +++ b/spec/javascripts/oauth_remember_me_spec.js @@ -1,4 +1,4 @@ -import OAuthRememberMe from '~/oauth_remember_me'; +import OAuthRememberMe from '~/pages/sessions/new/oauth_remember_me'; describe('OAuthRememberMe', () => { preloadFixtures('static/oauth_remember_me.html.raw'); diff --git a/spec/javascripts/abuse_reports_spec.js b/spec/javascripts/pages/admin/abuse_reports/abuse_reports_spec.js index 7f6b5873011..d2386077aa6 100644 --- a/spec/javascripts/abuse_reports_spec.js +++ b/spec/javascripts/pages/admin/abuse_reports/abuse_reports_spec.js @@ -1,5 +1,5 @@ import '~/lib/utils/text_utility'; -import AbuseReports from '~/abuse_reports'; +import AbuseReports from '~/pages/admin/abuse_reports/abuse_reports'; describe('Abuse Reports', () => { const FIXTURE = 'abuse_reports/abuse_reports_list.html.raw'; diff --git a/spec/javascripts/pipelines/pipeline_details_mediator_spec.js b/spec/javascripts/pipelines/pipeline_details_mediator_spec.js index 9fec2f61f78..bc6413a159f 100644 --- a/spec/javascripts/pipelines/pipeline_details_mediator_spec.js +++ b/spec/javascripts/pipelines/pipeline_details_mediator_spec.js @@ -1,3 +1,4 @@ +import _ from 'underscore'; import Vue from 'vue'; import PipelineMediator from '~/pipelines/pipeline_details_mediatior'; diff --git a/spec/javascripts/pipelines/pipelines_spec.js b/spec/javascripts/pipelines/pipelines_spec.js index 367b42cefb0..a99ebc4e51a 100644 --- a/spec/javascripts/pipelines/pipelines_spec.js +++ b/spec/javascripts/pipelines/pipelines_spec.js @@ -1,3 +1,4 @@ +import _ from 'underscore'; import Vue from 'vue'; import pipelinesComp from '~/pipelines/components/pipelines.vue'; import Store from '~/pipelines/stores/pipelines_store'; diff --git a/spec/javascripts/pipelines/stage_spec.js b/spec/javascripts/pipelines/stage_spec.js index 1b96b2e3d51..61c2f783acc 100644 --- a/spec/javascripts/pipelines/stage_spec.js +++ b/spec/javascripts/pipelines/stage_spec.js @@ -1,3 +1,4 @@ +import _ from 'underscore'; import Vue from 'vue'; import stage from '~/pipelines/components/stage.vue'; diff --git a/spec/javascripts/registry/components/app_spec.js b/spec/javascripts/registry/components/app_spec.js index 87259fe0bab..6a8a85e3dfb 100644 --- a/spec/javascripts/registry/components/app_spec.js +++ b/spec/javascripts/registry/components/app_spec.js @@ -1,3 +1,4 @@ +import _ from 'underscore'; import Vue from 'vue'; import registry from '~/registry/components/app.vue'; import mountComponent from '../../helpers/vue_mount_component_helper'; diff --git a/spec/javascripts/sidebar/mock_data.js b/spec/javascripts/sidebar/mock_data.js index 3b094d20838..7bc591d2d47 100644 --- a/spec/javascripts/sidebar/mock_data.js +++ b/spec/javascripts/sidebar/mock_data.js @@ -15,7 +15,6 @@ const RESPONSE_MAP = { updated_by_id: 1, created_at: '2017-02-02T21: 49: 49.664Z', updated_at: '2017-05-03T22: 26: 03.760Z', - deleted_at: null, time_estimate: 0, total_time_spent: 0, human_time_estimate: null, @@ -153,7 +152,6 @@ const RESPONSE_MAP = { updated_by_id: 1, created_at: '2017-06-27T19:54:42.437Z', updated_at: '2017-08-18T03:39:49.222Z', - deleted_at: null, time_estimate: 0, total_time_spent: 0, human_time_estimate: null, diff --git a/spec/javascripts/sidebar/sidebar_assignees_spec.js b/spec/javascripts/sidebar/sidebar_assignees_spec.js index b97e24d9dcf..6bb6d639f24 100644 --- a/spec/javascripts/sidebar/sidebar_assignees_spec.js +++ b/spec/javascripts/sidebar/sidebar_assignees_spec.js @@ -1,3 +1,4 @@ +import _ from 'underscore'; import Vue from 'vue'; import SidebarAssignees from '~/sidebar/components/assignees/sidebar_assignees'; import SidebarMediator from '~/sidebar/sidebar_mediator'; diff --git a/spec/javascripts/sidebar/sidebar_mediator_spec.js b/spec/javascripts/sidebar/sidebar_mediator_spec.js index 9efd109b996..afa18cc127e 100644 --- a/spec/javascripts/sidebar/sidebar_mediator_spec.js +++ b/spec/javascripts/sidebar/sidebar_mediator_spec.js @@ -1,3 +1,4 @@ +import _ from 'underscore'; import Vue from 'vue'; import * as urlUtils from '~/lib/utils/url_utility'; import SidebarMediator from '~/sidebar/sidebar_mediator'; diff --git a/spec/javascripts/sidebar/sidebar_move_issue_spec.js b/spec/javascripts/sidebar/sidebar_move_issue_spec.js index 8b0d51bbcc8..97f762d07a7 100644 --- a/spec/javascripts/sidebar/sidebar_move_issue_spec.js +++ b/spec/javascripts/sidebar/sidebar_move_issue_spec.js @@ -1,3 +1,4 @@ +import _ from 'underscore'; import Vue from 'vue'; import SidebarMediator from '~/sidebar/sidebar_mediator'; import SidebarStore from '~/sidebar/stores/sidebar_store'; diff --git a/spec/javascripts/signin_tabs_memoizer_spec.js b/spec/javascripts/signin_tabs_memoizer_spec.js index a53e8a94d89..c4f500788b2 100644 --- a/spec/javascripts/signin_tabs_memoizer_spec.js +++ b/spec/javascripts/signin_tabs_memoizer_spec.js @@ -1,5 +1,5 @@ import AccessorUtilities from '~/lib/utils/accessor'; -import SigninTabsMemoizer from '~/signin_tabs_memoizer'; +import SigninTabsMemoizer from '~/pages/sessions/new/signin_tabs_memoizer'; (() => { describe('SigninTabsMemoizer', () => { diff --git a/spec/javascripts/smart_interval_spec.js b/spec/javascripts/smart_interval_spec.js index 1c87fcec245..7265e1b6cb5 100644 --- a/spec/javascripts/smart_interval_spec.js +++ b/spec/javascripts/smart_interval_spec.js @@ -1,3 +1,4 @@ +import _ from 'underscore'; import SmartInterval from '~/smart_interval'; describe('SmartInterval', function () { diff --git a/spec/javascripts/test_bundle.js b/spec/javascripts/test_bundle.js index 6897c991066..2f6691df9cd 100644 --- a/spec/javascripts/test_bundle.js +++ b/spec/javascripts/test_bundle.js @@ -1,6 +1,5 @@ /* eslint-disable jasmine/no-global-setup */ import $ from 'jquery'; -import _ from 'underscore'; import 'jasmine-jquery'; import '~/commons'; @@ -31,7 +30,6 @@ jasmine.getJSONFixtures().fixturesPath = '/base/spec/javascripts/fixtures'; // globalize common libraries window.$ = window.jQuery = $; -window._ = _; // stub expected globals window.gl = window.gl || {}; diff --git a/spec/javascripts/vue_mr_widget/mock_data.js b/spec/javascripts/vue_mr_widget/mock_data.js index ca29c9fee32..ae494267659 100644 --- a/spec/javascripts/vue_mr_widget/mock_data.js +++ b/spec/javascripts/vue_mr_widget/mock_data.js @@ -14,7 +14,6 @@ export default { "updated_by_id": null, "created_at": "2017-04-07T12:27:26.718Z", "updated_at": "2017-04-07T15:39:25.852Z", - "deleted_at": null, "time_estimate": 0, "total_time_spent": 0, "human_time_estimate": null, diff --git a/spec/javascripts/vue_shared/components/clipboard_button_spec.js b/spec/javascripts/vue_shared/components/clipboard_button_spec.js new file mode 100644 index 00000000000..08e4e1f8337 --- /dev/null +++ b/spec/javascripts/vue_shared/components/clipboard_button_spec.js @@ -0,0 +1,31 @@ +import Vue from 'vue'; +import clipboardButton from '~/vue_shared/components/clipboard_button.vue'; +import mountComponent from '../../helpers/vue_mount_component_helper'; + +describe('clipboard button', () => { + let vm; + + beforeEach(() => { + const Component = Vue.extend(clipboardButton); + vm = mountComponent(Component, { + text: 'copy me', + title: 'Copy this value into Clipboard!', + }); + }); + + afterEach(() => { + vm.$destroy(); + }); + + it('renders a button for clipboard', () => { + expect(vm.$el.tagName).toEqual('BUTTON'); + expect(vm.$el.getAttribute('data-clipboard-text')).toEqual('copy me'); + expect(vm.$el.querySelector('i').className).toEqual('fa fa-clipboard'); + }); + + it('should have a tooltip with default values', () => { + expect(vm.$el.getAttribute('data-original-title')).toEqual('Copy this value into Clipboard!'); + expect(vm.$el.getAttribute('data-placement')).toEqual('top'); + expect(vm.$el.getAttribute('data-container')).toEqual(null); + }); +}); diff --git a/spec/javascripts/vue_shared/components/user_avatar/user_avatar_link_spec.js b/spec/javascripts/vue_shared/components/user_avatar/user_avatar_link_spec.js index 8450ad9dbcb..adf80d0c2bb 100644 --- a/spec/javascripts/vue_shared/components/user_avatar/user_avatar_link_spec.js +++ b/spec/javascripts/vue_shared/components/user_avatar/user_avatar_link_spec.js @@ -1,3 +1,4 @@ +import _ from 'underscore'; import Vue from 'vue'; import UserAvatarLink from '~/vue_shared/components/user_avatar/user_avatar_link.vue'; diff --git a/spec/javascripts/zen_mode_spec.js b/spec/javascripts/zen_mode_spec.js index 45a0bb0650f..8edba1f47a3 100644 --- a/spec/javascripts/zen_mode_spec.js +++ b/spec/javascripts/zen_mode_spec.js @@ -1,4 +1,4 @@ -/* global Mousetrap */ +import Mousetrap from 'mousetrap'; import Dropzone from 'dropzone'; import ZenMode from '~/zen_mode'; diff --git a/spec/lib/banzai/filter/relative_link_filter_spec.rb b/spec/lib/banzai/filter/relative_link_filter_spec.rb index f38f0776303..7e17457ce70 100644 --- a/spec/lib/banzai/filter/relative_link_filter_spec.rb +++ b/spec/lib/banzai/filter/relative_link_filter_spec.rb @@ -8,7 +8,8 @@ describe Banzai::Filter::RelativeLinkFilter do group: group, project_wiki: project_wiki, ref: ref, - requested_path: requested_path + requested_path: requested_path, + only_path: only_path }) described_class.call(doc, contexts) @@ -37,6 +38,7 @@ describe Banzai::Filter::RelativeLinkFilter do let(:commit) { project.commit(ref) } let(:project_wiki) { nil } let(:requested_path) { '/' } + let(:only_path) { true } shared_examples :preserve_unchanged do it 'does not modify any relative URL in anchor' do @@ -240,26 +242,35 @@ describe Banzai::Filter::RelativeLinkFilter do let(:commit) { nil } let(:ref) { nil } let(:requested_path) { nil } + let(:upload_path) { '/uploads/e90decf88d8f96fe9e1389afc2e4a91f/test.jpg' } + let(:relative_path) { "/#{project.full_path}#{upload_path}" } context 'to a project upload' do + context 'with an absolute URL' do + let(:absolute_path) { Gitlab.config.gitlab.url + relative_path } + let(:only_path) { false } + + it 'rewrites the link correctly' do + doc = filter(link(upload_path)) + + expect(doc.at_css('a')['href']).to eq(absolute_path) + end + end + it 'rebuilds relative URL for a link' do - doc = filter(link('/uploads/e90decf88d8f96fe9e1389afc2e4a91f/test.jpg')) - expect(doc.at_css('a')['href']) - .to eq "/#{project.full_path}/uploads/e90decf88d8f96fe9e1389afc2e4a91f/test.jpg" + doc = filter(link(upload_path)) + expect(doc.at_css('a')['href']).to eq(relative_path) - doc = filter(nested(link('/uploads/e90decf88d8f96fe9e1389afc2e4a91f/test.jpg'))) - expect(doc.at_css('a')['href']) - .to eq "/#{project.full_path}/uploads/e90decf88d8f96fe9e1389afc2e4a91f/test.jpg" + doc = filter(nested(link(upload_path))) + expect(doc.at_css('a')['href']).to eq(relative_path) end it 'rebuilds relative URL for an image' do - doc = filter(image('/uploads/e90decf88d8f96fe9e1389afc2e4a91f/test.jpg')) - expect(doc.at_css('img')['src']) - .to eq "/#{project.full_path}/uploads/e90decf88d8f96fe9e1389afc2e4a91f/test.jpg" + doc = filter(image(upload_path)) + expect(doc.at_css('img')['src']).to eq(relative_path) - doc = filter(nested(image('/uploads/e90decf88d8f96fe9e1389afc2e4a91f/test.jpg'))) - expect(doc.at_css('img')['src']) - .to eq "/#{project.full_path}/uploads/e90decf88d8f96fe9e1389afc2e4a91f/test.jpg" + doc = filter(nested(image(upload_path))) + expect(doc.at_css('img')['src']).to eq(relative_path) end it 'does not modify absolute URL' do @@ -288,6 +299,17 @@ describe Banzai::Filter::RelativeLinkFilter do let(:project) { nil } let(:relative_path) { "/groups/#{group.full_path}/-/uploads/e90decf88d8f96fe9e1389afc2e4a91f/test.jpg" } + context 'with an absolute URL' do + let(:absolute_path) { Gitlab.config.gitlab.url + relative_path } + let(:only_path) { false } + + it 'rewrites the link correctly' do + doc = filter(upload_link) + + expect(doc.at_css('a')['href']).to eq(absolute_path) + end + end + it 'rewrites the link correctly' do doc = filter(upload_link) diff --git a/spec/lib/gitlab/background_migration/add_merge_request_diff_commits_count_spec.rb b/spec/lib/gitlab/background_migration/add_merge_request_diff_commits_count_spec.rb new file mode 100644 index 00000000000..21a791f5695 --- /dev/null +++ b/spec/lib/gitlab/background_migration/add_merge_request_diff_commits_count_spec.rb @@ -0,0 +1,50 @@ +require 'spec_helper' + +describe Gitlab::BackgroundMigration::AddMergeRequestDiffCommitsCount, :migration, schema: 20180105212544 do + let(:projects_table) { table(:projects) } + let(:merge_requests_table) { table(:merge_requests) } + let(:merge_request_diffs_table) { table(:merge_request_diffs) } + let(:merge_request_diff_commits_table) { table(:merge_request_diff_commits) } + + let(:project) { projects_table.create!(name: 'gitlab', path: 'gitlab-org/gitlab-ce') } + let(:merge_request) do + merge_requests_table.create!(target_project_id: project.id, + target_branch: 'master', + source_project_id: project.id, + source_branch: 'mr name', + title: 'mr name') + end + + def create_diff!(name, commits: 0) + mr_diff = merge_request_diffs_table.create!( + merge_request_id: merge_request.id) + + commits.times do |i| + merge_request_diff_commits_table.create!( + merge_request_diff_id: mr_diff.id, + relative_order: i, sha: i) + end + + mr_diff + end + + describe '#perform' do + it 'migrates diffs that have no commits' do + diff = create_diff!('with_multiple_commits', commits: 0) + + subject.perform(diff.id, diff.id) + + expect(diff.reload.commits_count).to eq(0) + end + + it 'migrates multiple diffs to the correct values' do + diffs = Array.new(3).map.with_index { |_, i| create_diff!(i, commits: 3) } + + subject.perform(diffs.first.id, diffs.last.id) + + diffs.each do |diff| + expect(diff.reload.commits_count).to eq(3) + end + end + end +end diff --git a/spec/lib/gitlab/background_migration/deserialize_merge_request_diffs_and_commits_spec.rb b/spec/lib/gitlab/background_migration/deserialize_merge_request_diffs_and_commits_spec.rb index 84d9e635810..98730602863 100644 --- a/spec/lib/gitlab/background_migration/deserialize_merge_request_diffs_and_commits_spec.rb +++ b/spec/lib/gitlab/background_migration/deserialize_merge_request_diffs_and_commits_spec.rb @@ -10,6 +10,11 @@ describe Gitlab::BackgroundMigration::DeserializeMergeRequestDiffsAndCommits, :t let(:merge_request_diff) { MergeRequest.find(merge_request.id).create_merge_request_diff } let(:updated_merge_request_diff) { MergeRequestDiff.find(merge_request_diff.id) } + before do + allow_any_instance_of(MergeRequestDiff) + .to receive(:commits_count=).and_return(nil) + end + def diffs_to_hashes(diffs) diffs.as_json(only: Gitlab::Git::Diff::SERIALIZE_KEYS).map(&:with_indifferent_access) end diff --git a/spec/lib/gitlab/background_migration/populate_merge_request_metrics_with_events_data_spec.rb b/spec/lib/gitlab/background_migration/populate_merge_request_metrics_with_events_data_spec.rb index dfe3b31f1c0..e99257e3481 100644 --- a/spec/lib/gitlab/background_migration/populate_merge_request_metrics_with_events_data_spec.rb +++ b/spec/lib/gitlab/background_migration/populate_merge_request_metrics_with_events_data_spec.rb @@ -1,6 +1,12 @@ require 'rails_helper' describe Gitlab::BackgroundMigration::PopulateMergeRequestMetricsWithEventsData, :migration, schema: 20171128214150 do + # commits_count attribute is added in a next migration + before do + allow_any_instance_of(MergeRequestDiff) + .to receive(:commits_count=).and_return(nil) + end + describe '#perform' do let(:mr_with_event) { create(:merge_request) } let!(:merged_event) { create(:event, :merged, target: mr_with_event) } diff --git a/spec/lib/gitlab/git/repository_spec.rb b/spec/lib/gitlab/git/repository_spec.rb index f346a345f00..f4e781c599e 100644 --- a/spec/lib/gitlab/git/repository_spec.rb +++ b/spec/lib/gitlab/git/repository_spec.rb @@ -1283,48 +1283,58 @@ describe Gitlab::Git::Repository, seed_helper: true do end describe '#merged_branch_names' do - context 'when branch names are passed' do - it 'only returns the names we are asking' do - names = repository.merged_branch_names(%w[merge-test]) + shared_examples 'finding merged branch names' do + context 'when branch names are passed' do + it 'only returns the names we are asking' do + names = repository.merged_branch_names(%w[merge-test]) - expect(names).to contain_exactly('merge-test') - end + expect(names).to contain_exactly('merge-test') + end - it 'does not return unmerged branch names' do - names = repository.merged_branch_names(%w[feature]) + it 'does not return unmerged branch names' do + names = repository.merged_branch_names(%w[feature]) - expect(names).to be_empty + expect(names).to be_empty + end end - end - context 'when no root ref is available' do - it 'returns empty list' do - project = create(:project, :empty_repo) + context 'when no root ref is available' do + it 'returns empty list' do + project = create(:project, :empty_repo) - names = project.repository.merged_branch_names(%w[feature]) + names = project.repository.merged_branch_names(%w[feature]) - expect(names).to be_empty + expect(names).to be_empty + end end - end - context 'when no branch names are specified' do - before do - repository.create_branch('identical', 'master') - end + context 'when no branch names are specified' do + before do + repository.create_branch('identical', 'master') + end - after do - ensure_seeds - end + after do + ensure_seeds + end - it 'returns all merged branch names except for identical one' do - names = repository.merged_branch_names + it 'returns all merged branch names except for identical one' do + names = repository.merged_branch_names - expect(names).to include('merge-test') - expect(names).to include('fix-mode') - expect(names).not_to include('feature') - expect(names).not_to include('identical') + expect(names).to include('merge-test') + expect(names).to include('fix-mode') + expect(names).not_to include('feature') + expect(names).not_to include('identical') + end end end + + context 'when Gitaly merged_branch_names feature is enabled' do + it_behaves_like 'finding merged branch names' + end + + context 'when Gitaly merged_branch_names feature is disabled', :disable_gitaly do + it_behaves_like 'finding merged branch names' + end end describe "#ls_files" do diff --git a/spec/lib/gitlab/hook_data/issue_builder_spec.rb b/spec/lib/gitlab/hook_data/issue_builder_spec.rb index aeacd577d18..506b2c0be20 100644 --- a/spec/lib/gitlab/hook_data/issue_builder_spec.rb +++ b/spec/lib/gitlab/hook_data/issue_builder_spec.rb @@ -14,7 +14,6 @@ describe Gitlab::HookData::IssueBuilder do closed_at confidential created_at - deleted_at description due_date id diff --git a/spec/lib/gitlab/hook_data/merge_request_builder_spec.rb b/spec/lib/gitlab/hook_data/merge_request_builder_spec.rb index 78475403f9e..b61614e4790 100644 --- a/spec/lib/gitlab/hook_data/merge_request_builder_spec.rb +++ b/spec/lib/gitlab/hook_data/merge_request_builder_spec.rb @@ -12,7 +12,6 @@ describe Gitlab::HookData::MergeRequestBuilder do assignee_id author_id created_at - deleted_at description head_pipeline_id id diff --git a/spec/lib/gitlab/import_export/project.group.json b/spec/lib/gitlab/import_export/project.group.json index 82a1fbd2fc5..1a561e81e4a 100644 --- a/spec/lib/gitlab/import_export/project.group.json +++ b/spec/lib/gitlab/import_export/project.group.json @@ -54,7 +54,6 @@ "iid": 1, "updated_by_id": 1, "confidential": false, - "deleted_at": null, "due_date": null, "moved_to_id": null, "lock_version": null, @@ -134,7 +133,6 @@ "iid": 2, "updated_by_id": 1, "confidential": false, - "deleted_at": null, "due_date": null, "moved_to_id": null, "lock_version": null, diff --git a/spec/lib/gitlab/import_export/project.json b/spec/lib/gitlab/import_export/project.json index 7580b62cfc0..4cf33778d15 100644 --- a/spec/lib/gitlab/import_export/project.json +++ b/spec/lib/gitlab/import_export/project.json @@ -56,7 +56,6 @@ "iid": 10, "updated_by_id": null, "confidential": false, - "deleted_at": null, "due_date": null, "moved_to_id": null, "test_ee_field": "test", @@ -350,7 +349,6 @@ "iid": 9, "updated_by_id": null, "confidential": false, - "deleted_at": null, "due_date": null, "moved_to_id": null, "milestone": { @@ -586,7 +584,6 @@ "iid": 8, "updated_by_id": null, "confidential": false, - "deleted_at": null, "due_date": null, "moved_to_id": null, "label_links": [ @@ -820,7 +817,6 @@ "iid": 7, "updated_by_id": null, "confidential": false, - "deleted_at": null, "due_date": null, "moved_to_id": null, "notes": [ @@ -1033,7 +1029,6 @@ "iid": 6, "updated_by_id": null, "confidential": false, - "deleted_at": null, "due_date": null, "moved_to_id": null, "notes": [ @@ -1246,7 +1241,6 @@ "iid": 5, "updated_by_id": null, "confidential": false, - "deleted_at": null, "due_date": null, "moved_to_id": null, "notes": [ @@ -1459,7 +1453,6 @@ "iid": 4, "updated_by_id": null, "confidential": false, - "deleted_at": null, "due_date": null, "moved_to_id": null, "notes": [ @@ -1672,7 +1665,6 @@ "iid": 3, "updated_by_id": null, "confidential": false, - "deleted_at": null, "due_date": null, "moved_to_id": null, "notes": [ @@ -1885,7 +1877,6 @@ "iid": 2, "updated_by_id": null, "confidential": false, - "deleted_at": null, "due_date": null, "moved_to_id": null, "notes": [ @@ -2098,7 +2089,6 @@ "iid": 1, "updated_by_id": null, "confidential": false, - "deleted_at": null, "due_date": null, "moved_to_id": null, "notes": [ @@ -2504,7 +2494,6 @@ "merge_when_pipeline_succeeds": true, "merge_user_id": null, "merge_commit_sha": null, - "deleted_at": null, "notes": [ { "id": 671, @@ -2948,7 +2937,6 @@ "merge_when_pipeline_succeeds": false, "merge_user_id": null, "merge_commit_sha": null, - "deleted_at": null, "notes": [ { "id": 679, @@ -3228,7 +3216,6 @@ "merge_when_pipeline_succeeds": false, "merge_user_id": null, "merge_commit_sha": null, - "deleted_at": null, "notes": [ { "id": 777, @@ -3508,7 +3495,6 @@ "merge_when_pipeline_succeeds": false, "merge_user_id": null, "merge_commit_sha": null, - "deleted_at": null, "notes": [ { "id": 785, @@ -4198,7 +4184,6 @@ "merge_when_pipeline_succeeds": false, "merge_user_id": null, "merge_commit_sha": null, - "deleted_at": null, "notes": [ { "id": 793, @@ -4734,7 +4719,6 @@ "merge_when_pipeline_succeeds": false, "merge_user_id": null, "merge_commit_sha": null, - "deleted_at": null, "notes": [ { "id": 801, @@ -5223,7 +5207,6 @@ "merge_when_pipeline_succeeds": false, "merge_user_id": null, "merge_commit_sha": null, - "deleted_at": null, "notes": [ { "id": 809, @@ -5478,7 +5461,6 @@ "merge_when_pipeline_succeeds": false, "merge_user_id": null, "merge_commit_sha": null, - "deleted_at": null, "notes": [ { "id": 817, @@ -6168,7 +6150,6 @@ "merge_when_pipeline_succeeds": false, "merge_user_id": null, "merge_commit_sha": null, - "deleted_at": null, "notes": [ { "id": 825, @@ -6968,7 +6949,6 @@ "id": 123, "token": "cdbfasdf44a5958c83654733449e585", "project_id": 5, - "deleted_at": null, "created_at": "2017-01-16T15:25:28.637Z", "updated_at": "2017-01-16T15:25:28.637Z" } diff --git a/spec/lib/gitlab/import_export/project.light.json b/spec/lib/gitlab/import_export/project.light.json index 02450478a77..5dbf0ed289b 100644 --- a/spec/lib/gitlab/import_export/project.light.json +++ b/spec/lib/gitlab/import_export/project.light.json @@ -54,7 +54,6 @@ "iid": 20, "updated_by_id": 1, "confidential": false, - "deleted_at": null, "due_date": null, "moved_to_id": null, "lock_version": null, diff --git a/spec/lib/gitlab/import_export/safe_model_attributes.yml b/spec/lib/gitlab/import_export/safe_model_attributes.yml index ec577903eb5..5a33fa3fd53 100644 --- a/spec/lib/gitlab/import_export/safe_model_attributes.yml +++ b/spec/lib/gitlab/import_export/safe_model_attributes.yml @@ -14,7 +14,6 @@ Issue: - iid - updated_by_id - confidential -- deleted_at - closed_at - due_date - moved_to_id @@ -159,7 +158,6 @@ MergeRequest: - merge_when_pipeline_succeeds - merge_user_id - merge_commit_sha -- deleted_at - in_progress_merge_commit_sha - lock_version - milestone_id @@ -180,6 +178,7 @@ MergeRequestDiff: - real_size - head_commit_sha - start_commit_sha +- commits_count MergeRequestDiffCommit: - merge_request_diff_id - relative_order @@ -293,7 +292,6 @@ Ci::Trigger: - id - token - project_id -- deleted_at - created_at - updated_at - owner_id @@ -309,7 +307,6 @@ Ci::PipelineSchedule: - project_id - owner_id - active -- deleted_at - created_at - updated_at Clusters::Cluster: diff --git a/spec/lib/gitlab/workhorse_spec.rb b/spec/lib/gitlab/workhorse_spec.rb index 249c77dc636..2e7a0265a0b 100644 --- a/spec/lib/gitlab/workhorse_spec.rb +++ b/spec/lib/gitlab/workhorse_spec.rb @@ -26,11 +26,16 @@ describe Gitlab::Workhorse do 'GitalyRepository' => repository.gitaly_repository.to_h.deep_stringify_keys ) end + let(:cache_disabled) { false } subject do described_class.send_git_archive(repository, ref: ref, format: format) end + before do + allow(described_class).to receive(:git_archive_cache_disabled?).and_return(cache_disabled) + end + context 'when Gitaly workhorse_archive feature is enabled' do it 'sets the header correctly' do key, command, params = decode_workhorse_header(subject) @@ -39,6 +44,15 @@ describe Gitlab::Workhorse do expect(command).to eq('git-archive') expect(params).to include(gitaly_params) end + + context 'when archive caching is disabled' do + let(:cache_disabled) { true } + + it 'tells workhorse not to use the cache' do + _, _, params = decode_workhorse_header(subject) + expect(params).to include({ 'DisableCache' => true }) + end + end end context 'when Gitaly workhorse_archive feature is disabled', :skip_gitaly_mock do diff --git a/spec/migrations/remove_soft_removed_objects_spec.rb b/spec/migrations/remove_soft_removed_objects_spec.rb new file mode 100644 index 00000000000..ec089f9106d --- /dev/null +++ b/spec/migrations/remove_soft_removed_objects_spec.rb @@ -0,0 +1,77 @@ +require 'spec_helper' +require Rails.root.join('db', 'post_migrate', '20171207150343_remove_soft_removed_objects.rb') + +describe RemoveSoftRemovedObjects, :migration do + describe '#up' do + it 'removes various soft removed objects' do + 5.times do + create_with_deleted_at(:issue) + end + + regular_issue = create(:issue) + + run_migration + + expect(Issue.count).to eq(1) + expect(Issue.first).to eq(regular_issue) + end + + it 'removes the temporary indexes once soft removed data has been removed' do + migration = described_class.new + + run_migration + + disable_migrations_output do + expect(migration.temporary_index_exists?(Issue)).to eq(false) + end + end + + it 'removes routes of soft removed personal namespaces' do + namespace = create_with_deleted_at(:namespace) + group = create(:group) + + expect(Route.where(source: namespace).exists?).to eq(true) + expect(Route.where(source: group).exists?).to eq(true) + + run_migration + + expect(Route.where(source: namespace).exists?).to eq(false) + expect(Route.where(source: group).exists?).to eq(true) + end + + it 'schedules the removal of soft removed groups' do + group = create_with_deleted_at(:group) + admin = create(:user, admin: true) + + expect_any_instance_of(GroupDestroyWorker) + .to receive(:perform) + .with(group.id, admin.id) + + run_migration + end + + it 'does not remove soft removed groups when no admin user could be found' do + create_with_deleted_at(:group) + + expect_any_instance_of(GroupDestroyWorker) + .not_to receive(:perform) + + run_migration + end + end + + def run_migration + disable_migrations_output do + migrate! + end + end + + def create_with_deleted_at(*args) + row = create(*args) + + # We set "deleted_at" this way so we don't run into any column cache issues. + row.class.where(id: row.id).update_all(deleted_at: 1.year.ago) + + row + end +end diff --git a/spec/migrations/schedule_populate_merge_request_metrics_with_events_data_spec.rb b/spec/migrations/schedule_populate_merge_request_metrics_with_events_data_spec.rb index 2e6b2cff0ab..7494624066a 100644 --- a/spec/migrations/schedule_populate_merge_request_metrics_with_events_data_spec.rb +++ b/spec/migrations/schedule_populate_merge_request_metrics_with_events_data_spec.rb @@ -2,6 +2,12 @@ require 'spec_helper' require Rails.root.join('db', 'post_migrate', '20171128214150_schedule_populate_merge_request_metrics_with_events_data.rb') describe SchedulePopulateMergeRequestMetricsWithEventsData, :migration, :sidekiq do + # commits_count attribute is added in a next migration + before do + allow_any_instance_of(MergeRequestDiff) + .to receive(:commits_count=).and_return(nil) + end + let!(:mrs) { create_list(:merge_request, 3) } it 'correctly schedules background migrations' do diff --git a/spec/models/ci/pipeline_schedule_spec.rb b/spec/models/ci/pipeline_schedule_spec.rb index 9a278212efc..8ee15f0e734 100644 --- a/spec/models/ci/pipeline_schedule_spec.rb +++ b/spec/models/ci/pipeline_schedule_spec.rb @@ -12,7 +12,6 @@ describe Ci::PipelineSchedule do it { is_expected.to respond_to(:cron_timezone) } it { is_expected.to respond_to(:description) } it { is_expected.to respond_to(:next_run_at) } - it { is_expected.to respond_to(:deleted_at) } describe 'validations' do it 'does not allow invalid cron patters' do diff --git a/spec/models/issue_spec.rb b/spec/models/issue_spec.rb index 5ced000cdb6..f5c9f551e65 100644 --- a/spec/models/issue_spec.rb +++ b/spec/models/issue_spec.rb @@ -18,11 +18,6 @@ describe Issue do subject { create(:issue) } - describe "act_as_paranoid" do - it { is_expected.to have_db_column(:deleted_at) } - it { is_expected.to have_db_index(:deleted_at) } - end - describe 'callbacks' do describe '#ensure_metrics' do it 'creates metrics after saving' do diff --git a/spec/models/merge_request_spec.rb b/spec/models/merge_request_spec.rb index 07b3e1c1758..8ff82c4f791 100644 --- a/spec/models/merge_request_spec.rb +++ b/spec/models/merge_request_spec.rb @@ -24,11 +24,6 @@ describe MergeRequest do it { is_expected.to include_module(Taskable) } end - describe "act_as_paranoid" do - it { is_expected.to have_db_column(:deleted_at) } - it { is_expected.to have_db_index(:deleted_at) } - end - describe 'validation' do it { is_expected.to validate_presence_of(:target_branch) } it { is_expected.to validate_presence_of(:source_branch) } @@ -1915,38 +1910,44 @@ describe MergeRequest do end describe '#rebase_in_progress?' do - # Create merge request and project before we stub file calls - before do - subject - end + shared_examples 'checking whether a rebase is in progress' do + let(:repo_path) { subject.source_project.repository.path } + let(:rebase_path) { File.join(repo_path, "gitlab-worktree", "rebase-#{subject.id}") } - it 'returns true when there is a current rebase directory' do - allow(File).to receive(:exist?).and_return(true) - allow(File).to receive(:mtime).and_return(Time.now) + before do + system(*%W(#{Gitlab.config.git.bin_path} -C #{repo_path} worktree add --detach #{rebase_path} master)) + end - expect(subject.rebase_in_progress?).to be_truthy - end + it 'returns true when there is a current rebase directory' do + expect(subject.rebase_in_progress?).to be_truthy + end - it 'returns false when there is no rebase directory' do - allow(File).to receive(:exist?).and_return(false) + it 'returns false when there is no rebase directory' do + FileUtils.rm_rf(rebase_path) - expect(subject.rebase_in_progress?).to be_falsey - end + expect(subject.rebase_in_progress?).to be_falsey + end + + it 'returns false when the rebase directory has expired' do + time = 20.minutes.ago.to_time + File.utime(time, time, rebase_path) + + expect(subject.rebase_in_progress?).to be_falsey + end - it 'returns false when the rebase directory has expired' do - allow(File).to receive(:exist?).and_return(true) - allow(File).to receive(:mtime).and_return(20.minutes.ago) + it 'returns false when the source project has been removed' do + allow(subject).to receive(:source_project).and_return(nil) - expect(subject.rebase_in_progress?).to be_falsey + expect(subject.rebase_in_progress?).to be_falsey + end end - it 'returns false when the source project has been removed' do - allow(subject).to receive(:source_project).and_return(nil) - allow(File).to receive(:exist?).and_return(true) - allow(File).to receive(:mtime).and_return(Time.now) + context 'when Gitaly rebase_in_progress is enabled' do + it_behaves_like 'checking whether a rebase is in progress' + end - expect(File).not_to have_received(:exist?) - expect(subject.rebase_in_progress?).to be_falsey + context 'when Gitaly rebase_in_progress is enabled', :disable_gitaly do + it_behaves_like 'checking whether a rebase is in progress' end end end diff --git a/spec/models/namespace_spec.rb b/spec/models/namespace_spec.rb index b3f160f3119..c3673a0e2a3 100644 --- a/spec/models/namespace_spec.rb +++ b/spec/models/namespace_spec.rb @@ -410,17 +410,6 @@ describe Namespace do end end - describe '#soft_delete_without_removing_associations' do - let(:project1) { create(:project_empty_repo, namespace: namespace) } - - it 'updates the deleted_at timestamp but preserves projects' do - namespace.soft_delete_without_removing_associations - - expect(Project.all).to include(project1) - expect(namespace.deleted_at).not_to be_nil - end - end - describe '#user_ids_for_project_authorizations' do it 'returns the user IDs for which to refresh authorizations' do expect(namespace.user_ids_for_project_authorizations) diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb index 00afa09f1a3..78223c44999 100644 --- a/spec/models/project_spec.rb +++ b/spec/models/project_spec.rb @@ -1871,9 +1871,8 @@ describe Project do end it 'creates the new reference with rugged' do - expect(project.repository.rugged.references).to receive(:create).with('HEAD', - "refs/heads/#{project.default_branch}", - force: true) + expect(project.repository.raw_repository).to receive(:write_ref).with('HEAD', "refs/heads/#{project.default_branch}", shell: false) + project.change_head(project.default_branch) end diff --git a/spec/models/route_spec.rb b/spec/models/route_spec.rb index ddad6862a63..8a3b1034f3c 100644 --- a/spec/models/route_spec.rb +++ b/spec/models/route_spec.rb @@ -16,6 +16,66 @@ describe Route do it { is_expected.to validate_presence_of(:source) } it { is_expected.to validate_presence_of(:path) } it { is_expected.to validate_uniqueness_of(:path).case_insensitive } + + describe '#ensure_permanent_paths' do + context 'when the route is not yet persisted' do + let(:new_route) { described_class.new(path: 'foo', source: build(:group)) } + + context 'when permanent conflicting redirects exist' do + it 'is invalid' do + redirect = build(:redirect_route, :permanent, path: 'foo/bar/baz') + redirect.save!(validate: false) + + expect(new_route.valid?).to be_falsey + expect(new_route.errors.first[1]).to eq('foo has been taken before. Please use another one') + end + end + + context 'when no permanent conflicting redirects exist' do + it 'is valid' do + expect(new_route.valid?).to be_truthy + end + end + end + + context 'when path has changed' do + before do + route.path = 'foo' + end + + context 'when permanent conflicting redirects exist' do + it 'is invalid' do + redirect = build(:redirect_route, :permanent, path: 'foo/bar/baz') + redirect.save!(validate: false) + + expect(route.valid?).to be_falsey + expect(route.errors.first[1]).to eq('foo has been taken before. Please use another one') + end + end + + context 'when no permanent conflicting redirects exist' do + it 'is valid' do + expect(route.valid?).to be_truthy + end + end + end + + context 'when path has not changed' do + context 'when permanent conflicting redirects exist' do + it 'is valid' do + redirect = build(:redirect_route, :permanent, path: 'git_lab/foo/bar') + redirect.save!(validate: false) + + expect(route.valid?).to be_truthy + end + end + context 'when no permanent conflicting redirects exist' do + it 'is valid' do + expect(route.valid?).to be_truthy + end + end + end + end end describe 'callbacks' do diff --git a/spec/requests/api/commit_statuses_spec.rb b/spec/requests/api/commit_statuses_spec.rb index ffa17d296e8..f246bb79ab7 100644 --- a/spec/requests/api/commit_statuses_spec.rb +++ b/spec/requests/api/commit_statuses_spec.rb @@ -142,6 +142,7 @@ describe API::CommitStatuses do expect(json_response['ref']).not_to be_empty expect(json_response['target_url']).to be_nil expect(json_response['description']).to be_nil + if status == 'failed' expect(CommitStatus.find(json_response['id'])).to be_api_failure end diff --git a/spec/requests/api/merge_requests_spec.rb b/spec/requests/api/merge_requests_spec.rb index 0c9fbb1f187..4eae3e50602 100644 --- a/spec/requests/api/merge_requests_spec.rb +++ b/spec/requests/api/merge_requests_spec.rb @@ -551,6 +551,49 @@ describe API::MergeRequests do end end + describe 'GET /projects/:id/merge_requests/:merge_request_iid/pipelines' do + context 'when authorized' do + let!(:pipeline) { create(:ci_empty_pipeline, project: project, user: user, ref: merge_request.source_branch, sha: merge_request.diff_head_sha) } + let!(:pipeline2) { create(:ci_empty_pipeline, project: project) } + + it 'returns a paginated array of corresponding pipelines' do + get api("/projects/#{project.id}/merge_requests/#{merge_request.iid}/pipelines") + + expect(response).to have_gitlab_http_status(200) + expect(response).to include_pagination_headers + expect(json_response).to be_an Array + expect(json_response.count).to eq(1) + expect(json_response.first['id']).to eq(pipeline.id) + end + + it 'exposes basic attributes' do + get api("/projects/#{project.id}/merge_requests/#{merge_request.iid}/pipelines") + + expect(response).to have_gitlab_http_status(200) + expect(response).to match_response_schema('public_api/v4/pipelines') + end + + it 'returns 404 if MR does not exist' do + get api("/projects/#{project.id}/merge_requests/777/pipelines") + + expect(response).to have_gitlab_http_status(404) + end + end + + context 'when unauthorized' do + it 'returns 403' do + project = create(:project, public_builds: false) + merge_request = create(:merge_request, :simple, source_project: project) + guest = create(:user) + project.add_guest(guest) + + get api("/projects/#{project.id}/merge_requests/#{merge_request.iid}/pipelines", guest) + + expect(response).to have_gitlab_http_status(403) + end + end + end + describe "POST /projects/:id/merge_requests" do context 'between branches projects' do it "returns merge_request" do diff --git a/spec/requests/api/runner_spec.rb b/spec/requests/api/runner_spec.rb index 679d391caa5..cb66d23b77c 100644 --- a/spec/requests/api/runner_spec.rb +++ b/spec/requests/api/runner_spec.rb @@ -1142,6 +1142,7 @@ describe API::Runner do else { 'file' => file } end + post api("/jobs/#{job.id}/artifacts"), params, headers end end diff --git a/spec/rubocop/cop/line_break_around_conditional_block_spec.rb b/spec/rubocop/cop/line_break_around_conditional_block_spec.rb new file mode 100644 index 00000000000..7ddf9141fcd --- /dev/null +++ b/spec/rubocop/cop/line_break_around_conditional_block_spec.rb @@ -0,0 +1,411 @@ +require 'spec_helper' +require 'rubocop' +require 'rubocop/rspec/support' +require_relative '../../../rubocop/cop/line_break_around_conditional_block' + +describe RuboCop::Cop::LineBreakAroundConditionalBlock do + include CopHelper + + subject(:cop) { described_class.new } + + shared_examples 'examples with conditional' do |conditional| + it "flags violation for #{conditional} without line break before" do + source = <<~RUBY + do_something + #{conditional} condition + do_something_more + end + RUBY + inspect_source(source) + + expect(cop.offenses.size).to eq(1) + offense = cop.offenses.first + + expect(offense.line).to eq(2) + expect(cop.highlights).to eq(["#{conditional} condition\n do_something_more\nend"]) + expect(offense.message).to eq('Add a line break around conditional blocks') + end + + it "flags violation for #{conditional} without line break after" do + source = <<~RUBY + #{conditional} condition + do_something + end + do_something_more + RUBY + inspect_source(source) + + expect(cop.offenses.size).to eq(1) + offense = cop.offenses.first + + expect(offense.line).to eq(1) + expect(cop.highlights).to eq(["#{conditional} condition\n do_something\nend"]) + expect(offense.message).to eq('Add a line break around conditional blocks') + end + + it "doesn't flag violation for #{conditional} with line break before and after" do + source = <<~RUBY + #{conditional} condition + do_something + end + RUBY + inspect_source(source) + + expect(cop.offenses).to be_empty + end + + it "doesn't flag violation for #{conditional} preceded by a method definition" do + source = <<~RUBY + def a_method + #{conditional} condition + do_something + end + end + RUBY + inspect_source(source) + + expect(cop.offenses).to be_empty + end + + it "doesn't flag violation for #{conditional} preceded by a class definition" do + source = <<~RUBY + class Foo + #{conditional} condition + do_something + end + end + RUBY + inspect_source(source) + + expect(cop.offenses).to be_empty + end + + it "doesn't flag violation for #{conditional} preceded by a module definition" do + source = <<~RUBY + module Foo + #{conditional} condition + do_something + end + end + RUBY + inspect_source(source) + + expect(cop.offenses).to be_empty + end + + it "doesn't flag violation for #{conditional} preceded by a begin definition" do + source = <<~RUBY + begin + #{conditional} condition + do_something + end + end + RUBY + inspect_source(source) + + expect(cop.offenses).to be_empty + end + + it "doesn't flag violation for #{conditional} preceded by an assign/begin definition" do + source = <<~RUBY + @project ||= begin + #{conditional} condition + do_something + end + end + RUBY + inspect_source(source) + + expect(cop.offenses).to be_empty + end + + it "doesn't flag violation for #{conditional} preceded by a block definition" do + source = <<~RUBY + on_block(param_a) do |item| + #{conditional} condition + do_something + end + end + RUBY + inspect_source(source) + + expect(cop.offenses).to be_empty + end + + it "doesn't flag violation for #{conditional} preceded by a block definition using brackets" do + source = <<~RUBY + on_block(param_a) { |item| + #{conditional} condition + do_something + end + } + RUBY + inspect_source(source) + + expect(cop.offenses).to be_empty + end + + it "doesn't flag violation for #{conditional} preceded by a comment" do + source = <<~RUBY + # a short comment + #{conditional} condition + do_something + end + RUBY + inspect_source(source) + + expect(cop.offenses).to be_empty + end + + it "doesn't flag violation for #{conditional} preceded by an assignment" do + source = <<~RUBY + foo = + #{conditional} condition + do_something + else + do_something_more + end + RUBY + inspect_source(source) + + expect(cop.offenses).to be_empty + end + + it "doesn't flag violation for #{conditional} preceded by a multiline comment" do + source = <<~RUBY + =begin + a multiline comment + =end + #{conditional} condition + do_something + end + RUBY + inspect_source(source) + + expect(cop.offenses).to be_empty + end + + it "doesn't flag violation for #{conditional} preceded by another conditional" do + source = <<~RUBY + #{conditional} condition_a + #{conditional} condition_b + do_something + end + end + RUBY + inspect_source(source) + + expect(cop.offenses).to be_empty + end + + it "doesn't flag violation for #{conditional} preceded by an else" do + source = <<~RUBY + if condition_a + do_something + else + #{conditional} condition_b + do_something_extra + end + end + RUBY + inspect_source(source) + + expect(cop.offenses).to be_empty + end + + it "doesn't flag violation for #{conditional} preceded by an elsif" do + source = <<~RUBY + if condition_a + do_something + elsif condition_b + #{conditional} condition_c + do_something_extra + end + end + RUBY + inspect_source(source) + + expect(cop.offenses).to be_empty + end + + it "doesn't flag violation for #{conditional} preceded by an ensure" do + source = <<~RUBY + def a_method + ensure + #{conditional} condition_c + do_something_extra + end + end + RUBY + inspect_source(source) + + expect(cop.offenses).to be_empty + end + + it "doesn't flag violation for #{conditional} preceded by a when" do + source = <<~RUBY + case field + when value + #{conditional} condition_c + do_something_extra + end + end + RUBY + inspect_source(source) + + expect(cop.offenses).to be_empty + end + + it "doesn't flag violation for #{conditional} followed by an end" do + source = <<~RUBY + class Foo + + #{conditional} condition + do_something + end + end + RUBY + inspect_source(source) + + expect(cop.offenses).to be_empty + end + + it "doesn't flag violation for #{conditional} followed by an else" do + source = <<~RUBY + #{conditional} condition_a + #{conditional} condition_b + do_something + end + else + do_something_extra + end + RUBY + inspect_source(source) + + expect(cop.offenses).to be_empty + end + + it "doesn't flag violation for #{conditional} followed by a when" do + source = <<~RUBY + case + when condition_a + #{conditional} condition_b + do_something + end + when condition_c + do_something_extra + end + RUBY + inspect_source(source) + + expect(cop.offenses).to be_empty + end + + it "doesn't flag violation for #{conditional} followed by an elsif" do + source = <<~RUBY + if condition_a + #{conditional} condition_b + do_something + end + elsif condition_c + do_something_extra + end + RUBY + inspect_source(source) + + expect(cop.offenses).to be_empty + end + + it "doesn't flag violation for #{conditional} followed by a rescue" do + source = <<~RUBY + def a_method + #{conditional} condition + do_something + end + rescue + do_something_extra + end + RUBY + inspect_source(source) + + expect(cop.offenses).to be_empty + end + + it "autocorrects #{conditional} without line break before" do + source = <<~RUBY + do_something + #{conditional} condition + do_something_more + end + RUBY + autocorrected = autocorrect_source(source) + + expected_source = <<~RUBY + do_something + + #{conditional} condition + do_something_more + end + RUBY + expect(autocorrected).to eql(expected_source) + end + + it "autocorrects #{conditional} without line break after" do + source = <<~RUBY + #{conditional} condition + do_something + end + do_something_more + RUBY + autocorrected = autocorrect_source(source) + + expected_source = <<~RUBY + #{conditional} condition + do_something + end + + do_something_more + RUBY + expect(autocorrected).to eql(expected_source) + end + + it "autocorrects #{conditional} without line break before and after" do + source = <<~RUBY + do_something + #{conditional} condition + do_something_more + end + do_something_extra + RUBY + autocorrected = autocorrect_source(source) + + expected_source = <<~RUBY + do_something + + #{conditional} condition + do_something_more + end + + do_something_extra + RUBY + expect(autocorrected).to eql(expected_source) + end + end + + %w[if unless].each do |example| + it_behaves_like 'examples with conditional', example + end + + it "doesn't flag violation for if with elsif" do + source = <<~RUBY + if condition + do_something + elsif another_condition + do_something_more + end + RUBY + inspect_source(source) + + expect(cop.offenses).to be_empty + end +end diff --git a/spec/services/check_gcp_project_billing_service_spec.rb b/spec/services/check_gcp_project_billing_service_spec.rb index f0e39ba6f49..3e68d906e71 100644 --- a/spec/services/check_gcp_project_billing_service_spec.rb +++ b/spec/services/check_gcp_project_billing_service_spec.rb @@ -1,29 +1,30 @@ require 'spec_helper' describe CheckGcpProjectBillingService do + include GoogleApi::CloudPlatformHelpers + let(:service) { described_class.new } - let(:projects) { [double(name: 'first_project'), double(name: 'second_project')] } + let(:project_id) { 'test-project-1234' } describe '#execute' do before do - expect_any_instance_of(GoogleApi::CloudPlatform::Client) - .to receive(:projects_list).and_return(projects) - - allow_any_instance_of(GoogleApi::CloudPlatform::Client) - .to receive_message_chain(:projects_get_billing_info, :billingEnabled) - .and_return(project_billing_enabled) + stub_cloud_platform_projects_list(project_id: project_id) end subject { service.execute('bogustoken') } context 'google account has a billing enabled gcp project' do - let(:project_billing_enabled) { true } + before do + stub_cloud_platform_projects_get_billing_info(project_id, true) + end - it { is_expected.to eq(projects) } + it { is_expected.to all(satisfy { |project| project.project_id == project_id }) } end context 'google account does not have a billing enabled gcp project' do - let(:project_billing_enabled) { false } + before do + stub_cloud_platform_projects_get_billing_info(project_id, false) + end it { is_expected.to eq([]) } end diff --git a/spec/services/issues/move_service_spec.rb b/spec/services/issues/move_service_spec.rb index 53ea88332fb..f3c98fa5416 100644 --- a/spec/services/issues/move_service_spec.rb +++ b/spec/services/issues/move_service_spec.rb @@ -179,13 +179,15 @@ describe Issues::MoveService do { system: true, note: 'Some system note' }, { system: false, note: 'Some comment 2' }] end - + let(:award_names) { %w(thumbsup thumbsdown facepalm) } let(:notes_contents) { notes_params.map { |n| n[:note] } } before do note_params = { noteable: old_issue, project: old_project, author: author } - notes_params.each do |note| - create(:note, note_params.merge(note)) + notes_params.each_with_index do |note, index| + new_note = create(:note, note_params.merge(note)) + award_emoji_params = { awardable: new_note, name: award_names[index] } + create(:award_emoji, award_emoji_params) end end @@ -199,6 +201,10 @@ describe Issues::MoveService do expect(all_notes.pluck(:note).first(3)).to eq notes_contents end + it 'creates new emojis for the new notes' do + expect(all_notes.map(&:award_emoji).to_a.flatten.map(&:name)).to eq award_names + end + it 'adds a system note about move after rewritten notes' do expect(system_notes.last.note).to match /^moved from/ end diff --git a/spec/services/merge_requests/build_service_spec.rb b/spec/services/merge_requests/build_service_spec.rb index a9605c6e4c6..cb4c3e72aa0 100644 --- a/spec/services/merge_requests/build_service_spec.rb +++ b/spec/services/merge_requests/build_service_spec.rb @@ -171,6 +171,24 @@ describe MergeRequests::BuildService do end end end + + context 'branch starts with external issue IID followed by a hyphen' do + let(:source_branch) { '12345-fix-issue' } + + before do + allow(project).to receive(:external_issue_tracker).and_return(true) + end + + it 'uses the title of the commit as the title of the merge request' do + expect(merge_request.title).to eq(commit_1.safe_message.split("\n").first) + end + + it 'uses the description of the commit as the description of the merge request and appends the closes text' do + commit_description = commit_1.safe_message.split(/\n+/, 2).last + + expect(merge_request.description).to eq("#{commit_description}\n\nCloses #12345") + end + end end context 'more than one commit in the diff' do @@ -241,8 +259,12 @@ describe MergeRequests::BuildService do allow(project).to receive(:external_issue_tracker).and_return(true) end - it 'sets the title to: Resolves External Issue $issue-iid' do - expect(merge_request.title).to eq('Resolve External Issue 12345') + it 'sets the title to the humanized branch title' do + expect(merge_request.title).to eq('12345 fix issue') + end + + it 'appends the closes text' do + expect(merge_request.description).to eq('Closes #12345') end end end diff --git a/spec/services/merge_requests/rebase_service_spec.rb b/spec/services/merge_requests/rebase_service_spec.rb index d1b37cdd073..fc1c3d67203 100644 --- a/spec/services/merge_requests/rebase_service_spec.rb +++ b/spec/services/merge_requests/rebase_service_spec.rb @@ -32,70 +32,80 @@ describe MergeRequests::RebaseService do it 'returns an error' do expect(service.execute(merge_request)).to match(status: :error, - message: 'Failed to rebase. Should be done manually') + message: described_class::REBASE_ERROR) end end - context 'when unexpected error occurs' do + context 'when unexpected error occurs', :disable_gitaly do before do allow(repository).to receive(:run_git!).and_raise('Something went wrong') end - it 'saves the error message' do + it 'saves a generic error message' do subject.execute(merge_request) - expect(merge_request.reload.merge_error).to eq 'Something went wrong' + expect(merge_request.reload.merge_error).to eq described_class::REBASE_ERROR end it 'returns an error' do expect(service.execute(merge_request)).to match(status: :error, - message: 'Failed to rebase. Should be done manually') + message: described_class::REBASE_ERROR) end end - context 'with git command failure' do + context 'with git command failure', :disable_gitaly do before do allow(repository).to receive(:run_git!).and_raise(Gitlab::Git::Repository::GitError, 'Something went wrong') end - it 'saves the error message' do + it 'saves a generic error message' do subject.execute(merge_request) - expect(merge_request.reload.merge_error).to eq 'Something went wrong' + expect(merge_request.reload.merge_error).to eq described_class::REBASE_ERROR end it 'returns an error' do expect(service.execute(merge_request)).to match(status: :error, - message: 'Failed to rebase. Should be done manually') + message: described_class::REBASE_ERROR) end end context 'valid params' do - before do - service.execute(merge_request) - end + shared_examples 'successful rebase' do + before do + service.execute(merge_request) + end - it 'rebases source branch' do - parent_sha = merge_request.source_project.repository.commit(merge_request.source_branch).parents.first.sha - target_branch_sha = merge_request.target_project.repository.commit(merge_request.target_branch).sha - expect(parent_sha).to eq(target_branch_sha) - end + it 'rebases source branch' do + parent_sha = merge_request.source_project.repository.commit(merge_request.source_branch).parents.first.sha + target_branch_sha = merge_request.target_project.repository.commit(merge_request.target_branch).sha + expect(parent_sha).to eq(target_branch_sha) + end + + it 'records the new SHA on the merge request' do + head_sha = merge_request.source_project.repository.commit(merge_request.source_branch).sha + expect(merge_request.reload.rebase_commit_sha).to eq(head_sha) + end + + it 'logs correct author and commiter' do + head_commit = merge_request.source_project.repository.commit(merge_request.source_branch) - it 'records the new SHA on the merge request' do - head_sha = merge_request.source_project.repository.commit(merge_request.source_branch).sha - expect(merge_request.reload.rebase_commit_sha).to eq(head_sha) + expect(head_commit.author_email).to eq('dmitriy.zaporozhets@gmail.com') + expect(head_commit.author_name).to eq('Dmitriy Zaporozhets') + expect(head_commit.committer_email).to eq(user.email) + expect(head_commit.committer_name).to eq(user.name) + end end - it 'logs correct author and commiter' do - head_commit = merge_request.source_project.repository.commit(merge_request.source_branch) + context 'when Gitaly rebase feature is enabled' do + it_behaves_like 'successful rebase' + end - expect(head_commit.author_email).to eq('dmitriy.zaporozhets@gmail.com') - expect(head_commit.author_name).to eq('Dmitriy Zaporozhets') - expect(head_commit.committer_email).to eq(user.email) - expect(head_commit.committer_name).to eq(user.name) + context 'when Gitaly rebase feature is disabled', :disable_gitaly do + it_behaves_like 'successful rebase' end - context 'git commands' do + context 'git commands', :disable_gitaly do it 'sets GL_REPOSITORY env variable when calling git commands' do expect(repository).to receive(:popen).exactly(3) .with(anything, anything, hash_including('GL_REPOSITORY')) @@ -106,27 +116,37 @@ describe MergeRequests::RebaseService do end context 'fork' do - let(:forked_project) do - fork_project(project, user, repository: true) + shared_examples 'successful fork rebase' do + let(:forked_project) do + fork_project(project, user, repository: true) + end + + let(:merge_request_from_fork) do + forked_project.repository.create_file( + user, + 'new-file-to-target', + '', + message: 'Add new file to target', + branch_name: 'master') + + create(:merge_request, + source_branch: 'master', source_project: forked_project, + target_branch: 'master', target_project: project) + end + + it 'rebases source branch' do + parent_sha = forked_project.repository.commit(merge_request_from_fork.source_branch).parents.first.sha + target_branch_sha = project.repository.commit(merge_request_from_fork.target_branch).sha + expect(parent_sha).to eq(target_branch_sha) + end end - let(:merge_request_from_fork) do - forked_project.repository.create_file( - user, - 'new-file-to-target', - '', - message: 'Add new file to target', - branch_name: 'master') - - create(:merge_request, - source_branch: 'master', source_project: forked_project, - target_branch: 'master', target_project: project) + context 'when Gitaly rebase feature is enabled' do + it_behaves_like 'successful fork rebase' end - it 'rebases source branch' do - parent_sha = forked_project.repository.commit(merge_request_from_fork.source_branch).parents.first.sha - target_branch_sha = project.repository.commit(merge_request_from_fork.target_branch).sha - expect(parent_sha).to eq(target_branch_sha) + context 'when Gitaly rebase feature is disabled', :disable_gitaly do + it_behaves_like 'successful fork rebase' end end end diff --git a/spec/services/projects/transfer_service_spec.rb b/spec/services/projects/transfer_service_spec.rb index 39f6388c25e..ef68742a463 100644 --- a/spec/services/projects/transfer_service_spec.rb +++ b/spec/services/projects/transfer_service_spec.rb @@ -150,6 +150,7 @@ describe Projects::TransferService do before do group.add_owner(user) + unless gitlab_shell.add_repository(repository_storage, "#{group.full_path}/#{project.path}") raise 'failed to add repository' end diff --git a/spec/services/system_note_service_spec.rb b/spec/services/system_note_service_spec.rb index 4e640a82dfc..965fd39c967 100644 --- a/spec/services/system_note_service_spec.rb +++ b/spec/services/system_note_service_spec.rb @@ -727,6 +727,7 @@ describe SystemNoteService do else "#{Settings.gitlab.base_url}/#{project.namespace.path}/#{project.path}/merge_requests/#{merge_request.iid}" end + link = double(object: { 'url' => url }) links << link expect(link).to receive(:save!) diff --git a/spec/services/users/destroy_service_spec.rb b/spec/services/users/destroy_service_spec.rb index aeba9cd60bc..bb3d73edf8e 100644 --- a/spec/services/users/destroy_service_spec.rb +++ b/spec/services/users/destroy_service_spec.rb @@ -15,7 +15,7 @@ describe Users::DestroyService do expect { user_data['email'].to eq(user.email) } expect { User.find(user.id) }.to raise_error(ActiveRecord::RecordNotFound) - expect { Namespace.with_deleted.find(namespace.id) }.to raise_error(ActiveRecord::RecordNotFound) + expect { Namespace.find(namespace.id) }.to raise_error(ActiveRecord::RecordNotFound) end it 'will delete the project' do diff --git a/spec/support/features/discussion_comments_shared_example.rb b/spec/support/features/discussion_comments_shared_example.rb index c24940393f9..fa94aa2ae3d 100644 --- a/spec/support/features/discussion_comments_shared_example.rb +++ b/spec/support/features/discussion_comments_shared_example.rb @@ -113,6 +113,7 @@ shared_examples 'discussion comments' do |resource_name| else expect(find(submit_selector).value).to eq 'Start discussion' end + expect(page).not_to have_selector menu_selector end @@ -200,6 +201,7 @@ shared_examples 'discussion comments' do |resource_name| else expect(find(submit_selector).value).to eq 'Comment' end + expect(page).not_to have_selector menu_selector end diff --git a/spec/support/filtered_search_helpers.rb b/spec/support/filtered_search_helpers.rb index 05021ea9054..f3f96bd1f0a 100644 --- a/spec/support/filtered_search_helpers.rb +++ b/spec/support/filtered_search_helpers.rb @@ -61,9 +61,11 @@ module FilteredSearchHelpers token_emoji = tokens[index][:emoji_name] expect(el.find('.name')).to have_content(token_name) + if token_value expect(el.find('.value')).to have_content(token_value) end + # gl-emoji content is blank when the emoji unicode is not supported if token_emoji selector = %(gl-emoji[data-name="#{token_emoji}"]) diff --git a/spec/support/generate-seed-repo-rb b/spec/support/generate-seed-repo-rb index 4ee33f9725b..876b3b8242d 100755 --- a/spec/support/generate-seed-repo-rb +++ b/spec/support/generate-seed-repo-rb @@ -24,6 +24,7 @@ def main unless system(*%W[git clone --bare #{SOURCE} #{REPO_NAME}], chdir: dir) abort "git clone failed" end + repo = File.join(dir, REPO_NAME) erb = ERB.new(DATA.read) erb.run(binding) diff --git a/spec/support/google_api/cloud_platform_helpers.rb b/spec/support/google_api/cloud_platform_helpers.rb index 99752ed396e..2fdbddd40c2 100644 --- a/spec/support/google_api/cloud_platform_helpers.rb +++ b/spec/support/google_api/cloud_platform_helpers.rb @@ -10,10 +10,14 @@ module GoogleApi request.session[GoogleApi::CloudPlatform::Client.session_key_for_expires_at] = 1.hour.ago.to_i.to_s end - def stub_google_project_billing_status - redis_double = double - allow(Gitlab::Redis::SharedState).to receive(:with).and_yield(redis_double) - allow(redis_double).to receive(:get).with(CheckGcpProjectBillingWorker.redis_shared_state_key_for('token')).and_return('true') + def stub_cloud_platform_projects_list(options) + WebMock.stub_request(:get, cloud_platform_projects_list_url) + .to_return(cloud_platform_response(cloud_platform_projects_body(options))) + end + + def stub_cloud_platform_projects_get_billing_info(project_id, billing_enabled) + WebMock.stub_request(:get, cloud_platform_projects_get_billing_info_url(project_id)) + .to_return(cloud_platform_response(cloud_platform_projects_billing_info_body(project_id, billing_enabled))) end def stub_cloud_platform_get_zone_cluster(project_id, zone, cluster_id, **options) @@ -46,6 +50,14 @@ module GoogleApi .to_return(status: [500, "Internal Server Error"]) end + def cloud_platform_projects_list_url + "https://cloudresourcemanager.googleapis.com/v1/projects" + end + + def cloud_platform_projects_get_billing_info_url(project_id) + "https://cloudbilling.googleapis.com/v1/projects/#{project_id}/billingInfo" + end + def cloud_platform_get_zone_cluster_url(project_id, zone, cluster_id) "https://container.googleapis.com/v1/projects/#{project_id}/zones/#{zone}/clusters/#{cluster_id}" end @@ -121,5 +133,32 @@ module GoogleApi "endTime": options[:endTime] || '' } end + + def cloud_platform_projects_body(**options) + { + "projects": [ + { + "projectNumber": options[:project_number] || "1234", + "projectId": options[:project_id] || "test-project-1234", + "lifecycleState": "ACTIVE", + "name": options[:name] || "test-project", + "createTime": "2017-12-16T01:48:29.129Z", + "parent": { + "type": "organization", + "id": "12345" + } + } + ] + } + end + + def cloud_platform_projects_billing_info_body(project_id, billing_enabled) + { + "name": "projects/#{project_id}/billingInfo", + "projectId": "#{project_id}", + "billingAccountName": "account-name", + "billingEnabled": billing_enabled + } + end end end diff --git a/spec/support/matchers/access_matchers_for_controller.rb b/spec/support/matchers/access_matchers_for_controller.rb index cdb62a5deee..42a9ed9ff34 100644 --- a/spec/support/matchers/access_matchers_for_controller.rb +++ b/spec/support/matchers/access_matchers_for_controller.rb @@ -43,6 +43,7 @@ module AccessMatchersForController user = create(:user) membership.public_send(:"add_#{role}", user) end + user end diff --git a/spec/support/select2_helper.rb b/spec/support/select2_helper.rb index 55da961e173..90618ba5b19 100644 --- a/spec/support/select2_helper.rb +++ b/spec/support/select2_helper.rb @@ -17,6 +17,7 @@ module Select2Helper selector = options.fetch(:from) first(selector, visible: false) + if options[:multiple] execute_script("$('#{selector}').select2('val', ['#{value}']).trigger('change');") else diff --git a/spec/support/stub_env.rb b/spec/support/stub_env.rb index f621463e621..695152e2d4e 100644 --- a/spec/support/stub_env.rb +++ b/spec/support/stub_env.rb @@ -4,6 +4,7 @@ module StubENV def stub_env(key_or_hash, value = nil) init_stub unless env_stubbed? + if key_or_hash.is_a? Hash key_or_hash.each { |k, v| add_stubbed_value(k, v) } else diff --git a/spec/support/test_env.rb b/spec/support/test_env.rb index 664698fcbaf..25ff6094408 100644 --- a/spec/support/test_env.rb +++ b/spec/support/test_env.rb @@ -325,6 +325,7 @@ module TestEnv if component_needs_update?(install_dir, version) # Cleanup the component entirely to ensure we start fresh FileUtils.rm_rf(install_dir) + unless system('rake', task) raise ComponentFailedToInstallError end diff --git a/spec/support/wait_for_requests.rb b/spec/support/wait_for_requests.rb index f4130d68271..fda0e29f983 100644 --- a/spec/support/wait_for_requests.rb +++ b/spec/support/wait_for_requests.rb @@ -53,6 +53,7 @@ module WaitForRequests wait_until = Time.now + max_wait_time.seconds loop do break if yield + if Time.now > wait_until raise "Condition not met: #{condition_name}" else diff --git a/spec/views/projects/buttons/_dropdown.html.haml_spec.rb b/spec/views/projects/buttons/_dropdown.html.haml_spec.rb new file mode 100644 index 00000000000..d0e692635b9 --- /dev/null +++ b/spec/views/projects/buttons/_dropdown.html.haml_spec.rb @@ -0,0 +1,39 @@ +require 'spec_helper' + +describe 'projects/buttons/_dropdown' do + let(:user) { create(:user) } + + context 'user with all abilities' do + before do + assign(:project, project) + + allow(view).to receive(:current_user).and_return(user) + allow(view).to receive(:can?).and_return(true) + end + + context 'empty repository' do + let(:project) { create(:project, :empty_repo) } + + it 'has a link to create a new file' do + render + + expect(view).to render_template('projects/buttons/_dropdown') + expect(rendered).to have_link('New file') + end + + it 'does not have a link to create a new branch' do + render + + expect(view).to render_template('projects/buttons/_dropdown') + expect(rendered).not_to have_link('New branch') + end + + it 'does not have a link to create a new tag' do + render + + expect(view).to render_template('projects/buttons/_dropdown') + expect(rendered).not_to have_link('New tag') + end + end + end +end diff --git a/spec/workers/check_gcp_project_billing_worker_spec.rb b/spec/workers/check_gcp_project_billing_worker_spec.rb index f52a903327c..7b7a7c1bc44 100644 --- a/spec/workers/check_gcp_project_billing_worker_spec.rb +++ b/spec/workers/check_gcp_project_billing_worker_spec.rb @@ -8,7 +8,7 @@ describe CheckGcpProjectBillingWorker do context 'when there is a token in redis' do before do - allow_any_instance_of(described_class).to receive(:get_session_token).and_return(token) + allow(described_class).to receive(:get_session_token).and_return(token) end context 'when there is no lease' do |