diff --git a/Gemfile b/Gemfile index f2cd20ada73ed69777c5b85e78de973e077c53f0..cde8d631ab5e139aeac1fe909790b745e5e3d21e 100644 --- a/Gemfile +++ b/Gemfile @@ -203,7 +203,7 @@ gem 'jquery-ui-rails' gem 'nprogress-rails' gem 'raphael-rails', '~> 2.1.2' gem 'request_store' -gem 'select2-rails' +gem 'select2-rails', '~> 3.5.9' gem 'virtus' group :development do @@ -250,7 +250,7 @@ group :development, :test do # Generate Fake data gem 'ffaker', '~> 2.0.0' - gem 'capybara', '~> 2.3.0' + gem 'capybara', '~> 2.4.0' gem 'capybara-screenshot', '~> 1.0.0' gem 'poltergeist', '~> 1.6.0' diff --git a/Gemfile.lock b/Gemfile.lock index 7641d908131cc725a7983ef895c8bb35f98eecaa..8114063cee06202f609f67c1a599b52187a14cdf 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -82,7 +82,7 @@ GEM columnize (~> 0.8) debugger-linecache (~> 1.2) cal-heatmap-rails (0.0.1) - capybara (2.3.0) + capybara (2.4.4) mime-types (>= 1.16) nokogiri (>= 1.3.3) rack (>= 1.0.0) @@ -598,7 +598,7 @@ GEM seed-fu (2.3.5) activerecord (>= 3.1, < 4.3) activesupport (>= 3.1, < 4.3) - select2-rails (3.5.2) + select2-rails (3.5.9.3) thor (~> 0.14) settingslogic (2.0.9) sexp_processor (4.4.5) @@ -753,7 +753,7 @@ DEPENDENCIES browser (~> 0.8.0) byebug cal-heatmap-rails (~> 0.0.1) - capybara (~> 2.3.0) + capybara (~> 2.4.0) capybara-screenshot (~> 1.0.0) carrierwave charlock_holmes @@ -842,7 +842,7 @@ DEPENDENCIES sass-rails (~> 4.0.5) sdoc seed-fu - select2-rails + select2-rails (~> 3.5.9) settingslogic shoulda-matchers (~> 2.8.0) sidekiq (~> 3.3) @@ -878,4 +878,4 @@ DEPENDENCIES wikicloth (= 0.8.1) BUNDLED WITH - 1.10.4 + 1.10.5 diff --git a/app/finders/issuable_finder.rb b/app/finders/issuable_finder.rb index 2eccc0ee31f118436f10e2791c7cdddb3dd1b128..ab89aa2c53a5ef5b294ec23ef06e9a614f6de16e 100644 --- a/app/finders/issuable_finder.rb +++ b/app/finders/issuable_finder.rb @@ -10,7 +10,7 @@ # state: 'open' or 'closed' or 'all' # group_id: integer # project_id: integer -# milestone_id: integer +# milestone_title: string # assignee_id: integer # search: string # label_name: string @@ -76,7 +76,7 @@ class IssuableFinder return @milestones if defined?(@milestones) @milestones = - if milestones? && params[:milestone_title] != NONE + if milestones? && params[:milestone_title] != Milestone::None.title Milestone.where(title: params[:milestone_title]) else nil diff --git a/app/helpers/milestones_helper.rb b/app/helpers/milestones_helper.rb index 93e33ebefd8000d93b960d2c9c0609fad6166c31..132a893e532965be442e9f11a4c18cd16a7eb52b 100644 --- a/app/helpers/milestones_helper.rb +++ b/app/helpers/milestones_helper.rb @@ -29,6 +29,8 @@ module MilestonesHelper end.active grouped_milestones = Milestones::GroupService.new(milestones).execute + grouped_milestones.unshift(Milestone::None) + options_from_collection_for_select(grouped_milestones, 'title', 'title', params[:milestone_title]) end end diff --git a/app/models/milestone.rb b/app/models/milestone.rb index e0c5fec97b7b3b84da1363bdf466cc2fbf2b53f9..d28f3c8d3f908756cfa196c2a76cbe3da1317f8a 100644 --- a/app/models/milestone.rb +++ b/app/models/milestone.rb @@ -14,6 +14,10 @@ # class Milestone < ActiveRecord::Base + # Represents a "No Milestone" state used for filtering Issues and Merge + # Requests that have no milestone assigned. + None = Struct.new(:title).new('No Milestone') + include InternalId include Sortable diff --git a/app/views/shared/issuable/_filter.html.haml b/app/views/shared/issuable/_filter.html.haml index a829782fc4f2af615c5d6909b4a4dee69e9b6e26..0e8da8de723736c556887ff54cf0c13ae8611d00 100644 --- a/app/views/shared/issuable/_filter.html.haml +++ b/app/views/shared/issuable/_filter.html.haml @@ -43,11 +43,15 @@ placeholder: 'Author', class: 'trigger-submit', any_user: true, first_user: true) .filter-item.inline.milestone-filter - = select_tag('milestone_title', projects_milestones_options, class: "select2 trigger-submit", prompt: 'Milestone') + = select_tag('milestone_title', projects_milestones_options, + class: 'select2 trigger-submit', include_blank: true, + data: {placeholder: 'Milestone'}) - if @project .filter-item.inline.labels-filter - = select_tag('label_name', project_labels_options(@project), class: "select2 trigger-submit", prompt: 'Label') + = select_tag('label_name', project_labels_options(@project), + class: 'select2 trigger-submit', include_blank: true, + data: {placeholder: 'Label'}) .pull-right = render 'shared/sort_dropdown' diff --git a/spec/features/groups_spec.rb b/spec/features/groups_spec.rb index edc1c63a0aab3f90f38786fc79f9d8063791f6b7..891df65216d1d46649c798bdcab848a74a8f32da 100644 --- a/spec/features/groups_spec.rb +++ b/spec/features/groups_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -feature 'Group' do +feature 'Group', feature: true do describe 'description' do let(:group) { create(:group) } let(:path) { group_path(group) } diff --git a/spec/features/issues/filter_by_milestone_spec.rb b/spec/features/issues/filter_by_milestone_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..f600f8684acfcf2eeec6a5e73bc1c964fb2b4498 --- /dev/null +++ b/spec/features/issues/filter_by_milestone_spec.rb @@ -0,0 +1,36 @@ +require 'rails_helper' + +feature 'Issue filtering by Milestone', feature: true do + include Select2Helper + + let(:project) { create(:project, :public) } + let(:milestone) { create(:milestone, project: project) } + + scenario 'filters by no Milestone', js: true do + create(:issue, project: project) + create(:issue, project: project, milestone: milestone) + + visit_issues(project) + filter_by_milestone(Milestone::None.title) + + expect(page).to have_css('.issue-title', count: 1) + end + + scenario 'filters by a specific Milestone', js: true do + create(:issue, project: project, milestone: milestone) + create(:issue, project: project) + + visit_issues(project) + filter_by_milestone(milestone.title) + + expect(page).to have_css('.issue-title', count: 1) + end + + def visit_issues(project) + visit namespace_project_issues_path(project.namespace, project) + end + + def filter_by_milestone(title) + select2(title, from: '#milestone_title') + end +end diff --git a/spec/features/issues_spec.rb b/spec/features/issues_spec.rb index 808a6eeb958a0f47376ad2e9d6a5c300542f391d..32fd4065bb41d55ca0c25f8dcaaf5d573dd897d8 100644 --- a/spec/features/issues_spec.rb +++ b/spec/features/issues_spec.rb @@ -92,22 +92,6 @@ describe 'Issues', feature: true do let(:issue) { @issue } - it 'should allow filtering by issues with no specified milestone' do - visit namespace_project_issues_path(project.namespace, project, milestone_title: IssuableFinder::NONE) - - expect(page).not_to have_content 'foobar' - expect(page).to have_content 'barbaz' - expect(page).to have_content 'gitlab' - end - - it 'should allow filtering by a specified milestone' do - visit namespace_project_issues_path(project.namespace, project, milestone_title: issue.milestone.title) - - expect(page).to have_content 'foobar' - expect(page).not_to have_content 'barbaz' - expect(page).not_to have_content 'gitlab' - end - it 'should allow filtering by issues with no specified assignee' do visit namespace_project_issues_path(project.namespace, project, assignee_id: IssuableFinder::NONE) diff --git a/spec/features/login_spec.rb b/spec/features/login_spec.rb index 046a9f6191df4ed372da3f1b43feec905dfa7d15..cef432e512b8a285c7fb14bc3bdfd51e8d4db78e 100644 --- a/spec/features/login_spec.rb +++ b/spec/features/login_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -feature 'Login' do +feature 'Login', feature: true do describe 'with two-factor authentication' do context 'with valid username/password' do let(:user) { create(:user, :two_factor) } diff --git a/spec/features/markdown_spec.rb b/spec/features/markdown_spec.rb index 902968cebcb6924bec6ced442c707f7840281942..b8199aa5e618fd604c61e4686972e8f977a241e9 100644 --- a/spec/features/markdown_spec.rb +++ b/spec/features/markdown_spec.rb @@ -32,7 +32,7 @@ require 'erb' # # See the MarkdownFeature class for setup details. -describe 'GitLab Markdown' do +describe 'GitLab Markdown', feature: true do include ActionView::Helpers::TagHelper include ActionView::Helpers::UrlHelper include Capybara::Node::Matchers diff --git a/spec/features/merge_requests/filter_by_milestone_spec.rb b/spec/features/merge_requests/filter_by_milestone_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..f70214e11223ae0928d39360f67c1b5c84bc1873 --- /dev/null +++ b/spec/features/merge_requests/filter_by_milestone_spec.rb @@ -0,0 +1,36 @@ +require 'rails_helper' + +feature 'Merge Request filtering by Milestone', feature: true do + include Select2Helper + + let(:project) { create(:project, :public) } + let(:milestone) { create(:milestone, project: project) } + + scenario 'filters by no Milestone', js: true do + create(:merge_request, :with_diffs, source_project: project) + create(:merge_request, :simple, source_project: project, milestone: milestone) + + visit_merge_requests(project) + filter_by_milestone(Milestone::None.title) + + expect(page).to have_css('.merge-request-title', count: 1) + end + + scenario 'filters by a specific Milestone', js: true do + create(:merge_request, :with_diffs, source_project: project, milestone: milestone) + create(:merge_request, :simple, source_project: project) + + visit_merge_requests(project) + filter_by_milestone(milestone.title) + + expect(page).to have_css('.merge-request-title', count: 1) + end + + def visit_merge_requests(project) + visit namespace_project_merge_requests_path(project.namespace, project) + end + + def filter_by_milestone(title) + select2(title, from: '#milestone_title') + end +end diff --git a/spec/features/notes_on_merge_requests_spec.rb b/spec/features/notes_on_merge_requests_spec.rb index ad37b589b84b6d4f97918cd58a86e558b6d84baf..d7cb3b2e86ead6487a8e965f03168475274c9c96 100644 --- a/spec/features/notes_on_merge_requests_spec.rb +++ b/spec/features/notes_on_merge_requests_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe 'Comments' do +describe 'Comments', feature: true do include RepoHelpers describe 'On a merge request', js: true, feature: true do diff --git a/spec/features/password_reset_spec.rb b/spec/features/password_reset_spec.rb index a34efce09ef2ca4b2fe13c1df3230a14207405fc..2b6311e4fd7ae6a2884e9843ba3293129331724b 100644 --- a/spec/features/password_reset_spec.rb +++ b/spec/features/password_reset_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -feature 'Password reset' do +feature 'Password reset', feature: true do def forgot_password click_on 'Forgot your password?' fill_in 'Email', with: user.email diff --git a/spec/features/profile_spec.rb b/spec/features/profile_spec.rb index 9fe2e610555ddcfa30f30d517ae737bf193cd7e2..c80253fead8c7dd280eaa2036d22f244c09a0bc8 100644 --- a/spec/features/profile_spec.rb +++ b/spec/features/profile_spec.rb @@ -9,8 +9,7 @@ describe 'Profile account page', feature: true do describe 'when signup is enabled' do before do - allow_any_instance_of(ApplicationSetting). - to receive(:signup_enabled?).and_return(true) + stub_application_setting(signup_enabled: true) visit profile_account_path end @@ -24,8 +23,7 @@ describe 'Profile account page', feature: true do describe 'when signup is disabled' do before do - allow_any_instance_of(ApplicationSetting). - to receive(:signup_enabled?).and_return(false) + stub_application_setting(signup_enabled: false) visit profile_account_path end diff --git a/spec/features/profiles/preferences_spec.rb b/spec/features/profiles/preferences_spec.rb index 03e78c533db2785d246c086276f07ab7cc268774..9bc6145dda4378bcce792a603b254256a7de8d33 100644 --- a/spec/features/profiles/preferences_spec.rb +++ b/spec/features/profiles/preferences_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe 'Profile > Preferences' do +describe 'Profile > Preferences', feature: true do let(:user) { create(:user) } before do diff --git a/spec/features/projects_spec.rb b/spec/features/projects_spec.rb index f8eea70ec4a62e4c7ee389c5bf9a5ccfdd63809c..c8d44efdc8b4d3b9cd5ec483cab556bfb0c2eb47 100644 --- a/spec/features/projects_spec.rb +++ b/spec/features/projects_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -feature 'Project' do +feature 'Project', feature: true do describe 'description' do let(:project) { create(:project) } let(:path) { namespace_project_path(project.namespace, project) } diff --git a/spec/features/users_spec.rb b/spec/features/users_spec.rb index a4c3dfe9205fdc7ebe86979f64f478133210628d..efcb8a31abe64fc0e8f6e12110156c0960521d13 100644 --- a/spec/features/users_spec.rb +++ b/spec/features/users_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -feature 'Users' do +feature 'Users', feature: true do scenario 'GET /users/sign_in creates a new user account' do visit new_user_session_path fill_in 'user_name', with: 'Name Surname' diff --git a/spec/requests/api/projects_spec.rb b/spec/requests/api/projects_spec.rb index e9ff832603fdbab91f89627611bdb2f6fc161233..4178bb2e83666f9756cd4300d2a837476022f35d 100644 --- a/spec/requests/api/projects_spec.rb +++ b/spec/requests/api/projects_spec.rb @@ -220,9 +220,7 @@ describe API::API, api: true do context 'when a visibility level is restricted' do before do @project = attributes_for(:project, { public: true }) - allow_any_instance_of(ApplicationSetting).to( - receive(:restricted_visibility_levels).and_return([20]) - ) + stub_application_setting(restricted_visibility_levels: [Gitlab::VisibilityLevel::PUBLIC]) end it 'should not allow a non-admin to use a restricted visibility level' do diff --git a/spec/services/create_snippet_service_spec.rb b/spec/services/create_snippet_service_spec.rb index 08689c15ca8ac937677691c73bd00bf21834ba80..8edabe9450bcf7243bae886dde5a0bffbc8e012d 100644 --- a/spec/services/create_snippet_service_spec.rb +++ b/spec/services/create_snippet_service_spec.rb @@ -14,11 +14,7 @@ describe CreateSnippetService do context 'When public visibility is restricted' do before do - allow_any_instance_of(ApplicationSetting).to( - receive(:restricted_visibility_levels).and_return( - [Gitlab::VisibilityLevel::PUBLIC] - ) - ) + stub_application_setting(restricted_visibility_levels: [Gitlab::VisibilityLevel::PUBLIC]) @opts.merge!(visibility_level: Gitlab::VisibilityLevel::PUBLIC) end diff --git a/spec/services/git_push_service_spec.rb b/spec/services/git_push_service_spec.rb index 3373b97bfd4ed7323bd79ddb178cfc2ed83b00fe..62cef9db5344f1754c156cf4ca551287d44b0598 100644 --- a/spec/services/git_push_service_spec.rb +++ b/spec/services/git_push_service_spec.rb @@ -124,9 +124,7 @@ describe GitPushService do end it "when pushing a branch for the first time with default branch protection disabled" do - allow(ApplicationSetting.current_application_settings). - to receive(:default_branch_protection). - and_return(Gitlab::Access::PROTECTION_NONE) + stub_application_setting(default_branch_protection: Gitlab::Access::PROTECTION_NONE) expect(project).to receive(:execute_hooks) expect(project.default_branch).to eq("master") @@ -135,9 +133,7 @@ describe GitPushService do end it "when pushing a branch for the first time with default branch protection set to 'developers can push'" do - allow(ApplicationSetting.current_application_settings). - to receive(:default_branch_protection). - and_return(Gitlab::Access::PROTECTION_DEV_CAN_PUSH) + stub_application_setting(default_branch_protection: Gitlab::Access::PROTECTION_DEV_CAN_PUSH) expect(project).to receive(:execute_hooks) expect(project.default_branch).to eq("master") diff --git a/spec/services/projects/create_service_spec.rb b/spec/services/projects/create_service_spec.rb index 337dae592dd488024653093b2c918ec94880c2bc..97b206c9854739bd310cd1bd38f5bd849e4ac69d 100644 --- a/spec/services/projects/create_service_spec.rb +++ b/spec/services/projects/create_service_spec.rb @@ -58,9 +58,7 @@ describe Projects::CreateService do context 'restricted visibility level' do before do - allow_any_instance_of(ApplicationSetting).to( - receive(:restricted_visibility_levels).and_return([20]) - ) + stub_application_setting(restricted_visibility_levels: [Gitlab::VisibilityLevel::PUBLIC]) @opts.merge!( visibility_level: Gitlab::VisibilityLevel.options['Public'] diff --git a/spec/services/projects/update_service_spec.rb b/spec/services/projects/update_service_spec.rb index 0dd6980a44fd79838fc2a74a20518213e6922535..b347fa15f878ba1226b56a5d44eabf8904e89599 100644 --- a/spec/services/projects/update_service_spec.rb +++ b/spec/services/projects/update_service_spec.rb @@ -47,9 +47,7 @@ describe Projects::UpdateService do context 'respect configured visibility restrictions setting' do before(:each) do - allow_any_instance_of(ApplicationSetting).to( - receive(:restricted_visibility_levels).and_return([20]) - ) + stub_application_setting(restricted_visibility_levels: [Gitlab::VisibilityLevel::PUBLIC]) end context 'should be private when updated to private' do diff --git a/spec/services/update_snippet_service_spec.rb b/spec/services/update_snippet_service_spec.rb index 841ef9bfed11e5e037b686e80a5f0ecfbae2e97a..d7c516e3934e6d2925189373f40e1410aa867e6d 100644 --- a/spec/services/update_snippet_service_spec.rb +++ b/spec/services/update_snippet_service_spec.rb @@ -14,11 +14,7 @@ describe UpdateSnippetService do context 'When public visibility is restricted' do before do - allow_any_instance_of(ApplicationSetting).to( - receive(:restricted_visibility_levels).and_return( - [Gitlab::VisibilityLevel::PUBLIC] - ) - ) + stub_application_setting(restricted_visibility_levels: [Gitlab::VisibilityLevel::PUBLIC]) @snippet = create_snippet(@project, @user, @opts) @opts.merge!(visibility_level: Gitlab::VisibilityLevel::PUBLIC) diff --git a/spec/support/stub_configuration.rb b/spec/support/stub_configuration.rb index ad86abdbb413a6758da2805b270624541cdfbdc2..e4004ec8f79b88eb43298b6b9b54319ce18e68bd 100644 --- a/spec/support/stub_configuration.rb +++ b/spec/support/stub_configuration.rb @@ -1,5 +1,10 @@ module StubConfiguration def stub_application_setting(messages) + add_predicates(messages) + + # Stubbing both of these because we're not yet consistent with how we access + # current application settings + allow_any_instance_of(ApplicationSetting).to receive_messages(messages) allow(Gitlab::CurrentSettings.current_application_settings). to receive_messages(messages) end @@ -11,4 +16,25 @@ module StubConfiguration def stub_gravatar_setting(messages) allow(Gitlab.config.gravatar).to receive_messages(messages) end + + private + + # Modifies stubbed messages to also stub possible predicate versions + # + # Examples: + # + # add_predicates(foo: true) + # # => {foo: true, foo?: true} + # + # add_predicates(signup_enabled?: false) + # # => {signup_enabled? false} + def add_predicates(messages) + # Only modify keys that aren't already predicates + keys = messages.keys.map(&:to_s).reject { |k| k.end_with?('?') } + + keys.each do |key| + predicate = key + '?' + messages[predicate.to_sym] = messages[key.to_sym] + end + end end