summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--app/assets/javascripts/vue_pipelines_index/index.js.es61
-rw-r--r--app/assets/javascripts/vue_pipelines_index/store.js.es626
-rw-r--r--app/models/concerns/project_features_compatibility.rb2
-rw-r--r--app/models/forked_project_link.rb4
-rw-r--r--app/models/project.rb4
-rw-r--r--app/models/repository.rb5
-rw-r--r--app/views/projects/empty.html.haml11
-rw-r--r--app/views/projects/pipelines/index.html.haml1
-rw-r--r--changelogs/unreleased/allow_plus_sign_for_snippets.yml4
-rw-r--r--changelogs/unreleased/bug-project-feature-compatibility.yml5
-rw-r--r--changelogs/unreleased/dot-in-project-queries.yml4
-rw-r--r--changelogs/unreleased/pmq20-gitlab-ce-psvr-head-cache.yml4
-rw-r--r--config/application.rb1
-rw-r--r--db/migrate/20161226122833_remove_dot_git_from_usernames.rb60
-rw-r--r--features/admin/groups.feature49
-rw-r--r--features/steps/admin/groups.rb143
-rw-r--r--features/steps/shared/paths.rb4
-rw-r--r--lib/api/helpers.rb2
-rw-r--r--lib/api/projects.rb2
-rw-r--r--lib/gitlab/checks/change_access.rb4
-rw-r--r--lib/gitlab/regex.rb4
-rwxr-xr-xscripts/notify_slack.sh2
-rw-r--r--spec/features/admin/admin_groups_spec.rb113
-rw-r--r--spec/features/snippets/create_snippet_spec.rb14
-rw-r--r--spec/lib/gitlab/checks/change_access_spec.rb3
-rw-r--r--spec/models/project_spec.rb11
-rw-r--r--spec/models/repository_spec.rb26
-rw-r--r--spec/requests/api/projects_spec.rb17
28 files changed, 261 insertions, 265 deletions
diff --git a/app/assets/javascripts/vue_pipelines_index/index.js.es6 b/app/assets/javascripts/vue_pipelines_index/index.js.es6
index 9dfbedd73ab..edd01f17a97 100644
--- a/app/assets/javascripts/vue_pipelines_index/index.js.es6
+++ b/app/assets/javascripts/vue_pipelines_index/index.js.es6
@@ -1,5 +1,6 @@
/* global Vue, VueResource, gl */
/*= require vue_common_component/commit */
+/*= require vue_pagination/index */
/*= require vue-resource
/*= require boards/vue_resource_interceptor */
/*= require ./status.js.es6 */
diff --git a/app/assets/javascripts/vue_pipelines_index/store.js.es6 b/app/assets/javascripts/vue_pipelines_index/store.js.es6
index 6b34839b030..1982142853a 100644
--- a/app/assets/javascripts/vue_pipelines_index/store.js.es6
+++ b/app/assets/javascripts/vue_pipelines_index/store.js.es6
@@ -3,14 +3,24 @@
/*= require vue_realtime_listener/index.js */
((gl) => {
- const pageValues = headers => ({
- perPage: +headers['X-Per-Page'],
- page: +headers['X-Page'],
- total: +headers['X-Total'],
- totalPages: +headers['X-Total-Pages'],
- nextPage: +headers['X-Next-Page'],
- previousPage: +headers['X-Prev-Page'],
- });
+ const pageValues = (headers) => {
+ const normalizedHeaders = {};
+
+ Object.keys(headers).forEach((e) => {
+ normalizedHeaders[e.toUpperCase()] = headers[e];
+ });
+
+ const paginationInfo = {
+ perPage: +normalizedHeaders['X-PER-PAGE'],
+ page: +normalizedHeaders['X-PAGE'],
+ total: +normalizedHeaders['X-TOTAL'],
+ totalPages: +normalizedHeaders['X-TOTAL-PAGES'],
+ nextPage: +normalizedHeaders['X-NEXT-PAGE'],
+ previousPage: +normalizedHeaders['X-PREV-PAGE'],
+ };
+
+ return paginationInfo;
+ };
gl.PipelineStore = class {
fetchDataLoop(Vue, pageNum, url, apiScope) {
diff --git a/app/models/concerns/project_features_compatibility.rb b/app/models/concerns/project_features_compatibility.rb
index 6d88951c713..60734bc6660 100644
--- a/app/models/concerns/project_features_compatibility.rb
+++ b/app/models/concerns/project_features_compatibility.rb
@@ -32,6 +32,6 @@ module ProjectFeaturesCompatibility
build_project_feature unless project_feature
access_level = Gitlab::Utils.to_boolean(value) ? ProjectFeature::ENABLED : ProjectFeature::DISABLED
- project_feature.update_attribute(field, access_level)
+ project_feature.send(:write_attribute, field, access_level)
end
end
diff --git a/app/models/forked_project_link.rb b/app/models/forked_project_link.rb
index 9803bae0bee..36cf7ad6a28 100644
--- a/app/models/forked_project_link.rb
+++ b/app/models/forked_project_link.rb
@@ -1,4 +1,4 @@
class ForkedProjectLink < ActiveRecord::Base
- belongs_to :forked_to_project, class_name: Project
- belongs_to :forked_from_project, class_name: Project
+ belongs_to :forked_to_project, class_name: 'Project'
+ belongs_to :forked_from_project, class_name: 'Project'
end
diff --git a/app/models/project.rb b/app/models/project.rb
index c22386c84e9..1630975b0d3 100644
--- a/app/models/project.rb
+++ b/app/models/project.rb
@@ -122,7 +122,7 @@ class Project < ActiveRecord::Base
# Merge Requests for target project should be removed with it
has_many :merge_requests, dependent: :destroy, foreign_key: 'target_project_id'
# Merge requests from source project should be kept when source project was removed
- has_many :fork_merge_requests, foreign_key: 'source_project_id', class_name: MergeRequest
+ has_many :fork_merge_requests, foreign_key: 'source_project_id', class_name: 'MergeRequest'
has_many :issues, dependent: :destroy
has_many :labels, dependent: :destroy, class_name: 'ProjectLabel'
has_many :services, dependent: :destroy
@@ -1032,7 +1032,7 @@ class Project < ActiveRecord::Base
"refs/heads/#{branch}",
force: true)
repository.copy_gitattributes(branch)
- repository.expire_avatar_cache
+ repository.after_change_head
reload_default_branch
end
diff --git a/app/models/repository.rb b/app/models/repository.rb
index 3266e9c75f0..43dba86e5ed 100644
--- a/app/models/repository.rb
+++ b/app/models/repository.rb
@@ -439,6 +439,11 @@ class Repository
expire_content_cache
end
+ # Runs code after the HEAD of a repository is changed.
+ def after_change_head
+ expire_method_caches(METHOD_CACHES_FOR_FILE_TYPES.keys)
+ end
+
# Runs code after a repository has been forked/imported.
def after_import
expire_content_cache
diff --git a/app/views/projects/empty.html.haml b/app/views/projects/empty.html.haml
index 3525a07a687..58c085cdb9d 100644
--- a/app/views/projects/empty.html.haml
+++ b/app/views/projects/empty.html.haml
@@ -52,7 +52,7 @@
git push -u origin master
%fieldset
- %h5 Existing folder or Git repository
+ %h5 Existing folder
%pre.light-well
:preserve
cd existing_folder
@@ -62,6 +62,15 @@
git commit
git push -u origin master
+ %fieldset
+ %h5 Existing Git repository
+ %pre.light-well
+ :preserve
+ cd existing_repo
+ git remote add origin #{ content_tag(:span, default_url_to_repo, class: 'clone')}
+ git push -u origin --all
+ git push -u origin --tags
+
- if can? current_user, :remove_project, @project
.prepend-top-20
= link_to 'Remove project', [@project.namespace.becomes(Namespace), @project], data: { confirm: remove_project_message(@project)}, method: :delete, class: "btn btn-remove pull-right"
diff --git a/app/views/projects/pipelines/index.html.haml b/app/views/projects/pipelines/index.html.haml
index abea6932567..df36279ed75 100644
--- a/app/views/projects/pipelines/index.html.haml
+++ b/app/views/projects/pipelines/index.html.haml
@@ -64,5 +64,4 @@
.vue-pipelines-index
-= page_specific_javascript_tag('vue_pagination/index.js')
= page_specific_javascript_tag('vue_pipelines_index/index.js')
diff --git a/changelogs/unreleased/allow_plus_sign_for_snippets.yml b/changelogs/unreleased/allow_plus_sign_for_snippets.yml
new file mode 100644
index 00000000000..62d9dd74d07
--- /dev/null
+++ b/changelogs/unreleased/allow_plus_sign_for_snippets.yml
@@ -0,0 +1,4 @@
+---
+title: Allow to use + symbol in filenames
+merge_request: 6644
+author: blackst0ne
diff --git a/changelogs/unreleased/bug-project-feature-compatibility.yml b/changelogs/unreleased/bug-project-feature-compatibility.yml
new file mode 100644
index 00000000000..2124ee085e0
--- /dev/null
+++ b/changelogs/unreleased/bug-project-feature-compatibility.yml
@@ -0,0 +1,5 @@
+---
+title: Mutate the attribute instead of issuing a write operation to the DB in `ProjectFeaturesCompatibility`
+ concern.
+merge_request: 8552
+author:
diff --git a/changelogs/unreleased/dot-in-project-queries.yml b/changelogs/unreleased/dot-in-project-queries.yml
new file mode 100644
index 00000000000..fc48dc7b74d
--- /dev/null
+++ b/changelogs/unreleased/dot-in-project-queries.yml
@@ -0,0 +1,4 @@
+---
+title: Allow API query to find projects with dots in their name
+merge_request:
+author: Bruno Melli
diff --git a/changelogs/unreleased/pmq20-gitlab-ce-psvr-head-cache.yml b/changelogs/unreleased/pmq20-gitlab-ce-psvr-head-cache.yml
new file mode 100644
index 00000000000..23230128dc9
--- /dev/null
+++ b/changelogs/unreleased/pmq20-gitlab-ce-psvr-head-cache.yml
@@ -0,0 +1,4 @@
+---
+title: Expire related caches after changing HEAD
+merge_request:
+author: Minqi Pan
diff --git a/config/application.rb b/config/application.rb
index aa52b0cd512..8ce549cebf6 100644
--- a/config/application.rb
+++ b/config/application.rb
@@ -111,7 +111,6 @@ module Gitlab
config.assets.precompile << "lib/*.js"
config.assets.precompile << "u2f.js"
config.assets.precompile << "vue_pipelines_index/index.js"
- config.assets.precompile << "vue_pagination/index.js"
config.assets.precompile << "vendor/assets/fonts/*"
# Version of your assets, change this if you want to expire all your assets
diff --git a/db/migrate/20161226122833_remove_dot_git_from_usernames.rb b/db/migrate/20161226122833_remove_dot_git_from_usernames.rb
index 7d97339581f..a01f7e4ed52 100644
--- a/db/migrate/20161226122833_remove_dot_git_from_usernames.rb
+++ b/db/migrate/20161226122833_remove_dot_git_from_usernames.rb
@@ -14,16 +14,25 @@ class RemoveDotGitFromUsernames < ActiveRecord::Migration
namespace_id = user['namespace_id']
path_was = user['username']
path_was_wildcard = quote_string("#{path_was}/%")
+ path = quote_string(new_path(path_was))
- path = move_namespace(namespace_id, path_was, path)
+ move_namespace(namespace_id, path_was, path)
- execute "UPDATE routes SET path = '#{path}' WHERE source_type = 'Namespace' AND source_id = #{namespace_id}"
- execute "UPDATE namespaces SET path = '#{path}' WHERE id = #{namespace_id}"
- execute "UPDATE users SET username = '#{path}' WHERE id = #{id}"
+ begin
+ execute "UPDATE routes SET path = '#{path}' WHERE source_type = 'Namespace' AND source_id = #{namespace_id}"
+ execute "UPDATE namespaces SET path = '#{path}' WHERE id = #{namespace_id}"
+ execute "UPDATE users SET username = '#{path}' WHERE id = #{id}"
- select_all("SELECT id, path FROM routes WHERE path LIKE '#{path_was_wildcard}'").each do |route|
- new_path = "#{path}/#{route['path'].split('/').last}"
- execute "UPDATE routes SET path = '#{new_path}' WHERE id = #{route['id']}"
+ select_all("SELECT id, path FROM routes WHERE path LIKE '#{path_was_wildcard}'").each do |route|
+ new_path = "#{path}/#{route['path'].split('/').last}"
+ execute "UPDATE routes SET path = '#{new_path}' WHERE id = #{route['id']}"
+ end
+ rescue => e
+ say("Couldn't update routes for path #{path_was} to #{path}")
+ # Move namespace back
+ move_namespace(namespace_id, path, path_was)
+
+ raise e
end
end
end
@@ -44,23 +53,30 @@ class RemoveDotGitFromUsernames < ActiveRecord::Migration
select_all("SELECT id, path FROM routes WHERE path = '#{quote_string(path)}'").present?
end
- def path_exists?(repository_storage_path, path)
- gitlab_shell.exists?(repository_storage_path, path)
+ def path_exists?(path, repository_storage_path)
+ repository_storage_path && gitlab_shell.exists?(repository_storage_path, path)
end
# Accepts invalid path like test.git and returns test_git or
# test_git1 if test_git already taken
- def rename_path(repository_storage_path, path)
+ def new_path(path)
# To stay closer with original name and reduce risk of duplicates
# we rename suffix instead of removing it
path = path.sub(/\.git\z/, '_git')
- counter = 0
- base = path
+ check_routes(path.dup, 0, path)
+ end
+
+ def check_routes(base, counter, path)
+ route_exists = route_exists?(path)
- while route_exists?(path) || path_exists?(repository_storage_path, path)
- counter += 1
- path = "#{base}#{counter}"
+ Gitlab.config.repositories.storages.each_value do |storage|
+ if route_exists || path_exists?(path, storage)
+ counter += 1
+ path = "#{base}#{counter}"
+
+ return check_route(base, counter, path)
+ end
end
path
@@ -76,8 +92,6 @@ class RemoveDotGitFromUsernames < ActiveRecord::Migration
# Ensure old directory exists before moving it
gitlab_shell.add_namespace(repository_storage_path, path_was)
- path = quote_string(rename_path(repository_storage_path, path_was))
-
unless gitlab_shell.mv_namespace(repository_storage_path, path_was, path)
Rails.logger.error "Exception moving path #{repository_storage_path} from #{path_was} to #{path}"
@@ -87,8 +101,14 @@ class RemoveDotGitFromUsernames < ActiveRecord::Migration
end
end
- Gitlab::UploadsTransfer.new.rename_namespace(path_was, path)
-
- path
+ begin
+ Gitlab::UploadsTransfer.new.rename_namespace(path_was, path)
+ rescue => e
+ if path.nil?
+ say("Couldn't find a storage path for #{namespace_id}, #{path_was} -- skipping")
+ else
+ raise e
+ end
+ end
end
end
diff --git a/features/admin/groups.feature b/features/admin/groups.feature
deleted file mode 100644
index 657e847cf4a..00000000000
--- a/features/admin/groups.feature
+++ /dev/null
@@ -1,49 +0,0 @@
-@admin
-Feature: Admin Groups
- Background:
- Given I sign in as an admin
- And I have group with projects
- And User "John Doe" exists
- And I visit admin groups page
-
- Scenario: See group list
- Then I should be all groups
-
- Scenario: Create a group
- When I click new group link
- And submit form with new group info
- Then I should be redirected to group page
- And I should see newly created group
-
- @javascript
- Scenario: Add user into projects in group
- When I visit admin group page
- When I select user "John Doe" from user list as "Reporter"
- Then I should see "John Doe" in team list in every project as "Reporter"
-
- Scenario: Shared projects
- Given group has shared projects
- When I visit group page
- Then I should see project shared with group
-
- @javascript
- Scenario: Invite user to a group by e-mail
- When I visit admin group page
- When I select user "johndoe@gitlab.com" from user list as "Reporter"
- Then I should see "johndoe@gitlab.com" in team list in every project as "Reporter"
-
- @javascript
- Scenario: Signed in admin should be able to add himself to a group
- Given "John Doe" is owner of group "Owned"
- When I visit group "Owned" members page
- When I select current user as "Developer"
- Then I should see current user as "Developer"
-
- @javascript
- Scenario: Signed in admin should be able to remove himself from group
- Given current user is developer of group "Owned"
- When I visit group "Owned" members page
- Then I should see current user as "Developer"
- When I click on the "Remove User From Group" button for current user
- When I visit group "Owned" members page
- Then I should not see current user as "Developer"
diff --git a/features/steps/admin/groups.rb b/features/steps/admin/groups.rb
deleted file mode 100644
index 9396a76f0a2..00000000000
--- a/features/steps/admin/groups.rb
+++ /dev/null
@@ -1,143 +0,0 @@
-class Spinach::Features::AdminGroups < Spinach::FeatureSteps
- include SharedAuthentication
- include SharedGroup
- include SharedPaths
- include SharedUser
- include SharedActiveTab
- include Select2Helper
-
- When 'I visit admin group page' do
- visit admin_group_path(current_group)
- end
-
- When 'I click new group link' do
- click_link "New Group"
- end
-
- step 'I have group with projects' do
- @group = create(:group)
- @project = create(:project, group: @group)
- @event = create(:closed_issue_event, project: @project)
-
- @project.team << [current_user, :master]
- end
-
- step 'submit form with new group info' do
- fill_in 'group_path', with: 'gitlab'
- fill_in 'group_description', with: 'Group description'
- click_button "Create group"
- end
-
- step 'I should see newly created group' do
- expect(page).to have_content "Group: gitlab"
- expect(page).to have_content "Group description"
- end
-
- step 'I should be redirected to group page' do
- expect(current_path).to eq admin_group_path(Group.find_by(path: 'gitlab'))
- end
-
- When 'I select user "John Doe" from user list as "Reporter"' do
- select2(user_john.id, from: "#user_ids", multiple: true)
- page.within "#new_project_member" do
- select "Reporter", from: "access_level"
- end
- click_button "Add users to group"
- end
-
- When 'I select user "johndoe@gitlab.com" from user list as "Reporter"' do
- select2('johndoe@gitlab.com', from: "#user_ids", multiple: true)
- page.within "#new_project_member" do
- select "Reporter", from: "access_level"
- end
- click_button "Add users to group"
- end
-
- step 'I should see "John Doe" in team list in every project as "Reporter"' do
- page.within ".group-users-list" do
- expect(page).to have_content "John Doe"
- expect(page).to have_content "Reporter"
- end
- end
-
- step 'I should see "johndoe@gitlab.com" in team list in every project as "Reporter"' do
- page.within ".group-users-list" do
- expect(page).to have_content "johndoe@gitlab.com"
- expect(page).to have_content "Invited by"
- expect(page).to have_content "Reporter"
- end
- end
-
- step 'I should be all groups' do
- Group.all.each do |group|
- expect(page).to have_content group.name
- end
- end
-
- step 'group has shared projects' do
- share_link = shared_project.project_group_links.new(group_access: Gitlab::Access::MASTER)
- share_link.group_id = current_group.id
- share_link.save!
- end
-
- step 'I visit group page' do
- visit admin_group_path(current_group)
- end
-
- step 'I should see project shared with group' do
- expect(page).to have_content(shared_project.name_with_namespace)
- expect(page).to have_content "Projects shared with"
- end
-
- step 'we have user "John Doe" in group' do
- current_group.add_reporter(user_john)
- end
-
- step 'I should not see "John Doe" in team list' do
- page.within ".group-users-list" do
- expect(page).not_to have_content "John Doe"
- end
- end
-
- step 'I select current user as "Developer"' do
- page.within ".users-group-form" do
- select2(current_user.id, from: "#user_ids", multiple: true)
- select "Developer", from: "access_level"
- end
-
- click_button "Add to group"
- end
-
- step 'I should see current user as "Developer"' do
- page.within '.content-list' do
- expect(page).to have_content(current_user.name)
- expect(page).to have_content('Developer')
- end
- end
-
- step 'I click on the "Remove User From Group" button for current user' do
- find(:css, 'li', text: current_user.name).find(:css, 'a.btn-remove').click
- # poltergeist always confirms popups.
- end
-
- step 'I should not see current user as "Developer"' do
- page.within '.content-list' do
- expect(page).not_to have_content(current_user.name)
- expect(page).not_to have_content('Developer')
- end
- end
-
- protected
-
- def current_group
- @group ||= Group.first
- end
-
- def shared_project
- @shared_project ||= create(:empty_project)
- end
-
- def user_john
- @user_john ||= User.find_by(name: "John Doe")
- end
-end
diff --git a/features/steps/shared/paths.rb b/features/steps/shared/paths.rb
index 15b81fa529b..670e6ca49a3 100644
--- a/features/steps/shared/paths.rb
+++ b/features/steps/shared/paths.rb
@@ -191,10 +191,6 @@ module SharedPaths
visit admin_background_jobs_path
end
- step 'I visit admin groups page' do
- visit admin_groups_path
- end
-
step 'I visit admin teams page' do
visit admin_teams_path
end
diff --git a/lib/api/helpers.rb b/lib/api/helpers.rb
index 20b5bc1502a..eb2d370c68e 100644
--- a/lib/api/helpers.rb
+++ b/lib/api/helpers.rb
@@ -294,7 +294,7 @@ module API
header['X-Sendfile'] = path
body
else
- file FileStreamer.new(path)
+ path
end
end
diff --git a/lib/api/projects.rb b/lib/api/projects.rb
index 3be14e8eb76..7c66c340562 100644
--- a/lib/api/projects.rb
+++ b/lib/api/projects.rb
@@ -159,7 +159,7 @@ module API
use :sort_params
use :pagination
end
- get "/search/:query" do
+ get "/search/:query", requirements: { query: /[^\/]+/ } do
search_service = Search::GlobalService.new(current_user, search: params[:query]).execute
projects = search_service.objects('projects', params[:page])
projects = projects.reorder(params[:order_by] => params[:sort])
diff --git a/lib/gitlab/checks/change_access.rb b/lib/gitlab/checks/change_access.rb
index 9c391fa92a3..273118135a9 100644
--- a/lib/gitlab/checks/change_access.rb
+++ b/lib/gitlab/checks/change_access.rb
@@ -30,9 +30,9 @@ module Gitlab
return unless @branch_name
return unless project.protected_branch?(@branch_name)
- if forced_push? && user_access.cannot_do_action?(:force_push_code_to_protected_branches)
+ if forced_push?
return "You are not allowed to force push code to a protected branch on this project."
- elsif Gitlab::Git.blank_ref?(@newrev) && user_access.cannot_do_action?(:remove_protected_branches)
+ elsif Gitlab::Git.blank_ref?(@newrev)
return "You are not allowed to delete protected branches from this project."
end
diff --git a/lib/gitlab/regex.rb b/lib/gitlab/regex.rb
index 9e0b0e5ea98..a3fa7c1331a 100644
--- a/lib/gitlab/regex.rb
+++ b/lib/gitlab/regex.rb
@@ -61,11 +61,11 @@ module Gitlab
end
def file_name_regex
- @file_name_regex ||= /\A[[[:alnum:]]_\-\.\@]*\z/.freeze
+ @file_name_regex ||= /\A[[[:alnum:]]_\-\.\@\+]*\z/.freeze
end
def file_name_regex_message
- "can contain only letters, digits, '_', '-', '@' and '.'."
+ "can contain only letters, digits, '_', '-', '@', '+' and '.'."
end
def file_path_regex
diff --git a/scripts/notify_slack.sh b/scripts/notify_slack.sh
index 0a4239e132c..6b3bc563c7a 100755
--- a/scripts/notify_slack.sh
+++ b/scripts/notify_slack.sh
@@ -1,4 +1,4 @@
-#!/bin/bash
+#!/bin/sh
# Sends Slack notification ERROR_MSG to CHANNEL
# An env. variable CI_SLACK_WEBHOOK_URL needs to be set.
diff --git a/spec/features/admin/admin_groups_spec.rb b/spec/features/admin/admin_groups_spec.rb
index 9c19db6b420..a871e370ba2 100644
--- a/spec/features/admin/admin_groups_spec.rb
+++ b/spec/features/admin/admin_groups_spec.rb
@@ -1,15 +1,39 @@
require 'spec_helper'
feature 'Admin Groups', feature: true do
+ include Select2Helper
+
let(:internal) { Gitlab::VisibilityLevel::INTERNAL }
+ let(:user) { create :user }
+ let!(:group) { create :group }
+ let!(:current_user) { login_as :admin }
before do
- login_as(:admin)
-
stub_application_setting(default_group_visibility: internal)
end
+ describe 'list' do
+ it 'renders groups' do
+ visit admin_groups_path
+
+ expect(page).to have_content(group.name)
+ end
+ end
+
describe 'create a group' do
+ it 'creates new group' do
+ visit admin_groups_path
+
+ click_link "New Group"
+ fill_in 'group_path', with: 'gitlab'
+ fill_in 'group_description', with: 'Group description'
+ click_button "Create group"
+
+ expect(current_path).to eq admin_group_path(Group.find_by(path: 'gitlab'))
+ expect(page).to have_content('Group: gitlab')
+ expect(page).to have_content('Group description')
+ end
+
scenario 'shows the visibility level radio populated with the default value' do
visit new_admin_group_path
@@ -37,6 +61,91 @@ feature 'Admin Groups', feature: true do
end
end
+ describe 'add user into a group', js: true do
+ shared_context 'adds user into a group' do
+ it do
+ visit admin_group_path(group)
+
+ select2(user_selector, from: '#user_ids', multiple: true)
+ page.within '#new_project_member' do
+ select2(Gitlab::Access::REPORTER, from: '#access_level')
+ end
+ click_button "Add users to group"
+ page.within ".group-users-list" do
+ expect(page).to have_content(user.name)
+ expect(page).to have_content('Reporter')
+ end
+ end
+ end
+
+ it_behaves_like 'adds user into a group' do
+ let(:user_selector) { user.id }
+ end
+
+ it_behaves_like 'adds user into a group' do
+ let(:user_selector) { user.email }
+ end
+ end
+
+ describe 'add admin himself to a group' do
+ before do
+ group.add_user(:user, Gitlab::Access::OWNER)
+ end
+
+ it 'adds admin a to a group as developer', js: true do
+ visit group_group_members_path(group)
+
+ page.within '.users-group-form' do
+ select2(current_user.id, from: '#user_ids', multiple: true)
+ select 'Developer', from: 'access_level'
+ end
+
+ click_button 'Add to group'
+
+ page.within '.content-list' do
+ expect(page).to have_content(current_user.name)
+ expect(page).to have_content('Developer')
+ end
+ end
+ end
+
+ describe 'admin remove himself from a group', js: true do
+ it 'removes admin from the group' do
+ group.add_user(current_user, Gitlab::Access::DEVELOPER)
+
+ visit group_group_members_path(group)
+
+ page.within '.content-list' do
+ expect(page).to have_content(current_user.name)
+ expect(page).to have_content('Developer')
+ end
+
+ find(:css, 'li', text: current_user.name).find(:css, 'a.btn-remove').click
+
+ visit group_group_members_path(group)
+
+ page.within '.content-list' do
+ expect(page).not_to have_content(current_user.name)
+ expect(page).not_to have_content('Developer')
+ end
+ end
+ end
+
+ describe 'shared projects' do
+ it 'renders shared project' do
+ empty_project = create(:empty_project)
+ empty_project.project_group_links.create!(
+ group_access: Gitlab::Access::MASTER,
+ group: group
+ )
+
+ visit admin_group_path(group)
+
+ expect(page).to have_content(empty_project.name_with_namespace)
+ expect(page).to have_content('Projects shared with')
+ end
+ end
+
def expect_selected_visibility(level)
selector = "#group_visibility_level_#{level}[checked=checked]"
diff --git a/spec/features/snippets/create_snippet_spec.rb b/spec/features/snippets/create_snippet_spec.rb
index cb95e7828db..5470276bf06 100644
--- a/spec/features/snippets/create_snippet_spec.rb
+++ b/spec/features/snippets/create_snippet_spec.rb
@@ -17,4 +17,18 @@ feature 'Create Snippet', feature: true do
expect(page).to have_content('My Snippet Title')
expect(page).to have_content('Hello World!')
end
+
+ scenario 'Authenticated user creates a snippet with + in filename' do
+ fill_in 'personal_snippet_title', with: 'My Snippet Title'
+ page.within('.file-editor') do
+ find(:xpath, "//input[@id='personal_snippet_file_name']").set 'snippet+file+name'
+ find(:xpath, "//input[@id='personal_snippet_content']").set 'Hello World!'
+ end
+
+ click_button 'Create snippet'
+
+ expect(page).to have_content('My Snippet Title')
+ expect(page).to have_content('snippet+file+name')
+ expect(page).to have_content('Hello World!')
+ end
end
diff --git a/spec/lib/gitlab/checks/change_access_spec.rb b/spec/lib/gitlab/checks/change_access_spec.rb
index 39069b49978..98effecdbbc 100644
--- a/spec/lib/gitlab/checks/change_access_spec.rb
+++ b/spec/lib/gitlab/checks/change_access_spec.rb
@@ -56,7 +56,6 @@ describe Gitlab::Checks::ChangeAccess, lib: true do
it 'returns an error if the user is not allowed to do forced pushes to protected branches' do
expect(Gitlab::Checks::ForcePush).to receive(:force_push?).and_return(true)
- expect(user_access).to receive(:can_do_action?).with(:force_push_code_to_protected_branches).and_return(false)
expect(subject.status).to be(false)
expect(subject.message).to eq('You are not allowed to force push code to a protected branch on this project.')
@@ -88,8 +87,6 @@ describe Gitlab::Checks::ChangeAccess, lib: true do
end
it 'returns an error if the user is not allowed to delete protected branches' do
- expect(user_access).to receive(:can_do_action?).with(:remove_protected_branches).and_return(false)
-
expect(subject.status).to be(false)
expect(subject.message).to eq('You are not allowed to delete protected branches from this project.')
end
diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb
index 32779eb92ef..e93a4e62244 100644
--- a/spec/models/project_spec.rb
+++ b/spec/models/project_spec.rb
@@ -1545,11 +1545,13 @@ describe Project, models: true do
end
end
- describe 'change_head' do
+ describe '#change_head' do
let(:project) { create(:project) }
- it 'calls the before_change_head method' do
+ it 'calls the before_change_head and after_change_head methods' do
expect(project.repository).to receive(:before_change_head)
+ expect(project.repository).to receive(:after_change_head)
+
project.change_head(project.default_branch)
end
@@ -1565,11 +1567,6 @@ describe Project, models: true do
project.change_head(project.default_branch)
end
- it 'expires the avatar cache' do
- expect(project.repository).to receive(:expire_avatar_cache)
- project.change_head(project.default_branch)
- end
-
it 'reloads the default branch' do
expect(project).to receive(:reload_default_branch)
project.change_head(project.default_branch)
diff --git a/spec/models/repository_spec.rb b/spec/models/repository_spec.rb
index af7e89eae05..99ca53938c8 100644
--- a/spec/models/repository_spec.rb
+++ b/spec/models/repository_spec.rb
@@ -1150,6 +1150,24 @@ describe Repository, models: true do
end
end
+ describe '#after_change_head' do
+ it 'flushes the readme cache' do
+ expect(repository).to receive(:expire_method_caches).with([
+ :readme,
+ :changelog,
+ :license,
+ :contributing,
+ :version,
+ :gitignore,
+ :koding,
+ :gitlab_ci,
+ :avatar
+ ])
+
+ repository.after_change_head
+ end
+ end
+
describe '#before_push_tag' do
it 'flushes the cache' do
expect(repository).to receive(:expire_statistics_caches)
@@ -1513,14 +1531,6 @@ describe Repository, models: true do
end
end
- describe '#expire_avatar_cache' do
- it 'expires the cache' do
- expect(repository).to receive(:expire_method_caches).with(%i(avatar))
-
- repository.expire_avatar_cache
- end
- end
-
describe '#file_on_head' do
context 'with a non-existing repository' do
it 'returns nil' do
diff --git a/spec/requests/api/projects_spec.rb b/spec/requests/api/projects_spec.rb
index f5788d15f93..cdb16b4c46b 100644
--- a/spec/requests/api/projects_spec.rb
+++ b/spec/requests/api/projects_spec.rb
@@ -1085,7 +1085,7 @@ describe API::Projects, api: true do
end
describe 'GET /projects/search/:query' do
- let!(:query) { 'query'}
+ let!(:query) { 'query'}
let!(:search) { create(:empty_project, name: query, creator_id: user.id, namespace: user.namespace) }
let!(:pre) { create(:empty_project, name: "pre_#{query}", creator_id: user.id, namespace: user.namespace) }
let!(:post) { create(:empty_project, name: "#{query}_post", creator_id: user.id, namespace: user.namespace) }
@@ -1095,32 +1095,37 @@ describe API::Projects, api: true do
let!(:unfound_internal) { create(:empty_project, :internal, name: 'unfound internal') }
let!(:public) { create(:empty_project, :public, name: "public #{query}") }
let!(:unfound_public) { create(:empty_project, :public, name: 'unfound public') }
+ let!(:one_dot_two) { create(:empty_project, :public, name: "one.dot.two") }
shared_examples_for 'project search response' do |args = {}|
it 'returns project search responses' do
- get api("/projects/search/#{query}", current_user)
+ get api("/projects/search/#{args[:query]}", current_user)
expect(response).to have_http_status(200)
expect(json_response).to be_an Array
expect(json_response.size).to eq(args[:results])
- json_response.each { |project| expect(project['name']).to match(args[:match_regex] || /.*query.*/) }
+ json_response.each { |project| expect(project['name']).to match(args[:match_regex] || /.*#{args[:query]}.*/) }
end
end
context 'when unauthenticated' do
- it_behaves_like 'project search response', results: 1 do
+ it_behaves_like 'project search response', query: 'query', results: 1 do
let(:current_user) { nil }
end
end
context 'when authenticated' do
- it_behaves_like 'project search response', results: 6 do
+ it_behaves_like 'project search response', query: 'query', results: 6 do
let(:current_user) { user }
end
+ it_behaves_like 'project search response', query: 'one.dot.two', results: 1 do
+ let(:current_user) { user }
+ end
+
end
context 'when authenticated as a different user' do
- it_behaves_like 'project search response', results: 2, match_regex: /(internal|public) query/ do
+ it_behaves_like 'project search response', query: 'query', results: 2, match_regex: /(internal|public) query/ do
let(:current_user) { user2 }
end
end