diff options
author | Douglas Barbosa Alexandre <dbalexandre@gmail.com> | 2019-09-10 23:03:14 +0300 |
---|---|---|
committer | Douglas Barbosa Alexandre <dbalexandre@gmail.com> | 2019-09-10 23:03:14 +0300 |
commit | cfe77ce4a3494b9c1516fe7e915f76b21a68e763 (patch) | |
tree | cef43c4d6c2fdee57ad19ab6815e41617eace2f2 /spec | |
parent | 934d4925d85f22c67e7ad57f607e8fe430a9ea92 (diff) | |
parent | 95d16dc007f3fe8831f2baa511bbb7bd708baff0 (diff) |
Merge remote-tracking branch 'origin/master' into camilstaps/gitlab-ce-new-66023-public-private-fork-counts
Diffstat (limited to 'spec')
21 files changed, 478 insertions, 123 deletions
diff --git a/spec/fixtures/api/schemas/public_api/v4/tag.json b/spec/fixtures/api/schemas/public_api/v4/tag.json index 5713ea1f526..bb0190955f0 100644 --- a/spec/fixtures/api/schemas/public_api/v4/tag.json +++ b/spec/fixtures/api/schemas/public_api/v4/tag.json @@ -16,7 +16,8 @@ { "type": "null" }, { "$ref": "release/tag_release.json" } ] - } + }, + "protected": { "type": "boolean" } }, "additionalProperties": false } diff --git a/spec/fixtures/valid.po b/spec/fixtures/valid.po index 155b6cbb95d..28826f05595 100644 --- a/spec/fixtures/valid.po +++ b/spec/fixtures/valid.po @@ -1128,3 +1128,8 @@ msgid "parent" msgid_plural "parents" msgstr[0] "padre" msgstr[1] "padres" + +msgid "CycleAnalytics|%{stageName}" +msgid_plural "CycleAnalytics|%d stages selected" +msgstr[0] "%{stageName}" +msgstr[1] "%d stages selected" diff --git a/spec/helpers/events_helper_spec.rb b/spec/helpers/events_helper_spec.rb index 3d15306d4d2..e062c841717 100644 --- a/spec/helpers/events_helper_spec.rb +++ b/spec/helpers/events_helper_spec.rb @@ -27,7 +27,7 @@ describe EventsHelper do end describe '#event_feed_url' do - let(:event) { create(:event) } + let(:event) { create(:event).present } let(:project) { create(:project, :public, :repository) } context 'issue' do diff --git a/spec/helpers/releases_helper_spec.rb b/spec/helpers/releases_helper_spec.rb new file mode 100644 index 00000000000..ff820b3cc95 --- /dev/null +++ b/spec/helpers/releases_helper_spec.rb @@ -0,0 +1,46 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe ReleasesHelper do + describe '#illustration' do + it 'returns the correct image path' do + expect(helper.illustration).to match(/illustrations\/releases-(\w+)\.svg/) + end + end + + describe '#help_page' do + it 'returns the correct link to the help page' do + expect(helper.help_page).to include('user/project/releases/index') + end + end + + context 'url helpers' do + let(:project) { build(:project, namespace: create(:group)) } + + before do + helper.instance_variable_set(:@project, project) + end + + describe '#url_for_merge_requests' do + it 'returns the the correct link with the correct parameters' do + path = "#{project.group.path}/#{project.path}/merge_requests?scope=all&state=opened" + expect(helper.url_for_merge_requests).to include(path) + end + end + + describe '#url_for_issues' do + it 'returns the the correct link with the correct parameters' do + path = "#{project.group.path}/#{project.path}/issues?scope=all&state=opened" + expect(helper.url_for_issues).to include(path) + end + end + + describe '#data_for_releases_page' do + it 'has the needed data to display release blocks' do + keys = %i(project_id illustration_path documentation_path merge_requests_url issues_url) + expect(helper.data_for_releases_page.keys).to eq(keys) + end + end + end +end diff --git a/spec/javascripts/monitoring/components/graph_group_spec.js b/spec/javascripts/monitoring/components/graph_group_spec.js new file mode 100644 index 00000000000..068c4b5302c --- /dev/null +++ b/spec/javascripts/monitoring/components/graph_group_spec.js @@ -0,0 +1,47 @@ +import { shallowMount } from '@vue/test-utils'; +import GraphGroup from '~/monitoring/components/graph_group.vue'; + +describe('Graph group component', () => { + let graphGroup; + + afterEach(() => { + graphGroup.destroy(); + }); + + describe('When groups can be collapsed', () => { + beforeEach(() => { + graphGroup = shallowMount(GraphGroup, { + propsData: { + name: 'panel', + collapseGroup: true, + }, + }); + }); + + it('should show the angle-down caret icon when collapseGroup is true', () => { + expect(graphGroup.vm.caretIcon).toBe('angle-down'); + }); + + it('should show the angle-right caret icon when collapseGroup is false', () => { + graphGroup.vm.collapse(); + + expect(graphGroup.vm.caretIcon).toBe('angle-right'); + }); + }); + + describe('When groups can not be collapsed', () => { + beforeEach(() => { + graphGroup = shallowMount(GraphGroup, { + propsData: { + name: 'panel', + collapseGroup: true, + showPanels: false, + }, + }); + }); + + it('should not contain a prometheus-graph-group container when showPanels is false', () => { + expect(graphGroup.vm.$el.querySelector('.prometheus-graph-group')).toBe(null); + }); + }); +}); diff --git a/spec/javascripts/sidebar/mock_data.js b/spec/javascripts/sidebar/mock_data.js index 7f20b0da991..3ee97b978fd 100644 --- a/spec/javascripts/sidebar/mock_data.js +++ b/spec/javascripts/sidebar/mock_data.js @@ -210,14 +210,4 @@ const mockData = { }, }; -mockData.sidebarMockInterceptor = function(request, next) { - const body = this.responseMap[request.method.toUpperCase()][request.url]; - - next( - request.respondWith(JSON.stringify(body), { - status: 200, - }), - ); -}.bind(mockData); - export default mockData; diff --git a/spec/javascripts/sidebar/sidebar_assignees_spec.js b/spec/javascripts/sidebar/sidebar_assignees_spec.js index 016f5e033a5..e808f4003ff 100644 --- a/spec/javascripts/sidebar/sidebar_assignees_spec.js +++ b/spec/javascripts/sidebar/sidebar_assignees_spec.js @@ -1,4 +1,3 @@ -import _ from 'underscore'; import Vue from 'vue'; import SidebarAssignees from '~/sidebar/components/assignees/sidebar_assignees.vue'; import SidebarMediator from '~/sidebar/sidebar_mediator'; @@ -14,8 +13,6 @@ describe('sidebar assignees', () => { preloadFixtures('issues/open-issue.html'); beforeEach(() => { - Vue.http.interceptors.push(Mock.sidebarMockInterceptor); - loadFixtures('issues/open-issue.html'); mediator = new SidebarMediator(Mock.mediator); @@ -38,7 +35,6 @@ describe('sidebar assignees', () => { SidebarService.singleton = null; SidebarStore.singleton = null; SidebarMediator.singleton = null; - Vue.http.interceptors = _.without(Vue.http.interceptors, Mock.sidebarMockInterceptor); }); it('calls the mediator when saves the assignees', () => { diff --git a/spec/javascripts/sidebar/sidebar_mediator_spec.js b/spec/javascripts/sidebar/sidebar_mediator_spec.js index 6c69c08e733..b0412105e3f 100644 --- a/spec/javascripts/sidebar/sidebar_mediator_spec.js +++ b/spec/javascripts/sidebar/sidebar_mediator_spec.js @@ -1,31 +1,37 @@ -import _ from 'underscore'; -import Vue from 'vue'; +import MockAdapter from 'axios-mock-adapter'; +import axios from '~/lib/utils/axios_utils'; import SidebarMediator from '~/sidebar/sidebar_mediator'; import SidebarStore from '~/sidebar/stores/sidebar_store'; import SidebarService from '~/sidebar/services/sidebar_service'; import Mock from './mock_data'; +const { mediator: mediatorMockData } = Mock; + describe('Sidebar mediator', function() { + let mock; + beforeEach(() => { - Vue.http.interceptors.push(Mock.sidebarMockInterceptor); - this.mediator = new SidebarMediator(Mock.mediator); + mock = new MockAdapter(axios); + + this.mediator = new SidebarMediator(mediatorMockData); }); afterEach(() => { SidebarService.singleton = null; SidebarStore.singleton = null; SidebarMediator.singleton = null; - Vue.http.interceptors = _.without(Vue.http.interceptors, Mock.sidebarMockInterceptor); + mock.restore(); }); it('assigns yourself ', () => { this.mediator.assignYourself(); - expect(this.mediator.store.currentUser).toEqual(Mock.mediator.currentUser); - expect(this.mediator.store.assignees[0]).toEqual(Mock.mediator.currentUser); + expect(this.mediator.store.currentUser).toEqual(mediatorMockData.currentUser); + expect(this.mediator.store.assignees[0]).toEqual(mediatorMockData.currentUser); }); it('saves assignees', done => { + mock.onPut(mediatorMockData.endpoint).reply(200, {}); this.mediator .saveAssignees('issue[assignee_ids]') .then(resp => { @@ -36,8 +42,8 @@ describe('Sidebar mediator', function() { }); it('fetches the data', done => { - const mockData = - Mock.responseMap.GET['/gitlab-org/gitlab-shell/issues/5.json?serializer=sidebar_extras']; + const mockData = Mock.responseMap.GET[mediatorMockData.endpoint]; + mock.onGet(mediatorMockData.endpoint).reply(200, mockData); spyOn(this.mediator, 'processFetchedData').and.callThrough(); this.mediator @@ -50,8 +56,7 @@ describe('Sidebar mediator', function() { }); it('processes fetched data', () => { - const mockData = - Mock.responseMap.GET['/gitlab-org/gitlab-shell/issues/5.json?serializer=sidebar_extras']; + const mockData = Mock.responseMap.GET[mediatorMockData.endpoint]; this.mediator.processFetchedData(mockData); expect(this.mediator.store.assignees).toEqual(mockData.assignees); @@ -74,6 +79,7 @@ describe('Sidebar mediator', function() { it('fetches autocomplete projects', done => { const searchTerm = 'foo'; + mock.onGet(mediatorMockData.projectsAutocompleteEndpoint).reply(200, {}); spyOn(this.mediator.service, 'getProjectsAutocomplete').and.callThrough(); spyOn(this.mediator.store, 'setAutocompleteProjects').and.callThrough(); @@ -88,7 +94,9 @@ describe('Sidebar mediator', function() { }); it('moves issue', done => { + const mockData = Mock.responseMap.POST[mediatorMockData.moveIssueEndpoint]; const moveToProjectId = 7; + mock.onPost(mediatorMockData.moveIssueEndpoint).reply(200, mockData); this.mediator.store.setMoveToProjectId(moveToProjectId); spyOn(this.mediator.service, 'moveIssue').and.callThrough(); const visitUrl = spyOnDependency(SidebarMediator, 'visitUrl'); @@ -97,7 +105,7 @@ describe('Sidebar mediator', function() { .moveIssue() .then(() => { expect(this.mediator.service.moveIssue).toHaveBeenCalledWith(moveToProjectId); - expect(visitUrl).toHaveBeenCalledWith('/root/some-project/issues/5'); + expect(visitUrl).toHaveBeenCalledWith(mockData.web_url); }) .then(done) .catch(done.fail); @@ -105,6 +113,7 @@ describe('Sidebar mediator', function() { it('toggle subscription', done => { this.mediator.store.setSubscribedState(false); + mock.onPost(mediatorMockData.toggleSubscriptionEndpoint).reply(200, {}); spyOn(this.mediator.service, 'toggleSubscription').and.callThrough(); this.mediator diff --git a/spec/javascripts/sidebar/sidebar_move_issue_spec.js b/spec/javascripts/sidebar/sidebar_move_issue_spec.js index 230e0a933a9..ec712450f2e 100644 --- a/spec/javascripts/sidebar/sidebar_move_issue_spec.js +++ b/spec/javascripts/sidebar/sidebar_move_issue_spec.js @@ -1,6 +1,6 @@ import $ from 'jquery'; -import _ from 'underscore'; -import Vue from 'vue'; +import MockAdapter from 'axios-mock-adapter'; +import axios from '~/lib/utils/axios_utils'; import SidebarMediator from '~/sidebar/sidebar_mediator'; import SidebarStore from '~/sidebar/stores/sidebar_store'; import SidebarService from '~/sidebar/services/sidebar_service'; @@ -8,8 +8,12 @@ import SidebarMoveIssue from '~/sidebar/lib/sidebar_move_issue'; import Mock from './mock_data'; describe('SidebarMoveIssue', function() { + let mock; + beforeEach(() => { - Vue.http.interceptors.push(Mock.sidebarMockInterceptor); + mock = new MockAdapter(axios); + const mockData = Mock.responseMap.GET['/autocomplete/projects?project_id=15']; + mock.onGet('/autocomplete/projects?project_id=15').reply(200, mockData); this.mediator = new SidebarMediator(Mock.mediator); this.$content = $(` <div class="dropdown"> @@ -37,8 +41,7 @@ describe('SidebarMoveIssue', function() { SidebarMediator.singleton = null; this.sidebarMoveIssue.destroy(); - - Vue.http.interceptors = _.without(Vue.http.interceptors, Mock.sidebarMockInterceptor); + mock.restore(); }); describe('init', () => { diff --git a/spec/lib/gitlab/import_export/all_models.yml b/spec/lib/gitlab/import_export/all_models.yml index dafa4243145..e496ab4cd35 100644 --- a/spec/lib/gitlab/import_export/all_models.yml +++ b/spec/lib/gitlab/import_export/all_models.yml @@ -376,6 +376,7 @@ project: - index_status - feature_usage - approval_rules +- approval_merge_request_rules - approvers - approver_users - pages_domains diff --git a/spec/lib/gitlab/repository_cache_adapter_spec.rb b/spec/lib/gitlab/repository_cache_adapter_spec.rb index 808eb865a21..fd1338b55a6 100644 --- a/spec/lib/gitlab/repository_cache_adapter_spec.rb +++ b/spec/lib/gitlab/repository_cache_adapter_spec.rb @@ -6,6 +6,7 @@ describe Gitlab::RepositoryCacheAdapter do let(:project) { create(:project, :repository) } let(:repository) { project.repository } let(:cache) { repository.send(:cache) } + let(:redis_set_cache) { repository.send(:redis_set_cache) } describe '#cache_method_output', :use_clean_rails_memory_store_caching do let(:fallback) { 10 } @@ -208,9 +209,11 @@ describe Gitlab::RepositoryCacheAdapter do describe '#expire_method_caches' do it 'expires the caches of the given methods' do expect(cache).to receive(:expire).with(:rendered_readme) - expect(cache).to receive(:expire).with(:gitignore) + expect(cache).to receive(:expire).with(:branch_names) + expect(redis_set_cache).to receive(:expire).with(:rendered_readme) + expect(redis_set_cache).to receive(:expire).with(:branch_names) - repository.expire_method_caches(%i(rendered_readme gitignore)) + repository.expire_method_caches(%i(rendered_readme branch_names)) end it 'does not expire caches for non-existent methods' do diff --git a/spec/lib/gitlab/repository_set_cache_spec.rb b/spec/lib/gitlab/repository_set_cache_spec.rb new file mode 100644 index 00000000000..87e51f801e5 --- /dev/null +++ b/spec/lib/gitlab/repository_set_cache_spec.rb @@ -0,0 +1,75 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe Gitlab::RepositorySetCache, :clean_gitlab_redis_cache do + let(:project) { create(:project) } + let(:repository) { project.repository } + let(:namespace) { "#{repository.full_path}:#{project.id}" } + let(:cache) { described_class.new(repository) } + + describe '#cache_key' do + subject { cache.cache_key(:foo) } + + it 'includes the namespace' do + is_expected.to eq("foo:#{namespace}:set") + end + + context 'with a given namespace' do + let(:extra_namespace) { 'my:data' } + let(:cache) { described_class.new(repository, extra_namespace: extra_namespace) } + + it 'includes the full namespace' do + is_expected.to eq("foo:#{namespace}:#{extra_namespace}:set") + end + end + end + + describe '#expire' do + it 'expires the given key from the cache' do + cache.write(:foo, ['value']) + + expect(cache.read(:foo)).to contain_exactly('value') + expect(cache.expire(:foo)).to eq(1) + expect(cache.read(:foo)).to be_empty + end + end + + describe '#exist?' do + it 'checks whether the key exists' do + expect(cache.exist?(:foo)).to be(false) + + cache.write(:foo, ['value']) + + expect(cache.exist?(:foo)).to be(true) + end + end + + describe '#fetch' do + let(:blk) { -> { ['block value'] } } + + subject { cache.fetch(:foo, &blk) } + + it 'fetches the key from the cache when filled' do + cache.write(:foo, ['value']) + + is_expected.to contain_exactly('value') + end + + it 'writes the value of the provided block when empty' do + cache.expire(:foo) + + is_expected.to contain_exactly('block value') + expect(cache.read(:foo)).to contain_exactly('block value') + end + end + + describe '#include?' do + it 'checks inclusion in the Redis set' do + cache.write(:foo, ['value']) + + expect(cache.include?(:foo, 'value')).to be(true) + expect(cache.include?(:foo, 'bar')).to be(false) + end + end +end diff --git a/spec/lib/gitlab/sidekiq_monitor_spec.rb b/spec/lib/gitlab/sidekiq_daemon/monitor_spec.rb index bbd7bf90217..acbb09e3542 100644 --- a/spec/lib/gitlab/sidekiq_monitor_spec.rb +++ b/spec/lib/gitlab/sidekiq_daemon/monitor_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe Gitlab::SidekiqMonitor do +describe Gitlab::SidekiqDaemon::Monitor do let(:monitor) { described_class.new } describe '#within_job' do @@ -43,7 +43,7 @@ describe Gitlab::SidekiqMonitor do before do # we want to run at most once cycle # we toggle `enabled?` flag after the first call - stub_const('Gitlab::SidekiqMonitor::RECONNECT_TIME', 0) + stub_const('Gitlab::SidekiqDaemon::Monitor::RECONNECT_TIME', 0) allow(monitor).to receive(:enabled?).and_return(true, false) allow(Sidekiq.logger).to receive(:info) diff --git a/spec/lib/gitlab/sidekiq_middleware/monitor_spec.rb b/spec/lib/gitlab/sidekiq_middleware/monitor_spec.rb index 7319cdc2399..023df1a6391 100644 --- a/spec/lib/gitlab/sidekiq_middleware/monitor_spec.rb +++ b/spec/lib/gitlab/sidekiq_middleware/monitor_spec.rb @@ -10,8 +10,8 @@ describe Gitlab::SidekiqMiddleware::Monitor do let(:job) { { 'jid' => 'job-id' } } let(:queue) { 'my-queue' } - it 'calls SidekiqMonitor' do - expect(Gitlab::SidekiqMonitor.instance).to receive(:within_job) + it 'calls Gitlab::SidekiqDaemon::Monitor' do + expect(Gitlab::SidekiqDaemon::Monitor.instance).to receive(:within_job) .with('job-id', 'my-queue') .and_call_original @@ -29,7 +29,7 @@ describe Gitlab::SidekiqMiddleware::Monitor do context 'when cancel happens' do subject do monitor.call(worker, job, queue) do - raise Gitlab::SidekiqMonitor::CancelledError + raise Gitlab::SidekiqDaemon::Monitor::CancelledError end end diff --git a/spec/lib/gitlab/utils/strong_memoize_spec.rb b/spec/lib/gitlab/utils/strong_memoize_spec.rb index 26baaf873a8..624e799c5e9 100644 --- a/spec/lib/gitlab/utils/strong_memoize_spec.rb +++ b/spec/lib/gitlab/utils/strong_memoize_spec.rb @@ -52,6 +52,22 @@ describe Gitlab::Utils::StrongMemoize do end end + describe '#strong_memoized?' do + let(:value) { :anything } + + subject { object.strong_memoized?(:method_name) } + + it 'returns false if the value is uncached' do + is_expected.to be(false) + end + + it 'returns true if the value is cached' do + object.method_name + + is_expected.to be(true) + end + end + describe '#clear_memoization' do let(:value) { 'mepmep' } diff --git a/spec/presenters/event_presenter_spec.rb b/spec/presenters/event_presenter_spec.rb new file mode 100644 index 00000000000..79f5e359141 --- /dev/null +++ b/spec/presenters/event_presenter_spec.rb @@ -0,0 +1,41 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe EventPresenter do + include Gitlab::Routing.url_helpers + + set(:group) { create(:group) } + set(:project) { create(:project, group: group) } + set(:target) { create(:milestone, project: project) } + set(:group_event) { create(:event, :created, project: nil, group: group, target: target) } + set(:project_event) { create(:event, :created, project: project, target: target) } + + describe '#resource_parent_name' do + context 'with group event' do + subject { group_event.present.resource_parent_name } + + it { is_expected.to eq(group.full_name) } + end + + context 'with project label' do + subject { project_event.present.resource_parent_name } + + it { is_expected.to eq(project.full_name) } + end + end + + describe '#target_link_options' do + context 'with group event' do + subject { group_event.present.target_link_options } + + it { is_expected.to eq([group, target]) } + end + + context 'with project label' do + subject { project_event.present.target_link_options } + + it { is_expected.to eq([group.becomes(Namespace), project, target]) } + end + end +end diff --git a/spec/routing/project_routing_spec.rb b/spec/routing/project_routing_spec.rb index 8a3de2a52fc..7e2d70d6eb5 100644 --- a/spec/routing/project_routing_spec.rb +++ b/spec/routing/project_routing_spec.rb @@ -530,15 +530,22 @@ describe 'project routing' do end end - # project_blame GET /:project_id/blame/:id(.:format) blame#show {id: /.+/, project_id: /[^\/]+/} + # project_blame GET /:project_id/blame/:id(.:format) blame#show {id: /[^\0]+/, project_id: /[^\/]+/} describe Projects::BlameController, 'routing' do it 'to #show' do expect(get('/gitlab/gitlabhq/blame/master/app/models/project.rb')).to route_to('projects/blame#show', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'master/app/models/project.rb') expect(get('/gitlab/gitlabhq/blame/master/files.scss')).to route_to('projects/blame#show', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'master/files.scss') + newline_file = "new\n\nline.txt" + url_encoded_newline_file = ERB::Util.url_encode(newline_file) + assert_routing({ path: "/gitlab/gitlabhq/blame/master/#{url_encoded_newline_file}", + method: :get }, + { controller: 'projects/blame', action: 'show', + namespace_id: 'gitlab', project_id: 'gitlabhq', + id: "master/#{newline_file}" }) end end - # project_blob GET /:project_id/blob/:id(.:format) blob#show {id: /.+/, project_id: /[^\/]+/} + # project_blob GET /:project_id/blob/:id(.:format) blob#show {id: /[^\0]+/, project_id: /[^\/]+/} describe Projects::BlobController, 'routing' do it 'to #show' do expect(get('/gitlab/gitlabhq/blob/master/app/models/project.rb')).to route_to('projects/blob#show', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'master/app/models/project.rb') @@ -547,28 +554,56 @@ describe 'project routing' do expect(get('/gitlab/gitlabhq/blob/master/files.scss')).to route_to('projects/blob#show', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'master/files.scss') expect(get('/gitlab/gitlabhq/blob/master/blob/index.js')).to route_to('projects/blob#show', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'master/blob/index.js') expect(get('/gitlab/gitlabhq/blob/blob/master/blob/index.js')).to route_to('projects/blob#show', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'blob/master/blob/index.js') + newline_file = "new\n\nline.txt" + url_encoded_newline_file = ERB::Util.url_encode(newline_file) + assert_routing({ path: "/gitlab/gitlabhq/blob/blob/master/blob/#{url_encoded_newline_file}", + method: :get }, + { controller: 'projects/blob', action: 'show', + namespace_id: 'gitlab', project_id: 'gitlabhq', + id: "blob/master/blob/#{newline_file}" }) end end - # project_tree GET /:project_id/tree/:id(.:format) tree#show {id: /.+/, project_id: /[^\/]+/} + # project_tree GET /:project_id/tree/:id(.:format) tree#show {id: /[^\0]+/, project_id: /[^\/]+/} describe Projects::TreeController, 'routing' do it 'to #show' do expect(get('/gitlab/gitlabhq/tree/master/app/models/project.rb')).to route_to('projects/tree#show', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'master/app/models/project.rb') expect(get('/gitlab/gitlabhq/tree/master/files.scss')).to route_to('projects/tree#show', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'master/files.scss') expect(get('/gitlab/gitlabhq/tree/master/tree/files')).to route_to('projects/tree#show', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'master/tree/files') expect(get('/gitlab/gitlabhq/tree/tree/master/tree/files')).to route_to('projects/tree#show', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'tree/master/tree/files') + newline_file = "new\n\nline.txt" + url_encoded_newline_file = ERB::Util.url_encode(newline_file) + assert_routing({ path: "/gitlab/gitlabhq/tree/master/#{url_encoded_newline_file}", + method: :get }, + { controller: 'projects/tree', action: 'show', + namespace_id: 'gitlab', project_id: 'gitlabhq', + id: "master/#{newline_file}" }) end end - # project_find_file GET /:namespace_id/:project_id/find_file/*id(.:format) projects/find_file#show {:id=>/.+/, :namespace_id=>/[a-zA-Z.0-9_\-]+/, :project_id=>/[a-zA-Z.0-9_\-]+(?<!\.atom)/, :format=>/html/} + # project_find_file GET /:namespace_id/:project_id/find_file/*id(.:format) projects/find_file#show {:id=>/[^\0]+/, :namespace_id=>/[a-zA-Z.0-9_\-]+/, :project_id=>/[a-zA-Z.0-9_\-]+(?<!\.atom)/, :format=>/html/} # project_files GET /:namespace_id/:project_id/files/*id(.:format) projects/find_file#list {:id=>/(?:[^.]|\.(?!json$))+/, :namespace_id=>/[a-zA-Z.0-9_\-]+/, :project_id=>/[a-zA-Z.0-9_\-]+(?<!\.atom)/, :format=>/json/} describe Projects::FindFileController, 'routing' do it 'to #show' do expect(get('/gitlab/gitlabhq/find_file/master')).to route_to('projects/find_file#show', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'master') + newline_file = "new\n\nline.txt" + url_encoded_newline_file = ERB::Util.url_encode(newline_file) + assert_routing({ path: "/gitlab/gitlabhq/find_file/#{url_encoded_newline_file}", + method: :get }, + { controller: 'projects/find_file', action: 'show', + namespace_id: 'gitlab', project_id: 'gitlabhq', + id: "#{newline_file}" }) end it 'to #list' do expect(get('/gitlab/gitlabhq/files/master.json')).to route_to('projects/find_file#list', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'master.json') + newline_file = "new\n\nline.txt" + url_encoded_newline_file = ERB::Util.url_encode(newline_file) + assert_routing({ path: "/gitlab/gitlabhq/files/#{url_encoded_newline_file}", + method: :get }, + { controller: 'projects/find_file', action: 'list', + namespace_id: 'gitlab', project_id: 'gitlabhq', + id: "#{newline_file}" }) end end @@ -578,6 +613,13 @@ describe 'project routing' do route_to('projects/blob#edit', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'master/app/models/project.rb')) + newline_file = "new\n\nline.txt" + url_encoded_newline_file = ERB::Util.url_encode(newline_file) + assert_routing({ path: "/gitlab/gitlabhq/edit/master/docs/#{url_encoded_newline_file}", + method: :get }, + { controller: 'projects/blob', action: 'edit', + namespace_id: 'gitlab', project_id: 'gitlabhq', + id: "master/docs/#{newline_file}" }) end it 'to #preview' do @@ -585,6 +627,26 @@ describe 'project routing' do route_to('projects/blob#preview', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'master/app/models/project.rb')) + newline_file = "new\n\nline.txt" + url_encoded_newline_file = ERB::Util.url_encode(newline_file) + assert_routing({ path: "/gitlab/gitlabhq/edit/master/docs/#{url_encoded_newline_file}", + method: :get }, + { controller: 'projects/blob', action: 'edit', + namespace_id: 'gitlab', project_id: 'gitlabhq', + id: "master/docs/#{newline_file}" }) + end + end + + # project_raw GET /:project_id/raw/:id(.:format) raw#show {id: /[^\0]+/, project_id: /[^\/]+/} + describe Projects::RawController, 'routing' do + it 'to #show' do + newline_file = "new\n\nline.txt" + url_encoded_newline_file = ERB::Util.url_encode(newline_file) + assert_routing({ path: "/gitlab/gitlabhq/raw/master/#{url_encoded_newline_file}", + method: :get }, + { controller: 'projects/raw', action: 'show', + namespace_id: 'gitlab', project_id: 'gitlabhq', + id: "master/#{newline_file}" }) end end diff --git a/spec/rubocop/cop/scalability/file_uploads_spec.rb b/spec/rubocop/cop/scalability/file_uploads_spec.rb new file mode 100644 index 00000000000..2a94fde5ba2 --- /dev/null +++ b/spec/rubocop/cop/scalability/file_uploads_spec.rb @@ -0,0 +1,54 @@ +# frozen_string_literal: true + +require 'fast_spec_helper' +require 'rubocop' +require_relative '../../../support/helpers/expect_offense' +require_relative '../../../../rubocop/cop/scalability/file_uploads' + +describe RuboCop::Cop::Scalability::FileUploads do + include CopHelper + include ExpectOffense + + subject(:cop) { described_class.new } + let(:message) { 'Do not upload files without workhorse acceleration. Please refer to https://docs.gitlab.com/ee/development/uploads.html' } + + context 'with required params' do + it 'detects File in types array' do + expect_offense(<<~PATTERN.strip_indent) + params do + requires :certificate, allow_blank: false, types: [String, File] + ^^^^ #{message} + end + PATTERN + end + + it 'detects File as type argument' do + expect_offense(<<~PATTERN.strip_indent) + params do + requires :attachment, type: File + ^^^^ #{message} + end + PATTERN + end + end + + context 'with optional params' do + it 'detects File in types array' do + expect_offense(<<~PATTERN.strip_indent) + params do + optional :certificate, allow_blank: false, types: [String, File] + ^^^^ #{message} + end + PATTERN + end + + it 'detects File as type argument' do + expect_offense(<<~PATTERN.strip_indent) + params do + optional :attachment, type: File + ^^^^ #{message} + end + PATTERN + end + end +end diff --git a/spec/services/ci/retry_build_service_spec.rb b/spec/services/ci/retry_build_service_spec.rb index fe7c6fe4700..281c7438eee 100644 --- a/spec/services/ci/retry_build_service_spec.rb +++ b/spec/services/ci/retry_build_service_spec.rb @@ -40,7 +40,7 @@ describe Ci::RetryBuildService do user_id auto_canceled_by_id retried failure_reason sourced_pipelines artifacts_file_store artifacts_metadata_store metadata runner_session trace_chunks upstream_pipeline_id - artifacts_file artifacts_metadata artifacts_size].freeze + artifacts_file artifacts_metadata artifacts_size commands].freeze shared_examples 'build duplication' do let(:another_pipeline) { create(:ci_empty_pipeline, project: project) } diff --git a/spec/services/merge_requests/build_service_spec.rb b/spec/services/merge_requests/build_service_spec.rb index f18239f6d39..d546a092680 100644 --- a/spec/services/merge_requests/build_service_spec.rb +++ b/spec/services/merge_requests/build_service_spec.rb @@ -49,6 +49,22 @@ describe MergeRequests::BuildService do allow(project).to receive(:commit).and_return(commit_2) end + shared_examples 'allows the merge request to be created' do + it do + expect(merge_request.can_be_created).to eq(true) + end + end + + shared_examples 'forbids the merge request from being created' do + it 'returns that the merge request cannot be created' do + expect(merge_request.can_be_created).to eq(false) + end + + it 'adds an error message to the merge request' do + expect(merge_request.errors).to contain_exactly(*Array(error_message)) + end + end + describe '#execute' do it 'calls the compare service with the correct arguments' do allow_any_instance_of(described_class).to receive(:projects_and_branches_valid?).and_return(true) @@ -79,12 +95,8 @@ describe MergeRequests::BuildService do context 'missing source branch' do let(:source_branch) { '' } - it 'forbids the merge request from being created' do - expect(merge_request.can_be_created).to eq(false) - end - - it 'adds an error message to the merge request' do - expect(merge_request.errors).to contain_exactly('You must select source and target branch') + it_behaves_like 'forbids the merge request from being created' do + let(:error_message) { 'You must select source and target branch' } end end @@ -96,25 +108,44 @@ describe MergeRequests::BuildService do stub_compare end - it 'creates compare object with target branch as default branch' do - expect(merge_request.compare).to be_present - expect(merge_request.target_branch).to eq(project.default_branch) - end + context 'when source branch' do + context 'is not the repository default branch' do + it 'creates compare object with target branch as default branch' do + expect(merge_request.compare).to be_present + expect(merge_request.target_branch).to eq(project.default_branch) + end + + it_behaves_like 'allows the merge request to be created' + end + + context 'the repository default branch' do + let(:source_branch) { 'master' } + + it_behaves_like 'forbids the merge request from being created' do + let(:error_message) { 'You must select source and target branch' } + end - it 'allows the merge request to be created' do - expect(merge_request.can_be_created).to eq(true) + context 'when source project is different from the target project' do + let(:target_project) { create(:project, :public, :repository) } + let!(:project) { fork_project(target_project, user, namespace: user.namespace, repository: true) } + let(:source_project) { project } + + it 'creates compare object with target branch as default branch' do + expect(merge_request.compare).to be_present + expect(merge_request.target_branch).to eq(project.default_branch) + end + + it_behaves_like 'allows the merge request to be created' + end + end end end context 'same source and target branch' do let(:source_branch) { 'master' } - it 'forbids the merge request from being created' do - expect(merge_request.can_be_created).to eq(false) - end - - it 'adds an error message to the merge request' do - expect(merge_request.errors).to contain_exactly('You must select different branches') + it_behaves_like 'forbids the merge request from being created' do + let(:error_message) { 'You must select different branches' } end end @@ -125,9 +156,7 @@ describe MergeRequests::BuildService do stub_compare end - it 'allows the merge request to be created' do - expect(merge_request.can_be_created).to eq(true) - end + it_behaves_like 'allows the merge request to be created' it 'adds a WIP prefix to the merge request title' do expect(merge_request.title).to eq('WIP: Feature branch') @@ -142,9 +171,7 @@ describe MergeRequests::BuildService do stub_compare end - it 'allows the merge request to be created' do - expect(merge_request.can_be_created).to eq(true) - end + it_behaves_like 'allows the merge request to be created' 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) @@ -254,9 +281,7 @@ describe MergeRequests::BuildService do stub_compare end - it 'allows the merge request to be created' do - expect(merge_request.can_be_created).to eq(true) - end + it_behaves_like 'allows the merge request to be created' it 'uses the title of the branch as the merge request title' do expect(merge_request.title).to eq('Feature branch') @@ -340,12 +365,8 @@ describe MergeRequests::BuildService do allow(project).to receive(:commit).with(target_branch).and_return(commit_1) end - it 'forbids the merge request from being created' do - expect(merge_request.can_be_created).to eq(false) - end - - it 'adds an error message to the merge request' do - expect(merge_request.errors).to contain_exactly('Source branch "feature-branch" does not exist') + it_behaves_like 'forbids the merge request from being created' do + let(:error_message) { 'Source branch "feature-branch" does not exist' } end end @@ -355,12 +376,8 @@ describe MergeRequests::BuildService do allow(project).to receive(:commit).with(target_branch).and_return(nil) end - it 'forbids the merge request from being created' do - expect(merge_request.can_be_created).to eq(false) - end - - it 'adds an error message to the merge request' do - expect(merge_request.errors).to contain_exactly('Target branch "master" does not exist') + it_behaves_like 'forbids the merge request from being created' do + let(:error_message) { 'Target branch "master" does not exist' } end end @@ -369,15 +386,10 @@ describe MergeRequests::BuildService do allow(project).to receive(:commit).and_return(nil) end - it 'forbids the merge request from being created' do - expect(merge_request.can_be_created).to eq(false) - end - - it 'adds both error messages to the merge request' do - expect(merge_request.errors).to contain_exactly( - 'Source branch "feature-branch" does not exist', - 'Target branch "master" does not exist' - ) + it_behaves_like 'forbids the merge request from being created' do + let(:error_message) do + ['Source branch "feature-branch" does not exist', 'Target branch "master" does not exist'] + end end end diff --git a/spec/support/shared_examples/boards/multiple_issue_boards_shared_examples.rb b/spec/support/shared_examples/boards/multiple_issue_boards_shared_examples.rb index 76d82649c5f..f2f31e1b7f2 100644 --- a/spec/support/shared_examples/boards/multiple_issue_boards_shared_examples.rb +++ b/spec/support/shared_examples/boards/multiple_issue_boards_shared_examples.rb @@ -1,8 +1,6 @@ # frozen_string_literal: true shared_examples_for 'multiple issue boards' do - dropdown_selector = '.js-boards-selector .dropdown-menu' - context 'authorized user' do before do parent.add_maintainer(user) @@ -20,18 +18,14 @@ shared_examples_for 'multiple issue boards' do end it 'shows a list of boards' do - click_button board.name - - page.within(dropdown_selector) do + in_boards_switcher_dropdown do expect(page).to have_content(board.name) expect(page).to have_content(board2.name) end end it 'switches current board' do - click_button board.name - - page.within(dropdown_selector) do + in_boards_switcher_dropdown do click_link board2.name end @@ -43,9 +37,7 @@ shared_examples_for 'multiple issue boards' do end it 'creates new board without detailed configuration' do - click_button board.name - - page.within(dropdown_selector) do + in_boards_switcher_dropdown do click_button 'Create new board' end @@ -57,28 +49,23 @@ shared_examples_for 'multiple issue boards' do end it 'deletes board' do - click_button board.name - - wait_for_requests - - page.within(dropdown_selector) do + in_boards_switcher_dropdown do click_button 'Delete board' end expect(page).to have_content('Are you sure you want to delete this board?') click_button 'Delete' - click_button board2.name - page.within(dropdown_selector) do + wait_for_requests + + in_boards_switcher_dropdown do expect(page).not_to have_content(board.name) expect(page).to have_content(board2.name) end end it 'adds a list to the none default board' do - click_button board.name - - page.within(dropdown_selector) do + in_boards_switcher_dropdown do click_link board2.name end @@ -100,9 +87,7 @@ shared_examples_for 'multiple issue boards' do expect(page).to have_selector('.board', count: 3) - click_button board2.name - - page.within(dropdown_selector) do + in_boards_switcher_dropdown do click_link board.name end @@ -114,9 +99,9 @@ shared_examples_for 'multiple issue boards' do it 'maintains sidebar state over board switch' do assert_boards_nav_active - find('.boards-switcher').click - wait_for_requests - click_link board2.name + in_boards_switcher_dropdown do + click_link board2.name + end assert_boards_nav_active end @@ -129,15 +114,24 @@ shared_examples_for 'multiple issue boards' do end it 'does not show action links' do - click_button board.name - - page.within(dropdown_selector) do + in_boards_switcher_dropdown do expect(page).not_to have_content('Create new board') expect(page).not_to have_content('Delete board') end end end + def in_boards_switcher_dropdown + find('.boards-switcher').click + + wait_for_requests + + dropdown_selector = '.js-boards-selector .dropdown-menu' + page.within(dropdown_selector) do + yield + end + end + def assert_boards_nav_active expect(find('.nav-sidebar .active .active')).to have_selector('a', text: 'Boards') end |