diff options
78 files changed, 575 insertions, 223 deletions
diff --git a/CHANGELOG b/CHANGELOG index ead17253c79..08a952ff8e5 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -2,13 +2,21 @@ Please view this file on the master branch, on stable branches it's out of date. v 8.13.0 (unreleased) - Speed-up group milestones show page + - Fix robots.txt disallowing access to groups starting with "s" (Matt Harrison) + - Revoke button in Applications Settings underlines on hover. + - Add organization field to user profile v 8.12.2 (unreleased) - Added University content to doc/university + - Fix Import/Export not recognising correctly the imported services. + - Fix snippets pagination + - Fix List-Unsubscribe header in emails + - Fix an issue with the "Commits" section of the cycle analytics summary. !6513 v 8.12.1 - Fix a memory leak in HTML::Pipeline::SanitizationFilter::WHITELIST - Fix issue with search filter labels not displaying + - Fix bug where 'Search results' repeated many times when a search in the emoji search form is cleared (Xavier Bick) (@zeiv) v 8.12.0 - Update the rouge gem to 2.0.6, which adds highlighting support for JSX, Prometheus, and others. !6251 @@ -24,6 +32,7 @@ v 8.12.0 - Filter tags by name !6121 - Update gitlab shell secret file also when it is empty. !3774 (glensc) - Give project selection dropdowns responsive width, make non-wrapping. + - Fix resolve discussion buttons endpoint path - Fix note form hint showing slash commands supported for commits. - Make push events have equal vertical spacing. - API: Ensure invitees are not returned in Members API. @@ -6,10 +6,8 @@ gem 'rails-deprecated_sanitizer', '~> 1.0.3' # Responders respond_to and respond_with gem 'responders', '~> 2.0' -# Specify a sprockets version due to increased performance -# See https://gitlab.com/gitlab-org/gitlab-ce/issues/6069 -gem 'sprockets', '~> 3.6.0' -gem 'sprockets-es6' +gem 'sprockets', '~> 3.7.0' +gem 'sprockets-es6', '~> 0.9.2' # Default values for AR models gem 'default_value_for', '~> 3.0.0' @@ -122,8 +120,8 @@ gem 'diffy', '~> 3.0.3' # Application server group :unicorn do - gem 'unicorn', '~> 4.9.0' - gem 'unicorn-worker-killer', '~> 0.4.2' + gem 'unicorn', '~> 5.1.0' + gem 'unicorn-worker-killer', '~> 0.4.4' end # State machine @@ -212,7 +210,7 @@ gem 'oj', '~> 2.17.4' gem 'chronic', '~> 0.10.2' gem 'chronic_duration', '~> 0.10.6' -gem 'sass-rails', '~> 5.0.0' +gem 'sass-rails', '~> 5.0.6' gem 'coffee-rails', '~> 4.1.0' gem 'uglifier', '~> 2.7.2' gem 'turbolinks', '~> 2.5.0' diff --git a/Gemfile.lock b/Gemfile.lock index 1db8c9dd8c8..1691f92c8ce 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -553,7 +553,7 @@ GEM rake (>= 0.8.7) thor (>= 0.18.1, < 2.0) rainbow (2.1.0) - raindrops (0.15.0) + raindrops (0.17.0) rake (10.5.0) rb-fsevent (0.9.6) rb-inotify (0.9.5) @@ -644,7 +644,7 @@ GEM sanitize (2.1.0) nokogiri (>= 1.4.4) sass (3.4.22) - sass-rails (5.0.5) + sass-rails (5.0.6) railties (>= 4.0.0, < 6) sass (~> 3.1) sprockets (>= 2.8, < 4.0) @@ -705,10 +705,10 @@ GEM spring (>= 0.9.1) spring-commands-teaspoon (0.0.2) spring (>= 0.9.1) - sprockets (3.6.3) + sprockets (3.7.0) concurrent-ruby (~> 1.0) rack (> 1, < 3) - sprockets-es6 (0.9.0) + sprockets-es6 (0.9.2) babel-source (>= 5.8.11) babel-transpiler sprockets (>= 3.0.0) @@ -759,9 +759,8 @@ GEM unf_ext unf_ext (0.0.7.2) unicode-display_width (1.1.1) - unicorn (4.9.0) + unicorn (5.1.0) kgio (~> 2.6) - rack raindrops (~> 0.7) unicorn-worker-killer (0.4.4) get_process_mem (~> 0) @@ -945,7 +944,7 @@ DEPENDENCIES ruby-fogbugz (~> 0.2.1) ruby-prof (~> 0.15.9) sanitize (~> 2.0) - sass-rails (~> 5.0.0) + sass-rails (~> 5.0.6) scss_lint (~> 0.47.0) sdoc (~> 0.3.20) seed-fu (~> 2.3.5) @@ -964,8 +963,8 @@ DEPENDENCIES spring-commands-rspec (~> 1.0.4) spring-commands-spinach (~> 1.1.0) spring-commands-teaspoon (~> 0.0.2) - sprockets (~> 3.6.0) - sprockets-es6 + sprockets (~> 3.7.0) + sprockets-es6 (~> 0.9.2) state_machines-activerecord (~> 0.4.0) sys-filesystem (~> 1.1.6) task_list (~> 1.0.2) @@ -979,8 +978,8 @@ DEPENDENCIES uglifier (~> 2.7.2) underscore-rails (~> 1.8.0) unf (~> 0.1.4) - unicorn (~> 4.9.0) - unicorn-worker-killer (~> 0.4.2) + unicorn (~> 5.1.0) + unicorn-worker-killer (~> 0.4.4) version_sorter (~> 2.1.0) virtus (~> 1.0.1) vmstat (~> 2.2) diff --git a/app/assets/javascripts/awards_handler.js b/app/assets/javascripts/awards_handler.js index 0decc6d09e6..44af1c135a0 100644 --- a/app/assets/javascripts/awards_handler.js +++ b/app/assets/javascripts/awards_handler.js @@ -357,7 +357,7 @@ $('ul.emoji-menu-search, h5.emoji-search').remove(); if (term) { // Generate a search result block - h5 = $('<h5>').text('Search results'); + h5 = $('<h5 class="emoji-search" />').text('Search results'); found_emojis = _this.searchEmojis(term).show(); ul = $('<ul>').addClass('emoji-menu-list emoji-menu-search').append(found_emojis); $('.emoji-menu-content ul, .emoji-menu-content h5').hide(); diff --git a/app/assets/javascripts/diff_notes/components/resolve_btn.js.es6 b/app/assets/javascripts/diff_notes/components/resolve_btn.js.es6 index be6ebc77947..cdedfd1af15 100644 --- a/app/assets/javascripts/diff_notes/components/resolve_btn.js.es6 +++ b/app/assets/javascripts/diff_notes/components/resolve_btn.js.es6 @@ -1,13 +1,9 @@ ((w) => { w.ResolveBtn = Vue.extend({ - mixins: [ - ButtonMixins - ], props: { noteId: Number, discussionId: String, resolved: Boolean, - namespacePath: String, projectPath: String, canResolve: Boolean, resolvedBy: String @@ -69,10 +65,10 @@ if (this.isResolved) { promise = ResolveService - .unresolve(this.namespace, this.noteId); + .unresolve(this.projectPath, this.noteId); } else { promise = ResolveService - .resolve(this.namespace, this.noteId); + .resolve(this.projectPath, this.noteId); } promise.then((response) => { diff --git a/app/assets/javascripts/diff_notes/components/resolve_discussion_btn.js.es6 b/app/assets/javascripts/diff_notes/components/resolve_discussion_btn.js.es6 index e373b06b1eb..0a617034502 100644 --- a/app/assets/javascripts/diff_notes/components/resolve_discussion_btn.js.es6 +++ b/app/assets/javascripts/diff_notes/components/resolve_discussion_btn.js.es6 @@ -1,12 +1,8 @@ ((w) => { w.ResolveDiscussionBtn = Vue.extend({ - mixins: [ - ButtonMixins - ], props: { discussionId: String, mergeRequestId: Number, - namespacePath: String, projectPath: String, canResolve: Boolean, }, @@ -50,7 +46,7 @@ }, methods: { resolve: function () { - ResolveService.toggleResolveForDiscussion(this.namespace, this.mergeRequestId, this.discussionId); + ResolveService.toggleResolveForDiscussion(this.projectPath, this.mergeRequestId, this.discussionId); } }, created: function () { diff --git a/app/assets/javascripts/diff_notes/mixins/namespace.js.es6 b/app/assets/javascripts/diff_notes/mixins/namespace.js.es6 deleted file mode 100644 index d278678085b..00000000000 --- a/app/assets/javascripts/diff_notes/mixins/namespace.js.es6 +++ /dev/null @@ -1,9 +0,0 @@ -((w) => { - w.ButtonMixins = { - computed: { - namespace: function () { - return `${this.namespacePath}/${this.projectPath}`; - } - } - }; -})(window); diff --git a/app/assets/javascripts/diff_notes/services/resolve.js.es6 b/app/assets/javascripts/diff_notes/services/resolve.js.es6 index de771ff814b..2a55f739b31 100644 --- a/app/assets/javascripts/diff_notes/services/resolve.js.es6 +++ b/app/assets/javascripts/diff_notes/services/resolve.js.es6 @@ -9,32 +9,32 @@ Vue.http.headers.common['X-CSRF-Token'] = $.rails.csrfToken(); } - prepareRequest(namespace) { + prepareRequest(root) { this.setCSRF(); - Vue.http.options.root = `/${namespace}`; + Vue.http.options.root = root; } - resolve(namespace, noteId) { - this.prepareRequest(namespace); + resolve(projectPath, noteId) { + this.prepareRequest(projectPath); return this.noteResource.save({ noteId }, {}); } - unresolve(namespace, noteId) { - this.prepareRequest(namespace); + unresolve(projectPath, noteId) { + this.prepareRequest(projectPath); return this.noteResource.delete({ noteId }, {}); } - toggleResolveForDiscussion(namespace, mergeRequestId, discussionId) { + toggleResolveForDiscussion(projectPath, mergeRequestId, discussionId) { const discussion = CommentsStore.state[discussionId], isResolved = discussion.isResolved(); let promise; if (isResolved) { - promise = this.unResolveAll(namespace, mergeRequestId, discussionId); + promise = this.unResolveAll(projectPath, mergeRequestId, discussionId); } else { - promise = this.resolveAll(namespace, mergeRequestId, discussionId); + promise = this.resolveAll(projectPath, mergeRequestId, discussionId); } promise.then((response) => { @@ -57,10 +57,10 @@ }) } - resolveAll(namespace, mergeRequestId, discussionId) { + resolveAll(projectPath, mergeRequestId, discussionId) { const discussion = CommentsStore.state[discussionId]; - this.prepareRequest(namespace); + this.prepareRequest(projectPath); discussion.loading = true; @@ -70,10 +70,10 @@ }, {}); } - unResolveAll(namespace, mergeRequestId, discussionId) { + unResolveAll(projectPath, mergeRequestId, discussionId) { const discussion = CommentsStore.state[discussionId]; - this.prepareRequest(namespace); + this.prepareRequest(projectPath); discussion.loading = true; diff --git a/app/assets/javascripts/lib/utils/url_utility.js b/app/assets/javascripts/lib/utils/url_utility.js index f84a20cf0fe..b8d52becb3f 100644 --- a/app/assets/javascripts/lib/utils/url_utility.js +++ b/app/assets/javascripts/lib/utils/url_utility.js @@ -19,7 +19,7 @@ while (i < sURLVariables.length) { sParameterName = sURLVariables[i].split('='); if (sParameterName[0] === sParam) { - values.push(sParameterName[1]); + values.push(sParameterName[1].replace(/\+/g, ' ')); } i++; } diff --git a/app/assets/javascripts/notes.js b/app/assets/javascripts/notes.js index c6854f703fb..866a04d3e21 100644 --- a/app/assets/javascripts/notes.js +++ b/app/assets/javascripts/notes.js @@ -432,14 +432,12 @@ var $form = $(xhr.target); if ($form.attr('data-resolve-all') != null) { - var namespacePath = $form.attr('data-namespace-path'), - projectPath = $form.attr('data-project-path') - discussionId = $form.attr('data-discussion-id'), - mergeRequestId = $form.attr('data-noteable-iid'), - namespace = namespacePath + '/' + projectPath; + var projectPath = $form.data('project-path') + discussionId = $form.data('discussion-id'), + mergeRequestId = $form.data('noteable-iid'); if (ResolveService != null) { - ResolveService.toggleResolveForDiscussion(namespace, mergeRequestId, discussionId); + ResolveService.toggleResolveForDiscussion(projectPath, mergeRequestId, discussionId); } } @@ -854,7 +852,6 @@ .closest('form') .attr('data-discussion-id', discussionId) .attr('data-resolve-all', 'true') - .attr('data-namespace-path', $this.attr('data-namespace-path')) .attr('data-project-path', $this.attr('data-project-path')); }; diff --git a/app/assets/javascripts/search_autocomplete.js b/app/assets/javascripts/search_autocomplete.js index 8abb09c626f..678d836f56f 100644 --- a/app/assets/javascripts/search_autocomplete.js +++ b/app/assets/javascripts/search_autocomplete.js @@ -389,4 +389,41 @@ })(); + $(function() { + var $projectOptionsDataEl = $('.js-search-project-options'); + var $groupOptionsDataEl = $('.js-search-group-options'); + var $dashboardOptionsDataEl = $('.js-search-dashboard-options'); + + if ($projectOptionsDataEl.length) { + gl.projectOptions = gl.projectOptions || {}; + + var projectPath = $projectOptionsDataEl.data('project-path'); + + gl.projectOptions[projectPath] = { + name: $projectOptionsDataEl.data('name'), + issuesPath: $projectOptionsDataEl.data('issues-path'), + mrPath: $projectOptionsDataEl.data('mr-path') + }; + } + + if ($groupOptionsDataEl.length) { + gl.groupOptions = gl.groupOptions || {}; + + var groupPath = $groupOptionsDataEl.data('group-path'); + + gl.groupOptions[groupPath] = { + name: $groupOptionsDataEl.data('name'), + issuesPath: $groupOptionsDataEl.data('issues-path'), + mrPath: $groupOptionsDataEl.data('mr-path') + }; + } + + if ($dashboardOptionsDataEl.length) { + gl.dashboardOptions = { + issuesPath: $dashboardOptionsDataEl.data('issues-path'), + mrPath: $dashboardOptionsDataEl.data('mr-path') + }; + } + }); + }).call(this); diff --git a/app/assets/stylesheets/framework/buttons.scss b/app/assets/stylesheets/framework/buttons.scss index 4618687a4be..ce489f7c3de 100644 --- a/app/assets/stylesheets/framework/buttons.scss +++ b/app/assets/stylesheets/framework/buttons.scss @@ -336,3 +336,9 @@ box-shadow: inset 0 0 0 white; } } + +@media (max-width: $screen-xs-max) { + .btn-wide-on-xs { + width: 100%; + } +} diff --git a/app/assets/stylesheets/framework/lists.scss b/app/assets/stylesheets/framework/lists.scss index 46af18580d5..efc348214c2 100644 --- a/app/assets/stylesheets/framework/lists.scss +++ b/app/assets/stylesheets/framework/lists.scss @@ -164,7 +164,7 @@ ul.content-list { } .no-comments { - opacity: 0.5; + opacity: .5; } } diff --git a/app/assets/stylesheets/pages/search.scss b/app/assets/stylesheets/pages/search.scss index 436fb00ba2e..e77f9816d8a 100644 --- a/app/assets/stylesheets/pages/search.scss +++ b/app/assets/stylesheets/pages/search.scss @@ -103,7 +103,7 @@ // Custom dropdown positioning .dropdown-menu { - top: 30px; + top: 37px; left: -5px; padding: 0; diff --git a/app/assets/stylesheets/pages/snippets.scss b/app/assets/stylesheets/pages/snippets.scss index 4d5df566d9b..857eb76131a 100644 --- a/app/assets/stylesheets/pages/snippets.scss +++ b/app/assets/stylesheets/pages/snippets.scss @@ -36,3 +36,7 @@ float: right; } } + +.snippet-scope-menu .btn-new { + margin-top: 15px; +} diff --git a/app/controllers/concerns/membership_actions.rb b/app/controllers/concerns/membership_actions.rb index 52682ef9dc9..b8ed2c159a7 100644 --- a/app/controllers/concerns/membership_actions.rb +++ b/app/controllers/concerns/membership_actions.rb @@ -1,6 +1,5 @@ module MembershipActions extend ActiveSupport::Concern - include MembersHelper def request_access membershipable.request_access(current_user) @@ -10,11 +9,7 @@ module MembershipActions end def approve_access_request - @member = membershipable.requesters.find(params[:id]) - - return render_403 unless can?(current_user, action_member_permission(:update, @member), @member) - - @member.accept_request + Members::ApproveAccessRequestService.new(membershipable, current_user, params).execute redirect_to polymorphic_url([membershipable, :members]) end diff --git a/app/controllers/profiles_controller.rb b/app/controllers/profiles_controller.rb index c5fa756d02b..f71e0a1302b 100644 --- a/app/controllers/profiles_controller.rb +++ b/app/controllers/profiles_controller.rb @@ -73,7 +73,8 @@ class ProfilesController < Profiles::ApplicationController :skype, :twitter, :username, - :website_url + :website_url, + :organization ) end end diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb index a4bedb3bfe6..838ecc837e4 100644 --- a/app/controllers/users_controller.rb +++ b/app/controllers/users_controller.rb @@ -65,7 +65,7 @@ class UsersController < ApplicationController format.html { render 'show' } format.json do render json: { - html: view_to_html_string("snippets/_snippets", collection: @snippets) + html: view_to_html_string("snippets/_snippets", collection: @snippets, remote: true) } end end diff --git a/app/helpers/lfs_helper.rb b/app/helpers/lfs_helper.rb index c15ecc8f86e..95b60aeab5f 100644 --- a/app/helpers/lfs_helper.rb +++ b/app/helpers/lfs_helper.rb @@ -1,11 +1,13 @@ module LfsHelper + include Gitlab::Routing.url_helpers + def require_lfs_enabled! return if Gitlab.config.lfs.enabled render( json: { message: 'Git LFS is not enabled on this GitLab server, contact your admin.', - documentation_url: "#{Gitlab.config.gitlab.url}/help", + documentation_url: help_url, }, status: 501 ) @@ -46,7 +48,7 @@ module LfsHelper render( json: { message: 'Access forbidden. Check your access level.', - documentation_url: "#{Gitlab.config.gitlab.url}/help", + documentation_url: help_url, }, content_type: "application/vnd.git-lfs+json", status: 403 @@ -57,7 +59,7 @@ module LfsHelper render( json: { message: 'Not found.', - documentation_url: "#{Gitlab.config.gitlab.url}/help", + documentation_url: help_url, }, content_type: "application/vnd.git-lfs+json", status: 404 diff --git a/app/mailers/notify.rb b/app/mailers/notify.rb index 9799f1dc886..29f1c527776 100644 --- a/app/mailers/notify.rb +++ b/app/mailers/notify.rb @@ -109,7 +109,7 @@ class Notify < BaseMailer headers['X-GitLab-Reply-Key'] = reply_key if !@labels_url && @sent_notification && @sent_notification.unsubscribable? - headers['List-Unsubscribe'] = unsubscribe_sent_notification_url(@sent_notification, force: true) + headers['List-Unsubscribe'] = "<#{unsubscribe_sent_notification_url(@sent_notification, force: true)}>" @sent_notification_url = unsubscribe_sent_notification_url(@sent_notification) end diff --git a/app/models/board.rb b/app/models/board.rb index 3240c4bede3..c56422914a9 100644 --- a/app/models/board.rb +++ b/app/models/board.rb @@ -4,4 +4,12 @@ class Board < ActiveRecord::Base has_many :lists, -> { order(:list_type, :position) }, dependent: :delete_all validates :project, presence: true + + def backlog_list + lists.merge(List.backlog).take + end + + def done_list + lists.merge(List.done).take + end end diff --git a/app/models/cycle_analytics/summary.rb b/app/models/cycle_analytics/summary.rb index 53b2cacb131..b46db449bf3 100644 --- a/app/models/cycle_analytics/summary.rb +++ b/app/models/cycle_analytics/summary.rb @@ -10,15 +10,33 @@ class CycleAnalytics end def commits - repository = @project.repository.raw_repository - - if @project.default_branch - repository.log(ref: @project.default_branch, after: @from).count - end + ref = @project.default_branch.presence + count_commits_for(ref) end def deploys @project.deployments.where("created_at > ?", @from).count end + + private + + # Don't use the `Gitlab::Git::Repository#log` method, because it enforces + # a limit. Since we need a commit count, we _can't_ enforce a limit, so + # the easiest way forward is to replicate the relevant portions of the + # `log` function here. + def count_commits_for(ref) + return unless ref + + repository = @project.repository.raw_repository + sha = @project.repository.commit(ref).sha + + cmd = %W(git --git-dir=#{repository.path} log) + cmd << '--format=%H' + cmd << "--after=#{@from.iso8601}" + cmd << sha + + raw_output = IO.popen(cmd) { |io| io.read } + raw_output.lines.count + end end end diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb index 2dcf7f89bfc..aec555dcec0 100644 --- a/app/models/merge_request.rb +++ b/app/models/merge_request.rb @@ -590,13 +590,11 @@ class MergeRequest < ActiveRecord::Base end def merge_commit_message - message = "Merge branch '#{source_branch}' into '#{target_branch}'" - message << "\n\n" - message << title.to_s - message << "\n\n" - message << description.to_s - message << "\n\n" - message << "See merge request !#{iid}" + message = "Merge branch '#{source_branch}' into '#{target_branch}'\n\n" + message << "#{title}\n\n" + message << "#{description}\n\n" if description.present? + message << "See merge request #{to_reference}" + message end diff --git a/app/models/project_services/custom_issue_tracker_service.rb b/app/models/project_services/custom_issue_tracker_service.rb index 63a5ed14484..d9fba3d4a41 100644 --- a/app/models/project_services/custom_issue_tracker_service.rb +++ b/app/models/project_services/custom_issue_tracker_service.rb @@ -9,6 +9,10 @@ class CustomIssueTrackerService < IssueTrackerService end end + def title=(value) + self.properties['title'] = value if self.properties + end + def description if self.properties && self.properties['description'].present? self.properties['description'] diff --git a/app/services/members/approve_access_request_service.rb b/app/services/members/approve_access_request_service.rb new file mode 100644 index 00000000000..416aee2ab51 --- /dev/null +++ b/app/services/members/approve_access_request_service.rb @@ -0,0 +1,31 @@ +module Members + class ApproveAccessRequestService < BaseService + include MembersHelper + + attr_accessor :source + + def initialize(source, current_user, params = {}) + @source = source + @current_user = current_user + @params = params + end + + def execute + condition = params[:user_id] ? { user_id: params[:user_id] } : { id: params[:id] } + access_requester = source.requesters.find_by!(condition) + + raise Gitlab::Access::AccessDeniedError unless can_update_access_requester?(access_requester) + + access_requester.access_level = params[:access_level] if params[:access_level] + access_requester.accept_request + + access_requester + end + + private + + def can_update_access_requester?(access_requester) + access_requester && can?(current_user, action_member_permission(:update, access_requester), access_requester) + end + end +end diff --git a/app/views/dashboard/snippets/index.html.haml b/app/views/dashboard/snippets/index.html.haml index d4e7862981c..b2af438ea57 100644 --- a/app/views/dashboard/snippets/index.html.haml +++ b/app/views/dashboard/snippets/index.html.haml @@ -4,10 +4,10 @@ = render 'dashboard/snippets_head' .nav-block - .controls - = link_to new_snippet_path, class: "btn btn-new", title: "New Snippet" do + .controls.hidden-xs + = link_to new_snippet_path, class: "btn btn-new", title: "New snippet" do = icon('plus') - New Snippet + New snippet .nav-links.snippet-scope-menu %li{ class: ("active" unless params[:scope]) } @@ -34,5 +34,9 @@ %span.badge = current_user.snippets.are_public.count -= render 'snippets/snippets' + .visible-xs + = link_to new_snippet_path, class: "btn btn-new btn-block", title: "New snippet" do + = icon('plus') + New snippet += render 'snippets/snippets' diff --git a/app/views/devise/sessions/_new_ldap.html.haml b/app/views/devise/sessions/_new_ldap.html.haml index 689cd6ed665..2ef383960f4 100644 --- a/app/views/devise/sessions/_new_ldap.html.haml +++ b/app/views/devise/sessions/_new_ldap.html.haml @@ -1,4 +1,4 @@ -= form_tag(user_omniauth_callback_path(server['provider_name']), id: 'new_ldap_user' ) do += form_tag(omniauth_callback_path(:user, server['provider_name']), id: 'new_ldap_user') do = text_field_tag :username, nil, {class: "form-control top", placeholder: "#{server['label']} Login", autofocus: "autofocus"} = password_field_tag :password, nil, {class: "form-control bottom", placeholder: "Password"} - if devise_mapping.rememberable? diff --git a/app/views/discussions/_resolve_all.html.haml b/app/views/discussions/_resolve_all.html.haml index 7a8767ddba0..c77889a4d38 100644 --- a/app/views/discussions/_resolve_all.html.haml +++ b/app/views/discussions/_resolve_all.html.haml @@ -1,11 +1,10 @@ - if discussion.for_merge_request? - %resolve-discussion-btn{ ":namespace-path" => "'#{discussion.project.namespace.path}'", - ":project-path" => "'#{discussion.project.path}'", + %resolve-discussion-btn{ ":project-path" => "'#{project_path(discussion.project)}'", ":discussion-id" => "'#{discussion.id}'", ":merge-request-id" => discussion.noteable.iid, ":can-resolve" => discussion.can_resolve?(current_user), "inline-template" => true } .btn-group{ role: "group", "v-if" => "showButton" } - %button.btn.btn-default{ type: "button", "@click" => "resolve", ":disabled" => "loading" } + %button.btn.btn-default{ type: "button", "@click" => "resolve", ":disabled" => "loading", "v-cloak": true } = icon("spinner spin", "v-show" => "loading") {{ buttonText }} diff --git a/app/views/doorkeeper/authorized_applications/_delete_form.html.haml b/app/views/doorkeeper/authorized_applications/_delete_form.html.haml index bfa95ce79a7..9f02a8d2ed9 100644 --- a/app/views/doorkeeper/authorized_applications/_delete_form.html.haml +++ b/app/views/doorkeeper/authorized_applications/_delete_form.html.haml @@ -6,4 +6,4 @@ = form_tag path do %input{:name => "_method", :type => "hidden", :value => "delete"}/ - = submit_tag 'Revoke', onclick: "return confirm('Are you sure?')", class: 'btn btn-link btn-remove btn-sm' + = submit_tag 'Revoke', onclick: "return confirm('Are you sure?')", class: 'btn btn-remove btn-sm' diff --git a/app/views/explore/snippets/index.html.haml b/app/views/explore/snippets/index.html.haml index 6306fe6d0bf..7def9eacdc9 100644 --- a/app/views/explore/snippets/index.html.haml +++ b/app/views/explore/snippets/index.html.haml @@ -8,9 +8,8 @@ .row-content-block - if current_user - .pull-right - = link_to new_snippet_path, class: "btn btn-new", title: "New Snippet" do - New Snippet + = link_to new_snippet_path, class: "btn btn-new btn-wide-on-sm pull-right", title: "New snippet" do + New snippet .oneline Public snippets created by you and other users are listed here diff --git a/app/views/layouts/_search.html.haml b/app/views/layouts/_search.html.haml index f7580f00159..d7386105b7d 100644 --- a/app/views/layouts/_search.html.haml +++ b/app/views/layouts/_search.html.haml @@ -2,15 +2,18 @@ - label = 'This group' - if controller.controller_path =~ /^projects/ && @project.persisted? - label = 'This project' - +- if @group && @group.persisted? && @group.path + - group_data_attrs = { group_path: j(@group.path), name: @group.name, issues_path: issues_group_path(j(@group.path)), mr_path: merge_requests_group_path(j(@group.path)) } +- if @project && @project.persisted? + - project_data_attrs = { project_path: j(@project.path), name: j(@project.name), issues_path: namespace_project_issues_path(@project.namespace, @project), mr_path: namespace_project_merge_requests_path(@project.namespace, @project) } .search.search-form{class: "#{'has-location-badge' if label.present?}"} = form_tag search_path, method: :get, class: 'navbar-form' do |f| .search-input-container - if label.present? .location-badge= label .search-input-wrap - .dropdown{ data: {url: search_autocomplete_path } } - = search_field_tag "search", nil, placeholder: 'Search', class: "search-input dropdown-menu-toggle", spellcheck: false, tabindex: "1", autocomplete: 'off', data: { toggle: 'dropdown' } + .dropdown{ data: { url: search_autocomplete_path } } + = search_field_tag 'search', nil, placeholder: 'Search', class: 'search-input dropdown-menu-toggle js-search-dashboard-options', spellcheck: false, tabindex: '1', autocomplete: 'off', data: { toggle: 'dropdown', issues_path: issues_dashboard_url, mr_path: merge_requests_dashboard_url } .dropdown-menu.dropdown-select = dropdown_content do %ul @@ -21,8 +24,9 @@ %i.search-icon %i.clear-icon.js-clear-input - = hidden_field_tag :group_id, @group.try(:id) - = hidden_field_tag :project_id, @project && @project.persisted? ? @project.id : '', id: 'search_project_id' + = hidden_field_tag :group_id, @group.try(:id), class: 'js-search-group-options', data: group_data_attrs + + = hidden_field_tag :project_id, @project && @project.persisted? ? @project.id : '', id: 'search_project_id', class: 'js-search-project-options', data: project_data_attrs - if @project && @project.persisted? - if current_controller?(:issues) @@ -36,31 +40,6 @@ - else = hidden_field_tag :search_code, true - :javascript - gl.projectOptions = gl.projectOptions || {}; - gl.projectOptions["#{j(@project.path)}"] = { - issuesPath: "#{namespace_project_issues_path(@project.namespace, @project)}", - mrPath: "#{namespace_project_merge_requests_path(@project.namespace, @project)}", - name: "#{j(@project.name)}" - }; - - - if @group && @group.persisted? && @group.path - :javascript - gl.groupOptions = gl.groupOptions || {}; - gl.groupOptions["#{j(@group.path)}"] = { - name: "#{j(@group.name)}", - issuesPath: "#{issues_group_path(j(@group.path))}", - mrPath: "#{merge_requests_group_path(j(@group.path))}" - }; - - - :javascript - gl.dashboardOptions = { - issuesPath: "#{issues_dashboard_url}", - mrPath: "#{merge_requests_dashboard_url}" - }; - - - if @snippet || @snippets = hidden_field_tag :snippets, true = hidden_field_tag :repository_ref, @ref diff --git a/app/views/profiles/show.html.haml b/app/views/profiles/show.html.haml index d9fa74fad90..578af9fe98d 100644 --- a/app/views/profiles/show.html.haml +++ b/app/views/profiles/show.html.haml @@ -87,6 +87,9 @@ = f.label :location, 'Location', class: "label-light" = f.text_field :location, class: "form-control" .form-group + = f.label :organization, 'Organization', class: "label-light" + = f.text_field :organization, class: "form-control" + .form-group = f.label :bio, class: "label-light" = f.text_area :bio, rows: 4, class: "form-control", maxlength: 250 %span.help-block Tell us about yourself in fewer than 250 characters. diff --git a/app/views/projects/merge_requests/_discussion.html.haml b/app/views/projects/merge_requests/_discussion.html.haml index 3900b4f6f17..cfb44bd206c 100644 --- a/app/views/projects/merge_requests/_discussion.html.haml +++ b/app/views/projects/merge_requests/_discussion.html.haml @@ -5,7 +5,7 @@ - if @merge_request.reopenable? = link_to 'Reopen merge request', merge_request_path(@merge_request, merge_request: {state_event: :reopen }), method: :put, class: "btn btn-nr btn-comment btn-reopen reopen-mr-link js-note-target-reopen", title: "Reopen merge request", data: {original_text: "Reopen merge request", alternative_text: "Comment & reopen merge request"} %comment-and-resolve-btn{ "inline-template" => true, ":discussion-id" => "" } - %button.btn.btn-nr.btn-default.append-right-10.js-comment-resolve-button{ "v-if" => "showButton", type: "submit", data: { namespace_path: "#{@merge_request.project.namespace.path}", project_path: "#{@merge_request.project.path}" } } + %button.btn.btn-nr.btn-default.append-right-10.js-comment-resolve-button{ "v-if" => "showButton", type: "submit", data: { project_path: "#{project_path(@merge_request.project)}" } } {{ buttonText }} #notes= render "projects/notes/notes_with_form" diff --git a/app/views/projects/notes/_note.html.haml b/app/views/projects/notes/_note.html.haml index 9ec17cf6e76..788be4a0047 100644 --- a/app/views/projects/notes/_note.html.haml +++ b/app/views/projects/notes/_note.html.haml @@ -24,14 +24,12 @@ - if note.resolvable? - can_resolve = can?(current_user, :resolve_note, note) - - %resolve-btn{ ":namespace-path" => "'#{note.project.namespace.path}'", - ":project-path" => "'#{note.project.path}'", - ":discussion-id" => "'#{note.discussion_id}'", + %resolve-btn{ "project-path" => "#{project_path(note.project)}", + "discussion-id" => "#{note.discussion_id}", ":note-id" => note.id, ":resolved" => note.resolved?, ":can-resolve" => can_resolve, - ":resolved-by" => "'#{note.resolved_by.try(:name)}'", + "resolved-by" => "#{note.resolved_by.try(:name)}", "v-show" => "#{can_resolve || note.resolved?}", "inline-template" => true, "v-ref:note_#{note.id}" => true } diff --git a/app/views/projects/snippets/_actions.html.haml b/app/views/projects/snippets/_actions.html.haml index 4aa4ab46a2f..9773b8438ec 100644 --- a/app/views/projects/snippets/_actions.html.haml +++ b/app/views/projects/snippets/_actions.html.haml @@ -1,7 +1,7 @@ .hidden-xs - if can?(current_user, :create_project_snippet, @project) - = link_to new_namespace_project_snippet_path(@project.namespace, @project), class: 'btn btn-grouped btn-create new-snippet-link', title: "New Snippet" do - New Snippet + = link_to new_namespace_project_snippet_path(@project.namespace, @project), class: 'btn btn-grouped btn-create new-snippet-link', title: "New snippet" do + New snippet - if can?(current_user, :update_project_snippet, @snippet) = link_to namespace_project_snippet_path(@project.namespace, @project, @snippet), method: :delete, data: { confirm: "Are you sure?" }, class: "btn btn-grouped btn-danger", title: 'Delete Snippet' do Delete @@ -17,8 +17,8 @@ %ul - if can?(current_user, :create_project_snippet, @project) %li - = link_to new_namespace_project_snippet_path(@project.namespace, @project), title: "New Snippet" do - New Snippet + = link_to new_namespace_project_snippet_path(@project.namespace, @project), title: "New snippet" do + New snippet - if can?(current_user, :update_project_snippet, @snippet) %li = link_to namespace_project_snippet_path(@project.namespace, @project, @snippet), method: :delete, data: { confirm: "Are you sure?" }, title: 'Delete Snippet' do diff --git a/app/views/projects/snippets/index.html.haml b/app/views/projects/snippets/index.html.haml index 1646bcf4b8a..e77e1b026f6 100644 --- a/app/views/projects/snippets/index.html.haml +++ b/app/views/projects/snippets/index.html.haml @@ -1,10 +1,9 @@ - page_title "Snippets" .sub-header-block - .pull-right - - if can?(current_user, :create_project_snippet, @project) - = link_to new_namespace_project_snippet_path(@project.namespace, @project), class: "btn btn-new", title: "New Snippet" do - New Snippet + - if can?(current_user, :create_project_snippet, @project) + = link_to new_namespace_project_snippet_path(@project.namespace, @project), class: "btn btn-new btn-wide-on-sm pull-right", title: "New snippet" do + New snippet .oneline Share code pastes with others out of git repository diff --git a/app/views/projects/wikis/_form.html.haml b/app/views/projects/wikis/_form.html.haml index 643f7c589e6..6624d5cb427 100644 --- a/app/views/projects/wikis/_form.html.haml +++ b/app/views/projects/wikis/_form.html.haml @@ -24,7 +24,7 @@ = succeed '.' do More examples are in the - = link_to 'documentation', help_page_path("user/project/markdown", anchor: "wiki-specific-markdown") + = link_to 'documentation', help_page_path("user/markdown", anchor: "wiki-specific-markdown") .form-group = f.label :commit_message, class: 'control-label' diff --git a/app/views/snippets/_actions.html.haml b/app/views/snippets/_actions.html.haml index fdaca199218..c446dc3bdc1 100644 --- a/app/views/snippets/_actions.html.haml +++ b/app/views/snippets/_actions.html.haml @@ -1,7 +1,7 @@ .hidden-xs - if current_user - = link_to new_snippet_path, class: "btn btn-grouped btn-create new-snippet-link", title: "New Snippet" do - New Snippet + = link_to new_snippet_path, class: "btn btn-grouped btn-create new-snippet-link", title: "New snippet" do + New snippet - if can?(current_user, :admin_personal_snippet, @snippet) = link_to snippet_path(@snippet), method: :delete, data: { confirm: "Are you sure?" }, class: "btn btn-grouped btn-danger", title: 'Delete Snippet' do Delete @@ -16,8 +16,8 @@ .dropdown-menu.dropdown-menu-full-width %ul %li - = link_to new_snippet_path, title: "New Snippet" do - New Snippet + = link_to new_snippet_path, title: "New snippet" do + New snippet - if can?(current_user, :admin_personal_snippet, @snippet) %li = link_to snippet_path(@snippet), method: :delete, data: { confirm: "Are you sure?" }, title: 'Delete Snippet' do diff --git a/app/views/snippets/_snippets.html.haml b/app/views/snippets/_snippets.html.haml index 7be4a471579..77b66ca74b6 100644 --- a/app/views/snippets/_snippets.html.haml +++ b/app/views/snippets/_snippets.html.haml @@ -1,3 +1,5 @@ +- remote = local_assigns.fetch(:remote, false) + .snippets-list-holder %ul.content-list = render partial: 'shared/snippets/snippet', collection: @snippets @@ -5,7 +7,7 @@ %li .nothing-here-block Nothing here. - = paginate @snippets, theme: 'gitlab', remote: true + = paginate @snippets, theme: 'gitlab', remote: remote :javascript gl.SnippetsList(); diff --git a/app/views/users/show.html.haml b/app/views/users/show.html.haml index 2a57ac90bab..60fc0c0daf6 100644 --- a/app/views/users/show.html.haml +++ b/app/views/users/show.html.haml @@ -36,7 +36,7 @@ .avatar-holder = link_to avatar_icon(@user, 400), target: '_blank' do = image_tag avatar_icon(@user, 90), class: "avatar s90", alt: '' - + .user-info .cover-title = @user.name @@ -70,6 +70,10 @@ .profile-link-holder.middle-dot-divider = icon('map-marker') = @user.location + - unless @user.organization.blank? + .profile-link-holder.middle-dot-divider + = icon('building') + = @user.organization - if @user.bio.present? .cover-desc diff --git a/db/migrate/20160926145521_add_organization_to_user.rb b/db/migrate/20160926145521_add_organization_to_user.rb new file mode 100644 index 00000000000..e0bef6e7548 --- /dev/null +++ b/db/migrate/20160926145521_add_organization_to_user.rb @@ -0,0 +1,12 @@ +# See http://doc.gitlab.com/ce/development/migration_style_guide.html +# for more information on how to write migrations for GitLab. + +class AddOrganizationToUser < ActiveRecord::Migration + include Gitlab::Database::MigrationHelpers + + DOWNTIME = false + + def change + add_column :users, :organization, :string + end +end diff --git a/db/schema.rb b/db/schema.rb index 425fc33b7b3..ad62c249b3f 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,7 +11,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 20160920160832) do +ActiveRecord::Schema.define(version: 20160926145521) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -1132,6 +1132,7 @@ ActiveRecord::Schema.define(version: 20160920160832) do t.datetime "otp_grace_period_started_at" t.boolean "ldap_email", default: false, null: false t.boolean "external", default: false + t.string "organization" end add_index "users", ["admin"], name: "index_users_on_admin", using: :btree diff --git a/doc/api/users.md b/doc/api/users.md index 54f7a2a2ace..9be4f2e6ec3 100644 --- a/doc/api/users.md +++ b/doc/api/users.md @@ -57,6 +57,7 @@ GET /users "linkedin": "", "twitter": "", "website_url": "", + "organization": "", "last_sign_in_at": "2012-06-01T11:41:01Z", "confirmed_at": "2012-05-23T09:05:22Z", "theme_id": 1, @@ -89,6 +90,7 @@ GET /users "linkedin": "", "twitter": "", "website_url": "", + "organization": "", "last_sign_in_at": null, "confirmed_at": "2012-05-30T16:53:06.148Z", "theme_id": 1, @@ -147,7 +149,8 @@ Parameters: "skype": "", "linkedin": "", "twitter": "", - "website_url": "" + "website_url": "", + "organization": "" } ``` @@ -178,6 +181,7 @@ Parameters: "linkedin": "", "twitter": "", "website_url": "", + "organization": "", "last_sign_in_at": "2012-06-01T11:41:01Z", "confirmed_at": "2012-05-23T09:05:22Z", "theme_id": 1, @@ -214,6 +218,7 @@ Parameters: - `linkedin` (optional) - LinkedIn - `twitter` (optional) - Twitter account - `website_url` (optional) - Website URL +- `organization` (optional) - Organization name - `projects_limit` (optional) - Number of projects user can create - `extern_uid` (optional) - External UID - `provider` (optional) - External provider name @@ -242,6 +247,7 @@ Parameters: - `linkedin` - LinkedIn - `twitter` - Twitter account - `website_url` - Website URL +- `organization` - Organization name - `projects_limit` - Limit projects each user can create - `extern_uid` - External UID - `provider` - External provider name @@ -296,6 +302,7 @@ GET /user "linkedin": "", "twitter": "", "website_url": "", + "organization": "", "last_sign_in_at": "2012-06-01T11:41:01Z", "confirmed_at": "2012-05-23T09:05:22Z", "theme_id": 1, diff --git a/features/project/snippets.feature b/features/project/snippets.feature index 270557cbde7..3c51ea56585 100644 --- a/features/project/snippets.feature +++ b/features/project/snippets.feature @@ -12,7 +12,7 @@ Feature: Project Snippets And I should not see "Snippet two" in snippets Scenario: I create new project snippet - Given I click link "New Snippet" + Given I click link "New snippet" And I submit new snippet "Snippet three" Then I should see snippet "Snippet three" diff --git a/features/steps/profile/profile.rb b/features/steps/profile/profile.rb index 4ee6784a086..05ab2a7dc73 100644 --- a/features/steps/profile/profile.rb +++ b/features/steps/profile/profile.rb @@ -13,6 +13,7 @@ class Spinach::Features::Profile < Spinach::FeatureSteps fill_in 'user_website_url', with: 'testurl' fill_in 'user_location', with: 'Ukraine' fill_in 'user_bio', with: 'I <3 GitLab' + fill_in 'user_organization', with: 'GitLab' click_button 'Update profile settings' @user.reload end @@ -23,6 +24,7 @@ class Spinach::Features::Profile < Spinach::FeatureSteps expect(@user.twitter).to eq 'testtwitter' expect(@user.website_url).to eq 'testurl' expect(@user.bio).to eq 'I <3 GitLab' + expect(@user.organization).to eq 'GitLab' expect(find('#user_location').value).to eq 'Ukraine' end diff --git a/features/steps/project/snippets.rb b/features/steps/project/snippets.rb index beb8ecfc799..5e7d539add6 100644 --- a/features/steps/project/snippets.rb +++ b/features/steps/project/snippets.rb @@ -21,8 +21,8 @@ class Spinach::Features::ProjectSnippets < Spinach::FeatureSteps author: project.users.first) end - step 'I click link "New Snippet"' do - click_link "New Snippet" + step 'I click link "New snippet"' do + click_link "New snippet" end step 'I click link "Snippet one"' do diff --git a/lib/api/access_requests.rb b/lib/api/access_requests.rb index 29a97ccbd75..9d1d9058996 100644 --- a/lib/api/access_requests.rb +++ b/lib/api/access_requests.rb @@ -55,13 +55,8 @@ module API put ':id/access_requests/:user_id/approve' do required_attributes! [:user_id] source = find_source(source_type, params[:id]) - authorize_admin_source!(source_type, source) - member = source.requesters.find_by!(user_id: params[:user_id]) - if params[:access_level] - member.update(access_level: params[:access_level]) - end - member.accept_request + member = ::Members::ApproveAccessRequestService.new(source, current_user, params).execute status :created present member.user, with: Entities::Member, member: member diff --git a/lib/api/api.rb b/lib/api/api.rb index 74ca4728695..cb47ec8f33f 100644 --- a/lib/api/api.rb +++ b/lib/api/api.rb @@ -28,6 +28,7 @@ module API helpers ::SentryHelper helpers ::API::Helpers + # Keep in alphabetical order mount ::API::AccessRequests mount ::API::AwardEmoji mount ::API::Branches @@ -48,6 +49,7 @@ module API mount ::API::Lint mount ::API::Members mount ::API::MergeRequests + mount ::API::MergeRequestDiffs mount ::API::Milestones mount ::API::Namespaces mount ::API::Notes @@ -70,6 +72,5 @@ module API mount ::API::Triggers mount ::API::Users mount ::API::Variables - mount ::API::MergeRequestDiffs end end diff --git a/lib/api/entities.rb b/lib/api/entities.rb index 92a6f29adb0..0adc118ba27 100644 --- a/lib/api/entities.rb +++ b/lib/api/entities.rb @@ -15,7 +15,7 @@ module API class User < UserBasic expose :created_at expose :is_admin?, as: :is_admin - expose :bio, :location, :skype, :linkedin, :twitter, :website_url + expose :bio, :location, :skype, :linkedin, :twitter, :website_url, :organization end class Identity < Grape::Entity diff --git a/lib/api/users.rb b/lib/api/users.rb index c440305ff0f..18c4cad09ae 100644 --- a/lib/api/users.rb +++ b/lib/api/users.rb @@ -60,6 +60,7 @@ module API # linkedin - Linkedin # twitter - Twitter account # website_url - Website url + # organization - Organization # projects_limit - Number of projects user can create # extern_uid - External authentication provider UID # provider - External provider @@ -74,7 +75,7 @@ module API post do authenticated_as_admin! required_attributes! [:email, :password, :name, :username] - attrs = attributes_for_keys [:email, :name, :password, :skype, :linkedin, :twitter, :projects_limit, :username, :bio, :location, :can_create_group, :admin, :confirm, :external] + attrs = attributes_for_keys [:email, :name, :password, :skype, :linkedin, :twitter, :projects_limit, :username, :bio, :location, :can_create_group, :admin, :confirm, :external, :organization] admin = attrs.delete(:admin) confirm = !(attrs.delete(:confirm) =~ /(false|f|no|0)$/i) user = User.build_user(attrs) @@ -111,6 +112,7 @@ module API # linkedin - Linkedin # twitter - Twitter account # website_url - Website url + # organization - Organization # projects_limit - Limit projects each user can create # bio - Bio # location - Location of the user @@ -122,7 +124,7 @@ module API put ":id" do authenticated_as_admin! - attrs = attributes_for_keys [:email, :name, :password, :skype, :linkedin, :twitter, :website_url, :projects_limit, :username, :bio, :location, :can_create_group, :admin, :external] + attrs = attributes_for_keys [:email, :name, :password, :skype, :linkedin, :twitter, :website_url, :projects_limit, :username, :bio, :location, :can_create_group, :admin, :external, :organization] user = User.find(params[:id]) not_found!('User') unless user diff --git a/lib/gitlab/import_export/import_export.yml b/lib/gitlab/import_export/import_export.yml index 88803d76623..1c42acab9c1 100644 --- a/lib/gitlab/import_export/import_export.yml +++ b/lib/gitlab/import_export/import_export.yml @@ -73,5 +73,7 @@ excluded_attributes: methods: statuses: - :type + services: + - :type merge_request_diff: - :utf8_st_diffs diff --git a/public/deploy.html b/public/deploy.html index 142472b6c35..49ec4ac5ce1 100644 --- a/public/deploy.html +++ b/public/deploy.html @@ -2,6 +2,11 @@ <html> <head> <meta content="width=device-width, initial-scale=1, maximum-scale=1" name="viewport"> + <meta name="refresh" content="60"> + <meta name="retry-after" content="100"> + <meta name="robots" content="noindex, nofollow, noarchive, nostore"> + <meta name="cache-control" content="no-cache, no-store"> + <meta name="pragma" content="no-cache"> <title>Deploy in progress</title> <style> body { @@ -61,4 +66,4 @@ <p>Please contact your GitLab administrator if this problem persists.</p> </div> </body> -</html> +</html>
\ No newline at end of file diff --git a/public/robots.txt b/public/robots.txt index 334f4c03533..7d69fad59d1 100644 --- a/public/robots.txt +++ b/public/robots.txt @@ -23,7 +23,7 @@ Disallow: /groups/*/edit Disallow: /users # Global snippets -Disallow: /s +Disallow: /s/ Disallow: /snippets/new Disallow: /snippets/*/edit Disallow: /snippets/*/raw diff --git a/spec/controllers/groups/group_members_controller_spec.rb b/spec/controllers/groups/group_members_controller_spec.rb index c34475976c6..92b97bf3d0c 100644 --- a/spec/controllers/groups/group_members_controller_spec.rb +++ b/spec/controllers/groups/group_members_controller_spec.rb @@ -2,9 +2,10 @@ require 'spec_helper' describe Groups::GroupMembersController do let(:user) { create(:user) } - let(:group) { create(:group) } describe '#index' do + let(:group) { create(:group) } + before do group.add_owner(user) stub_application_setting(restricted_visibility_levels: [Gitlab::VisibilityLevel::PUBLIC]) diff --git a/spec/controllers/projects/boards/lists_controller_spec.rb b/spec/controllers/projects/boards/lists_controller_spec.rb index d687dea3c3b..709006a3601 100644 --- a/spec/controllers/projects/boards/lists_controller_spec.rb +++ b/spec/controllers/projects/boards/lists_controller_spec.rb @@ -20,10 +20,7 @@ describe Projects::Boards::ListsController do end it 'returns a list of board lists' do - board = project.create_board - create(:backlog_list, board: board) create(:list, board: board) - create(:done_list, board: board) read_board_list user: user diff --git a/spec/factories/projects.rb b/spec/factories/projects.rb index fb84ba07d25..e61b1fd9647 100644 --- a/spec/factories/projects.rb +++ b/spec/factories/projects.rb @@ -106,6 +106,8 @@ FactoryGirl.define do factory :project_with_board, parent: :empty_project do after(:create) do |project| project.create_board + project.board.lists.create(list_type: :backlog) + project.board.lists.create(list_type: :done) end end end diff --git a/spec/features/boards/boards_spec.rb b/spec/features/boards/boards_spec.rb index 19941978c5f..2dcbc4703e1 100644 --- a/spec/features/boards/boards_spec.rb +++ b/spec/features/boards/boards_spec.rb @@ -4,15 +4,11 @@ describe 'Issue Boards', feature: true, js: true do include WaitForAjax include WaitForVueResource - let(:project) { create(:empty_project, :public) } + let(:project) { create(:project_with_board, :public) } let(:user) { create(:user) } let!(:user2) { create(:user) } before do - project.create_board - project.board.lists.create(list_type: :backlog) - project.board.lists.create(list_type: :done) - project.team << [user, :master] project.team << [user2, :master] @@ -62,6 +58,7 @@ describe 'Issue Boards', feature: true, js: true do let(:bug) { create(:label, project: project, name: 'Bug') } let!(:backlog) { create(:label, project: project, name: 'Backlog') } let!(:done) { create(:label, project: project, name: 'Done') } + let!(:accepting) { create(:label, project: project, name: 'Accepting Merge Requests') } let!(:list1) { create(:list, board: project.board, label: planning, position: 0) } let!(:list2) { create(:list, board: project.board, label: development, position: 1) } @@ -75,7 +72,7 @@ describe 'Issue Boards', feature: true, js: true do let!(:issue6) { create(:labeled_issue, project: project, labels: [planning, development]) } let!(:issue7) { create(:labeled_issue, project: project, labels: [development]) } let!(:issue8) { create(:closed_issue, project: project) } - let!(:issue9) { create(:labeled_issue, project: project, labels: [testing, bug]) } + let!(:issue9) { create(:labeled_issue, project: project, labels: [testing, bug, accepting]) } before do visit namespace_project_board_path(project.namespace, project) @@ -441,6 +438,34 @@ describe 'Issue Boards', feature: true, js: true do wait_for_empty_boards((2..4)) end + it 'filters by label with space after reload' do + page.within '.issues-filters' do + click_button('Label') + wait_for_ajax + + page.within '.dropdown-menu-labels' do + click_link(accepting.title) + wait_for_vue_resource(spinner: false) + find('.dropdown-menu-close').click + end + end + + # Test after reload + page.evaluate_script 'window.location.reload()' + + wait_for_vue_resource + + page.within(find('.board', match: :first)) do + expect(page.find('.board-header')).to have_content('1') + expect(page).to have_selector('.card', count: 1) + end + + page.within(find('.board:nth-child(2)')) do + expect(page.find('.board-header')).to have_content('0') + expect(page).to have_selector('.card', count: 0) + end + end + it 'infinite scrolls list with label filter' do 50.times do create(:labeled_issue, project: project, labels: [testing]) diff --git a/spec/features/dashboard/snippets_spec.rb b/spec/features/dashboard/snippets_spec.rb new file mode 100644 index 00000000000..62937688c22 --- /dev/null +++ b/spec/features/dashboard/snippets_spec.rb @@ -0,0 +1,15 @@ +require 'spec_helper' + +describe 'Dashboard snippets', feature: true do + context 'when the project has snippets' do + let(:project) { create(:empty_project, :public) } + let!(:snippets) { create_list(:project_snippet, 2, :public, author: project.owner, project: project) } + before do + allow(Snippet).to receive(:default_per_page).and_return(1) + login_as(project.owner) + visit dashboard_snippets_path + end + + it_behaves_like 'paginated snippets' + end +end diff --git a/spec/features/projects/snippets_spec.rb b/spec/features/projects/snippets_spec.rb new file mode 100644 index 00000000000..d37e8ed4699 --- /dev/null +++ b/spec/features/projects/snippets_spec.rb @@ -0,0 +1,14 @@ +require 'spec_helper' + +describe 'Project snippets', feature: true do + context 'when the project has snippets' do + let(:project) { create(:empty_project, :public) } + let!(:snippets) { create_list(:project_snippet, 2, :public, author: project.owner, project: project) } + before do + allow(Snippet).to receive(:default_per_page).and_return(1) + visit namespace_project_snippets_path(project.namespace, project) + end + + it_behaves_like 'paginated snippets' + end +end diff --git a/spec/features/snippets_spec.rb b/spec/features/snippets_spec.rb new file mode 100644 index 00000000000..70b16bfc810 --- /dev/null +++ b/spec/features/snippets_spec.rb @@ -0,0 +1,14 @@ +require 'spec_helper' + +describe 'Snippets', feature: true do + context 'when the project has snippets' do + let(:project) { create(:empty_project, :public) } + let!(:snippets) { create_list(:project_snippet, 2, :public, author: project.owner, project: project) } + before do + allow(Snippet).to receive(:default_per_page).and_return(1) + visit snippets_path(username: project.owner.username) + end + + it_behaves_like 'paginated snippets' + end +end diff --git a/spec/features/unsubscribe_links_spec.rb b/spec/features/unsubscribe_links_spec.rb index cc40671787c..33b52d1547e 100644 --- a/spec/features/unsubscribe_links_spec.rb +++ b/spec/features/unsubscribe_links_spec.rb @@ -11,7 +11,7 @@ describe 'Unsubscribe links', feature: true do let(:mail) { ActionMailer::Base.deliveries.last } let(:body) { Capybara::Node::Simple.new(mail.default_part_body.to_s) } - let(:header_link) { mail.header['List-Unsubscribe'] } + let(:header_link) { mail.header['List-Unsubscribe'].to_s[1..-2] } # Strip angle brackets let(:body_link) { body.find_link('unsubscribe')['href'] } before do diff --git a/spec/features/users/snippets_spec.rb b/spec/features/users/snippets_spec.rb index f00abd82fea..ce7e809ec76 100644 --- a/spec/features/users/snippets_spec.rb +++ b/spec/features/users/snippets_spec.rb @@ -3,30 +3,16 @@ require 'spec_helper' describe 'Snippets tab on a user profile', feature: true, js: true do include WaitForAjax - let(:user) { create(:user) } - context 'when the user has snippets' do + let(:user) { create(:user) } + let!(:snippets) { create_list(:snippet, 2, :public, author: user) } before do - create_list(:snippet, 25, :public, author: user) - + allow(Snippet).to receive(:default_per_page).and_return(1) visit user_path(user) page.within('.user-profile-nav') { click_link 'Snippets' } wait_for_ajax end - it 'is limited to 20 items per page' do - expect(page.all('.snippets-list-holder .snippet-row').count).to eq(20) - end - - context 'clicking on the link to the second page' do - before do - click_link('2') - wait_for_ajax - end - - it 'shows the remaining snippets' do - expect(page.all('.snippets-list-holder .snippet-row').count).to eq(5) - end - end + it_behaves_like 'paginated snippets', remote: true end end diff --git a/spec/lib/gitlab/import_export/project.json b/spec/lib/gitlab/import_export/project.json index 281f6cf1177..056eaa2d719 100644 --- a/spec/lib/gitlab/import_export/project.json +++ b/spec/lib/gitlab/import_export/project.json @@ -6918,6 +6918,7 @@ "note_events": true, "build_events": true, "category": "issue_tracker", + "type": "CustomIssueTrackerService", "default": true, "wiki_page_events": true }, diff --git a/spec/lib/gitlab/import_export/project_tree_restorer_spec.rb b/spec/lib/gitlab/import_export/project_tree_restorer_spec.rb index feacb295231..65d0aaf53d6 100644 --- a/spec/lib/gitlab/import_export/project_tree_restorer_spec.rb +++ b/spec/lib/gitlab/import_export/project_tree_restorer_spec.rb @@ -107,6 +107,12 @@ describe Gitlab::ImportExport::ProjectTreeRestorer, services: true do expect(Label.first.label_links.first.target).not_to be_nil end + it 'restores the correct service' do + restored_project_json + + expect(CustomIssueTrackerService.first).not_to be_nil + end + context 'Merge requests' do before do restored_project_json diff --git a/spec/lib/gitlab/import_export/project_tree_saver_spec.rb b/spec/lib/gitlab/import_export/project_tree_saver_spec.rb index d891c2d0cc6..cf8f2200c57 100644 --- a/spec/lib/gitlab/import_export/project_tree_saver_spec.rb +++ b/spec/lib/gitlab/import_export/project_tree_saver_spec.rb @@ -111,6 +111,10 @@ describe Gitlab::ImportExport::ProjectTreeSaver, services: true do expect(saved_project_json['issues'].first['label_links'].first['label']).not_to be_empty end + it 'saves the correct service type' do + expect(saved_project_json['services'].first['type']).to eq('CustomIssueTrackerService') + end + it 'has project feature' do project_feature = saved_project_json['project_feature'] expect(project_feature).not_to be_empty @@ -161,6 +165,7 @@ describe Gitlab::ImportExport::ProjectTreeSaver, services: true do commit_id: ci_pipeline.sha) create(:event, target: milestone, project: project, action: Event::CREATED, author: user) + create(:service, project: project, type: 'CustomIssueTrackerService', category: 'issue_tracker') project.project_feature.update_attribute(:issues_access_level, ProjectFeature::DISABLED) project.project_feature.update_attribute(:wiki_access_level, ProjectFeature::ENABLED) diff --git a/spec/mailers/shared/notify.rb b/spec/mailers/shared/notify.rb index 56872da9a8f..5c9851f14c7 100644 --- a/spec/mailers/shared/notify.rb +++ b/spec/mailers/shared/notify.rb @@ -169,8 +169,9 @@ shared_examples 'it should show Gmail Actions View Commit link' do end shared_examples 'an unsubscribeable thread' do - it 'has a List-Unsubscribe header' do + it 'has a List-Unsubscribe header in the correct format' do is_expected.to have_header 'List-Unsubscribe', /unsubscribe/ + is_expected.to have_header 'List-Unsubscribe', /^<.+>$/ end it { is_expected.to have_body_text /unsubscribe/ } diff --git a/spec/models/cycle_analytics/summary_spec.rb b/spec/models/cycle_analytics/summary_spec.rb index 743bc2da33f..9d67bc82cba 100644 --- a/spec/models/cycle_analytics/summary_spec.rb +++ b/spec/models/cycle_analytics/summary_spec.rb @@ -34,6 +34,12 @@ describe CycleAnalytics::Summary, models: true do expect(subject.commits).to eq(0) end + + it "finds a large (> 100) snumber of commits if present" do + Timecop.freeze(5.days.from_now) { create_commit("Test message", project, user, 'master', count: 100) } + + expect(subject.commits).to eq(100) + end end describe "#deploys" do diff --git a/spec/models/merge_request_spec.rb b/spec/models/merge_request_spec.rb index 12df6adde44..580a3235127 100644 --- a/spec/models/merge_request_spec.rb +++ b/spec/models/merge_request_spec.rb @@ -328,6 +328,42 @@ describe MergeRequest, models: true do end end + describe '#merge_commit_message' do + it 'includes merge information as the title' do + request = build(:merge_request, source_branch: 'source', target_branch: 'target') + + expect(request.merge_commit_message) + .to match("Merge branch 'source' into 'target'\n\n") + end + + it 'includes its title in the body' do + request = build(:merge_request, title: 'Remove all technical debt') + + expect(request.merge_commit_message) + .to match("Remove all technical debt\n\n") + end + + it 'includes its description in the body' do + request = build(:merge_request, description: 'By removing all code') + + expect(request.merge_commit_message) + .to match("By removing all code\n\n") + end + + it 'includes its reference in the body' do + request = build_stubbed(:merge_request) + + expect(request.merge_commit_message) + .to match("See merge request #{request.to_reference}") + end + + it 'excludes multiple linebreak runs when description is blank' do + request = build(:merge_request, title: 'Title', description: nil) + + expect(request.merge_commit_message).not_to match("Title\n\n\n\n") + end + end + describe "#reset_merge_when_build_succeeds" do let(:merge_if_green) do create :merge_request, merge_when_build_succeeds: true, merge_user: create(:user), diff --git a/spec/models/project_services/custom_issue_tracker_service_spec.rb b/spec/models/project_services/custom_issue_tracker_service_spec.rb index 7020667ea58..63320931e76 100644 --- a/spec/models/project_services/custom_issue_tracker_service_spec.rb +++ b/spec/models/project_services/custom_issue_tracker_service_spec.rb @@ -25,5 +25,21 @@ describe CustomIssueTrackerService, models: true do it { is_expected.not_to validate_presence_of(:issues_url) } it { is_expected.not_to validate_presence_of(:new_issue_url) } end + + context 'title' do + let(:issue_tracker) { described_class.new(properties: {}) } + + it 'sets a default title' do + issue_tracker.title = nil + + expect(issue_tracker.title).to eq('Custom Issue Tracker') + end + + it 'sets the custom title' do + issue_tracker.title = 'test title' + + expect(issue_tracker.title).to eq('test title') + end + end end end diff --git a/spec/requests/api/users_spec.rb b/spec/requests/api/users_spec.rb index ef73778efa9..f4ea3bebb4c 100644 --- a/spec/requests/api/users_spec.rb +++ b/spec/requests/api/users_spec.rb @@ -62,6 +62,7 @@ describe API::API, api: true do expect(response).to have_http_status(200) expect(json_response).to be_an Array expect(json_response.first.keys).to include 'email' + expect(json_response.first.keys).to include 'organization' expect(json_response.first.keys).to include 'identities' expect(json_response.first.keys).to include 'can_create_project' expect(json_response.first.keys).to include 'two_factor_enabled' @@ -265,6 +266,14 @@ describe API::API, api: true do expect(user.reload.bio).to eq('new test bio') end + it "updates user with organization" do + put api("/users/#{user.id}", admin), { organization: 'GitLab' } + + expect(response).to have_http_status(200) + expect(json_response['organization']).to eq('GitLab') + expect(user.reload.organization).to eq('GitLab') + end + it 'updates user with his own email' do put api("/users/#{user.id}", admin), email: user.email expect(response).to have_http_status(200) diff --git a/spec/services/boards/issues/list_service_spec.rb b/spec/services/boards/issues/list_service_spec.rb index cf4c5f13635..e65da15aca8 100644 --- a/spec/services/boards/issues/list_service_spec.rb +++ b/spec/services/boards/issues/list_service_spec.rb @@ -13,10 +13,10 @@ describe Boards::Issues::ListService, services: true do let(:p2) { create(:label, title: 'P2', project: project, priority: 2) } let(:p3) { create(:label, title: 'P3', project: project, priority: 3) } - let!(:backlog) { create(:backlog_list, board: board) } + let!(:backlog) { project.board.backlog_list } let!(:list1) { create(:list, board: board, label: development, position: 0) } let!(:list2) { create(:list, board: board, label: testing, position: 1) } - let!(:done) { create(:done_list, board: board) } + let!(:done) { project.board.done_list } let!(:opened_issue1) { create(:labeled_issue, project: project, labels: [bug]) } let!(:opened_issue2) { create(:labeled_issue, project: project, labels: [p2]) } diff --git a/spec/services/boards/issues/move_service_spec.rb b/spec/services/boards/issues/move_service_spec.rb index 0122159cab8..180f1b08631 100644 --- a/spec/services/boards/issues/move_service_spec.rb +++ b/spec/services/boards/issues/move_service_spec.rb @@ -10,10 +10,10 @@ describe Boards::Issues::MoveService, services: true do let(:development) { create(:label, project: project, name: 'Development') } let(:testing) { create(:label, project: project, name: 'Testing') } - let!(:backlog) { create(:backlog_list, board: board) } + let!(:backlog) { project.board.backlog_list } let!(:list1) { create(:list, board: board, label: development, position: 0) } let!(:list2) { create(:list, board: board, label: testing, position: 1) } - let!(:done) { create(:done_list, board: board) } + let!(:done) { project.board.done_list } before do project.team << [user, :developer] diff --git a/spec/services/boards/lists/create_service_spec.rb b/spec/services/boards/lists/create_service_spec.rb index 90764b86b16..bff9c1fd1fe 100644 --- a/spec/services/boards/lists/create_service_spec.rb +++ b/spec/services/boards/lists/create_service_spec.rb @@ -17,17 +17,15 @@ describe Boards::Lists::CreateService, services: true do end end - context 'when board lists has only a backlog list' do + context 'when board lists has backlog, and done lists' do it 'creates a new list at beginning of the list' do - create(:backlog_list, board: board) - list = service.execute expect(list.position).to eq 0 end end - context 'when board lists has only labels lists' do + context 'when board lists has labels lists' do it 'creates a new list at end of the lists' do create(:list, board: board, position: 0) create(:list, board: board, position: 1) @@ -40,8 +38,6 @@ describe Boards::Lists::CreateService, services: true do context 'when board lists has backlog, label and done lists' do it 'creates a new list at end of the label lists' do - create(:backlog_list, board: board) - create(:done_list, board: board) list1 = create(:list, board: board, position: 0) list2 = service.execute diff --git a/spec/services/boards/lists/destroy_service_spec.rb b/spec/services/boards/lists/destroy_service_spec.rb index 6eff445feee..474c4512471 100644 --- a/spec/services/boards/lists/destroy_service_spec.rb +++ b/spec/services/boards/lists/destroy_service_spec.rb @@ -15,11 +15,11 @@ describe Boards::Lists::DestroyService, services: true do end it 'decrements position of higher lists' do - backlog = create(:backlog_list, board: board) + backlog = project.board.backlog_list development = create(:list, board: board, position: 0) review = create(:list, board: board, position: 1) staging = create(:list, board: board, position: 2) - done = create(:done_list, board: board) + done = project.board.done_list described_class.new(project, user).execute(development) @@ -31,14 +31,14 @@ describe Boards::Lists::DestroyService, services: true do end it 'does not remove list from board when list type is backlog' do - list = create(:backlog_list, board: board) + list = project.board.backlog_list service = described_class.new(project, user) expect { service.execute(list) }.not_to change(board.lists, :count) end it 'does not remove list from board when list type is done' do - list = create(:done_list, board: board) + list = project.board.done_list service = described_class.new(project, user) expect { service.execute(list) }.not_to change(board.lists, :count) diff --git a/spec/services/boards/lists/move_service_spec.rb b/spec/services/boards/lists/move_service_spec.rb index 3e9b7d07fc6..102ed67449d 100644 --- a/spec/services/boards/lists/move_service_spec.rb +++ b/spec/services/boards/lists/move_service_spec.rb @@ -6,12 +6,12 @@ describe Boards::Lists::MoveService, services: true do let(:board) { project.board } let(:user) { create(:user) } - let!(:backlog) { create(:backlog_list, board: board) } + let!(:backlog) { project.board.backlog_list } let!(:planning) { create(:list, board: board, position: 0) } let!(:development) { create(:list, board: board, position: 1) } let!(:review) { create(:list, board: board, position: 2) } let!(:staging) { create(:list, board: board, position: 3) } - let!(:done) { create(:done_list, board: board) } + let!(:done) { project.board.done_list } context 'when list type is set to label' do it 'keeps position of lists when new position is nil' do diff --git a/spec/services/members/approve_access_request_service_spec.rb b/spec/services/members/approve_access_request_service_spec.rb new file mode 100644 index 00000000000..6fca80b5613 --- /dev/null +++ b/spec/services/members/approve_access_request_service_spec.rb @@ -0,0 +1,96 @@ +require 'spec_helper' + +describe Members::ApproveAccessRequestService, services: true do + let(:user) { create(:user) } + let(:access_requester) { create(:user) } + let(:project) { create(:project, :public) } + let(:group) { create(:group, :public) } + + shared_examples 'a service raising ActiveRecord::RecordNotFound' do + it 'raises ActiveRecord::RecordNotFound' do + expect { described_class.new(source, user, params).execute }.to raise_error(ActiveRecord::RecordNotFound) + end + end + + shared_examples 'a service raising Gitlab::Access::AccessDeniedError' do + it 'raises Gitlab::Access::AccessDeniedError' do + expect { described_class.new(source, user, params).execute }.to raise_error(Gitlab::Access::AccessDeniedError) + end + end + + shared_examples 'a service approving an access request' do + it 'succeeds' do + expect { described_class.new(source, user, params).execute }.to change { source.requesters.count }.by(-1) + end + + it 'returns a <Source>Member' do + member = described_class.new(source, user, params).execute + + expect(member).to be_a "#{source.class.to_s}Member".constantize + expect(member.requested_at).to be_nil + end + + context 'with a custom access level' do + let(:params) { { user_id: access_requester.id, access_level: Gitlab::Access::MASTER } } + + it 'returns a ProjectMember with the custom access level' do + member = described_class.new(source, user, params).execute + + expect(member.access_level).to eq Gitlab::Access::MASTER + end + end + end + + context 'when no access requester are found' do + let(:params) { { user_id: 42 } } + + it_behaves_like 'a service raising ActiveRecord::RecordNotFound' do + let(:source) { project } + end + + it_behaves_like 'a service raising ActiveRecord::RecordNotFound' do + let(:source) { group } + end + end + + context 'when an access requester is found' do + before do + project.request_access(access_requester) + group.request_access(access_requester) + end + let(:params) { { user_id: access_requester.id } } + + context 'when current user cannot approve access request to the project' do + it_behaves_like 'a service raising Gitlab::Access::AccessDeniedError' do + let(:source) { project } + end + + it_behaves_like 'a service raising Gitlab::Access::AccessDeniedError' do + let(:source) { group } + end + end + + context 'when current user can approve access request to the project' do + before do + project.team << [user, :master] + group.add_owner(user) + end + + it_behaves_like 'a service approving an access request' do + let(:source) { project } + end + + it_behaves_like 'a service approving an access request' do + let(:source) { group } + end + + context 'when given a :id' do + let(:params) { { id: project.requesters.find_by!(user_id: access_requester.id).id } } + + it_behaves_like 'a service approving an access request' do + let(:source) { project } + end + end + end + end +end diff --git a/spec/support/cycle_analytics_helpers.rb b/spec/support/cycle_analytics_helpers.rb index e8e760a6187..62a5b46d47b 100644 --- a/spec/support/cycle_analytics_helpers.rb +++ b/spec/support/cycle_analytics_helpers.rb @@ -4,24 +4,28 @@ module CycleAnalyticsHelpers create_commit("Commit for ##{issue.iid}", issue.project, user, branch_name) end - def create_commit(message, project, user, branch_name) - filename = random_git_name + def create_commit(message, project, user, branch_name, count: 1) oldrev = project.repository.commit(branch_name).sha + commit_shas = Array.new(count) do |index| + filename = random_git_name - options = { - committer: project.repository.user_to_committer(user), - author: project.repository.user_to_committer(user), - commit: { message: message, branch: branch_name, update_ref: true }, - file: { content: "content", path: filename, update: false } - } + options = { + committer: project.repository.user_to_committer(user), + author: project.repository.user_to_committer(user), + commit: { message: message, branch: branch_name, update_ref: true }, + file: { content: "content", path: filename, update: false } + } + + commit_sha = Gitlab::Git::Blob.commit(project.repository, options) + project.repository.commit(commit_sha) - commit_sha = Gitlab::Git::Blob.commit(project.repository, options) - project.repository.commit(commit_sha) + commit_sha + end GitPushService.new(project, user, oldrev: oldrev, - newrev: commit_sha, + newrev: commit_shas.last, ref: 'refs/heads/master').execute end diff --git a/spec/support/snippets_shared_examples.rb b/spec/support/snippets_shared_examples.rb new file mode 100644 index 00000000000..57dfff3471f --- /dev/null +++ b/spec/support/snippets_shared_examples.rb @@ -0,0 +1,18 @@ +# These shared examples expect a `snippets` array of snippets +RSpec.shared_examples 'paginated snippets' do |remote: false| + it "is limited to #{Snippet.default_per_page} items per page" do + expect(page.all('.snippets-list-holder .snippet-row').count).to eq(Snippet.default_per_page) + end + + context 'clicking on the link to the second page' do + before do + click_link('2') + wait_for_ajax if remote + end + + it 'shows the remaining snippets' do + remaining_snippets_count = [snippets.size - Snippet.default_per_page, Snippet.default_per_page].min + expect(page).to have_selector('.snippets-list-holder .snippet-row', count: remaining_snippets_count) + end + end +end |