summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Gemfile4
-rw-r--r--Gemfile.lock35
-rw-r--r--app/assets/javascripts/boards/components/board_list.js.es65
-rw-r--r--app/assets/javascripts/boards/models/list.js.es68
-rw-r--r--app/assets/javascripts/boards/stores/boards_store.js.es64
-rw-r--r--app/assets/javascripts/commits.js6
-rw-r--r--app/assets/javascripts/cycle_analytics/cycle_analytics_store.js.es68
-rw-r--r--app/assets/javascripts/diff_notes/models/discussion.js.es69
-rw-r--r--app/assets/javascripts/gfm_auto_complete.js.es634
-rw-r--r--app/assets/javascripts/notes.js2
-rw-r--r--app/assets/stylesheets/pages/boards.scss2
-rw-r--r--app/controllers/application_controller.rb8
-rw-r--r--app/controllers/projects/notes_controller.rb1
-rw-r--r--app/models/ci/pipeline.rb20
-rw-r--r--app/models/concerns/has_status.rb5
-rw-r--r--app/models/namespace.rb2
-rw-r--r--app/models/note.rb3
-rw-r--r--app/models/project.rb1
-rw-r--r--app/models/user.rb6
-rw-r--r--app/services/notes/create_service.rb2
-rw-r--r--app/services/notification_service.rb1
-rw-r--r--app/services/system_note_service.rb86
-rw-r--r--app/validators/namespace_validator.rb22
-rw-r--r--app/validators/project_path_validator.rb36
-rw-r--r--app/views/discussions/_discussion.html.haml1
-rw-r--r--app/views/profiles/update_username.js.haml3
-rw-r--r--app/views/projects/boards/components/_card.html.haml1
-rw-r--r--app/views/projects/builds/show.html.haml5
-rw-r--r--app/views/projects/edit.html.haml13
-rw-r--r--app/views/projects/tags/index.html.haml7
-rw-r--r--app/views/projects/wikis/_nav.html.haml2
-rw-r--r--app/views/shared/issuable/_filter.html.haml4
-rw-r--r--app/views/shared/issuable/_label_page_default.html.haml8
-rw-r--r--app/views/shared/issuable/_sidebar.html.haml6
-rw-r--r--changelogs/unreleased/24779-last-deployment-call-on-nil-environment-fix.yml4
-rw-r--r--changelogs/unreleased/boards-issue-sorting.yml4
-rw-r--r--changelogs/unreleased/dz-allow-nested-group-routing.yml4
-rw-r--r--changelogs/unreleased/fix-cancelling-pipelines.yml4
-rw-r--r--changelogs/unreleased/fixed-commit-timeago.yml4
-rw-r--r--changelogs/unreleased/issue-boards-scrollable-element.yml4
-rw-r--r--changelogs/unreleased/issue_24748.yml4
-rw-r--r--changelogs/unreleased/issue_24958.yml4
-rw-r--r--changelogs/unreleased/move-abuse-report-spinach-test-to-rspec.yml4
-rw-r--r--changelogs/unreleased/move-admin-abuse-report-spinach-test-to-rspec.yml4
-rw-r--r--changelogs/unreleased/move-admin-spam-spinach-test-to-rspec.yml4
-rw-r--r--changelogs/unreleased/remove-backup-strategies.yml4
-rw-r--r--changelogs/unreleased/rephrase-system-notes.yml4
-rw-r--r--changelogs/unreleased/resolve-discussions-timeago.yml4
-rw-r--r--changelogs/unreleased/simplify-create-new-list-issue-boards.yml4
-rw-r--r--changelogs/unreleased/zj-upgrade-grape.yml4
-rw-r--r--config/routes.rb20
-rw-r--r--config/routes/git_http.rb68
-rw-r--r--config/routes/group.rb20
-rw-r--r--config/routes/project.rb58
-rw-r--r--config/routes/repository.rb122
-rw-r--r--config/routes/user.rb3
-rw-r--r--config/routes/wiki.rb27
-rw-r--r--doc/api/notes.md2
-rw-r--r--doc/api/projects.md2
-rw-r--r--doc/development/testing.md2
-rw-r--r--doc/install/installation.md10
-rw-r--r--doc/integration/jira.md3
-rw-r--r--doc/raketasks/backup_restore.md2
-rw-r--r--doc/university/README.md12
-rw-r--r--doc/user/project/img/issue_board.pngbin81649 -> 90664 bytes
-rw-r--r--doc/user/project/img/issue_board_add_list.pngbin9516 -> 23632 bytes
-rw-r--r--doc/user/project/img/issue_board_welcome_message.pngbin29956 -> 97419 bytes
-rw-r--r--doc/user/project/issue_board.md2
-rw-r--r--doc/web_hooks/web_hooks.md58
-rw-r--r--doc/workflow/img/todos_add_todo_sidebar.pngbin33350 -> 42360 bytes
-rw-r--r--doc/workflow/img/todos_mark_done_sidebar.pngbin33703 -> 42317 bytes
-rw-r--r--doc/workflow/todos.md4
-rw-r--r--features/abuse_report.feature17
-rw-r--r--features/admin/abuse_report.feature8
-rw-r--r--features/admin/spam_logs.feature8
-rw-r--r--features/steps/abuse_reports.rb32
-rw-r--r--features/steps/admin/abuse_reports.rb15
-rw-r--r--features/steps/admin/spam_logs.rb28
-rw-r--r--features/steps/project/labels.rb2
-rw-r--r--features/steps/project/merge_requests.rb2
-rw-r--r--features/steps/shared/diff_note.rb5
-rw-r--r--features/steps/shared/issuable.rb2
-rw-r--r--features/steps/shared/note.rb4
-rw-r--r--lib/api/commit_statuses.rb2
-rw-r--r--lib/api/commits.rb2
-rw-r--r--lib/api/groups.rb2
-rw-r--r--lib/api/variables.rb2
-rw-r--r--lib/constraints/constrainer_helper.rb15
-rw-r--r--lib/constraints/group_url_constrainer.rb20
-rw-r--r--lib/constraints/project_url_constrainer.rb13
-rw-r--r--lib/constraints/user_url_constrainer.rb12
-rw-r--r--lib/gitlab/identifier.rb6
-rw-r--r--lib/gitlab/regex.rb14
-rw-r--r--spec/controllers/application_controller_spec.rb23
-rw-r--r--spec/controllers/projects/milestones_controller_spec.rb2
-rw-r--r--spec/factories/ci/builds.rb4
-rw-r--r--spec/factories/commit_statuses.rb4
-rw-r--r--spec/features/abuse_report_spec.rb24
-rw-r--r--spec/features/admin/admin_abuse_reports_spec.rb16
-rw-r--r--spec/features/admin/admin_browse_spam_logs_spec.rb22
-rw-r--r--spec/features/boards/boards_spec.rb16
-rw-r--r--spec/features/issues/gfm_autocomplete_spec.rb43
-rw-r--r--spec/features/issues/move_spec.rb6
-rw-r--r--spec/features/issues/new_branch_button_spec.rb4
-rw-r--r--spec/features/issues/todo_spec.rb8
-rw-r--r--spec/features/merge_requests/diff_notes_resolve_spec.rb2
-rw-r--r--spec/features/merge_requests/merge_when_build_succeeds_spec.rb2
-rw-r--r--spec/features/notes_on_merge_requests_spec.rb2
-rw-r--r--spec/features/projects/pipelines_spec.rb11
-rw-r--r--spec/javascripts/fixtures/right_sidebar.html.haml4
-rw-r--r--spec/lib/constraints/constrainer_helper_spec.rb20
-rw-r--r--spec/lib/constraints/group_url_constrainer_spec.rb20
-rw-r--r--spec/lib/constraints/project_url_constrainer_spec.rb32
-rw-r--r--spec/lib/constraints/user_url_constrainer_spec.rb21
-rw-r--r--spec/lib/gitlab/identifier_spec.rb5
-rw-r--r--spec/models/ci/pipeline_spec.rb154
-rw-r--r--spec/models/concerns/has_status_spec.rb96
-rw-r--r--spec/models/note_spec.rb2
-rw-r--r--spec/models/user_spec.rb11
-rw-r--r--spec/requests/api/notes_spec.rb2
-rw-r--r--spec/routing/project_routing_spec.rb1034
-rw-r--r--spec/routing/routing_spec.rb16
-rw-r--r--spec/serializers/analytics_build_serializer_spec.rb4
-rw-r--r--spec/serializers/analytics_issue_serializer_spec.rb4
-rw-r--r--spec/serializers/analytics_merge_request_serializer_spec.rb4
-rw-r--r--spec/serializers/environment_serializer_spec.rb4
-rw-r--r--spec/services/issues/close_service_spec.rb2
-rw-r--r--spec/services/issues/move_service_spec.rb6
-rw-r--r--spec/services/issues/update_service_spec.rb28
-rw-r--r--spec/services/merge_requests/close_service_spec.rb2
-rw-r--r--spec/services/merge_requests/merge_service_spec.rb2
-rw-r--r--spec/services/merge_requests/merge_when_build_succeeds_service_spec.rb4
-rw-r--r--spec/services/merge_requests/refresh_service_spec.rb16
-rw-r--r--spec/services/merge_requests/reopen_service_spec.rb2
-rw-r--r--spec/services/merge_requests/update_service_spec.rb24
-rw-r--r--spec/services/system_note_service_spec.rb90
-rw-r--r--spec/support/carrierwave.rb2
-rw-r--r--spec/support/setup_builds_storage.rb17
-rw-r--r--spec/views/projects/builds/show.html.haml_spec.rb52
-rw-r--r--spec/views/projects/edit.html.haml_spec.rb24
140 files changed, 1788 insertions, 1155 deletions
diff --git a/Gemfile b/Gemfile
index 9e815925a1f..1d686199557 100644
--- a/Gemfile
+++ b/Gemfile
@@ -68,7 +68,7 @@ gem 'github-linguist', '~> 4.7.0', require: 'linguist'
# API
gem 'grape', '~> 0.15.0'
-gem 'grape-entity', '~> 0.4.2'
+gem 'grape-entity', '~> 0.6.0'
gem 'rack-cors', '~> 0.4.0', require: 'rack/cors'
# Pagination
@@ -85,10 +85,8 @@ gem 'dropzonejs-rails', '~> 0.7.1'
# for backups
gem 'fog-aws', '~> 0.9'
-gem 'fog-azure', '~> 0.0'
gem 'fog-core', '~> 1.40'
gem 'fog-local', '~> 0.3'
-gem 'fog-google', '~> 0.3'
gem 'fog-openstack', '~> 0.1'
gem 'fog-rackspace', '~> 0.1.1'
diff --git a/Gemfile.lock b/Gemfile.lock
index bdc60552480..bf9702b2562 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -66,21 +66,6 @@ GEM
descendants_tracker (~> 0.0.4)
ice_nine (~> 0.11.0)
thread_safe (~> 0.3, >= 0.3.1)
- azure (0.7.5)
- addressable (~> 2.3)
- azure-core (~> 0.1)
- faraday (~> 0.9)
- faraday_middleware (~> 0.10)
- json (~> 1.8)
- mime-types (>= 1, < 3.0)
- nokogiri (~> 1.6)
- systemu (~> 2.6)
- thor (~> 0.19)
- uuid (~> 2.0)
- azure-core (0.1.2)
- faraday (~> 0.9)
- faraday_middleware (~> 0.10)
- nokogiri (~> 1.6)
babel-source (5.8.35)
babel-transpiler (0.7.0)
babel-source (>= 4.0, < 6)
@@ -217,19 +202,10 @@ GEM
fog-json (~> 1.0)
fog-xml (~> 0.1)
ipaddress (~> 0.8)
- fog-azure (0.0.2)
- azure (~> 0.6)
- fog-core (~> 1.27)
- fog-json (~> 1.0)
- fog-xml (~> 0.1)
fog-core (1.42.0)
builder
excon (~> 0.49)
formatador (~> 0.2)
- fog-google (0.3.2)
- fog-core
- fog-json
- fog-xml
fog-json (1.0.2)
fog-core (~> 1.0)
multi_json (~> 1.10)
@@ -316,7 +292,7 @@ GEM
rack-accept
rack-mount
virtus (>= 1.0.0)
- grape-entity (0.4.8)
+ grape-entity (0.6.0)
activesupport
multi_json (>= 1.3.2)
haml (4.0.7)
@@ -397,8 +373,6 @@ GEM
rb-inotify (>= 0.9)
loofah (2.0.3)
nokogiri (>= 1.5.9)
- macaddr (1.7.1)
- systemu (~> 2.6.2)
mail (2.6.4)
mime-types (>= 1.16, < 4)
mail_room (0.9.0)
@@ -728,7 +702,6 @@ GEM
sys-filesystem (1.1.6)
ffi
sysexits (1.2.0)
- systemu (2.6.5)
teaspoon (1.1.5)
railties (>= 3.2.5, < 6)
teaspoon-jasmine (2.2.0)
@@ -768,8 +741,6 @@ GEM
get_process_mem (~> 0)
unicorn (>= 4, < 6)
uniform_notifier (1.10.0)
- uuid (2.3.8)
- macaddr (~> 1.0)
version_sorter (2.1.0)
virtus (1.0.5)
axiom-types (~> 0.1)
@@ -849,9 +820,7 @@ DEPENDENCIES
ffaker (~> 2.0.0)
flay (~> 2.6.1)
fog-aws (~> 0.9)
- fog-azure (~> 0.0)
fog-core (~> 1.40)
- fog-google (~> 0.3)
fog-local (~> 0.3)
fog-openstack (~> 0.1)
fog-rackspace (~> 0.1.1)
@@ -869,7 +838,7 @@ DEPENDENCIES
gollum-rugged_adapter (~> 0.4.2)
gon (~> 6.1.0)
grape (~> 0.15.0)
- grape-entity (~> 0.4.2)
+ grape-entity (~> 0.6.0)
haml_lint (~> 0.18.2)
hamlit (~> 2.6.1)
health_check (~> 2.2.0)
diff --git a/app/assets/javascripts/boards/components/board_list.js.es6 b/app/assets/javascripts/boards/components/board_list.js.es6
index 8e91cbfac75..43ebeef39c4 100644
--- a/app/assets/javascripts/boards/components/board_list.js.es6
+++ b/app/assets/javascripts/boards/components/board_list.js.es6
@@ -80,6 +80,7 @@
},
mounted () {
const options = gl.issueBoards.getBoardSortableDefaultOptions({
+ scroll: document.querySelectorAll('.boards-list')[0],
group: 'issues',
sort: false,
disabled: this.disabled,
@@ -88,13 +89,13 @@
const card = this.$refs.issue[e.oldIndex];
card.showDetail = false;
- Store.moving.issue = card.issue;
Store.moving.list = card.list;
+ Store.moving.issue = Store.moving.list.findIssue(+e.item.dataset.issueId);
gl.issueBoards.onStart();
},
onAdd: (e) => {
- gl.issueBoards.BoardsStore.moveIssueToList(Store.moving.list, this.list, Store.moving.issue);
+ gl.issueBoards.BoardsStore.moveIssueToList(Store.moving.list, this.list, Store.moving.issue, e.newIndex);
this.$nextTick(() => {
e.item.remove();
diff --git a/app/assets/javascripts/boards/models/list.js.es6 b/app/assets/javascripts/boards/models/list.js.es6
index 8a7dc67409e..429bd27c3fb 100644
--- a/app/assets/javascripts/boards/models/list.js.es6
+++ b/app/assets/javascripts/boards/models/list.js.es6
@@ -106,9 +106,13 @@ class List {
});
}
- addIssue (issue, listFrom) {
+ addIssue (issue, listFrom, newIndex) {
if (!this.findIssue(issue.id)) {
- this.issues.push(issue);
+ if (newIndex !== undefined) {
+ this.issues.splice(newIndex, 0, issue);
+ } else {
+ this.issues.push(issue);
+ }
if (this.label) {
issue.addLabel(this.label);
diff --git a/app/assets/javascripts/boards/stores/boards_store.js.es6 b/app/assets/javascripts/boards/stores/boards_store.js.es6
index 6bc95aa60f2..bb2a4de8b18 100644
--- a/app/assets/javascripts/boards/stores/boards_store.js.es6
+++ b/app/assets/javascripts/boards/stores/boards_store.js.es6
@@ -89,14 +89,14 @@
});
listFrom.update();
},
- moveIssueToList (listFrom, listTo, issue) {
+ moveIssueToList (listFrom, listTo, issue, newIndex) {
const issueTo = listTo.findIssue(issue.id),
issueLists = issue.getLists(),
listLabels = issueLists.map( listIssue => listIssue.label );
// Add to new lists issues if it doesn't already exist
if (!issueTo) {
- listTo.addIssue(issue, listFrom);
+ listTo.addIssue(issue, listFrom, newIndex);
}
if (listTo.type === 'done' && listFrom.type !== 'backlog') {
diff --git a/app/assets/javascripts/commits.js b/app/assets/javascripts/commits.js
index 951fb338f9d..3627aaf5080 100644
--- a/app/assets/javascripts/commits.js
+++ b/app/assets/javascripts/commits.js
@@ -1,4 +1,4 @@
-/* eslint-disable func-names, space-before-function-paren, wrap-iife, quotes, consistent-return, no-undef, no-return-assign, no-param-reassign, one-var, no-var, one-var-declaration-per-line, no-unused-vars, prefer-template, object-shorthand, comma-dangle, padded-blocks, max-len */
+/* eslint-disable func-names, space-before-function-paren, wrap-iife, quotes, consistent-return, no-undef, no-return-assign, no-param-reassign, one-var, no-var, one-var-declaration-per-line, no-unused-vars, prefer-template, object-shorthand, comma-dangle, padded-blocks, max-len, prefer-arrow-callback */
(function() {
this.CommitsList = (function() {
function CommitsList() {}
@@ -13,7 +13,9 @@
return false;
}
});
- Pager.init(limit, false);
+ Pager.init(limit, false, false, function() {
+ gl.utils.localTimeAgo($('.js-timeago'));
+ });
this.content = $("#commits-list");
this.searchField = $("#commits-search");
return this.initSearch();
diff --git a/app/assets/javascripts/cycle_analytics/cycle_analytics_store.js.es6 b/app/assets/javascripts/cycle_analytics/cycle_analytics_store.js.es6
index 9b905874167..be732971c7f 100644
--- a/app/assets/javascripts/cycle_analytics/cycle_analytics_store.js.es6
+++ b/app/assets/javascripts/cycle_analytics/cycle_analytics_store.js.es6
@@ -62,9 +62,11 @@
this.state.events = this.decorateEvents(events);
},
decorateEvents(events) {
- const newEvents = events;
+ const newEvents = [];
+
+ events.forEach((item) => {
+ if (!item) return;
- newEvents.forEach((item) => {
item.totalTime = item.total_time;
item.author.webUrl = item.author.web_url;
item.author.avatarUrl = item.author.avatar_url;
@@ -79,6 +81,8 @@
delete item.created_at;
delete item.short_sha;
delete item.commit_url;
+
+ newEvents.push(item);
});
return newEvents;
diff --git a/app/assets/javascripts/diff_notes/models/discussion.js.es6 b/app/assets/javascripts/diff_notes/models/discussion.js.es6
index 439f55520ef..badcdccc840 100644
--- a/app/assets/javascripts/diff_notes/models/discussion.js.es6
+++ b/app/assets/javascripts/diff_notes/models/discussion.js.es6
@@ -57,14 +57,17 @@ class DiscussionModel {
}
updateHeadline (data) {
- const $discussionHeadline = $(`.discussion[data-discussion-id="${this.id}"] .js-discussion-headline`);
+ const discussionSelector = `.discussion[data-discussion-id="${this.id}"]`;
+ const $discussionHeadline = $(`${discussionSelector} .js-discussion-headline`);
if (data.discussion_headline_html) {
if ($discussionHeadline.length) {
$discussionHeadline.replaceWith(data.discussion_headline_html);
} else {
- $(`.discussion[data-discussion-id="${this.id}"] .discussion-header`).append(data.discussion_headline_html);
+ $(`${discussionSelector} .discussion-header`).append(data.discussion_headline_html);
}
+
+ gl.utils.localTimeAgo($('.js-timeago', `${discussionSelector}`));
} else {
$discussionHeadline.remove();
}
@@ -74,7 +77,7 @@ class DiscussionModel {
if (!this.canResolve) {
return false;
}
-
+
for (const noteId in this.notes) {
const note = this.notes[noteId];
diff --git a/app/assets/javascripts/gfm_auto_complete.js.es6 b/app/assets/javascripts/gfm_auto_complete.js.es6
index 5d9ac4d350a..89fe13b7a45 100644
--- a/app/assets/javascripts/gfm_auto_complete.js.es6
+++ b/app/assets/javascripts/gfm_auto_complete.js.es6
@@ -53,6 +53,26 @@
} else {
return value;
}
+ },
+ matcher: function (flag, subtext) {
+ // The below is taken from At.js source
+ // Tweaked to commands to start without a space only if char before is a non-word character
+ // https://github.com/ichord/At.js
+ var _a, _y, regexp, match;
+ flag = flag.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&");
+
+ _a = decodeURI("%C3%80");
+ _y = decodeURI("%C3%BF");
+
+ regexp = new RegExp("(?:\\B|\\W|\\s)" + flag + "([A-Za-z" + _a + "-" + _y + "0-9_\'\.\+\-]*)|([^\\x00-\\xff]*)$", 'gi');
+
+ match = regexp.exec(subtext);
+
+ if (match) {
+ return match[2] || match[1];
+ } else {
+ return null;
+ }
}
},
setup: _.debounce(function(input) {
@@ -91,10 +111,12 @@
})(this),
insertTpl: ':${name}:',
data: ['loading'],
+ startWithSpace: false,
callbacks: {
sorter: this.DefaultOptions.sorter,
filter: this.DefaultOptions.filter,
- beforeInsert: this.DefaultOptions.beforeInsert
+ beforeInsert: this.DefaultOptions.beforeInsert,
+ matcher: this.DefaultOptions.matcher
}
});
// Team Members
@@ -112,11 +134,13 @@
insertTpl: '${atwho-at}${username}',
searchKey: 'search',
data: ['loading'],
+ startWithSpace: false,
alwaysHighlightFirst: true,
callbacks: {
sorter: this.DefaultOptions.sorter,
filter: this.DefaultOptions.filter,
beforeInsert: this.DefaultOptions.beforeInsert,
+ matcher: this.DefaultOptions.matcher,
beforeSave: function(members) {
return $.map(members, function(m) {
let title = '';
@@ -157,10 +181,12 @@
})(this),
data: ['loading'],
insertTpl: '${atwho-at}${id}',
+ startWithSpace: false,
callbacks: {
sorter: this.DefaultOptions.sorter,
filter: this.DefaultOptions.filter,
beforeInsert: this.DefaultOptions.beforeInsert,
+ matcher: this.DefaultOptions.matcher,
beforeSave: function(issues) {
return $.map(issues, function(i) {
if (i.title == null) {
@@ -190,7 +216,9 @@
})(this),
insertTpl: '${atwho-at}"${title}"',
data: ['loading'],
+ startWithSpace: false,
callbacks: {
+ matcher: this.DefaultOptions.matcher,
sorter: this.DefaultOptions.sorter,
beforeSave: function(milestones) {
return $.map(milestones, function(m) {
@@ -220,11 +248,13 @@
};
})(this),
data: ['loading'],
+ startWithSpace: false,
insertTpl: '${atwho-at}${id}',
callbacks: {
sorter: this.DefaultOptions.sorter,
filter: this.DefaultOptions.filter,
beforeInsert: this.DefaultOptions.beforeInsert,
+ matcher: this.DefaultOptions.matcher,
beforeSave: function(merges) {
return $.map(merges, function(m) {
if (m.title == null) {
@@ -245,7 +275,9 @@
searchKey: 'search',
displayTpl: this.Labels.template,
insertTpl: '${atwho-at}${title}',
+ startWithSpace: false,
callbacks: {
+ matcher: this.DefaultOptions.matcher,
sorter: this.DefaultOptions.sorter,
beforeSave: function(merges) {
var sanitizeLabelTitle;
diff --git a/app/assets/javascripts/notes.js b/app/assets/javascripts/notes.js
index a84c514dac7..47e7b6f831b 100644
--- a/app/assets/javascripts/notes.js
+++ b/app/assets/javascripts/notes.js
@@ -333,7 +333,7 @@
gl.diffNotesCompileComponents();
}
- gl.utils.localTimeAgo($('.js-timeago', note_html), false);
+ gl.utils.localTimeAgo($('.js-timeago'), false);
return this.updateNotesCount(1);
};
diff --git a/app/assets/stylesheets/pages/boards.scss b/app/assets/stylesheets/pages/boards.scss
index 4f5753f6fc6..4327f8bf640 100644
--- a/app/assets/stylesheets/pages/boards.scss
+++ b/app/assets/stylesheets/pages/boards.scss
@@ -243,7 +243,7 @@
}
.issue-boards-search {
- width: 335px;
+ width: 290px;
.form-control {
display: inline-block;
diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb
index 2713c0f1dc8..bcc0b17bce2 100644
--- a/app/controllers/application_controller.rb
+++ b/app/controllers/application_controller.rb
@@ -49,6 +49,14 @@ class ApplicationController < ActionController::Base
render_404
end
+ def route_not_found
+ if current_user
+ not_found
+ else
+ redirect_to new_user_session_path
+ end
+ end
+
protected
# This filter handles both private tokens and personal access tokens
diff --git a/app/controllers/projects/notes_controller.rb b/app/controllers/projects/notes_controller.rb
index f029fde2a2f..15ca080c696 100644
--- a/app/controllers/projects/notes_controller.rb
+++ b/app/controllers/projects/notes_controller.rb
@@ -197,6 +197,7 @@ class Projects::NotesController < Projects::ApplicationController
)
end
+ attrs[:commands_changes] = note.commands_changes unless attrs[:award]
attrs
end
diff --git a/app/models/ci/pipeline.rb b/app/models/ci/pipeline.rb
index 3fee6c18770..4294a10e9e3 100644
--- a/app/models/ci/pipeline.rb
+++ b/app/models/ci/pipeline.rb
@@ -161,23 +161,27 @@ module Ci
end
def retryable?
- builds.latest.any? do |build|
- (build.failed? || build.canceled?) && build.retryable?
- end
+ builds.latest.failed_or_canceled.any?(&:retryable?)
end
def cancelable?
- builds.running_or_pending.any?
+ statuses.cancelable.any?
end
def cancel_running
- builds.running_or_pending.each(&:cancel)
+ Gitlab::OptimisticLocking.retry_lock(
+ statuses.cancelable) do |cancelable|
+ cancelable.each(&:cancel)
+ end
end
def retry_failed(user)
- builds.latest.failed.select(&:retryable?).each do |build|
- Ci::Build.retry(build, user)
- end
+ Gitlab::OptimisticLocking.retry_lock(
+ builds.latest.failed_or_canceled) do |failed_or_canceled|
+ failed_or_canceled.select(&:retryable?).each do |build|
+ Ci::Build.retry(build, user)
+ end
+ end
end
def mark_as_processable_after_stage(stage_idx)
diff --git a/app/models/concerns/has_status.rb b/app/models/concerns/has_status.rb
index ef3e73a4072..2f5aa91a964 100644
--- a/app/models/concerns/has_status.rb
+++ b/app/models/concerns/has_status.rb
@@ -73,6 +73,11 @@ module HasStatus
scope :skipped, -> { where(status: 'skipped') }
scope :running_or_pending, -> { where(status: [:running, :pending]) }
scope :finished, -> { where(status: [:success, :failed, :canceled]) }
+ scope :failed_or_canceled, -> { where(status: [:failed, :canceled]) }
+
+ scope :cancelable, -> do
+ where(status: [:running, :pending, :created])
+ end
end
def started?
diff --git a/app/models/namespace.rb b/app/models/namespace.rb
index 99da26a89fb..891dffac648 100644
--- a/app/models/namespace.rb
+++ b/app/models/namespace.rb
@@ -104,6 +104,8 @@ class Namespace < ActiveRecord::Base
gitlab_shell.add_namespace(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}"
+
# if we cannot move namespace directory we should rollback
# db changes in order to prevent out of sync between db and fs
raise Exception.new('namespace directory cannot be moved')
diff --git a/app/models/note.rb b/app/models/note.rb
index 9ff5e308ed2..ed4224e3046 100644
--- a/app/models/note.rb
+++ b/app/models/note.rb
@@ -19,6 +19,9 @@ class Note < ActiveRecord::Base
# Banzai::ObjectRenderer
attr_accessor :user_visible_reference_count
+ # Attribute used to store the attributes that have ben changed by slash commands.
+ attr_accessor :commands_changes
+
default_value_for :system, false
attr_mentionable :note, pipeline: :note
diff --git a/app/models/project.rb b/app/models/project.rb
index bd9fcb2f3b7..9256e9ddd95 100644
--- a/app/models/project.rb
+++ b/app/models/project.rb
@@ -177,6 +177,7 @@ class Project < ActiveRecord::Base
message: Gitlab::Regex.project_name_regex_message }
validates :path,
presence: true,
+ project_path: true,
length: { within: 0..255 },
format: { with: Gitlab::Regex.project_path_regex,
message: Gitlab::Regex.project_path_regex_message }
diff --git a/app/models/user.rb b/app/models/user.rb
index 223d84ba916..513a19d81d2 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -291,8 +291,12 @@ class User < ActiveRecord::Base
end
end
+ def find_by_username(username)
+ iwhere(username: username).take
+ end
+
def find_by_username!(username)
- find_by!('lower(username) = ?', username.downcase)
+ iwhere(username: username).take!
end
def find_by_personal_access_token(token_string)
diff --git a/app/services/notes/create_service.rb b/app/services/notes/create_service.rb
index 7935fabe2da..d75592e31f3 100644
--- a/app/services/notes/create_service.rb
+++ b/app/services/notes/create_service.rb
@@ -43,6 +43,8 @@ module Notes
if only_commands
note.errors.add(:commands_only, 'Your commands have been executed!')
end
+
+ note.commands_changes = command_params.keys
end
note
diff --git a/app/services/notification_service.rb b/app/services/notification_service.rb
index ecdcbf08ee1..9a7af5730d2 100644
--- a/app/services/notification_service.rb
+++ b/app/services/notification_service.rb
@@ -171,7 +171,6 @@ class NotificationService
return true unless note.noteable_type.present?
# ignore gitlab service messages
- return true if note.note.start_with?('Status changed to closed')
return true if note.cross_reference? && note.system?
target = note.noteable
diff --git a/app/services/system_note_service.rb b/app/services/system_note_service.rb
index 1ce66d50368..a33845848b4 100644
--- a/app/services/system_note_service.rb
+++ b/app/services/system_note_service.rb
@@ -21,7 +21,7 @@ module SystemNoteService
total_count = new_commits.length + existing_commits.length
commits_text = "#{total_count} commit".pluralize(total_count)
- body = "Added #{commits_text}:\n\n"
+ body = "added #{commits_text}\n\n"
body << existing_commit_summary(noteable, existing_commits, oldrev)
body << new_commit_summary(new_commits).join("\n")
body << "\n\n[Compare with previous version](#{diff_comparison_url(noteable, project, oldrev)})"
@@ -38,13 +38,13 @@ module SystemNoteService
#
# Example Note text:
#
- # "Assignee removed"
+ # "removed assignee"
#
- # "Reassigned to @rspeicher"
+ # "assigned to @rspeicher"
#
# Returns the created Note object
def change_assignee(noteable, project, author, assignee)
- body = assignee.nil? ? 'Assignee removed' : "Reassigned to #{assignee.to_reference}"
+ body = assignee.nil? ? 'removed assignee' : "assigned to #{assignee.to_reference}"
create_note(noteable: noteable, project: project, author: author, note: body)
end
@@ -59,11 +59,11 @@ module SystemNoteService
#
# Example Note text:
#
- # "Added ~1 and removed ~2 ~3 labels"
+ # "added ~1 and removed ~2 ~3 labels"
#
- # "Added ~4 label"
+ # "added ~4 label"
#
- # "Removed ~5 label"
+ # "removed ~5 label"
#
# Returns the created Note object
def change_label(noteable, project, author, added_labels, removed_labels)
@@ -85,7 +85,6 @@ module SystemNoteService
end
body << ' ' << 'label'.pluralize(labels_count)
- body = body.capitalize
create_note(noteable: noteable, project: project, author: author, note: body)
end
@@ -99,14 +98,13 @@ module SystemNoteService
#
# Example Note text:
#
- # "Milestone removed"
+ # "removed milestone"
#
- # "Miletone changed to 7.11"
+ # "changed milestone to 7.11"
#
# Returns the created Note object
def change_milestone(noteable, project, author, milestone)
- body = 'Milestone '
- body += milestone.nil? ? 'removed' : "changed to #{milestone.to_reference(project)}"
+ body = milestone.nil? ? 'removed milestone' : "changed milestone to #{milestone.to_reference(project)}"
create_note(noteable: noteable, project: project, author: author, note: body)
end
@@ -121,46 +119,46 @@ module SystemNoteService
#
# Example Note text:
#
- # "Status changed to merged"
+ # "merged"
#
- # "Status changed to closed by bc17db76"
+ # "closed via bc17db76"
#
# Returns the created Note object
def change_status(noteable, project, author, status, source)
- body = "Status changed to #{status}"
- body << " by #{source.gfm_reference(project)}" if source
+ body = status.dup
+ body << " via #{source.gfm_reference(project)}" if source
create_note(noteable: noteable, project: project, author: author, note: body)
end
# Called when 'merge when build succeeds' is executed
def merge_when_build_succeeds(noteable, project, author, last_commit)
- body = "Enabled an automatic merge when the build for #{last_commit.to_reference(project)} succeeds"
+ body = "enabled an automatic merge when the build for #{last_commit.to_reference(project)} succeeds"
create_note(noteable: noteable, project: project, author: author, note: body)
end
# Called when 'merge when build succeeds' is canceled
def cancel_merge_when_build_succeeds(noteable, project, author)
- body = 'Canceled the automatic merge'
+ body = 'canceled the automatic merge'
create_note(noteable: noteable, project: project, author: author, note: body)
end
def remove_merge_request_wip(noteable, project, author)
- body = 'Unmarked this merge request as a Work In Progress'
+ body = 'unmarked as a Work In Progress'
create_note(noteable: noteable, project: project, author: author, note: body)
end
def add_merge_request_wip(noteable, project, author)
- body = 'Marked this merge request as a **Work In Progress**'
+ body = 'marked as a **Work In Progress**'
create_note(noteable: noteable, project: project, author: author, note: body)
end
def self.resolve_all_discussions(merge_request, project, author)
- body = "Resolved all discussions"
+ body = "resolved all discussions"
create_note(noteable: merge_request, project: project, author: author, note: body)
end
@@ -174,7 +172,7 @@ module SystemNoteService
#
# Example Note text:
#
- # "Title changed from **Old** to **New**"
+ # "changed title from **Old** to **New**"
#
# Returns the created Note object
def change_title(noteable, project, author, old_title)
@@ -185,7 +183,7 @@ module SystemNoteService
marked_old_title = Gitlab::Diff::InlineDiffMarker.new(old_title).mark(old_diffs, mode: :deletion, markdown: true)
marked_new_title = Gitlab::Diff::InlineDiffMarker.new(new_title).mark(new_diffs, mode: :addition, markdown: true)
- body = "Changed title: **#{marked_old_title}** → **#{marked_new_title}**"
+ body = "changed title from **#{marked_old_title}** to **#{marked_new_title}**"
create_note(noteable: noteable, project: project, author: author, note: body)
end
@@ -197,11 +195,11 @@ module SystemNoteService
#
# Example Note text:
#
- # "Made the issue confidential"
+ # "made the issue confidential"
#
# Returns the created Note object
def change_issue_confidentiality(issue, project, author)
- body = issue.confidential ? 'Made the issue confidential' : 'Made the issue visible'
+ body = issue.confidential ? 'made the issue confidential' : 'made the issue visible to everyone'
create_note(noteable: issue, project: project, author: author, note: body)
end
@@ -216,11 +214,11 @@ module SystemNoteService
#
# Example Note text:
#
- # "Target branch changed from `Old` to `New`"
+ # "changed target branch from `Old` to `New`"
#
# Returns the created Note object
def change_branch(noteable, project, author, branch_type, old_branch, new_branch)
- body = "#{branch_type} branch changed from `#{old_branch}` to `#{new_branch}`".capitalize
+ body = "changed #{branch_type} branch from `#{old_branch}` to `#{new_branch}`"
create_note(noteable: noteable, project: project, author: author, note: body)
end
@@ -235,7 +233,7 @@ module SystemNoteService
#
# Example Note text:
#
- # "Restored target branch `feature`"
+ # "restored target branch `feature`"
#
# Returns the created Note object
def change_branch_presence(noteable, project, author, branch_type, branch, presence)
@@ -246,18 +244,18 @@ module SystemNoteService
'deleted'
end
- body = "#{verb} #{branch_type} branch `#{branch}`".capitalize
+ body = "#{verb} #{branch_type} branch `#{branch}`"
create_note(noteable: noteable, project: project, author: author, note: body)
end
# Called when a branch is created from the 'new branch' button on a issue
# Example note text:
#
- # "Started branch `201-issue-branch-button`"
+ # "created branch `201-issue-branch-button`"
def new_issue_branch(issue, project, author, branch)
link = url_helpers.namespace_project_compare_url(project.namespace, project, from: project.default_branch, to: branch)
- body = "Started branch [`#{branch}`](#{link})"
+ body = "created branch [`#{branch}`](#{link})"
create_note(noteable: issue, project: project, author: author, note: body)
end
@@ -269,11 +267,11 @@ module SystemNoteService
#
# Example Note text:
#
- # "Mentioned in #1"
+ # "mentioned in #1"
#
- # "Mentioned in !2"
+ # "mentioned in !2"
#
- # "Mentioned in 54f7727c"
+ # "mentioned in 54f7727c"
#
# See cross_reference_note_content.
#
@@ -303,12 +301,12 @@ module SystemNoteService
end
def cross_reference?(note_text)
- note_text.start_with?(cross_reference_note_prefix)
+ note_text =~ /\A#{cross_reference_note_prefix}/i
end
# Check if a cross-reference is disallowed
#
- # This method prevents adding a "Mentioned in !1" note on every single commit
+ # This method prevents adding a "mentioned in !1" note on every single commit
# in a merge request. Additionally, it prevents the creation of references to
# external issues (which would fail).
#
@@ -370,12 +368,12 @@ module SystemNoteService
#
# Example Note text:
#
- # "Soandso marked the task Whatever as completed."
+ # "marked the task Whatever as completed."
#
# Returns the created Note object
def change_task_status(noteable, project, author, new_task)
status_label = new_task.complete? ? Taskable::COMPLETED : Taskable::INCOMPLETE
- body = "Marked the task **#{new_task.source}** as #{status_label}"
+ body = "marked the task **#{new_task.source}** as #{status_label}"
create_note(noteable: noteable, project: project, author: author, note: body)
end
@@ -388,7 +386,7 @@ module SystemNoteService
#
# Example Note text:
#
- # "Moved to some_namespace/project_new#11"
+ # "moved to some_namespace/project_new#11"
#
# Returns the created Note object
def noteable_moved(noteable, project, noteable_ref, author, direction:)
@@ -397,7 +395,7 @@ module SystemNoteService
end
cross_reference = noteable_ref.to_reference(project)
- body = "Moved #{direction} #{cross_reference}"
+ body = "moved #{direction} #{cross_reference}"
create_note(noteable: noteable, project: project, author: author, note: body)
end
@@ -405,10 +403,12 @@ module SystemNoteService
def notes_for_mentioner(mentioner, noteable, notes)
if mentioner.is_a?(Commit)
- notes.where('note LIKE ?', "#{cross_reference_note_prefix}%#{mentioner.to_reference(nil)}")
+ text = "#{cross_reference_note_prefix}%#{mentioner.to_reference(nil)}"
+ notes.where('(note LIKE ? OR note LIKE ?)', text, text.capitalize)
else
gfm_reference = mentioner.gfm_reference(noteable.project)
- notes.where(note: cross_reference_note_content(gfm_reference))
+ text = cross_reference_note_content(gfm_reference)
+ notes.where(note: [text, text.capitalize])
end
end
@@ -417,7 +417,7 @@ module SystemNoteService
end
def cross_reference_note_prefix
- 'Mentioned in '
+ 'mentioned in '
end
def cross_reference_note_content(gfm_reference)
diff --git a/app/validators/namespace_validator.rb b/app/validators/namespace_validator.rb
index 2821ecf0a88..eb3ed31b65b 100644
--- a/app/validators/namespace_validator.rb
+++ b/app/validators/namespace_validator.rb
@@ -35,8 +35,22 @@ class NamespaceValidator < ActiveModel::EachValidator
users
].freeze
+ def self.valid?(value)
+ !reserved?(value) && follow_format?(value)
+ end
+
+ def self.reserved?(value)
+ RESERVED.include?(value)
+ end
+
+ def self.follow_format?(value)
+ value =~ Gitlab::Regex.namespace_regex
+ end
+
+ delegate :reserved?, :follow_format?, to: :class
+
def validate_each(record, attribute, value)
- unless value =~ Gitlab::Regex.namespace_regex
+ unless follow_format?(value)
record.errors.add(attribute, Gitlab::Regex.namespace_regex_message)
end
@@ -44,10 +58,4 @@ class NamespaceValidator < ActiveModel::EachValidator
record.errors.add(attribute, "#{value} is a reserved name")
end
end
-
- private
-
- def reserved?(value)
- RESERVED.include?(value)
- end
end
diff --git a/app/validators/project_path_validator.rb b/app/validators/project_path_validator.rb
new file mode 100644
index 00000000000..927c67b65b0
--- /dev/null
+++ b/app/validators/project_path_validator.rb
@@ -0,0 +1,36 @@
+# ProjectPathValidator
+#
+# Custom validator for GitLab project path values.
+#
+# Values are checked for formatting and exclusion from a list of reserved path
+# names.
+class ProjectPathValidator < ActiveModel::EachValidator
+ # All project routes with wildcard argument must be listed here.
+ # Otherwise it can lead to routing issues when route considered as project name.
+ #
+ # Example:
+ # /group/project/tree/deploy_keys
+ #
+ # without tree as reserved name routing can match 'group/project' as group name,
+ # 'tree' as project name and 'deploy_keys' as route.
+ #
+ RESERVED = (NamespaceValidator::RESERVED +
+ %w[tree commits wikis new edit create update logs_tree
+ preview blob blame raw files create_dir find_file]).freeze
+
+ def self.valid?(value)
+ !reserved?(value)
+ end
+
+ def self.reserved?(value)
+ RESERVED.include?(value)
+ end
+
+ delegate :reserved?, to: :class
+
+ def validate_each(record, attribute, value)
+ if reserved?(value)
+ record.errors.add(attribute, "#{value} is a reserved name")
+ end
+ end
+end
diff --git a/app/views/discussions/_discussion.html.haml b/app/views/discussions/_discussion.html.haml
index e4b4ea675d2..2bce2780484 100644
--- a/app/views/discussions/_discussion.html.haml
+++ b/app/views/discussions/_discussion.html.haml
@@ -32,6 +32,7 @@
an outdated diff
= time_ago_with_tooltip(discussion.created_at, placement: "bottom", html_class: "note-created-ago")
+ = render "discussions/headline", discussion: discussion
.discussion-body.js-toggle-content{ class: ("hide" unless expanded) }
- if discussion.diff_discussion? && discussion.diff_file
diff --git a/app/views/profiles/update_username.js.haml b/app/views/profiles/update_username.js.haml
index de1337a2a24..5307e0b48cb 100644
--- a/app/views/profiles/update_username.js.haml
+++ b/app/views/profiles/update_username.js.haml
@@ -2,5 +2,6 @@
:plain
new Flash("Username successfully changed", "notice")
- else
+ - error = @user.errors.full_messages.first
:plain
- new Flash("Username change failed - #{@user.errors.full_messages.first}", "alert")
+ new Flash("Username change failed - #{escape_javascript error.html_safe}", "alert")
diff --git a/app/views/projects/boards/components/_card.html.haml b/app/views/projects/boards/components/_card.html.haml
index 34effac17b2..1f31496e73f 100644
--- a/app/views/projects/boards/components/_card.html.haml
+++ b/app/views/projects/boards/components/_card.html.haml
@@ -1,5 +1,6 @@
%li.card{ ":class" => '{ "user-can-drag": !disabled && issue.id, "is-disabled": disabled || !issue.id, "is-active": issueDetailVisible }',
":index" => "index",
+ ":data-issue-id" => "issue.id",
"@mousedown" => "mouseDown",
"@mousemove" => "mouseMove",
"@mouseup" => "showIssue($event)" }
diff --git a/app/views/projects/builds/show.html.haml b/app/views/projects/builds/show.html.haml
index d8cbfd7173a..108674dbba6 100644
--- a/app/views/projects/builds/show.html.haml
+++ b/app/views/projects/builds/show.html.haml
@@ -40,13 +40,12 @@
This build is the most recent deployment to #{environment_link_for_build(@build.project, @build)}.
- else
This build is an out-of-date deployment to #{environment_link_for_build(@build.project, @build)}.
- - if environment.last_deployment
- View the most recent deployment #{deployment_link(environment.last_deployment)}.
+ View the most recent deployment #{deployment_link(environment.last_deployment)}.
- elsif @build.complete? && !@build.success?
The deployment of this build to #{environment_link_for_build(@build.project, @build)} did not succeed.
- else
This build is creating a deployment to #{environment_link_for_build(@build.project, @build)}
- - if environment.last_deployment
+ - if environment.try(:last_deployment)
and will overwrite the
= link_to 'latest deployment', deployment_link(environment.last_deployment)
diff --git a/app/views/projects/edit.html.haml b/app/views/projects/edit.html.haml
index 0aa8801c2d8..3a5af2723c6 100644
--- a/app/views/projects/edit.html.haml
+++ b/app/views/projects/edit.html.haml
@@ -92,14 +92,15 @@
= project_feature_access_select(:wiki_access_level)
- if Gitlab.config.lfs.enabled && current_user.admin?
- .checkbox
- = f.label :lfs_enabled do
- = f.check_box :lfs_enabled
- %strong LFS
- %br
- %span.descr
+ .row
+ .col-md-9
+ = f.label :lfs_enabled, 'LFS', class: 'label-light'
+ %span.help-block
Git Large File Storage
= link_to icon('question-circle'), help_page_path('workflow/lfs/manage_large_binaries_with_git_lfs')
+ .col-md-3
+ = f.select :lfs_enabled, [%w(Enabled true), %w(Disabled false)], {}, selected: @project.lfs_enabled?, class: 'pull-right form-control', data: { field: 'lfs_enabled' }
+
- if Gitlab.config.registry.enabled
.form-group.js-container-registry{ style: ("display: none;" if @project.project_feature.send(:repository_access_level) == 0) }
diff --git a/app/views/projects/tags/index.html.haml b/app/views/projects/tags/index.html.haml
index 7a0d9dcc94f..b43b13de4ca 100644
--- a/app/views/projects/tags/index.html.haml
+++ b/app/views/projects/tags/index.html.haml
@@ -10,15 +10,16 @@
.nav-controls
= form_tag(filter_tags_path, method: :get) do
= search_field_tag :search, params[:search], { placeholder: 'Filter by tag name', id: 'tag-search', class: 'form-control search-text-input input-short', spellcheck: false }
+
.dropdown.inline
%button.dropdown-toggle.btn{ type: 'button', data: { toggle: 'dropdown'} }
%span.light
- = @sort.humanize
+ = projects_sort_options_hash[@sort]
= icon('caret-down')
%ul.dropdown-menu.dropdown-menu-align-right
%li
- = link_to filter_tags_path(sort: nil) do
- Name
+ = link_to filter_tags_path(sort: sort_value_name) do
+ = sort_title_name
= link_to filter_tags_path(sort: sort_value_recently_updated) do
= sort_title_recently_updated
= link_to filter_tags_path(sort: sort_value_oldest_updated) do
diff --git a/app/views/projects/wikis/_nav.html.haml b/app/views/projects/wikis/_nav.html.haml
index 09c4411d67e..afdef70e1cf 100644
--- a/app/views/projects/wikis/_nav.html.haml
+++ b/app/views/projects/wikis/_nav.html.haml
@@ -7,7 +7,7 @@
= link_to 'Home', namespace_project_wiki_path(@project.namespace, @project, :home)
= nav_link(path: 'wikis#pages') do
- = link_to 'Pages', namespace_project_wiki_pages_path(@project.namespace, @project)
+ = link_to 'Pages', namespace_project_wikis_pages_path(@project.namespace, @project)
= nav_link(path: 'wikis#git_access') do
= link_to namespace_project_wikis_git_access_path(@project.namespace, @project) do
diff --git a/app/views/shared/issuable/_filter.html.haml b/app/views/shared/issuable/_filter.html.haml
index ed93857e6d4..b7e5e928993 100644
--- a/app/views/shared/issuable/_filter.html.haml
+++ b/app/views/shared/issuable/_filter.html.haml
@@ -40,9 +40,9 @@
- if can?(current_user, :admin_list, @project)
.dropdown.pull-right
%button.btn.btn-create.js-new-board-list{ type: "button", data: { toggle: "dropdown", labels: labels_filter_path, namespace_path: @project.try(:namespace).try(:path), project_path: @project.try(:path) } }
- Create new list
+ Add list
.dropdown-menu.dropdown-menu-paging.dropdown-menu-align-right.dropdown-menu-issues-board-new.dropdown-menu-selectable
- = render partial: "shared/issuable/label_page_default", locals: { show_footer: true, show_create: true, show_boards_content: true, title: "Create a new list" }
+ = render partial: "shared/issuable/label_page_default", locals: { show_footer: true, show_create: true, show_boards_content: true, title: "Add list" }
- if can?(current_user, :admin_label, @project)
= render partial: "shared/issuable/label_page_create"
= dropdown_loading
diff --git a/app/views/shared/issuable/_label_page_default.html.haml b/app/views/shared/issuable/_label_page_default.html.haml
index c0dc63be2bf..a8f01026ca5 100644
--- a/app/views/shared/issuable/_label_page_default.html.haml
+++ b/app/views/shared/issuable/_label_page_default.html.haml
@@ -1,17 +1,15 @@
- title = local_assigns.fetch(:title, 'Assign labels')
- show_create = local_assigns.fetch(:show_create, true)
- show_footer = local_assigns.fetch(:show_footer, true)
-- filter_placeholder = local_assigns.fetch(:filter_placeholder, 'Search labels')
+- filter_placeholder = local_assigns.fetch(:filter_placeholder, 'Search')
- show_boards_content = local_assigns.fetch(:show_boards_content, false)
.dropdown-page-one
= dropdown_title(title)
- if show_boards_content
.issue-board-dropdown-content
%p
- Each label that exists in your issue tracker can have its own dedicated
- list. Select a label below to add a list to your Board and it will
- automatically be populated with issues that have that label. To create
- a list for a label that doesn't exist yet, simply create the label below.
+ Create lists from the labels you use in your project. Issues with that
+ label will automatically be added to the list.
= dropdown_filter(filter_placeholder)
= dropdown_content
- if @project && show_footer
diff --git a/app/views/shared/issuable/_sidebar.html.haml b/app/views/shared/issuable/_sidebar.html.haml
index f166fac105d..02427650219 100644
--- a/app/views/shared/issuable/_sidebar.html.haml
+++ b/app/views/shared/issuable/_sidebar.html.haml
@@ -9,12 +9,12 @@
%a.gutter-toggle.pull-right.js-sidebar-toggle{ role: "button", href: "#", aria: { label: "Toggle sidebar" } }
= sidebar_gutter_toggle_icon
- if current_user
- %button.btn.btn-default.issuable-header-btn.pull-right.js-issuable-todo{ type: "button", aria: { label: (todo.nil? ? "Add Todo" : "Mark Done") }, data: { todo_text: "Add Todo", mark_text: "Mark Done", issuable_id: issuable.id, issuable_type: issuable.class.name.underscore, url: namespace_project_todos_path(@project.namespace, @project), delete_path: (dashboard_todo_path(todo) if todo) } }
+ %button.btn.btn-default.issuable-header-btn.pull-right.js-issuable-todo{ type: "button", aria: { label: (todo.nil? ? "Add todo" : "Mark done") }, data: { todo_text: "Add todo", mark_text: "Mark done", issuable_id: issuable.id, issuable_type: issuable.class.name.underscore, url: namespace_project_todos_path(@project.namespace, @project), delete_path: (dashboard_todo_path(todo) if todo) } }
%span.js-issuable-todo-text
- if todo
- Mark Done
+ Mark done
- else
- Add Todo
+ Add todo
= icon('spin spinner', class: 'hidden js-issuable-todo-loading')
= form_for [@project.namespace.becomes(Namespace), @project, issuable], remote: true, format: :json, html: {class: 'issuable-context-form inline-update js-issuable-update'} do |f|
diff --git a/changelogs/unreleased/24779-last-deployment-call-on-nil-environment-fix.yml b/changelogs/unreleased/24779-last-deployment-call-on-nil-environment-fix.yml
new file mode 100644
index 00000000000..5e7580fb8f2
--- /dev/null
+++ b/changelogs/unreleased/24779-last-deployment-call-on-nil-environment-fix.yml
@@ -0,0 +1,4 @@
+---
+title: fixes last_deployment call environment is nil
+merge_request: 7671
+author:
diff --git a/changelogs/unreleased/boards-issue-sorting.yml b/changelogs/unreleased/boards-issue-sorting.yml
new file mode 100644
index 00000000000..fb7dc2f9190
--- /dev/null
+++ b/changelogs/unreleased/boards-issue-sorting.yml
@@ -0,0 +1,4 @@
+---
+title: Fixed issue boards issue sorting when dragging issue into list
+merge_request:
+author:
diff --git a/changelogs/unreleased/dz-allow-nested-group-routing.yml b/changelogs/unreleased/dz-allow-nested-group-routing.yml
new file mode 100644
index 00000000000..9d8e6e17914
--- /dev/null
+++ b/changelogs/unreleased/dz-allow-nested-group-routing.yml
@@ -0,0 +1,4 @@
+---
+title: Add nested groups support to the routing
+merge_request: 7459
+author:
diff --git a/changelogs/unreleased/fix-cancelling-pipelines.yml b/changelogs/unreleased/fix-cancelling-pipelines.yml
new file mode 100644
index 00000000000..c21e663093a
--- /dev/null
+++ b/changelogs/unreleased/fix-cancelling-pipelines.yml
@@ -0,0 +1,4 @@
+---
+title: Fix cancelling created or external pipelines
+merge_request: 7508
+author:
diff --git a/changelogs/unreleased/fixed-commit-timeago.yml b/changelogs/unreleased/fixed-commit-timeago.yml
new file mode 100644
index 00000000000..295d8db63d0
--- /dev/null
+++ b/changelogs/unreleased/fixed-commit-timeago.yml
@@ -0,0 +1,4 @@
+---
+title: Fixed commit timeago not rendering after initial page
+merge_request:
+author:
diff --git a/changelogs/unreleased/issue-boards-scrollable-element.yml b/changelogs/unreleased/issue-boards-scrollable-element.yml
new file mode 100644
index 00000000000..90edc30e791
--- /dev/null
+++ b/changelogs/unreleased/issue-boards-scrollable-element.yml
@@ -0,0 +1,4 @@
+---
+title: Fixed issue boards scrolling with a lot of lists & issues
+merge_request:
+author:
diff --git a/changelogs/unreleased/issue_24748.yml b/changelogs/unreleased/issue_24748.yml
new file mode 100644
index 00000000000..4c1df542c53
--- /dev/null
+++ b/changelogs/unreleased/issue_24748.yml
@@ -0,0 +1,4 @@
+---
+title: Fix title case to sentence case
+merge_request:
+author: Luis Alonso Chavez Armendariz
diff --git a/changelogs/unreleased/issue_24958.yml b/changelogs/unreleased/issue_24958.yml
new file mode 100644
index 00000000000..dbbbbf9d28d
--- /dev/null
+++ b/changelogs/unreleased/issue_24958.yml
@@ -0,0 +1,4 @@
+---
+title: Fix bad selection on dropdown menu for tags filter
+merge_request:
+author: Luis Alonso Chavez Armendariz
diff --git a/changelogs/unreleased/move-abuse-report-spinach-test-to-rspec.yml b/changelogs/unreleased/move-abuse-report-spinach-test-to-rspec.yml
new file mode 100644
index 00000000000..9de7477c200
--- /dev/null
+++ b/changelogs/unreleased/move-abuse-report-spinach-test-to-rspec.yml
@@ -0,0 +1,4 @@
+---
+title: Move abuse report spinach test to rspec
+merge_request: 7659
+author: Semyon Pupkov
diff --git a/changelogs/unreleased/move-admin-abuse-report-spinach-test-to-rspec.yml b/changelogs/unreleased/move-admin-abuse-report-spinach-test-to-rspec.yml
new file mode 100644
index 00000000000..fb70fa2955a
--- /dev/null
+++ b/changelogs/unreleased/move-admin-abuse-report-spinach-test-to-rspec.yml
@@ -0,0 +1,4 @@
+---
+title: Move admin abuse report spinach test to rspec
+merge_request: 7691
+author: Semyon Pupkov
diff --git a/changelogs/unreleased/move-admin-spam-spinach-test-to-rspec.yml b/changelogs/unreleased/move-admin-spam-spinach-test-to-rspec.yml
new file mode 100644
index 00000000000..a7ec2c20554
--- /dev/null
+++ b/changelogs/unreleased/move-admin-spam-spinach-test-to-rspec.yml
@@ -0,0 +1,4 @@
+---
+title: Move admin spam spinach test to Rspec
+merge_request: 7708
+author: Semyon Pupkov
diff --git a/changelogs/unreleased/remove-backup-strategies.yml b/changelogs/unreleased/remove-backup-strategies.yml
new file mode 100644
index 00000000000..9f034613c2c
--- /dev/null
+++ b/changelogs/unreleased/remove-backup-strategies.yml
@@ -0,0 +1,4 @@
+---
+title: Stop supporting Google and Azure as backup strategies
+merge_request:
+author:
diff --git a/changelogs/unreleased/rephrase-system-notes.yml b/changelogs/unreleased/rephrase-system-notes.yml
new file mode 100644
index 00000000000..e77c3a31cb4
--- /dev/null
+++ b/changelogs/unreleased/rephrase-system-notes.yml
@@ -0,0 +1,4 @@
+---
+title: Rephrase some system notes to be compatible with new system note style
+merge_request: 7692
+author:
diff --git a/changelogs/unreleased/resolve-discussions-timeago.yml b/changelogs/unreleased/resolve-discussions-timeago.yml
new file mode 100644
index 00000000000..ffedeb93f1d
--- /dev/null
+++ b/changelogs/unreleased/resolve-discussions-timeago.yml
@@ -0,0 +1,4 @@
+---
+title: Fixed timeago not rendering when resolving a discussion
+merge_request:
+author:
diff --git a/changelogs/unreleased/simplify-create-new-list-issue-boards.yml b/changelogs/unreleased/simplify-create-new-list-issue-boards.yml
new file mode 100644
index 00000000000..ca11e3b94a7
--- /dev/null
+++ b/changelogs/unreleased/simplify-create-new-list-issue-boards.yml
@@ -0,0 +1,4 @@
+---
+title: Simplify copy on "Create a new list" dropdown in Issue Boards
+merge_request: 7605
+author: Victor Rodrigues
diff --git a/changelogs/unreleased/zj-upgrade-grape.yml b/changelogs/unreleased/zj-upgrade-grape.yml
new file mode 100644
index 00000000000..1df42d98733
--- /dev/null
+++ b/changelogs/unreleased/zj-upgrade-grape.yml
@@ -0,0 +1,4 @@
+---
+title: Update grape entity to 0.6.0
+merge_request: 7491
+author:
diff --git a/config/routes.rb b/config/routes.rb
index 7bf6c03e69b..03b47261e7e 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -1,6 +1,7 @@
require 'sidekiq/web'
require 'sidekiq/cron/web'
require 'api/api'
+require 'constraints/group_url_constrainer'
Rails.application.routes.draw do
concern :access_requestable do
@@ -78,10 +79,21 @@ Rails.application.routes.draw do
draw :user
draw :project
- # Get all keys of user
- get ':username.keys' => 'profiles/keys#get_keys', constraints: { username: /.*/ }
-
root to: "root#index"
- get '*unmatched_route', to: 'application#not_found'
+ # Since group show page is wildcard routing
+ # we want all other routing to be checked before matching this one
+ constraints(GroupUrlConstrainer.new) do
+ scope(path: '*id',
+ as: :group,
+ constraints: { id: Gitlab::Regex.namespace_route_regex, format: /(html|json|atom)/ },
+ controller: :groups) do
+ get '/', action: :show
+ patch '/', action: :update
+ put '/', action: :update
+ delete '/', action: :destroy
+ end
+ end
+
+ get '*unmatched_route', to: 'application#route_not_found'
end
diff --git a/config/routes/git_http.rb b/config/routes/git_http.rb
index 03adc4815f3..42d874eeebc 100644
--- a/config/routes/git_http.rb
+++ b/config/routes/git_http.rb
@@ -1,37 +1,47 @@
-scope constraints: { id: /.+\.git/, format: nil } do
- # Git HTTP clients ('git clone' etc.)
- get '/info/refs', to: 'git_http#info_refs'
- post '/git-upload-pack', to: 'git_http#git_upload_pack'
- post '/git-receive-pack', to: 'git_http#git_receive_pack'
+scope(path: '*namespace_id/:project_id', constraints: { format: nil }) do
+ scope(constraints: { project_id: Gitlab::Regex.project_git_route_regex }, module: :projects) do
+ # Git HTTP clients ('git clone' etc.)
+ scope(controller: :git_http) do
+ get '/info/refs', action: :info_refs
+ post '/git-upload-pack', action: :git_upload_pack
+ post '/git-receive-pack', action: :git_receive_pack
+ end
- # Git LFS API (metadata)
- post '/info/lfs/objects/batch', to: 'lfs_api#batch'
- post '/info/lfs/objects', to: 'lfs_api#deprecated'
- get '/info/lfs/objects/*oid', to: 'lfs_api#deprecated'
+ # Git LFS API (metadata)
+ scope(path: 'info/lfs/objects', controller: :lfs_api) do
+ post :batch
+ post '/', action: :deprecated
+ get '/*oid', action: :deprecated
+ end
- # GitLab LFS object storage
- scope constraints: { oid: /[a-f0-9]{64}/ } do
- get '/gitlab-lfs/objects/*oid', to: 'lfs_storage#download'
+ # GitLab LFS object storage
+ scope(path: 'gitlab-lfs/objects/*oid', controller: :lfs_storage, constraints: { oid: /[a-f0-9]{64}/ }) do
+ get '/', action: :download
- scope constraints: { size: /[0-9]+/ } do
- put '/gitlab-lfs/objects/*oid/*size/authorize', to: 'lfs_storage#upload_authorize'
- put '/gitlab-lfs/objects/*oid/*size', to: 'lfs_storage#upload_finalize'
+ scope constraints: { size: /[0-9]+/ } do
+ put '/*size/authorize', action: :upload_authorize
+ put '/*size', action: :upload_finalize
+ end
end
end
-end
-# Allow /info/refs, /info/refs?service=git-upload-pack, and
-# /info/refs?service=git-receive-pack, but nothing else.
-#
-git_http_handshake = lambda do |request|
- request.query_string.blank? ||
- request.query_string.match(/\Aservice=git-(upload|receive)-pack\z/)
-end
+ # Redirect /group/project/info/refs to /group/project.git/info/refs
+ scope(constraints: { project_id: Gitlab::Regex.project_route_regex }) do
+ # Allow /info/refs, /info/refs?service=git-upload-pack, and
+ # /info/refs?service=git-receive-pack, but nothing else.
+ #
+ git_http_handshake = lambda do |request|
+ ProjectUrlConstrainer.new.matches?(request) &&
+ (request.query_string.blank? ||
+ request.query_string.match(/\Aservice=git-(upload|receive)-pack\z/))
+ end
-ref_redirect = redirect do |params, request|
- path = "#{params[:namespace_id]}/#{params[:project_id]}.git/info/refs"
- path << "?#{request.query_string}" unless request.query_string.blank?
- path
-end
+ ref_redirect = redirect do |params, request|
+ path = "#{params[:namespace_id]}/#{params[:project_id]}.git/info/refs"
+ path << "?#{request.query_string}" unless request.query_string.blank?
+ path
+ end
-get '/info/refs', constraints: git_http_handshake, to: ref_redirect
+ get '/info/refs', constraints: git_http_handshake, to: ref_redirect
+ end
+end
diff --git a/config/routes/group.rb b/config/routes/group.rb
index a3a001178b4..9fe72990994 100644
--- a/config/routes/group.rb
+++ b/config/routes/group.rb
@@ -1,20 +1,6 @@
-require 'constraints/group_url_constrainer'
-
-constraints(GroupUrlConstrainer.new) do
- scope(path: ':id',
- as: :group,
- constraints: { id: Gitlab::Regex.namespace_route_regex },
- controller: :groups) do
- get '/', action: :show
- patch '/', action: :update
- put '/', action: :update
- delete '/', action: :destroy
- end
-end
-
resources :groups, only: [:index, :new, :create]
-scope(path: 'groups/:id',
+scope(path: 'groups/*id',
controller: :groups,
constraints: { id: Gitlab::Regex.namespace_route_regex }) do
get :edit, as: :edit_group
@@ -24,7 +10,7 @@ scope(path: 'groups/:id',
get :activity, as: :activity_group
end
-scope(path: 'groups/:group_id',
+scope(path: 'groups/*group_id',
module: :groups,
as: :group,
constraints: { group_id: Gitlab::Regex.namespace_route_regex }) do
@@ -42,4 +28,4 @@ scope(path: 'groups/:group_id',
end
# Must be last route in this file
-get 'groups/:id' => 'groups#show', as: :group_canonical, constraints: { id: Gitlab::Regex.namespace_route_regex }
+get 'groups/*id' => 'groups#show', as: :group_canonical, constraints: { id: Gitlab::Regex.namespace_route_regex }
diff --git a/config/routes/project.rb b/config/routes/project.rb
index d6eae1c9fce..1336484a399 100644
--- a/config/routes/project.rb
+++ b/config/routes/project.rb
@@ -1,28 +1,15 @@
-resources :projects, constraints: { id: /[^\/]+/ }, only: [:index, :new, :create]
-
-resources :namespaces, path: '/', constraints: { id: /[a-zA-Z.0-9_\-]+/ }, only: [] do
- resources(:projects, constraints: { id: /[a-zA-Z.0-9_\-]+(?<!\.atom)/ }, except:
- [:new, :create, :index], path: "/") do
- member do
- put :transfer
- delete :remove_fork
- post :archive
- post :unarchive
- post :housekeeping
- post :toggle_star
- post :preview_markdown
- post :export
- post :remove_export
- post :generate_new_export
- get :download_export
- get :autocomplete_sources
- get :activity
- get :refs
- put :new_issue_address
- end
+require 'constraints/project_url_constrainer'
+
+resources :projects, only: [:index, :new, :create]
+
+draw :git_http
- scope module: :projects do
- draw :git_http
+constraints(ProjectUrlConstrainer.new) do
+ scope(path: '*namespace_id', as: :namespace) do
+ scope(path: ':project_id',
+ constraints: { project_id: Gitlab::Regex.project_route_regex },
+ module: :projects,
+ as: :project) do
#
# Templates
@@ -311,5 +298,28 @@ resources :namespaces, path: '/', constraints: { id: /[a-zA-Z.0-9_\-]+/ }, only:
draw :wiki
draw :repository
end
+
+ resources(:projects,
+ path: '/',
+ constraints: { id: Gitlab::Regex.project_route_regex },
+ only: [:edit, :show, :update, :destroy]) do
+ member do
+ put :transfer
+ delete :remove_fork
+ post :archive
+ post :unarchive
+ post :housekeeping
+ post :toggle_star
+ post :preview_markdown
+ post :export
+ post :remove_export
+ post :generate_new_export
+ get :download_export
+ get :autocomplete_sources
+ get :activity
+ get :refs
+ put :new_issue_address
+ end
+ end
end
end
diff --git a/config/routes/repository.rb b/config/routes/repository.rb
index 76dcf113aea..f8966c5ae75 100644
--- a/config/routes/repository.rb
+++ b/config/routes/repository.rb
@@ -29,82 +29,60 @@ get '/edit/*id', to: 'blob#edit', constraints: { id: /.+/ }, as: 'edit_blob'
put '/update/*id', to: 'blob#update', constraints: { id: /.+/ }, as: 'update_blob'
post '/preview/*id', to: 'blob#preview', constraints: { id: /.+/ }, as: 'preview_blob'
-scope do
- get(
- '/blob/*id/diff',
- to: 'blob#diff',
- constraints: { id: /.+/, format: false },
- as: :blob_diff
- )
- get(
- '/blob/*id',
- to: 'blob#show',
- constraints: { id: /.+/, format: false },
- as: :blob
- )
- delete(
- '/blob/*id',
- to: 'blob#destroy',
- constraints: { id: /.+/, format: false }
- )
- put(
- '/blob/*id',
- to: 'blob#update',
- constraints: { id: /.+/, format: false }
- )
- post(
- '/blob/*id',
- to: 'blob#create',
- constraints: { id: /.+/, format: false }
- )
+scope('/blob/*id', as: :blob, controller: :blob, constraints: { id: /.+/, format: false }) do
+ get :diff
+ get '/', action: :show
+ delete '/', action: :destroy
+ post '/', action: :create
+ put '/', action: :update
+end
- get(
- '/raw/*id',
- to: 'raw#show',
- constraints: { id: /.+/, format: /(html|js)/ },
- as: :raw
- )
+get(
+ '/raw/*id',
+ to: 'raw#show',
+ constraints: { id: /.+/, format: /(html|js)/ },
+ as: :raw
+)
- get(
- '/tree/*id',
- to: 'tree#show',
- constraints: { id: /.+/, format: /(html|js)/ },
- as: :tree
- )
+get(
+ '/tree/*id',
+ to: 'tree#show',
+ constraints: { id: /.+/, format: /(html|js)/ },
+ as: :tree
+)
- get(
- '/find_file/*id',
- to: 'find_file#show',
- constraints: { id: /.+/, format: /html/ },
- as: :find_file
- )
+get(
+ '/find_file/*id',
+ to: 'find_file#show',
+ constraints: { id: /.+/, format: /html/ },
+ as: :find_file
+)
- get(
- '/files/*id',
- to: 'find_file#list',
- constraints: { id: /(?:[^.]|\.(?!json$))+/, format: /json/ },
- as: :files
- )
+get(
+ '/files/*id',
+ to: 'find_file#list',
+ constraints: { id: /(?:[^.]|\.(?!json$))+/, format: /json/ },
+ as: :files
+)
- post(
- '/create_dir/*id',
- to: 'tree#create_dir',
- constraints: { id: /.+/ },
- as: 'create_dir'
- )
+post(
+ '/create_dir/*id',
+ to: 'tree#create_dir',
+ constraints: { id: /.+/ },
+ as: 'create_dir'
+)
- get(
- '/blame/*id',
- to: 'blame#show',
- constraints: { id: /.+/, format: /(html|js)/ },
- as: :blame
- )
+get(
+ '/blame/*id',
+ to: 'blame#show',
+ constraints: { id: /.+/, format: /(html|js)/ },
+ as: :blame
+)
- # File/dir history
- get(
- '/commits/*id',
- to: 'commits#show',
- constraints: { id: /.+/, format: false },
- as: :commits
- )
-end
+# File/dir history
+get(
+ '/commits/*id',
+ to: 'commits#show',
+ constraints: { id: /.+/, format: false },
+ as: :commits
+)
diff --git a/config/routes/user.rb b/config/routes/user.rb
index dc1068af6f6..b064a15e802 100644
--- a/config/routes/user.rb
+++ b/config/routes/user.rb
@@ -12,6 +12,9 @@ devise_scope :user do
end
constraints(UserUrlConstrainer.new) do
+ # Get all keys of user
+ get ':username.keys' => 'profiles/keys#get_keys', constraints: { username: Gitlab::Regex.namespace_route_regex }
+
scope(path: ':username',
as: :user,
constraints: { username: Gitlab::Regex.namespace_route_regex },
diff --git a/config/routes/wiki.rb b/config/routes/wiki.rb
index ecd4d395d66..dad746d59a1 100644
--- a/config/routes/wiki.rb
+++ b/config/routes/wiki.rb
@@ -1,16 +1,19 @@
WIKI_SLUG_ID = { id: /\S+/ } unless defined? WIKI_SLUG_ID
-scope do
- # Order matters to give priority to these matches
- get '/wikis/git_access', to: 'wikis#git_access'
- get '/wikis/pages', to: 'wikis#pages', as: 'wiki_pages'
- post '/wikis', to: 'wikis#create'
+scope(controller: :wikis) do
+ scope(path: 'wikis', as: :wikis) do
+ get :git_access
+ get :pages
+ get '/', to: redirect('/%{namespace_id}/%{project_id}/wikis/home')
+ post '/', to: 'wikis#create'
+ end
- get '/wikis/*id/history', to: 'wikis#history', as: 'wiki_history', constraints: WIKI_SLUG_ID
- get '/wikis/*id/edit', to: 'wikis#edit', as: 'wiki_edit', constraints: WIKI_SLUG_ID
-
- get '/wikis/*id', to: 'wikis#show', as: 'wiki', constraints: WIKI_SLUG_ID
- delete '/wikis/*id', to: 'wikis#destroy', constraints: WIKI_SLUG_ID
- put '/wikis/*id', to: 'wikis#update', constraints: WIKI_SLUG_ID
- post '/wikis/*id/preview_markdown', to: 'wikis#preview_markdown', constraints: WIKI_SLUG_ID, as: 'wiki_preview_markdown'
+ scope(path: 'wikis/*id', as: :wiki, constraints: WIKI_SLUG_ID, format: false) do
+ get :edit
+ get :history
+ post :preview_markdown
+ get '/', action: :show
+ put '/', action: :update
+ delete '/', action: :destroy
+ end
end
diff --git a/doc/api/notes.md b/doc/api/notes.md
index 58d40eecf3e..9971806be56 100644
--- a/doc/api/notes.md
+++ b/doc/api/notes.md
@@ -21,7 +21,7 @@ Parameters:
[
{
"id": 302,
- "body": "Status changed to closed",
+ "body": "closed",
"attachment": null,
"author": {
"id": 1,
diff --git a/doc/api/projects.md b/doc/api/projects.md
index de5d3b07c21..bd27a0a6fae 100644
--- a/doc/api/projects.md
+++ b/doc/api/projects.md
@@ -624,6 +624,7 @@ Parameters:
| `user_id` | integer | yes | The user ID of the project owner |
| `name` | string | yes | The name of the new project |
| `path` | string | no | Custom repository name for new project. By default generated based on name |
+| `default_branch` | string | no | `master` by default |
| `namespace_id` | integer | no | Namespace for the new project (defaults to the current user's namespace) |
| `description` | string | no | Short project description |
| `issues_enabled` | boolean | no | Enable issues for this project |
@@ -657,6 +658,7 @@ Parameters:
| `id` | integer/string | yes | The ID or NAMESPACE/PROJECT_NAME of the project |
| `name` | string | yes | The name of the project |
| `path` | string | no | Custom repository name for the project. By default generated based on name |
+| `default_branch` | string | no | `master` by default |
| `description` | string | no | Short project description |
| `issues_enabled` | boolean | no | Enable issues for this project |
| `merge_requests_enabled` | boolean | no | Enable merge requests for this project |
diff --git a/doc/development/testing.md b/doc/development/testing.md
index 6106e47daa0..dbea6b9c9aa 100644
--- a/doc/development/testing.md
+++ b/doc/development/testing.md
@@ -63,6 +63,8 @@ the command line via `bundle exec teaspoon`, or via a web browser at
- Use `.method` to describe class methods and `#method` to describe instance
methods.
- Use `context` to test branching logic.
+- Use multi-line `do...end` blocks for `before` and `after`, even when it would
+ fit on a single line.
- Don't `describe` symbols (see [Gotchas](gotchas.md#dont-describe-symbols)).
- Don't assert against the absolute value of a sequence-generated attribute (see [Gotchas](gotchas.md#dont-assert-against-the-absolute-value-of-a-sequence-generated-attribute)).
- Don't supply the `:each` argument to hooks since it's the default.
diff --git a/doc/install/installation.md b/doc/install/installation.md
index b5e2640b380..dabd69d7466 100644
--- a/doc/install/installation.md
+++ b/doc/install/installation.md
@@ -175,17 +175,17 @@ We recommend using a PostgreSQL database. For MySQL check the
```bash
sudo -u postgres psql -d template1 -c "CREATE USER git CREATEDB;"
```
-
-1. Create the GitLab production database and grant all privileges on database:
+
+1. Create the `pg_trgm` extension (required for GitLab 8.6+):
```bash
- sudo -u postgres psql -d template1 -c "CREATE DATABASE gitlabhq_production OWNER git;"
+ sudo -u postgres psql -d template1 -c "CREATE EXTENSION IF NOT EXISTS pg_trgm;"
```
-1. Create the `pg_trgm` extension (required for GitLab 8.6+):
+1. Create the GitLab production database and grant all privileges on database:
```bash
- sudo -u postgres psql -d template1 -c "CREATE EXTENSION IF NOT EXISTS pg_trgm;"
+ sudo -u postgres psql -d template1 -c "CREATE DATABASE gitlabhq_production OWNER git;"
```
1. Try connecting to the new database with the new user:
diff --git a/doc/integration/jira.md b/doc/integration/jira.md
new file mode 100644
index 00000000000..e2f136bcc35
--- /dev/null
+++ b/doc/integration/jira.md
@@ -0,0 +1,3 @@
+# GitLab JIRA integration
+
+This document was moved to [project_services/jira](../project_services/jira.md).
diff --git a/doc/raketasks/backup_restore.md b/doc/raketasks/backup_restore.md
index 7484bc2295e..17485b11c09 100644
--- a/doc/raketasks/backup_restore.md
+++ b/doc/raketasks/backup_restore.md
@@ -88,7 +88,7 @@ It uses the [Fog library](http://fog.io/) to perform the upload.
In the example below we use Amazon S3 for storage, but Fog also lets you use
[other storage providers](http://fog.io/storage/). GitLab
[imports cloud drivers](https://gitlab.com/gitlab-org/gitlab-ce/blob/30f5b9a5b711b46f1065baf755e413ceced5646b/Gemfile#L88)
-for AWS, Azure, Google, OpenStack Swift and Rackspace as well. A local driver is
+for AWS, OpenStack Swift and Rackspace as well. A local driver is
[also available](#uploading-to-locally-mounted-shares).
For omnibus packages:
diff --git a/doc/university/README.md b/doc/university/README.md
index 4569bc72797..8917636c59b 100644
--- a/doc/university/README.md
+++ b/doc/university/README.md
@@ -19,7 +19,7 @@ The curriculum is composed of GitLab videos, screencasts, presentations, project
---
-### 1. <a name="beginner"></a> GitLab Beginner
+### 1. GitLab Beginner
#### 1.1. Version Control and Git
@@ -85,7 +85,7 @@ The curriculum is composed of GitLab videos, screencasts, presentations, project
---
-### 2. <a name="intermediate"></a> GitLab Intermediate
+### 2. GitLab Intermediate
#### 2.1 GitLab Pages
@@ -141,7 +141,7 @@ The curriculum is composed of GitLab videos, screencasts, presentations, project
---
-### 3. <a name="advanced"></a> GitLab Advanced
+### 3. GitLab Advanced
#### 3.1. Dev Ops
@@ -186,7 +186,7 @@ The curriculum is composed of GitLab videos, screencasts, presentations, project
1. [GitLab Cycle Analytics Overview](https://about.gitlab.com/2016/09/21/cycle-analytics-feature-highlight/)
1. [GitLab Cycle Analytics - Product Page](https://about.gitlab.com/solutions/cycle-analytics/)
-#### 3.9. <a name="integrations"></a> Integrations
+#### 3.9. Integrations
1. [How to Integrate JIRA and Jenkins with GitLab - Video](https://gitlabmeetings.webex.com/gitlabmeetings/ldr.php?RCID=44b548147a67ab4d8a62274047146415)
1. [How to Integrate Jira with GitLab](https://docs.gitlab.com/ee/integration/jira.html)
@@ -198,7 +198,7 @@ The curriculum is composed of GitLab videos, screencasts, presentations, project
---
-## 4. <a name="external"></a> External Articles
+## 4. External Articles
1. [2011 WSJ article by Marc Andreessen - Software is Eating the World](http://www.wsj.com/articles/SB10001424053111903480904576512250915629460)
1. [2014 Blog post by Chris Dixon - Software eats software development](http://cdixon.org/2014/04/13/software-eats-software-development/)
@@ -206,7 +206,7 @@ The curriculum is composed of GitLab videos, screencasts, presentations, project
---
-## 5. <a name="team"></a> Resources for GitLab Team Members
+## 5. Resources for GitLab Team Members
*Some content can only be accessed by GitLab team members*
diff --git a/doc/user/project/img/issue_board.png b/doc/user/project/img/issue_board.png
index 2a35a615d70..95e8532e908 100644
--- a/doc/user/project/img/issue_board.png
+++ b/doc/user/project/img/issue_board.png
Binary files differ
diff --git a/doc/user/project/img/issue_board_add_list.png b/doc/user/project/img/issue_board_add_list.png
index aa1a4ca4cfa..cdfc466d23f 100644
--- a/doc/user/project/img/issue_board_add_list.png
+++ b/doc/user/project/img/issue_board_add_list.png
Binary files differ
diff --git a/doc/user/project/img/issue_board_welcome_message.png b/doc/user/project/img/issue_board_welcome_message.png
index aa25cfb5b37..5bfdac88dde 100644
--- a/doc/user/project/img/issue_board_welcome_message.png
+++ b/doc/user/project/img/issue_board_welcome_message.png
Binary files differ
diff --git a/doc/user/project/issue_board.md b/doc/user/project/issue_board.md
index 4a6c0d88241..d1ae57c00d7 100644
--- a/doc/user/project/issue_board.md
+++ b/doc/user/project/issue_board.md
@@ -72,7 +72,7 @@ the list will be created and filled with the issues that have that label.
## Creating a new list
-Create a new list by clicking on the **Create new list** button at the upper
+Create a new list by clicking on the **Add list** button at the upper
right corner of the Issue Board.
![Issue Board welcome message](img/issue_board_add_list.png)
diff --git a/doc/web_hooks/web_hooks.md b/doc/web_hooks/web_hooks.md
index 33c1a79d59c..cd37189fdd2 100644
--- a/doc/web_hooks/web_hooks.md
+++ b/doc/web_hooks/web_hooks.md
@@ -922,6 +922,64 @@ X-Gitlab-Event: Pipeline Hook
}
```
+
+## Build events
+
+Triggered on status change of a Build.
+
+**Request Header**:
+
+```
+X-Gitlab-Event: Build Hook
+```
+
+**Request Body**:
+
+```
+{
+ "object_kind": "build",
+ "ref": "gitlab-script-trigger",
+ "tag": false,
+ "before_sha": "2293ada6b400935a1378653304eaf6221e0fdb8f",
+ "sha": "2293ada6b400935a1378653304eaf6221e0fdb8f",
+ "build_id": 1977,
+ "build_name": "test",
+ "build_stage": "test",
+ "build_status": "created",
+ "build_started_at": null,
+ "build_finished_at": null,
+ "build_duration": null,
+ "build_allow_failure": false,
+ "project_id": 380,
+ "project_name": "gitlab-org/gitlab-test",
+ "user": {
+ "id": 3,
+ "name": "User",
+ "email": "user@gitlab.com"
+ },
+ "commit": {
+ "id": 2366,
+ "sha": "2293ada6b400935a1378653304eaf6221e0fdb8f",
+ "message": "test\n",
+ "author_name": "User",
+ "author_email": "user@gitlab.com",
+ "status": "created",
+ "duration": null,
+ "started_at": null,
+ "finished_at": null
+ },
+ "repository": {
+ "name": "gitlab_test",
+ "git_ssh_url": "git@192.168.64.1:gitlab-org/gitlab-test.git",
+ "description": "Atque in sunt eos similique dolores voluptatem.",
+ "homepage": "http://192.168.64.1:3005/gitlab-org/gitlab-test",
+ "git_ssh_url": "git@192.168.64.1:gitlab-org/gitlab-test.git",
+ "git_http_url": "http://192.168.64.1:3005/gitlab-org/gitlab-test.git",
+ "visibility_level": 20
+ }
+}
+```
+
#### Example webhook receiver
If you want to see GitLab's webhooks in action for testing purposes you can use
diff --git a/doc/workflow/img/todos_add_todo_sidebar.png b/doc/workflow/img/todos_add_todo_sidebar.png
index 59175ae44c5..3fa37067d1e 100644
--- a/doc/workflow/img/todos_add_todo_sidebar.png
+++ b/doc/workflow/img/todos_add_todo_sidebar.png
Binary files differ
diff --git a/doc/workflow/img/todos_mark_done_sidebar.png b/doc/workflow/img/todos_mark_done_sidebar.png
index aa35bb672ea..a8e756a71db 100644
--- a/doc/workflow/img/todos_mark_done_sidebar.png
+++ b/doc/workflow/img/todos_mark_done_sidebar.png
Binary files differ
diff --git a/doc/workflow/todos.md b/doc/workflow/todos.md
index 54e7ae19ea5..1a8fc39bb33 100644
--- a/doc/workflow/todos.md
+++ b/doc/workflow/todos.md
@@ -35,7 +35,7 @@ A Todo appears in your Todos dashboard when:
### Manually creating a Todo
You can also add an issue or merge request to your Todos dashboard by clicking
-the "Add Todo" button in the issue or merge request sidebar.
+the "Add todo" button in the issue or merge request sidebar.
![Adding a Todo from the issuable sidebar](img/todos_add_todo_sidebar.png)
@@ -69,7 +69,7 @@ corresponding **Done** button, and it will disappear from your Todo list.
![A Todo in the Todos dashboard](img/todo_list_item.png)
A Todo can also be marked as done from the issue or merge request sidebar using
-the "Mark Done" button.
+the "Mark done" button.
![Mark Done from the issuable sidebar](img/todos_mark_done_sidebar.png)
diff --git a/features/abuse_report.feature b/features/abuse_report.feature
deleted file mode 100644
index 212972a762a..00000000000
--- a/features/abuse_report.feature
+++ /dev/null
@@ -1,17 +0,0 @@
-Feature: Abuse reports
- Background:
- Given I sign in as a user
- And user "Mike" exists
-
- Scenario: Report abuse
- Given I visit "Mike" user page
- And I click "Report abuse" button
- When I fill and submit abuse form
- Then I should see success message
-
- Scenario: Report abuse available only once
- Given I visit "Mike" user page
- And I click "Report abuse" button
- When I fill and submit abuse form
- And I visit "Mike" user page
- Then I should see a red "Report abuse" button
diff --git a/features/admin/abuse_report.feature b/features/admin/abuse_report.feature
deleted file mode 100644
index 7d4ec2556e5..00000000000
--- a/features/admin/abuse_report.feature
+++ /dev/null
@@ -1,8 +0,0 @@
-Feature: Admin Abuse reports
- Background:
- Given I sign in as an admin
- And abuse reports exist
-
- Scenario: Browse abuse reports
- When I visit abuse reports page
- Then I should see list of abuse reports
diff --git a/features/admin/spam_logs.feature b/features/admin/spam_logs.feature
deleted file mode 100644
index 92a5389e3a4..00000000000
--- a/features/admin/spam_logs.feature
+++ /dev/null
@@ -1,8 +0,0 @@
-Feature: Admin spam logs
- Background:
- Given I sign in as an admin
- And spam logs exist
-
- Scenario: Browse spam logs
- When I visit spam logs page
- Then I should see list of spam logs
diff --git a/features/steps/abuse_reports.rb b/features/steps/abuse_reports.rb
deleted file mode 100644
index 499accb0b08..00000000000
--- a/features/steps/abuse_reports.rb
+++ /dev/null
@@ -1,32 +0,0 @@
-class Spinach::Features::AbuseReports < Spinach::FeatureSteps
- include SharedAuthentication
-
- step 'I visit "Mike" user page' do
- visit user_path(user_mike)
- end
-
- step 'I click "Report abuse" button' do
- click_link 'Report abuse'
- end
-
- step 'I fill and submit abuse form' do
- fill_in 'abuse_report_message', with: 'This user send spam'
- click_button 'Send report'
- end
-
- step 'I should see success message' do
- page.should have_content 'Thank you for your report'
- end
-
- step 'user "Mike" exists' do
- user_mike
- end
-
- step 'I should see a red "Report abuse" button' do
- expect(page).to have_button("Already reported for abuse")
- end
-
- def user_mike
- @user_mike ||= create(:user, name: 'Mike')
- end
-end
diff --git a/features/steps/admin/abuse_reports.rb b/features/steps/admin/abuse_reports.rb
deleted file mode 100644
index 0149416c919..00000000000
--- a/features/steps/admin/abuse_reports.rb
+++ /dev/null
@@ -1,15 +0,0 @@
-class Spinach::Features::AdminAbuseReports < Spinach::FeatureSteps
- include SharedAuthentication
- include SharedPaths
- include SharedAdmin
-
- step 'I should see list of abuse reports' do
- page.should have_content("Abuse Reports")
- page.should have_content AbuseReport.first.message
- page.should have_link("Remove user")
- end
-
- step 'abuse reports exist' do
- create(:abuse_report)
- end
-end
diff --git a/features/steps/admin/spam_logs.rb b/features/steps/admin/spam_logs.rb
deleted file mode 100644
index ad825fd414c..00000000000
--- a/features/steps/admin/spam_logs.rb
+++ /dev/null
@@ -1,28 +0,0 @@
-class Spinach::Features::AdminSpamLogs < Spinach::FeatureSteps
- include SharedAuthentication
- include SharedPaths
- include SharedAdmin
-
- step 'I should see list of spam logs' do
- expect(page).to have_content('Spam Logs')
- expect(page).to have_content spam_log.source_ip
- expect(page).to have_content spam_log.noteable_type
- expect(page).to have_content 'N'
- expect(page).to have_content spam_log.title
- expect(page).to have_content truncate(spam_log.description)
- expect(page).to have_link('Remove user')
- expect(page).to have_link('Block user')
- end
-
- step 'spam logs exist' do
- create(:spam_log)
- end
-
- def spam_log
- @spam_log ||= SpamLog.first
- end
-
- def truncate(description)
- "#{spam_log.description[0...97]}..."
- end
-end
diff --git a/features/steps/project/labels.rb b/features/steps/project/labels.rb
index 118ffef4774..dbeb07c78db 100644
--- a/features/steps/project/labels.rb
+++ b/features/steps/project/labels.rb
@@ -2,9 +2,7 @@ class Spinach::Features::Labels < Spinach::FeatureSteps
include SharedAuthentication
include SharedIssuable
include SharedProject
- include SharedNote
include SharedPaths
- include SharedMarkdown
step 'And I visit project "Shop" labels page' do
visit namespace_project_labels_path(project.namespace, project)
diff --git a/features/steps/project/merge_requests.rb b/features/steps/project/merge_requests.rb
index f728d243cdc..d2fa8cd39af 100644
--- a/features/steps/project/merge_requests.rb
+++ b/features/steps/project/merge_requests.rb
@@ -515,7 +515,7 @@ class Spinach::Features::ProjectMergeRequests < Spinach::FeatureSteps
step 'I should see new target branch changes' do
expect(page).to have_content 'Request to merge fix into feature'
- expect(page).to have_content 'Target branch changed from merge-test to feature'
+ expect(page).to have_content 'changed target branch from merge-test to feature'
wait_for_ajax
end
diff --git a/features/steps/shared/diff_note.rb b/features/steps/shared/diff_note.rb
index 35b71599708..11fa85ed2fe 100644
--- a/features/steps/shared/diff_note.rb
+++ b/features/steps/shared/diff_note.rb
@@ -1,6 +1,11 @@
module SharedDiffNote
include Spinach::DSL
include RepoHelpers
+ include WaitForAjax
+
+ after do
+ wait_for_ajax if javascript_test?
+ end
step 'I cancel the diff comment' do
page.within(diff_file_selector) do
diff --git a/features/steps/shared/issuable.rb b/features/steps/shared/issuable.rb
index df9845ba569..aa666a954bc 100644
--- a/features/steps/shared/issuable.rb
+++ b/features/steps/shared/issuable.rb
@@ -179,7 +179,7 @@ module SharedIssuable
project = Project.find_by(name: from_project_name)
expect(page).to have_content(user_name)
- expect(page).to have_content("Mentioned in #{issuable.class.to_s.titleize.downcase} #{issuable.to_reference(project)}")
+ expect(page).to have_content("mentioned in #{issuable.class.to_s.titleize.downcase} #{issuable.to_reference(project)}")
end
def expect_sidebar_content(content)
diff --git a/features/steps/shared/note.rb b/features/steps/shared/note.rb
index 9dc1fc41b3b..1870f6bc0c3 100644
--- a/features/steps/shared/note.rb
+++ b/features/steps/shared/note.rb
@@ -2,6 +2,10 @@ module SharedNote
include Spinach::DSL
include WaitForAjax
+ after do
+ wait_for_ajax if javascript_test?
+ end
+
step 'I delete a comment' do
page.within('.main-notes-list') do
find('.note').hover
diff --git a/lib/api/commit_statuses.rb b/lib/api/commit_statuses.rb
index f54d4f06627..492884d162b 100644
--- a/lib/api/commit_statuses.rb
+++ b/lib/api/commit_statuses.rb
@@ -77,7 +77,7 @@ module API
)
begin
- case params[:state].to_s
+ case params[:state]
when 'pending'
status.enqueue!
when 'running'
diff --git a/lib/api/commits.rb b/lib/api/commits.rb
index 0319d076ecb..2670a2d413a 100644
--- a/lib/api/commits.rb
+++ b/lib/api/commits.rb
@@ -48,7 +48,7 @@ module API
requires :id, type: Integer, desc: 'The project ID'
requires :branch_name, type: String, desc: 'The name of branch'
requires :commit_message, type: String, desc: 'Commit message'
- requires :actions, type: Array, desc: 'Actions to perform in commit'
+ requires :actions, type: Array[Hash], desc: 'Actions to perform in commit'
optional :author_email, type: String, desc: 'Author email for commit'
optional :author_name, type: String, desc: 'Author name for commit'
end
diff --git a/lib/api/groups.rb b/lib/api/groups.rb
index a3489c4eb92..5315c22e1e4 100644
--- a/lib/api/groups.rb
+++ b/lib/api/groups.rb
@@ -33,7 +33,7 @@ module API
groups = groups.search(params[:search]) if params[:search].present?
groups = groups.where.not(id: params[:skip_groups]) if params[:skip_groups].present?
- groups = groups.reorder(params[:order_by] => params[:sort].to_sym)
+ groups = groups.reorder(params[:order_by] => params[:sort])
present paginate(groups), with: Entities::Group
end
diff --git a/lib/api/variables.rb b/lib/api/variables.rb
index 90f904b8a12..f623b1dfe9f 100644
--- a/lib/api/variables.rb
+++ b/lib/api/variables.rb
@@ -30,7 +30,7 @@ module API
end
get ':id/variables/:key' do
key = params[:key]
- variable = user_project.variables.find_by(key: key.to_s)
+ variable = user_project.variables.find_by(key: key)
return not_found!('Variable') unless variable
diff --git a/lib/constraints/constrainer_helper.rb b/lib/constraints/constrainer_helper.rb
deleted file mode 100644
index ab07a6793d9..00000000000
--- a/lib/constraints/constrainer_helper.rb
+++ /dev/null
@@ -1,15 +0,0 @@
-module ConstrainerHelper
- def extract_resource_path(path)
- id = path.dup
- id.sub!(/\A#{relative_url_root}/, '') if relative_url_root
- id.sub(/\A\/+/, '').sub(/\/+\z/, '').sub(/.atom\z/, '')
- end
-
- private
-
- def relative_url_root
- if defined?(Gitlab::Application.config.relative_url_root)
- Gitlab::Application.config.relative_url_root
- end
- end
-end
diff --git a/lib/constraints/group_url_constrainer.rb b/lib/constraints/group_url_constrainer.rb
index 2af6e1a11c8..5711d96a586 100644
--- a/lib/constraints/group_url_constrainer.rb
+++ b/lib/constraints/group_url_constrainer.rb
@@ -1,15 +1,17 @@
-require_relative 'constrainer_helper'
-
class GroupUrlConstrainer
- include ConstrainerHelper
-
def matches?(request)
- id = extract_resource_path(request.path)
+ id = request.params[:id]
+
+ return false unless valid?(id)
+
+ Group.find_by(path: id).present?
+ end
+
+ private
- if id =~ Gitlab::Regex.namespace_regex
- Group.find_by(path: id).present?
- else
- false
+ def valid?(id)
+ id.split('/').all? do |namespace|
+ NamespaceValidator.valid?(namespace)
end
end
end
diff --git a/lib/constraints/project_url_constrainer.rb b/lib/constraints/project_url_constrainer.rb
new file mode 100644
index 00000000000..730b05bed97
--- /dev/null
+++ b/lib/constraints/project_url_constrainer.rb
@@ -0,0 +1,13 @@
+class ProjectUrlConstrainer
+ def matches?(request)
+ namespace_path = request.params[:namespace_id]
+ project_path = request.params[:project_id] || request.params[:id]
+ full_path = namespace_path + '/' + project_path
+
+ unless ProjectPathValidator.valid?(project_path)
+ return false
+ end
+
+ Project.find_with_namespace(full_path).present?
+ end
+end
diff --git a/lib/constraints/user_url_constrainer.rb b/lib/constraints/user_url_constrainer.rb
index 4d722ad5af2..9ab5bcb12ff 100644
--- a/lib/constraints/user_url_constrainer.rb
+++ b/lib/constraints/user_url_constrainer.rb
@@ -1,15 +1,5 @@
-require_relative 'constrainer_helper'
-
class UserUrlConstrainer
- include ConstrainerHelper
-
def matches?(request)
- id = extract_resource_path(request.path)
-
- if id =~ Gitlab::Regex.namespace_regex
- User.find_by('lower(username) = ?', id.downcase).present?
- else
- false
- end
+ User.find_by_username(request.params[:username]).present?
end
end
diff --git a/lib/gitlab/identifier.rb b/lib/gitlab/identifier.rb
index c5acf18beb5..94678b6ec40 100644
--- a/lib/gitlab/identifier.rb
+++ b/lib/gitlab/identifier.rb
@@ -21,10 +21,8 @@ module Gitlab
return if !commit || !commit.author_email
- email = commit.author_email
-
- identify_with_cache(:email, email) do
- User.find_by_any_email(email)
+ identify_with_cache(:email, commit.author_email) do
+ commit.author
end
end
diff --git a/lib/gitlab/regex.rb b/lib/gitlab/regex.rb
index c12358ceef4..a06cf6a989c 100644
--- a/lib/gitlab/regex.rb
+++ b/lib/gitlab/regex.rb
@@ -8,8 +8,10 @@ module Gitlab
# allow non-regex validatiions, etc), `NAMESPACE_REGEX_STR_SIMPLE` serves as a Javascript-compatible version of
# `NAMESPACE_REGEX_STR`, with the negative lookbehind assertion removed. This means that the client-side validation
# will pass for usernames ending in `.atom` and `.git`, but will be caught by the server-side validation.
- NAMESPACE_REGEX_STR_SIMPLE = '[a-zA-Z0-9_\.][a-zA-Z0-9_\-\.]*[a-zA-Z0-9_\-]|[a-zA-Z0-9_]'.freeze
+ PATH_REGEX_STR = '[a-zA-Z0-9_\.][a-zA-Z0-9_\-\.]*'.freeze
+ NAMESPACE_REGEX_STR_SIMPLE = PATH_REGEX_STR + '[a-zA-Z0-9_\-]|[a-zA-Z0-9_]'.freeze
NAMESPACE_REGEX_STR = '(?:' + NAMESPACE_REGEX_STR_SIMPLE + ')(?<!\.git|\.atom)'.freeze
+ PROJECT_REGEX_STR = PATH_REGEX_STR + '(?<!\.git|\.atom)'.freeze
def namespace_regex
@namespace_regex ||= /\A#{NAMESPACE_REGEX_STR}\z/.freeze
@@ -42,7 +44,15 @@ module Gitlab
end
def project_path_regex
- @project_path_regex ||= /\A[a-zA-Z0-9_.][a-zA-Z0-9_\-\.]*(?<!\.git|\.atom)\z/.freeze
+ @project_path_regex ||= /\A#{PROJECT_REGEX_STR}\z/.freeze
+ end
+
+ def project_route_regex
+ @project_route_regex ||= /#{PROJECT_REGEX_STR}/.freeze
+ end
+
+ def project_git_route_regex
+ @project_route_git_regex ||= /#{PATH_REGEX_STR}\.git/.freeze
end
def project_path_regex_message
diff --git a/spec/controllers/application_controller_spec.rb b/spec/controllers/application_controller_spec.rb
index 98e912f000c..81cbccd5436 100644
--- a/spec/controllers/application_controller_spec.rb
+++ b/spec/controllers/application_controller_spec.rb
@@ -1,8 +1,9 @@
require 'spec_helper'
describe ApplicationController do
+ let(:user) { create(:user) }
+
describe '#check_password_expiration' do
- let(:user) { create(:user) }
let(:controller) { ApplicationController.new }
it 'redirects if the user is over their password expiry' do
@@ -39,8 +40,6 @@ describe ApplicationController do
end
end
- let(:user) { create(:user) }
-
context "when the 'private_token' param is populated with the private token" do
it "logs the user in" do
get :index, private_token: user.private_token
@@ -73,7 +72,6 @@ describe ApplicationController do
end
end
- let(:user) { create(:user) }
let(:personal_access_token) { create(:personal_access_token, user: user) }
context "when the 'personal_access_token' param is populated with the personal access token" do
@@ -100,4 +98,21 @@ describe ApplicationController do
end
end
end
+
+ describe '#route_not_found' do
+ let(:controller) { ApplicationController.new }
+
+ it 'renders 404 if authenticated' do
+ allow(controller).to receive(:current_user).and_return(user)
+ expect(controller).to receive(:not_found)
+ controller.send(:route_not_found)
+ end
+
+ it 'does redirect to login page if not authenticated' do
+ allow(controller).to receive(:current_user).and_return(nil)
+ expect(controller).to receive(:redirect_to)
+ expect(controller).to receive(:new_user_session_path)
+ controller.send(:route_not_found)
+ end
+ end
end
diff --git a/spec/controllers/projects/milestones_controller_spec.rb b/spec/controllers/projects/milestones_controller_spec.rb
index 7c5f33c63b8..6d30d085056 100644
--- a/spec/controllers/projects/milestones_controller_spec.rb
+++ b/spec/controllers/projects/milestones_controller_spec.rb
@@ -31,7 +31,7 @@ describe Projects::MilestonesController do
# Check system note left for milestone removal
last_note = project.issues.find(issue.id).notes[-1].note
- expect(last_note).to eq('Milestone removed')
+ expect(last_note).to eq('removed milestone')
end
end
end
diff --git a/spec/factories/ci/builds.rb b/spec/factories/ci/builds.rb
index eb20bd7dd58..c443af09075 100644
--- a/spec/factories/ci/builds.rb
+++ b/spec/factories/ci/builds.rb
@@ -38,6 +38,10 @@ FactoryGirl.define do
status 'canceled'
end
+ trait :skipped do
+ status 'skipped'
+ end
+
trait :running do
status 'running'
end
diff --git a/spec/factories/commit_statuses.rb b/spec/factories/commit_statuses.rb
index 995f2080f10..756b341ecba 100644
--- a/spec/factories/commit_statuses.rb
+++ b/spec/factories/commit_statuses.rb
@@ -19,6 +19,10 @@ FactoryGirl.define do
status 'canceled'
end
+ trait :skipped do
+ status 'skipped'
+ end
+
trait :running do
status 'running'
end
diff --git a/spec/features/abuse_report_spec.rb b/spec/features/abuse_report_spec.rb
new file mode 100644
index 00000000000..1e11fb756b2
--- /dev/null
+++ b/spec/features/abuse_report_spec.rb
@@ -0,0 +1,24 @@
+require 'spec_helper'
+
+feature 'Abuse reports', feature: true do
+ let(:another_user) { create(:user) }
+
+ before do
+ login_as :user
+ end
+
+ scenario 'Report abuse' do
+ visit user_path(another_user)
+
+ click_link 'Report abuse'
+
+ fill_in 'abuse_report_message', with: 'This user send spam'
+ click_button 'Send report'
+
+ expect(page).to have_content 'Thank you for your report'
+
+ visit user_path(another_user)
+
+ expect(page).to have_button("Already reported for abuse")
+ end
+end
diff --git a/spec/features/admin/admin_abuse_reports_spec.rb b/spec/features/admin/admin_abuse_reports_spec.rb
index c1731e6414a..7fcfe5a54c7 100644
--- a/spec/features/admin/admin_abuse_reports_spec.rb
+++ b/spec/features/admin/admin_abuse_reports_spec.rb
@@ -4,17 +4,21 @@ describe "Admin::AbuseReports", feature: true, js: true do
let(:user) { create(:user) }
context 'as an admin' do
+ before do
+ login_as :admin
+ end
+
describe 'if a user has been reported for abuse' do
- before do
- create(:abuse_report, user: user)
- login_as :admin
- end
+ let!(:abuse_report) { create(:abuse_report, user: user) }
describe 'in the abuse report view' do
- it "presents a link to the user's profile" do
+ it 'presents information about abuse report' do
visit admin_abuse_reports_path
- expect(page).to have_link user.name, href: user_path(user)
+ expect(page).to have_content('Abuse Reports')
+ expect(page).to have_content(abuse_report.message)
+ expect(page).to have_link(user.name, href: user_path(user))
+ expect(page).to have_link('Remove user')
end
end
diff --git a/spec/features/admin/admin_browse_spam_logs_spec.rb b/spec/features/admin/admin_browse_spam_logs_spec.rb
new file mode 100644
index 00000000000..562ace92598
--- /dev/null
+++ b/spec/features/admin/admin_browse_spam_logs_spec.rb
@@ -0,0 +1,22 @@
+require 'spec_helper'
+
+describe 'Admin browse spam logs' do
+ let!(:spam_log) { create(:spam_log) }
+
+ before do
+ login_as :admin
+ end
+
+ scenario 'Browse spam logs' do
+ visit admin_spam_logs_path
+
+ expect(page).to have_content('Spam Logs')
+ expect(page).to have_content(spam_log.source_ip)
+ expect(page).to have_content(spam_log.noteable_type)
+ expect(page).to have_content('N')
+ expect(page).to have_content(spam_log.title)
+ expect(page).to have_content("#{spam_log.description[0...97]}...")
+ expect(page).to have_link('Remove user')
+ expect(page).to have_link('Block user')
+ end
+end
diff --git a/spec/features/boards/boards_spec.rb b/spec/features/boards/boards_spec.rb
index 4aa84fb65d9..973d5b286e9 100644
--- a/spec/features/boards/boards_spec.rb
+++ b/spec/features/boards/boards_spec.rb
@@ -158,7 +158,7 @@ describe 'Issue Boards', feature: true, js: true do
end
it 'removes checkmark in new list dropdown after deleting' do
- click_button 'Create new list'
+ click_button 'Add list'
wait_for_ajax
page.within(find('.board:nth-child(2)')) do
@@ -304,7 +304,7 @@ describe 'Issue Boards', feature: true, js: true do
context 'new list' do
it 'shows all labels in new list dropdown' do
- click_button 'Create new list'
+ click_button 'Add list'
wait_for_ajax
page.within('.dropdown-menu-issues-board-new') do
@@ -315,7 +315,7 @@ describe 'Issue Boards', feature: true, js: true do
end
it 'creates new list for label' do
- click_button 'Create new list'
+ click_button 'Add list'
wait_for_ajax
page.within('.dropdown-menu-issues-board-new') do
@@ -328,7 +328,7 @@ describe 'Issue Boards', feature: true, js: true do
end
it 'creates new list for Backlog label' do
- click_button 'Create new list'
+ click_button 'Add list'
wait_for_ajax
page.within('.dropdown-menu-issues-board-new') do
@@ -341,7 +341,7 @@ describe 'Issue Boards', feature: true, js: true do
end
it 'creates new list for Done label' do
- click_button 'Create new list'
+ click_button 'Add list'
wait_for_ajax
page.within('.dropdown-menu-issues-board-new') do
@@ -354,7 +354,7 @@ describe 'Issue Boards', feature: true, js: true do
end
it 'keeps dropdown open after adding new list' do
- click_button 'Create new list'
+ click_button 'Add list'
wait_for_ajax
page.within('.dropdown-menu-issues-board-new') do
@@ -369,7 +369,7 @@ describe 'Issue Boards', feature: true, js: true do
it 'moves issues from backlog into new list' do
wait_for_board_cards(1, 6)
- click_button 'Create new list'
+ click_button 'Add list'
wait_for_ajax
page.within('.dropdown-menu-issues-board-new') do
@@ -382,7 +382,7 @@ describe 'Issue Boards', feature: true, js: true do
end
it 'creates new list from a new label' do
- click_button 'Create new list'
+ click_button 'Add list'
wait_for_ajax
diff --git a/spec/features/issues/gfm_autocomplete_spec.rb b/spec/features/issues/gfm_autocomplete_spec.rb
new file mode 100644
index 00000000000..c421da97d76
--- /dev/null
+++ b/spec/features/issues/gfm_autocomplete_spec.rb
@@ -0,0 +1,43 @@
+require 'rails_helper'
+
+feature 'GFM autocomplete', feature: true, js: true do
+ include WaitForAjax
+ let(:user) { create(:user) }
+ let(:project) { create(:project) }
+ let(:issue) { create(:issue, project: project) }
+
+ before do
+ project.team << [user, :master]
+ login_as(user)
+ visit namespace_project_issue_path(project.namespace, project, issue)
+
+ wait_for_ajax
+ end
+
+ it 'opens autocomplete menu when field starts with text' do
+ page.within '.timeline-content-form' do
+ find('#note_note').native.send_keys('')
+ find('#note_note').native.send_keys('@')
+ end
+
+ expect(page).to have_selector('.atwho-container')
+ end
+
+ it 'opens autocomplete menu when field is prefixed with non-text character' do
+ page.within '.timeline-content-form' do
+ find('#note_note').native.send_keys('')
+ find('#note_note').native.send_keys('@')
+ end
+
+ expect(page).to have_selector('.atwho-container')
+ end
+
+ it 'doesnt open autocomplete menu character is prefixed with text' do
+ page.within '.timeline-content-form' do
+ find('#note_note').native.send_keys('testing')
+ find('#note_note').native.send_keys('@')
+ end
+
+ expect(page).not_to have_selector('.atwho-view')
+ end
+end
diff --git a/spec/features/issues/move_spec.rb b/spec/features/issues/move_spec.rb
index 055210399a7..c9bec05a9da 100644
--- a/spec/features/issues/move_spec.rb
+++ b/spec/features/issues/move_spec.rb
@@ -27,7 +27,7 @@ feature 'issue move to another project' do
let!(:mr) { create(:merge_request, source_project: old_project) }
let(:new_project) { create(:project) }
let(:new_project_search) { create(:project) }
- let(:text) { 'Text with !1' }
+ let(:text) { "Text with #{mr.to_reference}" }
let(:cross_reference) { old_project.to_reference }
background do
@@ -43,8 +43,8 @@ feature 'issue move to another project' do
expect(current_url).to include project_path(new_project)
- expect(page).to have_content("Text with #{cross_reference}!1")
- expect(page).to have_content("Moved from #{cross_reference}#1")
+ expect(page).to have_content("Text with #{cross_reference}#{mr.to_reference}")
+ expect(page).to have_content("moved from #{cross_reference}#{issue.to_reference}")
expect(page).to have_content(issue.title)
end
diff --git a/spec/features/issues/new_branch_button_spec.rb b/spec/features/issues/new_branch_button_spec.rb
index ab901e74617..a4d3053d10c 100644
--- a/spec/features/issues/new_branch_button_spec.rb
+++ b/spec/features/issues/new_branch_button_spec.rb
@@ -20,12 +20,12 @@ feature 'Start new branch from an issue', feature: true do
context "when there is a referenced merge request" do
let!(:note) do
create(:note, :on_issue, :system, project: project, noteable: issue,
- note: "Mentioned in !#{referenced_mr.iid}")
+ note: "mentioned in #{referenced_mr.to_reference}")
end
let(:referenced_mr) do
create(:merge_request, :simple, source_project: project, target_project: project,
- description: "Fixes ##{issue.iid}", author: user)
+ description: "Fixes #{issue.to_reference}", author: user)
end
before do
diff --git a/spec/features/issues/todo_spec.rb b/spec/features/issues/todo_spec.rb
index de8fdda388d..41ff31d2b99 100644
--- a/spec/features/issues/todo_spec.rb
+++ b/spec/features/issues/todo_spec.rb
@@ -13,8 +13,8 @@ feature 'Manually create a todo item from issue', feature: true, js: true do
it 'creates todo when clicking button' do
page.within '.issuable-sidebar' do
- click_button 'Add Todo'
- expect(page).to have_content 'Mark Done'
+ click_button 'Add todo'
+ expect(page).to have_content 'Mark done'
end
page.within '.header-content .todos-pending-count' do
@@ -30,8 +30,8 @@ feature 'Manually create a todo item from issue', feature: true, js: true do
it 'marks a todo as done' do
page.within '.issuable-sidebar' do
- click_button 'Add Todo'
- click_button 'Mark Done'
+ click_button 'Add todo'
+ click_button 'Mark done'
end
expect(page).to have_selector('.todos-pending-count', visible: false)
diff --git a/spec/features/merge_requests/diff_notes_resolve_spec.rb b/spec/features/merge_requests/diff_notes_resolve_spec.rb
index eab64bd4b54..d5e3d8e7eff 100644
--- a/spec/features/merge_requests/diff_notes_resolve_spec.rb
+++ b/spec/features/merge_requests/diff_notes_resolve_spec.rb
@@ -201,7 +201,7 @@ feature 'Diff notes resolve', feature: true, js: true do
expect(first('.line-resolve-btn')['data-original-title']).to eq("Resolved by #{user.name}")
end
- expect(page).not_to have_content('Last updated')
+ expect(page).to have_content('Last updated')
page.within '.line-resolve-all-container' do
expect(page).to have_content('0/1 discussion resolved')
diff --git a/spec/features/merge_requests/merge_when_build_succeeds_spec.rb b/spec/features/merge_requests/merge_when_build_succeeds_spec.rb
index 8eceaad2457..9ad225e3a1b 100644
--- a/spec/features/merge_requests/merge_when_build_succeeds_spec.rb
+++ b/spec/features/merge_requests/merge_when_build_succeeds_spec.rb
@@ -44,7 +44,7 @@ feature 'Merge When Build Succeeds', feature: true, js: true do
expect(page).to have_content "The source branch will not be removed."
visit_merge_request(merge_request) # Needed to refresh the page
- expect(page).to have_content /Enabled an automatic merge when the build for [0-9a-f]{8} succeeds/i
+ expect(page).to have_content /enabled an automatic merge when the build for \h{8} succeeds/i
end
end
end
diff --git a/spec/features/notes_on_merge_requests_spec.rb b/spec/features/notes_on_merge_requests_spec.rb
index 5d7247e2a62..9fffbb43e87 100644
--- a/spec/features/notes_on_merge_requests_spec.rb
+++ b/spec/features/notes_on_merge_requests_spec.rb
@@ -141,7 +141,7 @@ describe 'Comments', feature: true do
let(:project2) { create(:project, :private) }
let(:issue) { create(:issue, project: project2) }
let(:merge_request) { create(:merge_request, source_project: project, source_branch: 'markdown') }
- let!(:note) { create(:note_on_merge_request, :system, noteable: merge_request, project: project, note: "Mentioned in #{issue.to_reference(project)}") }
+ let!(:note) { create(:note_on_merge_request, :system, noteable: merge_request, project: project, note: "mentioned in #{issue.to_reference(project)}") }
it 'shows the system note' do
login_as :admin
diff --git a/spec/features/projects/pipelines_spec.rb b/spec/features/projects/pipelines_spec.rb
index 002c6f6b359..10e5466fc85 100644
--- a/spec/features/projects/pipelines_spec.rb
+++ b/spec/features/projects/pipelines_spec.rb
@@ -90,13 +90,20 @@ describe "Pipelines" do
visit namespace_project_pipelines_path(project.namespace, project)
end
- it 'is not cancelable' do
- expect(page).not_to have_link('Cancel')
+ it 'is cancelable' do
+ expect(page).to have_link('Cancel')
end
it 'has pipeline running' do
expect(page).to have_selector('.ci-running')
end
+
+ context 'when canceling' do
+ before { click_link('Cancel') }
+
+ it { expect(page).not_to have_link('Cancel') }
+ it { expect(page).to have_selector('.ci-canceled') }
+ end
end
context 'when failed' do
diff --git a/spec/javascripts/fixtures/right_sidebar.html.haml b/spec/javascripts/fixtures/right_sidebar.html.haml
index d48b77cf0ce..d259b58f235 100644
--- a/spec/javascripts/fixtures/right_sidebar.html.haml
+++ b/spec/javascripts/fixtures/right_sidebar.html.haml
@@ -5,9 +5,9 @@
%div.block.issuable-sidebar-header
%a.gutter-toggle.pull-right.js-sidebar-toggle
%i.fa.fa-angle-double-left
- %button.btn.btn-default.issuable-header-btn.pull-right.js-issuable-todo{ type: "button", data: { todo_text: "Add Todo", mark_text: "Mark Done", issuable_id: "1", issuable_type: "issue", url: "/todos" }}
+ %button.btn.btn-default.issuable-header-btn.pull-right.js-issuable-todo{ type: "button", data: { todo_text: "Add todo", mark_text: "Mark done", issuable_id: "1", issuable_type: "issue", url: "/todos" }}
%span.js-issuable-todo-text
- Add Todo
+ Add todo
%i.fa.fa-spin.fa-spinner.js-issuable-todo-loading.hidden
%form.issuable-context-form
diff --git a/spec/lib/constraints/constrainer_helper_spec.rb b/spec/lib/constraints/constrainer_helper_spec.rb
deleted file mode 100644
index 27c8d72aefc..00000000000
--- a/spec/lib/constraints/constrainer_helper_spec.rb
+++ /dev/null
@@ -1,20 +0,0 @@
-require 'spec_helper'
-
-describe ConstrainerHelper, lib: true do
- include ConstrainerHelper
-
- describe '#extract_resource_path' do
- it { expect(extract_resource_path('/gitlab/')).to eq('gitlab') }
- it { expect(extract_resource_path('///gitlab//')).to eq('gitlab') }
- it { expect(extract_resource_path('/gitlab.atom')).to eq('gitlab') }
-
- context 'relative url' do
- before do
- allow(Gitlab::Application.config).to receive(:relative_url_root) { '/gitlab' }
- end
-
- it { expect(extract_resource_path('/gitlab/foo')).to eq('foo') }
- it { expect(extract_resource_path('/foo/bar')).to eq('foo/bar') }
- end
- end
-end
diff --git a/spec/lib/constraints/group_url_constrainer_spec.rb b/spec/lib/constraints/group_url_constrainer_spec.rb
index 42299b17c2b..892554f2870 100644
--- a/spec/lib/constraints/group_url_constrainer_spec.rb
+++ b/spec/lib/constraints/group_url_constrainer_spec.rb
@@ -4,16 +4,20 @@ describe GroupUrlConstrainer, lib: true do
let!(:group) { create(:group, path: 'gitlab') }
describe '#matches?' do
- context 'root group' do
- it { expect(subject.matches?(request '/gitlab')).to be_truthy }
- it { expect(subject.matches?(request '/gitlab.atom')).to be_truthy }
- it { expect(subject.matches?(request '/gitlab/edit')).to be_falsey }
- it { expect(subject.matches?(request '/gitlab-ce')).to be_falsey }
- it { expect(subject.matches?(request '/.gitlab')).to be_falsey }
+ context 'valid request' do
+ let(:request) { build_request(group.path) }
+
+ it { expect(subject.matches?(request)).to be_truthy }
+ end
+
+ context 'invalid request' do
+ let(:request) { build_request('foo') }
+
+ it { expect(subject.matches?(request)).to be_falsey }
end
end
- def request(path)
- double(:request, path: path)
+ def build_request(path)
+ double(:request, params: { id: path })
end
end
diff --git a/spec/lib/constraints/project_url_constrainer_spec.rb b/spec/lib/constraints/project_url_constrainer_spec.rb
new file mode 100644
index 00000000000..94266f6653b
--- /dev/null
+++ b/spec/lib/constraints/project_url_constrainer_spec.rb
@@ -0,0 +1,32 @@
+require 'spec_helper'
+
+describe ProjectUrlConstrainer, lib: true do
+ let!(:project) { create(:project) }
+ let!(:namespace) { project.namespace }
+
+ describe '#matches?' do
+ context 'valid request' do
+ let(:request) { build_request(namespace.path, project.path) }
+
+ it { expect(subject.matches?(request)).to be_truthy }
+ end
+
+ context 'invalid request' do
+ context "non-existing project" do
+ let(:request) { build_request('foo', 'bar') }
+
+ it { expect(subject.matches?(request)).to be_falsey }
+ end
+
+ context "project id ending with .git" do
+ let(:request) { build_request(namespace.path, project.path + '.git') }
+
+ it { expect(subject.matches?(request)).to be_falsey }
+ end
+ end
+ end
+
+ def build_request(namespace, project)
+ double(:request, params: { namespace_id: namespace, id: project })
+ end
+end
diff --git a/spec/lib/constraints/user_url_constrainer_spec.rb b/spec/lib/constraints/user_url_constrainer_spec.rb
index b3f8530c609..207b6fe6c9e 100644
--- a/spec/lib/constraints/user_url_constrainer_spec.rb
+++ b/spec/lib/constraints/user_url_constrainer_spec.rb
@@ -1,16 +1,23 @@
require 'spec_helper'
describe UserUrlConstrainer, lib: true do
- let!(:username) { create(:user, username: 'dz') }
+ let!(:user) { create(:user, username: 'dz') }
describe '#matches?' do
- it { expect(subject.matches?(request '/dz')).to be_truthy }
- it { expect(subject.matches?(request '/dz.atom')).to be_truthy }
- it { expect(subject.matches?(request '/dz/projects')).to be_falsey }
- it { expect(subject.matches?(request '/gitlab')).to be_falsey }
+ context 'valid request' do
+ let(:request) { build_request(user.username) }
+
+ it { expect(subject.matches?(request)).to be_truthy }
+ end
+
+ context 'invalid request' do
+ let(:request) { build_request('foo') }
+
+ it { expect(subject.matches?(request)).to be_falsey }
+ end
end
- def request(path)
- double(:request, path: path)
+ def build_request(username)
+ double(:request, params: { username: username })
end
end
diff --git a/spec/lib/gitlab/identifier_spec.rb b/spec/lib/gitlab/identifier_spec.rb
index f42c4453dd1..bb758a8a202 100644
--- a/spec/lib/gitlab/identifier_spec.rb
+++ b/spec/lib/gitlab/identifier_spec.rb
@@ -40,7 +40,7 @@ describe Gitlab::Identifier do
describe '#identify_using_commit' do
it "returns the User for an existing commit author's Email address" do
- commit = double(:commit, author_email: user.email)
+ commit = double(:commit, author: user, author_email: user.email)
expect(project).to receive(:commit).with('123').and_return(commit)
@@ -62,10 +62,9 @@ describe Gitlab::Identifier do
end
it 'caches the found users per Email' do
- commit = double(:commit, author_email: user.email)
+ commit = double(:commit, author: user, author_email: user.email)
expect(project).to receive(:commit).with('123').twice.and_return(commit)
- expect(User).to receive(:find_by_any_email).once.and_call_original
2.times do
expect(identifier.identify_using_commit(project, '123')).to eq(user)
diff --git a/spec/models/ci/pipeline_spec.rb b/spec/models/ci/pipeline_spec.rb
index ea022e03608..3b12f16b4db 100644
--- a/spec/models/ci/pipeline_spec.rb
+++ b/spec/models/ci/pipeline_spec.rb
@@ -402,6 +402,160 @@ describe Ci::Pipeline, models: true do
end
end
+ describe '#cancelable?' do
+ %i[created running pending].each do |status0|
+ context "when there is a build #{status0}" do
+ before do
+ create(:ci_build, status0, pipeline: pipeline)
+ end
+
+ it 'is cancelable' do
+ expect(pipeline.cancelable?).to be_truthy
+ end
+ end
+
+ context "when there is an external job #{status0}" do
+ before do
+ create(:generic_commit_status, status0, pipeline: pipeline)
+ end
+
+ it 'is cancelable' do
+ expect(pipeline.cancelable?).to be_truthy
+ end
+ end
+
+ %i[success failed canceled].each do |status1|
+ context "when there are generic_commit_status jobs for #{status0} and #{status1}" do
+ before do
+ create(:generic_commit_status, status0, pipeline: pipeline)
+ create(:generic_commit_status, status1, pipeline: pipeline)
+ end
+
+ it 'is cancelable' do
+ expect(pipeline.cancelable?).to be_truthy
+ end
+ end
+
+ context "when there are generic_commit_status and ci_build jobs for #{status0} and #{status1}" do
+ before do
+ create(:generic_commit_status, status0, pipeline: pipeline)
+ create(:ci_build, status1, pipeline: pipeline)
+ end
+
+ it 'is cancelable' do
+ expect(pipeline.cancelable?).to be_truthy
+ end
+ end
+
+ context "when there are ci_build jobs for #{status0} and #{status1}" do
+ before do
+ create(:ci_build, status0, pipeline: pipeline)
+ create(:ci_build, status1, pipeline: pipeline)
+ end
+
+ it 'is cancelable' do
+ expect(pipeline.cancelable?).to be_truthy
+ end
+ end
+ end
+ end
+
+ %i[success failed canceled].each do |status|
+ context "when there is a build #{status}" do
+ before do
+ create(:ci_build, status, pipeline: pipeline)
+ end
+
+ it 'is not cancelable' do
+ expect(pipeline.cancelable?).to be_falsey
+ end
+ end
+
+ context "when there is an external job #{status}" do
+ before do
+ create(:generic_commit_status, status, pipeline: pipeline)
+ end
+
+ it 'is not cancelable' do
+ expect(pipeline.cancelable?).to be_falsey
+ end
+ end
+ end
+ end
+
+ describe '#cancel_running' do
+ let(:latest_status) { pipeline.statuses.pluck(:status) }
+
+ context 'when there is a running external job and created build' do
+ before do
+ create(:ci_build, :running, pipeline: pipeline)
+ create(:generic_commit_status, :running, pipeline: pipeline)
+
+ pipeline.cancel_running
+ end
+
+ it 'cancels both jobs' do
+ expect(latest_status).to contain_exactly('canceled', 'canceled')
+ end
+ end
+
+ context 'when builds are in different stages' do
+ before do
+ create(:ci_build, :running, stage_idx: 0, pipeline: pipeline)
+ create(:ci_build, :running, stage_idx: 1, pipeline: pipeline)
+
+ pipeline.cancel_running
+ end
+
+ it 'cancels both jobs' do
+ expect(latest_status).to contain_exactly('canceled', 'canceled')
+ end
+ end
+ end
+
+ describe '#retry_failed' do
+ let(:latest_status) { pipeline.statuses.latest.pluck(:status) }
+
+ context 'when there is a failed build and failed external status' do
+ before do
+ create(:ci_build, :failed, name: 'build', pipeline: pipeline)
+ create(:generic_commit_status, :failed, name: 'jenkins', pipeline: pipeline)
+
+ pipeline.retry_failed(create(:user))
+ end
+
+ it 'retries only build' do
+ expect(latest_status).to contain_exactly('pending', 'failed')
+ end
+ end
+
+ context 'when builds are in different stages' do
+ before do
+ create(:ci_build, :failed, name: 'build', stage_idx: 0, pipeline: pipeline)
+ create(:ci_build, :failed, name: 'jenkins', stage_idx: 1, pipeline: pipeline)
+
+ pipeline.retry_failed(create(:user))
+ end
+
+ it 'retries both builds' do
+ expect(latest_status).to contain_exactly('pending', 'pending')
+ end
+ end
+
+ context 'when there are canceled and failed' do
+ before do
+ create(:ci_build, :failed, name: 'build', stage_idx: 0, pipeline: pipeline)
+ create(:ci_build, :canceled, name: 'jenkins', stage_idx: 1, pipeline: pipeline)
+
+ pipeline.retry_failed(create(:user))
+ end
+
+ it 'retries both builds' do
+ expect(latest_status).to contain_exactly('pending', 'pending')
+ end
+ end
+ end
+
describe '#execute_hooks' do
let!(:build_a) { create_build('a', 0) }
let!(:build_b) { create_build('b', 1) }
diff --git a/spec/models/concerns/has_status_spec.rb b/spec/models/concerns/has_status_spec.rb
index 87bffbdc54e..9defb17dc92 100644
--- a/spec/models/concerns/has_status_spec.rb
+++ b/spec/models/concerns/has_status_spec.rb
@@ -123,4 +123,100 @@ describe HasStatus do
it_behaves_like 'build status summary'
end
end
+
+ context 'for scope with one status' do
+ shared_examples 'having a job' do |status|
+ %i[ci_build generic_commit_status].each do |type|
+ context "when it's #{status} #{type} job" do
+ let!(:job) { create(type, status) }
+
+ describe ".#{status}" do
+ it 'contains the job' do
+ expect(CommitStatus.public_send(status).all).
+ to contain_exactly(job)
+ end
+ end
+
+ describe '.relevant' do
+ if status == :created
+ it 'contains nothing' do
+ expect(CommitStatus.relevant.all).to be_empty
+ end
+ else
+ it 'contains the job' do
+ expect(CommitStatus.relevant.all).to contain_exactly(job)
+ end
+ end
+ end
+ end
+ end
+ end
+
+ %i[created running pending success
+ failed canceled skipped].each do |status|
+ it_behaves_like 'having a job', status
+ end
+ end
+
+ context 'for scope with more statuses' do
+ shared_examples 'containing the job' do |status|
+ %i[ci_build generic_commit_status].each do |type|
+ context "when it's #{status} #{type} job" do
+ let!(:job) { create(type, status) }
+
+ it 'contains the job' do
+ is_expected.to contain_exactly(job)
+ end
+ end
+ end
+ end
+
+ shared_examples 'not containing the job' do |status|
+ %i[ci_build generic_commit_status].each do |type|
+ context "when it's #{status} #{type} job" do
+ let!(:job) { create(type, status) }
+
+ it 'contains nothing' do
+ is_expected.to be_empty
+ end
+ end
+ end
+ end
+
+ describe '.running_or_pending' do
+ subject { CommitStatus.running_or_pending }
+
+ %i[running pending].each do |status|
+ it_behaves_like 'containing the job', status
+ end
+
+ %i[created failed success].each do |status|
+ it_behaves_like 'not containing the job', status
+ end
+ end
+
+ describe '.finished' do
+ subject { CommitStatus.finished }
+
+ %i[success failed canceled].each do |status|
+ it_behaves_like 'containing the job', status
+ end
+
+ %i[created running pending].each do |status|
+ it_behaves_like 'not containing the job', status
+ end
+ end
+
+ describe '.cancelable' do
+ subject { CommitStatus.cancelable }
+
+ %i[running pending created].each do |status|
+ it_behaves_like 'containing the job', status
+ end
+
+ %i[failed success skipped canceled].each do |status|
+ it_behaves_like 'not containing the job', status
+ end
+ end
+ end
end
diff --git a/spec/models/note_spec.rb b/spec/models/note_spec.rb
index e6b6e7c0634..17a15b12dcb 100644
--- a/spec/models/note_spec.rb
+++ b/spec/models/note_spec.rb
@@ -223,7 +223,7 @@ describe Note, models: true do
let(:note) do
create :note,
noteable: ext_issue, project: ext_proj,
- note: "Mentioned in issue #{private_issue.to_reference(ext_proj)}",
+ note: "mentioned in issue #{private_issue.to_reference(ext_proj)}",
system: true
end
diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb
index e84042f8063..91826e5884d 100644
--- a/spec/models/user_spec.rb
+++ b/spec/models/user_spec.rb
@@ -752,6 +752,17 @@ describe User, models: true do
end
end
+ describe '.find_by_username' do
+ it 'returns nil if not found' do
+ expect(described_class.find_by_username('JohnDoe')).to be_nil
+ end
+
+ it 'is case-insensitive' do
+ user = create(:user, username: 'JohnDoe')
+ expect(described_class.find_by_username('JOHNDOE')).to eq user
+ end
+ end
+
describe '.find_by_username!' do
it 'raises RecordNotFound' do
expect { described_class.find_by_username!('JohnDoe') }.
diff --git a/spec/requests/api/notes_spec.rb b/spec/requests/api/notes_spec.rb
index 0124b7271b3..b71a4c5a56e 100644
--- a/spec/requests/api/notes_spec.rb
+++ b/spec/requests/api/notes_spec.rb
@@ -25,7 +25,7 @@ describe API::API, api: true do
let!(:cross_reference_note) do
create :note,
noteable: ext_issue, project: ext_proj,
- note: "Mentioned in issue #{private_issue.to_reference(ext_proj)}",
+ note: "mentioned in issue #{private_issue.to_reference(ext_proj)}",
system: true
end
diff --git a/spec/routing/project_routing_spec.rb b/spec/routing/project_routing_spec.rb
index 2322430d212..b6e7da841b1 100644
--- a/spec/routing/project_routing_spec.rb
+++ b/spec/routing/project_routing_spec.rb
@@ -1,511 +1,531 @@
require 'spec_helper'
-# Shared examples for a resource inside a Project
-#
-# By default it tests all the default REST actions: index, create, new, edit,
-# show, update, and destroy. You can remove actions by customizing the
-# `actions` variable.
-#
-# It also expects a `controller` variable to be available which defines both
-# the path to the resource as well as the controller name.
-#
-# Examples
-#
-# # Default behavior
-# it_behaves_like 'RESTful project resources' do
-# let(:controller) { 'issues' }
-# end
-#
-# # Customizing actions
-# it_behaves_like 'RESTful project resources' do
-# let(:actions) { [:index] }
-# let(:controller) { 'issues' }
-# end
-shared_examples 'RESTful project resources' do
- let(:actions) { [:index, :create, :new, :edit, :show, :update, :destroy] }
-
- it 'to #index' do
- expect(get("/gitlab/gitlabhq/#{controller}")).to route_to("projects/#{controller}#index", namespace_id: 'gitlab', project_id: 'gitlabhq') if actions.include?(:index)
- end
-
- it 'to #create' do
- expect(post("/gitlab/gitlabhq/#{controller}")).to route_to("projects/#{controller}#create", namespace_id: 'gitlab', project_id: 'gitlabhq') if actions.include?(:create)
- end
-
- it 'to #new' do
- expect(get("/gitlab/gitlabhq/#{controller}/new")).to route_to("projects/#{controller}#new", namespace_id: 'gitlab', project_id: 'gitlabhq') if actions.include?(:new)
- end
-
- it 'to #edit' do
- expect(get("/gitlab/gitlabhq/#{controller}/1/edit")).to route_to("projects/#{controller}#edit", namespace_id: 'gitlab', project_id: 'gitlabhq', id: '1') if actions.include?(:edit)
- end
-
- it 'to #show' do
- expect(get("/gitlab/gitlabhq/#{controller}/1")).to route_to("projects/#{controller}#show", namespace_id: 'gitlab', project_id: 'gitlabhq', id: '1') if actions.include?(:show)
- end
-
- it 'to #update' do
- expect(put("/gitlab/gitlabhq/#{controller}/1")).to route_to("projects/#{controller}#update", namespace_id: 'gitlab', project_id: 'gitlabhq', id: '1') if actions.include?(:update)
- end
-
- it 'to #destroy' do
- expect(delete("/gitlab/gitlabhq/#{controller}/1")).to route_to("projects/#{controller}#destroy", namespace_id: 'gitlab', project_id: 'gitlabhq', id: '1') if actions.include?(:destroy)
- end
-end
-
-# projects POST /projects(.:format) projects#create
-# new_project GET /projects/new(.:format) projects#new
-# files_project GET /:id/files(.:format) projects#files
-# edit_project GET /:id/edit(.:format) projects#edit
-# project GET /:id(.:format) projects#show
-# PUT /:id(.:format) projects#update
-# DELETE /:id(.:format) projects#destroy
-# preview_markdown_project POST /:id/preview_markdown(.:format) projects#preview_markdown
-describe ProjectsController, 'routing' do
- it 'to #create' do
- expect(post('/projects')).to route_to('projects#create')
- end
-
- it 'to #new' do
- expect(get('/projects/new')).to route_to('projects#new')
- end
-
- it 'to #edit' do
- expect(get('/gitlab/gitlabhq/edit')).to route_to('projects#edit', namespace_id: 'gitlab', id: 'gitlabhq')
- end
-
- it 'to #autocomplete_sources' do
- expect(get('/gitlab/gitlabhq/autocomplete_sources')).to route_to('projects#autocomplete_sources', namespace_id: 'gitlab', id: 'gitlabhq')
- end
-
- it 'to #show' do
- expect(get('/gitlab/gitlabhq')).to route_to('projects#show', namespace_id: 'gitlab', id: 'gitlabhq')
- expect(get('/gitlab/gitlabhq.keys')).to route_to('projects#show', namespace_id: 'gitlab', id: 'gitlabhq.keys')
- end
-
- it 'to #update' do
- expect(put('/gitlab/gitlabhq')).to route_to('projects#update', namespace_id: 'gitlab', id: 'gitlabhq')
- end
-
- it 'to #destroy' do
- expect(delete('/gitlab/gitlabhq')).to route_to('projects#destroy', namespace_id: 'gitlab', id: 'gitlabhq')
- end
-
- it 'to #preview_markdown' do
- expect(post('/gitlab/gitlabhq/preview_markdown')).to(
- route_to('projects#preview_markdown', namespace_id: 'gitlab', id: 'gitlabhq')
- )
- end
-end
-
-# pages_project_wikis GET /:project_id/wikis/pages(.:format) projects/wikis#pages
-# history_project_wiki GET /:project_id/wikis/:id/history(.:format) projects/wikis#history
-# project_wikis POST /:project_id/wikis(.:format) projects/wikis#create
-# edit_project_wiki GET /:project_id/wikis/:id/edit(.:format) projects/wikis#edit
-# project_wiki GET /:project_id/wikis/:id(.:format) projects/wikis#show
-# DELETE /:project_id/wikis/:id(.:format) projects/wikis#destroy
-describe Projects::WikisController, 'routing' do
- it 'to #pages' do
- expect(get('/gitlab/gitlabhq/wikis/pages')).to route_to('projects/wikis#pages', namespace_id: 'gitlab', project_id: 'gitlabhq')
- end
-
- it 'to #history' do
- expect(get('/gitlab/gitlabhq/wikis/1/history')).to route_to('projects/wikis#history', namespace_id: 'gitlab', project_id: 'gitlabhq', id: '1')
- end
-
- it_behaves_like 'RESTful project resources' do
- let(:actions) { [:create, :edit, :show, :destroy] }
- let(:controller) { 'wikis' }
- end
-end
-
-# branches_project_repository GET /:project_id/repository/branches(.:format) projects/repositories#branches
-# tags_project_repository GET /:project_id/repository/tags(.:format) projects/repositories#tags
-# archive_project_repository GET /:project_id/repository/archive(.:format) projects/repositories#archive
-# edit_project_repository GET /:project_id/repository/edit(.:format) projects/repositories#edit
-describe Projects::RepositoriesController, 'routing' do
- it 'to #archive' do
- expect(get('/gitlab/gitlabhq/repository/archive')).to route_to('projects/repositories#archive', namespace_id: 'gitlab', project_id: 'gitlabhq')
- end
-
- it 'to #archive format:zip' do
- expect(get('/gitlab/gitlabhq/repository/archive.zip')).to route_to('projects/repositories#archive', namespace_id: 'gitlab', project_id: 'gitlabhq', format: 'zip')
- end
-
- it 'to #archive format:tar.bz2' do
- expect(get('/gitlab/gitlabhq/repository/archive.tar.bz2')).to route_to('projects/repositories#archive', namespace_id: 'gitlab', project_id: 'gitlabhq', format: 'tar.bz2')
- end
-end
-
-describe Projects::BranchesController, 'routing' do
- it 'to #branches' do
- expect(get('/gitlab/gitlabhq/branches')).to route_to('projects/branches#index', namespace_id: 'gitlab', project_id: 'gitlabhq')
- expect(delete('/gitlab/gitlabhq/branches/feature%2345')).to route_to('projects/branches#destroy', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'feature#45')
- expect(delete('/gitlab/gitlabhq/branches/feature%2B45')).to route_to('projects/branches#destroy', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'feature+45')
- expect(delete('/gitlab/gitlabhq/branches/feature@45')).to route_to('projects/branches#destroy', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'feature@45')
- expect(delete('/gitlab/gitlabhq/branches/feature%2345/foo/bar/baz')).to route_to('projects/branches#destroy', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'feature#45/foo/bar/baz')
- expect(delete('/gitlab/gitlabhq/branches/feature%2B45/foo/bar/baz')).to route_to('projects/branches#destroy', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'feature+45/foo/bar/baz')
- expect(delete('/gitlab/gitlabhq/branches/feature@45/foo/bar/baz')).to route_to('projects/branches#destroy', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'feature@45/foo/bar/baz')
- end
-end
-
-describe Projects::TagsController, 'routing' do
- it 'to #tags' do
- expect(get('/gitlab/gitlabhq/tags')).to route_to('projects/tags#index', namespace_id: 'gitlab', project_id: 'gitlabhq')
- expect(delete('/gitlab/gitlabhq/tags/feature%2345')).to route_to('projects/tags#destroy', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'feature#45')
- expect(delete('/gitlab/gitlabhq/tags/feature%2B45')).to route_to('projects/tags#destroy', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'feature+45')
- expect(delete('/gitlab/gitlabhq/tags/feature@45')).to route_to('projects/tags#destroy', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'feature@45')
- expect(delete('/gitlab/gitlabhq/tags/feature%2345/foo/bar/baz')).to route_to('projects/tags#destroy', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'feature#45/foo/bar/baz')
- expect(delete('/gitlab/gitlabhq/tags/feature%2B45/foo/bar/baz')).to route_to('projects/tags#destroy', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'feature+45/foo/bar/baz')
- expect(delete('/gitlab/gitlabhq/tags/feature@45/foo/bar/baz')).to route_to('projects/tags#destroy', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'feature@45/foo/bar/baz')
- end
-end
-
-# project_deploy_keys GET /:project_id/deploy_keys(.:format) deploy_keys#index
-# POST /:project_id/deploy_keys(.:format) deploy_keys#create
-# new_project_deploy_key GET /:project_id/deploy_keys/new(.:format) deploy_keys#new
-# project_deploy_key GET /:project_id/deploy_keys/:id(.:format) deploy_keys#show
-# DELETE /:project_id/deploy_keys/:id(.:format) deploy_keys#destroy
-describe Projects::DeployKeysController, 'routing' do
- it_behaves_like 'RESTful project resources' do
- let(:actions) { [:index, :new, :create] }
- let(:controller) { 'deploy_keys' }
- end
-end
-
-# project_protected_branches GET /:project_id/protected_branches(.:format) protected_branches#index
-# POST /:project_id/protected_branches(.:format) protected_branches#create
-# project_protected_branch DELETE /:project_id/protected_branches/:id(.:format) protected_branches#destroy
-describe Projects::ProtectedBranchesController, 'routing' do
- it_behaves_like 'RESTful project resources' do
- let(:actions) { [:index, :create, :destroy] }
- let(:controller) { 'protected_branches' }
- end
-end
-
-# switch_project_refs GET /:project_id/refs/switch(.:format) refs#switch
-# logs_tree_project_ref GET /:project_id/refs/:id/logs_tree(.:format) refs#logs_tree
-# logs_file_project_ref GET /:project_id/refs/:id/logs_tree/:path(.:format) refs#logs_tree
-describe Projects::RefsController, 'routing' do
- it 'to #switch' do
- expect(get('/gitlab/gitlabhq/refs/switch')).to route_to('projects/refs#switch', namespace_id: 'gitlab', project_id: 'gitlabhq')
- end
-
- it 'to #logs_tree' do
- expect(get('/gitlab/gitlabhq/refs/stable/logs_tree')).to route_to('projects/refs#logs_tree', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'stable')
- expect(get('/gitlab/gitlabhq/refs/feature%2345/logs_tree')).to route_to('projects/refs#logs_tree', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'feature#45')
- expect(get('/gitlab/gitlabhq/refs/feature%2B45/logs_tree')).to route_to('projects/refs#logs_tree', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'feature+45')
- expect(get('/gitlab/gitlabhq/refs/feature@45/logs_tree')).to route_to('projects/refs#logs_tree', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'feature@45')
- expect(get('/gitlab/gitlabhq/refs/stable/logs_tree/foo/bar/baz')).to route_to('projects/refs#logs_tree', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'stable', path: 'foo/bar/baz')
- expect(get('/gitlab/gitlabhq/refs/feature%2345/logs_tree/foo/bar/baz')).to route_to('projects/refs#logs_tree', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'feature#45', path: 'foo/bar/baz')
- expect(get('/gitlab/gitlabhq/refs/feature%2B45/logs_tree/foo/bar/baz')).to route_to('projects/refs#logs_tree', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'feature+45', path: 'foo/bar/baz')
- expect(get('/gitlab/gitlabhq/refs/feature@45/logs_tree/foo/bar/baz')).to route_to('projects/refs#logs_tree', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'feature@45', path: 'foo/bar/baz')
- expect(get('/gitlab/gitlabhq/refs/stable/logs_tree/files.scss')).to route_to('projects/refs#logs_tree', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'stable', path: 'files.scss')
- end
-end
-
-# diffs_namespace_project_merge_request GET /:namespace_id/:project_id/merge_requests/:id/diffs(.:format) projects/merge_requests#diffs
-# commits_namespace_project_merge_request GET /:namespace_id/:project_id/merge_requests/:id/commits(.:format) projects/merge_requests#commits
-# merge_namespace_project_merge_request POST /:namespace_id/:project_id/merge_requests/:id/merge(.:format) projects/merge_requests#merge
-# merge_check_namespace_project_merge_request GET /:namespace_id/:project_id/merge_requests/:id/merge_check(.:format) projects/merge_requests#merge_check
-# ci_status_namespace_project_merge_request GET /:namespace_id/:project_id/merge_requests/:id/ci_status(.:format) projects/merge_requests#ci_status
-# toggle_subscription_namespace_project_merge_request POST /:namespace_id/:project_id/merge_requests/:id/toggle_subscription(.:format) projects/merge_requests#toggle_subscription
-# branch_from_namespace_project_merge_requests GET /:namespace_id/:project_id/merge_requests/branch_from(.:format) projects/merge_requests#branch_from
-# branch_to_namespace_project_merge_requests GET /:namespace_id/:project_id/merge_requests/branch_to(.:format) projects/merge_requests#branch_to
-# update_branches_namespace_project_merge_requests GET /:namespace_id/:project_id/merge_requests/update_branches(.:format) projects/merge_requests#update_branches
-# namespace_project_merge_requests GET /:namespace_id/:project_id/merge_requests(.:format) projects/merge_requests#index
-# POST /:namespace_id/:project_id/merge_requests(.:format) projects/merge_requests#create
-# new_namespace_project_merge_request GET /:namespace_id/:project_id/merge_requests/new(.:format) projects/merge_requests#new
-# edit_namespace_project_merge_request GET /:namespace_id/:project_id/merge_requests/:id/edit(.:format) projects/merge_requests#edit
-# namespace_project_merge_request GET /:namespace_id/:project_id/merge_requests/:id(.:format) projects/merge_requests#show
-# PATCH /:namespace_id/:project_id/merge_requests/:id(.:format) projects/merge_requests#update
-# PUT /:namespace_id/:project_id/merge_requests/:id(.:format) projects/merge_requests#update
-describe Projects::MergeRequestsController, 'routing' do
- it 'to #diffs' do
- expect(get('/gitlab/gitlabhq/merge_requests/1/diffs')).to route_to('projects/merge_requests#diffs', namespace_id: 'gitlab', project_id: 'gitlabhq', id: '1')
- end
-
- it 'to #commits' do
- expect(get('/gitlab/gitlabhq/merge_requests/1/commits')).to route_to('projects/merge_requests#commits', namespace_id: 'gitlab', project_id: 'gitlabhq', id: '1')
- end
-
- it 'to #merge' do
- expect(post('/gitlab/gitlabhq/merge_requests/1/merge')).to route_to(
- 'projects/merge_requests#merge',
- namespace_id: 'gitlab', project_id: 'gitlabhq', id: '1'
- )
- end
-
- it 'to #merge_check' do
- expect(get('/gitlab/gitlabhq/merge_requests/1/merge_check')).to route_to('projects/merge_requests#merge_check', namespace_id: 'gitlab', project_id: 'gitlabhq', id: '1')
- end
-
- it 'to #branch_from' do
- expect(get('/gitlab/gitlabhq/merge_requests/branch_from')).to route_to('projects/merge_requests#branch_from', namespace_id: 'gitlab', project_id: 'gitlabhq')
- end
-
- it 'to #branch_to' do
- expect(get('/gitlab/gitlabhq/merge_requests/branch_to')).to route_to('projects/merge_requests#branch_to', namespace_id: 'gitlab', project_id: 'gitlabhq')
- end
-
- it 'to #show' do
- expect(get('/gitlab/gitlabhq/merge_requests/1.diff')).to route_to('projects/merge_requests#show', namespace_id: 'gitlab', project_id: 'gitlabhq', id: '1', format: 'diff')
- expect(get('/gitlab/gitlabhq/merge_requests/1.patch')).to route_to('projects/merge_requests#show', namespace_id: 'gitlab', project_id: 'gitlabhq', id: '1', format: 'patch')
- end
-
- it_behaves_like 'RESTful project resources' do
- let(:controller) { 'merge_requests' }
- let(:actions) { [:index, :create, :new, :edit, :show, :update] }
- end
-end
-
-# raw_project_snippet GET /:project_id/snippets/:id/raw(.:format) snippets#raw
-# project_snippets GET /:project_id/snippets(.:format) snippets#index
-# POST /:project_id/snippets(.:format) snippets#create
-# new_project_snippet GET /:project_id/snippets/new(.:format) snippets#new
-# edit_project_snippet GET /:project_id/snippets/:id/edit(.:format) snippets#edit
-# project_snippet GET /:project_id/snippets/:id(.:format) snippets#show
-# PUT /:project_id/snippets/:id(.:format) snippets#update
-# DELETE /:project_id/snippets/:id(.:format) snippets#destroy
-describe SnippetsController, 'routing' do
- it 'to #raw' do
- expect(get('/gitlab/gitlabhq/snippets/1/raw')).to route_to('projects/snippets#raw', namespace_id: 'gitlab', project_id: 'gitlabhq', id: '1')
- end
-
- it 'to #index' do
- expect(get('/gitlab/gitlabhq/snippets')).to route_to('projects/snippets#index', namespace_id: 'gitlab', project_id: 'gitlabhq')
- end
-
- it 'to #create' do
- expect(post('/gitlab/gitlabhq/snippets')).to route_to('projects/snippets#create', namespace_id: 'gitlab', project_id: 'gitlabhq')
- end
-
- it 'to #new' do
- expect(get('/gitlab/gitlabhq/snippets/new')).to route_to('projects/snippets#new', namespace_id: 'gitlab', project_id: 'gitlabhq')
- end
-
- it 'to #edit' do
- expect(get('/gitlab/gitlabhq/snippets/1/edit')).to route_to('projects/snippets#edit', namespace_id: 'gitlab', project_id: 'gitlabhq', id: '1')
- end
-
- it 'to #show' do
- expect(get('/gitlab/gitlabhq/snippets/1')).to route_to('projects/snippets#show', namespace_id: 'gitlab', project_id: 'gitlabhq', id: '1')
- end
-
- it 'to #update' do
- expect(put('/gitlab/gitlabhq/snippets/1')).to route_to('projects/snippets#update', namespace_id: 'gitlab', project_id: 'gitlabhq', id: '1')
- end
-
- it 'to #destroy' do
- expect(delete('/gitlab/gitlabhq/snippets/1')).to route_to('projects/snippets#destroy', namespace_id: 'gitlab', project_id: 'gitlabhq', id: '1')
- end
-end
-
-# test_project_hook GET /:project_id/hooks/:id/test(.:format) hooks#test
-# project_hooks GET /:project_id/hooks(.:format) hooks#index
-# POST /:project_id/hooks(.:format) hooks#create
-# project_hook DELETE /:project_id/hooks/:id(.:format) hooks#destroy
-describe Projects::HooksController, 'routing' do
- it 'to #test' do
- expect(get('/gitlab/gitlabhq/hooks/1/test')).to route_to('projects/hooks#test', namespace_id: 'gitlab', project_id: 'gitlabhq', id: '1')
- end
-
- it_behaves_like 'RESTful project resources' do
- let(:actions) { [:index, :create, :destroy] }
- let(:controller) { 'hooks' }
- end
-end
-
-# project_commit GET /:project_id/commit/:id(.:format) commit#show {id: /\h{7,40}/, project_id: /[^\/]+/}
-describe Projects::CommitController, 'routing' do
- it 'to #show' do
- expect(get('/gitlab/gitlabhq/commit/4246fbd')).to route_to('projects/commit#show', namespace_id: 'gitlab', project_id: 'gitlabhq', id: '4246fbd')
- expect(get('/gitlab/gitlabhq/commit/4246fbd.diff')).to route_to('projects/commit#show', namespace_id: 'gitlab', project_id: 'gitlabhq', id: '4246fbd', format: 'diff')
- expect(get('/gitlab/gitlabhq/commit/4246fbd.patch')).to route_to('projects/commit#show', namespace_id: 'gitlab', project_id: 'gitlabhq', id: '4246fbd', format: 'patch')
- expect(get('/gitlab/gitlabhq/commit/4246fbd13872934f72a8fd0d6fb1317b47b59cb5')).to route_to('projects/commit#show', namespace_id: 'gitlab', project_id: 'gitlabhq', id: '4246fbd13872934f72a8fd0d6fb1317b47b59cb5')
- end
-end
-
-# patch_project_commit GET /:project_id/commits/:id/patch(.:format) commits#patch
-# project_commits GET /:project_id/commits(.:format) commits#index
-# POST /:project_id/commits(.:format) commits#create
-# project_commit GET /:project_id/commits/:id(.:format) commits#show
-describe Projects::CommitsController, 'routing' do
- it_behaves_like 'RESTful project resources' do
- let(:actions) { [:show] }
- let(:controller) { 'commits' }
- end
-
- it 'to #show' do
- expect(get('/gitlab/gitlabhq/commits/master.atom')).to route_to('projects/commits#show', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'master.atom')
- end
-end
-
-# project_project_members GET /:project_id/project_members(.:format) project_members#index
-# POST /:project_id/project_members(.:format) project_members#create
-# PUT /:project_id/project_members/:id(.:format) project_members#update
-# DELETE /:project_id/project_members/:id(.:format) project_members#destroy
-describe Projects::ProjectMembersController, 'routing' do
- it_behaves_like 'RESTful project resources' do
- let(:actions) { [:index, :create, :update, :destroy] }
- let(:controller) { 'project_members' }
- end
-end
-
-# project_milestones GET /:project_id/milestones(.:format) milestones#index
-# POST /:project_id/milestones(.:format) milestones#create
-# new_project_milestone GET /:project_id/milestones/new(.:format) milestones#new
-# edit_project_milestone GET /:project_id/milestones/:id/edit(.:format) milestones#edit
-# project_milestone GET /:project_id/milestones/:id(.:format) milestones#show
-# PUT /:project_id/milestones/:id(.:format) milestones#update
-# DELETE /:project_id/milestones/:id(.:format) milestones#destroy
-describe Projects::MilestonesController, 'routing' do
- it_behaves_like 'RESTful project resources' do
- let(:controller) { 'milestones' }
- let(:actions) { [:index, :create, :new, :edit, :show, :update] }
- end
-end
-
-# project_labels GET /:project_id/labels(.:format) labels#index
-describe Projects::LabelsController, 'routing' do
- it 'to #index' do
- expect(get('/gitlab/gitlabhq/labels')).to route_to('projects/labels#index', namespace_id: 'gitlab', project_id: 'gitlabhq')
- end
-end
-
-# sort_project_issues POST /:project_id/issues/sort(.:format) issues#sort
-# bulk_update_project_issues POST /:project_id/issues/bulk_update(.:format) issues#bulk_update
-# search_project_issues GET /:project_id/issues/search(.:format) issues#search
-# project_issues GET /:project_id/issues(.:format) issues#index
-# POST /:project_id/issues(.:format) issues#create
-# new_project_issue GET /:project_id/issues/new(.:format) issues#new
-# edit_project_issue GET /:project_id/issues/:id/edit(.:format) issues#edit
-# project_issue GET /:project_id/issues/:id(.:format) issues#show
-# PUT /:project_id/issues/:id(.:format) issues#update
-# DELETE /:project_id/issues/:id(.:format) issues#destroy
-describe Projects::IssuesController, 'routing' do
- it 'to #bulk_update' do
- expect(post('/gitlab/gitlabhq/issues/bulk_update')).to route_to('projects/issues#bulk_update', namespace_id: 'gitlab', project_id: 'gitlabhq')
- end
-
- it_behaves_like 'RESTful project resources' do
- let(:controller) { 'issues' }
- let(:actions) { [:index, :create, :new, :edit, :show, :update] }
- end
-end
-
-# project_notes GET /:project_id/notes(.:format) notes#index
-# POST /:project_id/notes(.:format) notes#create
-# project_note DELETE /:project_id/notes/:id(.:format) notes#destroy
-describe Projects::NotesController, 'routing' do
- it_behaves_like 'RESTful project resources' do
- let(:actions) { [:index, :create, :destroy] }
- let(:controller) { 'notes' }
- end
-end
-
-# project_blame GET /:project_id/blame/:id(.:format) blame#show {id: /.+/, project_id: /[^\/]+/}
-describe Projects::BlameController, 'routing' do
- it 'to #show' do
- expect(get('/gitlab/gitlabhq/blame/master/app/models/project.rb')).to route_to('projects/blame#show', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'master/app/models/project.rb')
- expect(get('/gitlab/gitlabhq/blame/master/files.scss')).to route_to('projects/blame#show', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'master/files.scss')
- end
-end
-
-# project_blob GET /:project_id/blob/:id(.:format) blob#show {id: /.+/, project_id: /[^\/]+/}
-describe Projects::BlobController, 'routing' do
- it 'to #show' do
- expect(get('/gitlab/gitlabhq/blob/master/app/models/project.rb')).to route_to('projects/blob#show', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'master/app/models/project.rb')
- expect(get('/gitlab/gitlabhq/blob/master/app/models/compare.rb')).to route_to('projects/blob#show', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'master/app/models/compare.rb')
- expect(get('/gitlab/gitlabhq/blob/master/app/models/diff.js')).to route_to('projects/blob#show', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'master/app/models/diff.js')
- expect(get('/gitlab/gitlabhq/blob/master/files.scss')).to route_to('projects/blob#show', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'master/files.scss')
- end
-end
-
-# project_tree GET /:project_id/tree/:id(.:format) tree#show {id: /.+/, project_id: /[^\/]+/}
-describe Projects::TreeController, 'routing' do
- it 'to #show' do
- expect(get('/gitlab/gitlabhq/tree/master/app/models/project.rb')).to route_to('projects/tree#show', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'master/app/models/project.rb')
- expect(get('/gitlab/gitlabhq/tree/master/files.scss')).to route_to('projects/tree#show', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'master/files.scss')
- end
-end
-
-# project_find_file GET /:namespace_id/:project_id/find_file/*id(.:format) projects/find_file#show {:id=>/.+/, :namespace_id=>/[a-zA-Z.0-9_\-]+/, :project_id=>/[a-zA-Z.0-9_\-]+(?<!\.atom)/, :format=>/html/}
-# project_files GET /:namespace_id/:project_id/files/*id(.:format) projects/find_file#list {:id=>/(?:[^.]|\.(?!json$))+/, :namespace_id=>/[a-zA-Z.0-9_\-]+/, :project_id=>/[a-zA-Z.0-9_\-]+(?<!\.atom)/, :format=>/json/}
-describe Projects::FindFileController, 'routing' do
- it 'to #show' do
- expect(get('/gitlab/gitlabhq/find_file/master')).to route_to('projects/find_file#show', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'master')
- end
-
- it 'to #list' do
- expect(get('/gitlab/gitlabhq/files/master.json')).to route_to('projects/find_file#list', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'master', format: 'json')
- end
-end
-
-describe Projects::BlobController, 'routing' do
- it 'to #edit' do
- expect(get('/gitlab/gitlabhq/edit/master/app/models/project.rb')).to(
- route_to('projects/blob#edit',
- namespace_id: 'gitlab', project_id: 'gitlabhq',
- id: 'master/app/models/project.rb'))
- end
-
- it 'to #preview' do
- expect(post('/gitlab/gitlabhq/preview/master/app/models/project.rb')).to(
- route_to('projects/blob#preview',
- namespace_id: 'gitlab', project_id: 'gitlabhq',
- id: 'master/app/models/project.rb'))
- end
-end
-
-# project_compare_index GET /:project_id/compare(.:format) compare#index {id: /[^\/]+/, project_id: /[^\/]+/}
-# POST /:project_id/compare(.:format) compare#create {id: /[^\/]+/, project_id: /[^\/]+/}
-# project_compare /:project_id/compare/:from...:to(.:format) compare#show {from: /.+/, to: /.+/, id: /[^\/]+/, project_id: /[^\/]+/}
-describe Projects::CompareController, 'routing' do
- it 'to #index' do
- expect(get('/gitlab/gitlabhq/compare')).to route_to('projects/compare#index', namespace_id: 'gitlab', project_id: 'gitlabhq')
- end
-
- it 'to #compare' do
- expect(post('/gitlab/gitlabhq/compare')).to route_to('projects/compare#create', namespace_id: 'gitlab', project_id: 'gitlabhq')
- end
-
- it 'to #show' do
- expect(get('/gitlab/gitlabhq/compare/master...stable')).to route_to('projects/compare#show', namespace_id: 'gitlab', project_id: 'gitlabhq', from: 'master', to: 'stable')
- expect(get('/gitlab/gitlabhq/compare/issue/1234...stable')).to route_to('projects/compare#show', namespace_id: 'gitlab', project_id: 'gitlabhq', from: 'issue/1234', to: 'stable')
- end
-end
-
-describe Projects::NetworkController, 'routing' do
- it 'to #show' do
- expect(get('/gitlab/gitlabhq/network/master')).to route_to('projects/network#show', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'master')
- expect(get('/gitlab/gitlabhq/network/ends-with.json')).to route_to('projects/network#show', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'ends-with.json')
- expect(get('/gitlab/gitlabhq/network/master?format=json')).to route_to('projects/network#show', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'master', format: 'json')
- end
-end
-
-describe Projects::GraphsController, 'routing' do
- it 'to #show' do
- expect(get('/gitlab/gitlabhq/graphs/master')).to route_to('projects/graphs#show', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'master')
- expect(get('/gitlab/gitlabhq/graphs/ends-with.json')).to route_to('projects/graphs#show', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'ends-with.json')
- expect(get('/gitlab/gitlabhq/graphs/master?format=json')).to route_to('projects/graphs#show', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'master', format: 'json')
- end
-end
-
-describe Projects::ForksController, 'routing' do
- it 'to #new' do
- expect(get('/gitlab/gitlabhq/forks/new')).to route_to('projects/forks#new', namespace_id: 'gitlab', project_id: 'gitlabhq')
- end
-
- it 'to #create' do
- expect(post('/gitlab/gitlabhq/forks')).to route_to('projects/forks#create', namespace_id: 'gitlab', project_id: 'gitlabhq')
- end
-end
-
-# project_avatar DELETE /project/avatar(.:format) projects/avatars#destroy
-describe Projects::AvatarsController, 'routing' do
- it 'to #destroy' do
- expect(delete('/gitlab/gitlabhq/avatar')).to route_to(
- 'projects/avatars#destroy', namespace_id: 'gitlab', project_id: 'gitlabhq')
+describe 'project routing' do
+ before do
+ allow(Project).to receive(:find_with_namespace).and_return(false)
+ allow(Project).to receive(:find_with_namespace).with('gitlab/gitlabhq').and_return(true)
+ end
+
+ # Shared examples for a resource inside a Project
+ #
+ # By default it tests all the default REST actions: index, create, new, edit,
+ # show, update, and destroy. You can remove actions by customizing the
+ # `actions` variable.
+ #
+ # It also expects a `controller` variable to be available which defines both
+ # the path to the resource as well as the controller name.
+ #
+ # Examples
+ #
+ # # Default behavior
+ # it_behaves_like 'RESTful project resources' do
+ # let(:controller) { 'issues' }
+ # end
+ #
+ # # Customizing actions
+ # it_behaves_like 'RESTful project resources' do
+ # let(:actions) { [:index] }
+ # let(:controller) { 'issues' }
+ # end
+ shared_examples 'RESTful project resources' do
+ let(:actions) { [:index, :create, :new, :edit, :show, :update, :destroy] }
+
+ it 'to #index' do
+ expect(get("/gitlab/gitlabhq/#{controller}")).to route_to("projects/#{controller}#index", namespace_id: 'gitlab', project_id: 'gitlabhq') if actions.include?(:index)
+ end
+
+ it 'to #create' do
+ expect(post("/gitlab/gitlabhq/#{controller}")).to route_to("projects/#{controller}#create", namespace_id: 'gitlab', project_id: 'gitlabhq') if actions.include?(:create)
+ end
+
+ it 'to #new' do
+ expect(get("/gitlab/gitlabhq/#{controller}/new")).to route_to("projects/#{controller}#new", namespace_id: 'gitlab', project_id: 'gitlabhq') if actions.include?(:new)
+ end
+
+ it 'to #edit' do
+ expect(get("/gitlab/gitlabhq/#{controller}/1/edit")).to route_to("projects/#{controller}#edit", namespace_id: 'gitlab', project_id: 'gitlabhq', id: '1') if actions.include?(:edit)
+ end
+
+ it 'to #show' do
+ expect(get("/gitlab/gitlabhq/#{controller}/1")).to route_to("projects/#{controller}#show", namespace_id: 'gitlab', project_id: 'gitlabhq', id: '1') if actions.include?(:show)
+ end
+
+ it 'to #update' do
+ expect(put("/gitlab/gitlabhq/#{controller}/1")).to route_to("projects/#{controller}#update", namespace_id: 'gitlab', project_id: 'gitlabhq', id: '1') if actions.include?(:update)
+ end
+
+ it 'to #destroy' do
+ expect(delete("/gitlab/gitlabhq/#{controller}/1")).to route_to("projects/#{controller}#destroy", namespace_id: 'gitlab', project_id: 'gitlabhq', id: '1') if actions.include?(:destroy)
+ end
+ end
+
+ # projects POST /projects(.:format) projects#create
+ # new_project GET /projects/new(.:format) projects#new
+ # files_project GET /:id/files(.:format) projects#files
+ # edit_project GET /:id/edit(.:format) projects#edit
+ # project GET /:id(.:format) projects#show
+ # PUT /:id(.:format) projects#update
+ # DELETE /:id(.:format) projects#destroy
+ # preview_markdown_project POST /:id/preview_markdown(.:format) projects#preview_markdown
+ describe ProjectsController, 'routing' do
+ it 'to #create' do
+ expect(post('/projects')).to route_to('projects#create')
+ end
+
+ it 'to #new' do
+ expect(get('/projects/new')).to route_to('projects#new')
+ end
+
+ it 'to #edit' do
+ expect(get('/gitlab/gitlabhq/edit')).to route_to('projects#edit', namespace_id: 'gitlab', id: 'gitlabhq')
+ end
+
+ it 'to #autocomplete_sources' do
+ expect(get('/gitlab/gitlabhq/autocomplete_sources')).to route_to('projects#autocomplete_sources', namespace_id: 'gitlab', id: 'gitlabhq')
+ end
+
+ describe 'to #show' do
+ context 'regular name' do
+ it { expect(get('/gitlab/gitlabhq')).to route_to('projects#show', namespace_id: 'gitlab', id: 'gitlabhq') }
+ end
+
+ context 'name with dot' do
+ before { allow(Project).to receive(:find_with_namespace).with('gitlab/gitlabhq.keys').and_return(true) }
+
+ it { expect(get('/gitlab/gitlabhq.keys')).to route_to('projects#show', namespace_id: 'gitlab', id: 'gitlabhq.keys') }
+ end
+
+ context 'with nested group' do
+ before { allow(Project).to receive(:find_with_namespace).with('gitlab/subgroup/gitlabhq').and_return(true) }
+
+ it { expect(get('/gitlab/subgroup/gitlabhq')).to route_to('projects#show', namespace_id: 'gitlab/subgroup', id: 'gitlabhq') }
+ end
+ end
+
+ it 'to #update' do
+ expect(put('/gitlab/gitlabhq')).to route_to('projects#update', namespace_id: 'gitlab', id: 'gitlabhq')
+ end
+
+ it 'to #destroy' do
+ expect(delete('/gitlab/gitlabhq')).to route_to('projects#destroy', namespace_id: 'gitlab', id: 'gitlabhq')
+ end
+
+ it 'to #preview_markdown' do
+ expect(post('/gitlab/gitlabhq/preview_markdown')).to(
+ route_to('projects#preview_markdown', namespace_id: 'gitlab', id: 'gitlabhq')
+ )
+ end
+ end
+
+ # pages_project_wikis GET /:project_id/wikis/pages(.:format) projects/wikis#pages
+ # history_project_wiki GET /:project_id/wikis/:id/history(.:format) projects/wikis#history
+ # project_wikis POST /:project_id/wikis(.:format) projects/wikis#create
+ # edit_project_wiki GET /:project_id/wikis/:id/edit(.:format) projects/wikis#edit
+ # project_wiki GET /:project_id/wikis/:id(.:format) projects/wikis#show
+ # DELETE /:project_id/wikis/:id(.:format) projects/wikis#destroy
+ describe Projects::WikisController, 'routing' do
+ it 'to #pages' do
+ expect(get('/gitlab/gitlabhq/wikis/pages')).to route_to('projects/wikis#pages', namespace_id: 'gitlab', project_id: 'gitlabhq')
+ end
+
+ it 'to #history' do
+ expect(get('/gitlab/gitlabhq/wikis/1/history')).to route_to('projects/wikis#history', namespace_id: 'gitlab', project_id: 'gitlabhq', id: '1')
+ end
+
+ it_behaves_like 'RESTful project resources' do
+ let(:actions) { [:create, :edit, :show, :destroy] }
+ let(:controller) { 'wikis' }
+ end
+ end
+
+ # branches_project_repository GET /:project_id/repository/branches(.:format) projects/repositories#branches
+ # tags_project_repository GET /:project_id/repository/tags(.:format) projects/repositories#tags
+ # archive_project_repository GET /:project_id/repository/archive(.:format) projects/repositories#archive
+ # edit_project_repository GET /:project_id/repository/edit(.:format) projects/repositories#edit
+ describe Projects::RepositoriesController, 'routing' do
+ it 'to #archive' do
+ expect(get('/gitlab/gitlabhq/repository/archive')).to route_to('projects/repositories#archive', namespace_id: 'gitlab', project_id: 'gitlabhq')
+ end
+
+ it 'to #archive format:zip' do
+ expect(get('/gitlab/gitlabhq/repository/archive.zip')).to route_to('projects/repositories#archive', namespace_id: 'gitlab', project_id: 'gitlabhq', format: 'zip')
+ end
+
+ it 'to #archive format:tar.bz2' do
+ expect(get('/gitlab/gitlabhq/repository/archive.tar.bz2')).to route_to('projects/repositories#archive', namespace_id: 'gitlab', project_id: 'gitlabhq', format: 'tar.bz2')
+ end
+ end
+
+ describe Projects::BranchesController, 'routing' do
+ it 'to #branches' do
+ expect(get('/gitlab/gitlabhq/branches')).to route_to('projects/branches#index', namespace_id: 'gitlab', project_id: 'gitlabhq')
+ expect(delete('/gitlab/gitlabhq/branches/feature%2345')).to route_to('projects/branches#destroy', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'feature#45')
+ expect(delete('/gitlab/gitlabhq/branches/feature%2B45')).to route_to('projects/branches#destroy', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'feature+45')
+ expect(delete('/gitlab/gitlabhq/branches/feature@45')).to route_to('projects/branches#destroy', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'feature@45')
+ expect(delete('/gitlab/gitlabhq/branches/feature%2345/foo/bar/baz')).to route_to('projects/branches#destroy', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'feature#45/foo/bar/baz')
+ expect(delete('/gitlab/gitlabhq/branches/feature%2B45/foo/bar/baz')).to route_to('projects/branches#destroy', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'feature+45/foo/bar/baz')
+ expect(delete('/gitlab/gitlabhq/branches/feature@45/foo/bar/baz')).to route_to('projects/branches#destroy', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'feature@45/foo/bar/baz')
+ end
+ end
+
+ describe Projects::TagsController, 'routing' do
+ it 'to #tags' do
+ expect(get('/gitlab/gitlabhq/tags')).to route_to('projects/tags#index', namespace_id: 'gitlab', project_id: 'gitlabhq')
+ expect(delete('/gitlab/gitlabhq/tags/feature%2345')).to route_to('projects/tags#destroy', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'feature#45')
+ expect(delete('/gitlab/gitlabhq/tags/feature%2B45')).to route_to('projects/tags#destroy', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'feature+45')
+ expect(delete('/gitlab/gitlabhq/tags/feature@45')).to route_to('projects/tags#destroy', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'feature@45')
+ expect(delete('/gitlab/gitlabhq/tags/feature%2345/foo/bar/baz')).to route_to('projects/tags#destroy', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'feature#45/foo/bar/baz')
+ expect(delete('/gitlab/gitlabhq/tags/feature%2B45/foo/bar/baz')).to route_to('projects/tags#destroy', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'feature+45/foo/bar/baz')
+ expect(delete('/gitlab/gitlabhq/tags/feature@45/foo/bar/baz')).to route_to('projects/tags#destroy', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'feature@45/foo/bar/baz')
+ end
+ end
+
+ # project_deploy_keys GET /:project_id/deploy_keys(.:format) deploy_keys#index
+ # POST /:project_id/deploy_keys(.:format) deploy_keys#create
+ # new_project_deploy_key GET /:project_id/deploy_keys/new(.:format) deploy_keys#new
+ # project_deploy_key GET /:project_id/deploy_keys/:id(.:format) deploy_keys#show
+ # DELETE /:project_id/deploy_keys/:id(.:format) deploy_keys#destroy
+ describe Projects::DeployKeysController, 'routing' do
+ it_behaves_like 'RESTful project resources' do
+ let(:actions) { [:index, :new, :create] }
+ let(:controller) { 'deploy_keys' }
+ end
+ end
+
+ # project_protected_branches GET /:project_id/protected_branches(.:format) protected_branches#index
+ # POST /:project_id/protected_branches(.:format) protected_branches#create
+ # project_protected_branch DELETE /:project_id/protected_branches/:id(.:format) protected_branches#destroy
+ describe Projects::ProtectedBranchesController, 'routing' do
+ it_behaves_like 'RESTful project resources' do
+ let(:actions) { [:index, :create, :destroy] }
+ let(:controller) { 'protected_branches' }
+ end
+ end
+
+ # switch_project_refs GET /:project_id/refs/switch(.:format) refs#switch
+ # logs_tree_project_ref GET /:project_id/refs/:id/logs_tree(.:format) refs#logs_tree
+ # logs_file_project_ref GET /:project_id/refs/:id/logs_tree/:path(.:format) refs#logs_tree
+ describe Projects::RefsController, 'routing' do
+ it 'to #switch' do
+ expect(get('/gitlab/gitlabhq/refs/switch')).to route_to('projects/refs#switch', namespace_id: 'gitlab', project_id: 'gitlabhq')
+ end
+
+ it 'to #logs_tree' do
+ expect(get('/gitlab/gitlabhq/refs/stable/logs_tree')).to route_to('projects/refs#logs_tree', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'stable')
+ expect(get('/gitlab/gitlabhq/refs/feature%2345/logs_tree')).to route_to('projects/refs#logs_tree', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'feature#45')
+ expect(get('/gitlab/gitlabhq/refs/feature%2B45/logs_tree')).to route_to('projects/refs#logs_tree', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'feature+45')
+ expect(get('/gitlab/gitlabhq/refs/feature@45/logs_tree')).to route_to('projects/refs#logs_tree', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'feature@45')
+ expect(get('/gitlab/gitlabhq/refs/stable/logs_tree/foo/bar/baz')).to route_to('projects/refs#logs_tree', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'stable', path: 'foo/bar/baz')
+ expect(get('/gitlab/gitlabhq/refs/feature%2345/logs_tree/foo/bar/baz')).to route_to('projects/refs#logs_tree', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'feature#45', path: 'foo/bar/baz')
+ expect(get('/gitlab/gitlabhq/refs/feature%2B45/logs_tree/foo/bar/baz')).to route_to('projects/refs#logs_tree', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'feature+45', path: 'foo/bar/baz')
+ expect(get('/gitlab/gitlabhq/refs/feature@45/logs_tree/foo/bar/baz')).to route_to('projects/refs#logs_tree', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'feature@45', path: 'foo/bar/baz')
+ expect(get('/gitlab/gitlabhq/refs/stable/logs_tree/files.scss')).to route_to('projects/refs#logs_tree', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'stable', path: 'files.scss')
+ end
+ end
+
+ # diffs_namespace_project_merge_request GET /:namespace_id/:project_id/merge_requests/:id/diffs(.:format) projects/merge_requests#diffs
+ # commits_namespace_project_merge_request GET /:namespace_id/:project_id/merge_requests/:id/commits(.:format) projects/merge_requests#commits
+ # merge_namespace_project_merge_request POST /:namespace_id/:project_id/merge_requests/:id/merge(.:format) projects/merge_requests#merge
+ # merge_check_namespace_project_merge_request GET /:namespace_id/:project_id/merge_requests/:id/merge_check(.:format) projects/merge_requests#merge_check
+ # ci_status_namespace_project_merge_request GET /:namespace_id/:project_id/merge_requests/:id/ci_status(.:format) projects/merge_requests#ci_status
+ # toggle_subscription_namespace_project_merge_request POST /:namespace_id/:project_id/merge_requests/:id/toggle_subscription(.:format) projects/merge_requests#toggle_subscription
+ # branch_from_namespace_project_merge_requests GET /:namespace_id/:project_id/merge_requests/branch_from(.:format) projects/merge_requests#branch_from
+ # branch_to_namespace_project_merge_requests GET /:namespace_id/:project_id/merge_requests/branch_to(.:format) projects/merge_requests#branch_to
+ # update_branches_namespace_project_merge_requests GET /:namespace_id/:project_id/merge_requests/update_branches(.:format) projects/merge_requests#update_branches
+ # namespace_project_merge_requests GET /:namespace_id/:project_id/merge_requests(.:format) projects/merge_requests#index
+ # POST /:namespace_id/:project_id/merge_requests(.:format) projects/merge_requests#create
+ # new_namespace_project_merge_request GET /:namespace_id/:project_id/merge_requests/new(.:format) projects/merge_requests#new
+ # edit_namespace_project_merge_request GET /:namespace_id/:project_id/merge_requests/:id/edit(.:format) projects/merge_requests#edit
+ # namespace_project_merge_request GET /:namespace_id/:project_id/merge_requests/:id(.:format) projects/merge_requests#show
+ # PATCH /:namespace_id/:project_id/merge_requests/:id(.:format) projects/merge_requests#update
+ # PUT /:namespace_id/:project_id/merge_requests/:id(.:format) projects/merge_requests#update
+ describe Projects::MergeRequestsController, 'routing' do
+ it 'to #diffs' do
+ expect(get('/gitlab/gitlabhq/merge_requests/1/diffs')).to route_to('projects/merge_requests#diffs', namespace_id: 'gitlab', project_id: 'gitlabhq', id: '1')
+ end
+
+ it 'to #commits' do
+ expect(get('/gitlab/gitlabhq/merge_requests/1/commits')).to route_to('projects/merge_requests#commits', namespace_id: 'gitlab', project_id: 'gitlabhq', id: '1')
+ end
+
+ it 'to #merge' do
+ expect(post('/gitlab/gitlabhq/merge_requests/1/merge')).to route_to(
+ 'projects/merge_requests#merge',
+ namespace_id: 'gitlab', project_id: 'gitlabhq', id: '1'
+ )
+ end
+
+ it 'to #merge_check' do
+ expect(get('/gitlab/gitlabhq/merge_requests/1/merge_check')).to route_to('projects/merge_requests#merge_check', namespace_id: 'gitlab', project_id: 'gitlabhq', id: '1')
+ end
+
+ it 'to #branch_from' do
+ expect(get('/gitlab/gitlabhq/merge_requests/branch_from')).to route_to('projects/merge_requests#branch_from', namespace_id: 'gitlab', project_id: 'gitlabhq')
+ end
+
+ it 'to #branch_to' do
+ expect(get('/gitlab/gitlabhq/merge_requests/branch_to')).to route_to('projects/merge_requests#branch_to', namespace_id: 'gitlab', project_id: 'gitlabhq')
+ end
+
+ it 'to #show' do
+ expect(get('/gitlab/gitlabhq/merge_requests/1.diff')).to route_to('projects/merge_requests#show', namespace_id: 'gitlab', project_id: 'gitlabhq', id: '1', format: 'diff')
+ expect(get('/gitlab/gitlabhq/merge_requests/1.patch')).to route_to('projects/merge_requests#show', namespace_id: 'gitlab', project_id: 'gitlabhq', id: '1', format: 'patch')
+ end
+
+ it_behaves_like 'RESTful project resources' do
+ let(:controller) { 'merge_requests' }
+ let(:actions) { [:index, :create, :new, :edit, :show, :update] }
+ end
+ end
+
+ # raw_project_snippet GET /:project_id/snippets/:id/raw(.:format) snippets#raw
+ # project_snippets GET /:project_id/snippets(.:format) snippets#index
+ # POST /:project_id/snippets(.:format) snippets#create
+ # new_project_snippet GET /:project_id/snippets/new(.:format) snippets#new
+ # edit_project_snippet GET /:project_id/snippets/:id/edit(.:format) snippets#edit
+ # project_snippet GET /:project_id/snippets/:id(.:format) snippets#show
+ # PUT /:project_id/snippets/:id(.:format) snippets#update
+ # DELETE /:project_id/snippets/:id(.:format) snippets#destroy
+ describe SnippetsController, 'routing' do
+ it 'to #raw' do
+ expect(get('/gitlab/gitlabhq/snippets/1/raw')).to route_to('projects/snippets#raw', namespace_id: 'gitlab', project_id: 'gitlabhq', id: '1')
+ end
+
+ it 'to #index' do
+ expect(get('/gitlab/gitlabhq/snippets')).to route_to('projects/snippets#index', namespace_id: 'gitlab', project_id: 'gitlabhq')
+ end
+
+ it 'to #create' do
+ expect(post('/gitlab/gitlabhq/snippets')).to route_to('projects/snippets#create', namespace_id: 'gitlab', project_id: 'gitlabhq')
+ end
+
+ it 'to #new' do
+ expect(get('/gitlab/gitlabhq/snippets/new')).to route_to('projects/snippets#new', namespace_id: 'gitlab', project_id: 'gitlabhq')
+ end
+
+ it 'to #edit' do
+ expect(get('/gitlab/gitlabhq/snippets/1/edit')).to route_to('projects/snippets#edit', namespace_id: 'gitlab', project_id: 'gitlabhq', id: '1')
+ end
+
+ it 'to #show' do
+ expect(get('/gitlab/gitlabhq/snippets/1')).to route_to('projects/snippets#show', namespace_id: 'gitlab', project_id: 'gitlabhq', id: '1')
+ end
+
+ it 'to #update' do
+ expect(put('/gitlab/gitlabhq/snippets/1')).to route_to('projects/snippets#update', namespace_id: 'gitlab', project_id: 'gitlabhq', id: '1')
+ end
+
+ it 'to #destroy' do
+ expect(delete('/gitlab/gitlabhq/snippets/1')).to route_to('projects/snippets#destroy', namespace_id: 'gitlab', project_id: 'gitlabhq', id: '1')
+ end
+ end
+
+ # test_project_hook GET /:project_id/hooks/:id/test(.:format) hooks#test
+ # project_hooks GET /:project_id/hooks(.:format) hooks#index
+ # POST /:project_id/hooks(.:format) hooks#create
+ # project_hook DELETE /:project_id/hooks/:id(.:format) hooks#destroy
+ describe Projects::HooksController, 'routing' do
+ it 'to #test' do
+ expect(get('/gitlab/gitlabhq/hooks/1/test')).to route_to('projects/hooks#test', namespace_id: 'gitlab', project_id: 'gitlabhq', id: '1')
+ end
+
+ it_behaves_like 'RESTful project resources' do
+ let(:actions) { [:index, :create, :destroy] }
+ let(:controller) { 'hooks' }
+ end
+ end
+
+ # project_commit GET /:project_id/commit/:id(.:format) commit#show {id: /\h{7,40}/, project_id: /[^\/]+/}
+ describe Projects::CommitController, 'routing' do
+ it 'to #show' do
+ expect(get('/gitlab/gitlabhq/commit/4246fbd')).to route_to('projects/commit#show', namespace_id: 'gitlab', project_id: 'gitlabhq', id: '4246fbd')
+ expect(get('/gitlab/gitlabhq/commit/4246fbd.diff')).to route_to('projects/commit#show', namespace_id: 'gitlab', project_id: 'gitlabhq', id: '4246fbd', format: 'diff')
+ expect(get('/gitlab/gitlabhq/commit/4246fbd.patch')).to route_to('projects/commit#show', namespace_id: 'gitlab', project_id: 'gitlabhq', id: '4246fbd', format: 'patch')
+ expect(get('/gitlab/gitlabhq/commit/4246fbd13872934f72a8fd0d6fb1317b47b59cb5')).to route_to('projects/commit#show', namespace_id: 'gitlab', project_id: 'gitlabhq', id: '4246fbd13872934f72a8fd0d6fb1317b47b59cb5')
+ end
+ end
+
+ # patch_project_commit GET /:project_id/commits/:id/patch(.:format) commits#patch
+ # project_commits GET /:project_id/commits(.:format) commits#index
+ # POST /:project_id/commits(.:format) commits#create
+ # project_commit GET /:project_id/commits/:id(.:format) commits#show
+ describe Projects::CommitsController, 'routing' do
+ it_behaves_like 'RESTful project resources' do
+ let(:actions) { [:show] }
+ let(:controller) { 'commits' }
+ end
+
+ it 'to #show' do
+ expect(get('/gitlab/gitlabhq/commits/master.atom')).to route_to('projects/commits#show', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'master.atom')
+ end
+ end
+
+ # project_project_members GET /:project_id/project_members(.:format) project_members#index
+ # POST /:project_id/project_members(.:format) project_members#create
+ # PUT /:project_id/project_members/:id(.:format) project_members#update
+ # DELETE /:project_id/project_members/:id(.:format) project_members#destroy
+ describe Projects::ProjectMembersController, 'routing' do
+ it_behaves_like 'RESTful project resources' do
+ let(:actions) { [:index, :create, :update, :destroy] }
+ let(:controller) { 'project_members' }
+ end
+ end
+
+ # project_milestones GET /:project_id/milestones(.:format) milestones#index
+ # POST /:project_id/milestones(.:format) milestones#create
+ # new_project_milestone GET /:project_id/milestones/new(.:format) milestones#new
+ # edit_project_milestone GET /:project_id/milestones/:id/edit(.:format) milestones#edit
+ # project_milestone GET /:project_id/milestones/:id(.:format) milestones#show
+ # PUT /:project_id/milestones/:id(.:format) milestones#update
+ # DELETE /:project_id/milestones/:id(.:format) milestones#destroy
+ describe Projects::MilestonesController, 'routing' do
+ it_behaves_like 'RESTful project resources' do
+ let(:controller) { 'milestones' }
+ let(:actions) { [:index, :create, :new, :edit, :show, :update] }
+ end
+ end
+
+ # project_labels GET /:project_id/labels(.:format) labels#index
+ describe Projects::LabelsController, 'routing' do
+ it 'to #index' do
+ expect(get('/gitlab/gitlabhq/labels')).to route_to('projects/labels#index', namespace_id: 'gitlab', project_id: 'gitlabhq')
+ end
+ end
+
+ # sort_project_issues POST /:project_id/issues/sort(.:format) issues#sort
+ # bulk_update_project_issues POST /:project_id/issues/bulk_update(.:format) issues#bulk_update
+ # search_project_issues GET /:project_id/issues/search(.:format) issues#search
+ # project_issues GET /:project_id/issues(.:format) issues#index
+ # POST /:project_id/issues(.:format) issues#create
+ # new_project_issue GET /:project_id/issues/new(.:format) issues#new
+ # edit_project_issue GET /:project_id/issues/:id/edit(.:format) issues#edit
+ # project_issue GET /:project_id/issues/:id(.:format) issues#show
+ # PUT /:project_id/issues/:id(.:format) issues#update
+ # DELETE /:project_id/issues/:id(.:format) issues#destroy
+ describe Projects::IssuesController, 'routing' do
+ it 'to #bulk_update' do
+ expect(post('/gitlab/gitlabhq/issues/bulk_update')).to route_to('projects/issues#bulk_update', namespace_id: 'gitlab', project_id: 'gitlabhq')
+ end
+
+ it_behaves_like 'RESTful project resources' do
+ let(:controller) { 'issues' }
+ let(:actions) { [:index, :create, :new, :edit, :show, :update] }
+ end
+ end
+
+ # project_notes GET /:project_id/notes(.:format) notes#index
+ # POST /:project_id/notes(.:format) notes#create
+ # project_note DELETE /:project_id/notes/:id(.:format) notes#destroy
+ describe Projects::NotesController, 'routing' do
+ it_behaves_like 'RESTful project resources' do
+ let(:actions) { [:index, :create, :destroy] }
+ let(:controller) { 'notes' }
+ end
+ end
+
+ # project_blame GET /:project_id/blame/:id(.:format) blame#show {id: /.+/, project_id: /[^\/]+/}
+ describe Projects::BlameController, 'routing' do
+ it 'to #show' do
+ expect(get('/gitlab/gitlabhq/blame/master/app/models/project.rb')).to route_to('projects/blame#show', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'master/app/models/project.rb')
+ expect(get('/gitlab/gitlabhq/blame/master/files.scss')).to route_to('projects/blame#show', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'master/files.scss')
+ end
+ end
+
+ # project_blob GET /:project_id/blob/:id(.:format) blob#show {id: /.+/, project_id: /[^\/]+/}
+ describe Projects::BlobController, 'routing' do
+ it 'to #show' do
+ expect(get('/gitlab/gitlabhq/blob/master/app/models/project.rb')).to route_to('projects/blob#show', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'master/app/models/project.rb')
+ expect(get('/gitlab/gitlabhq/blob/master/app/models/compare.rb')).to route_to('projects/blob#show', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'master/app/models/compare.rb')
+ expect(get('/gitlab/gitlabhq/blob/master/app/models/diff.js')).to route_to('projects/blob#show', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'master/app/models/diff.js')
+ expect(get('/gitlab/gitlabhq/blob/master/files.scss')).to route_to('projects/blob#show', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'master/files.scss')
+ end
+ end
+
+ # project_tree GET /:project_id/tree/:id(.:format) tree#show {id: /.+/, project_id: /[^\/]+/}
+ describe Projects::TreeController, 'routing' do
+ it 'to #show' do
+ expect(get('/gitlab/gitlabhq/tree/master/app/models/project.rb')).to route_to('projects/tree#show', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'master/app/models/project.rb')
+ expect(get('/gitlab/gitlabhq/tree/master/files.scss')).to route_to('projects/tree#show', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'master/files.scss')
+ end
+ end
+
+ # project_find_file GET /:namespace_id/:project_id/find_file/*id(.:format) projects/find_file#show {:id=>/.+/, :namespace_id=>/[a-zA-Z.0-9_\-]+/, :project_id=>/[a-zA-Z.0-9_\-]+(?<!\.atom)/, :format=>/html/}
+ # project_files GET /:namespace_id/:project_id/files/*id(.:format) projects/find_file#list {:id=>/(?:[^.]|\.(?!json$))+/, :namespace_id=>/[a-zA-Z.0-9_\-]+/, :project_id=>/[a-zA-Z.0-9_\-]+(?<!\.atom)/, :format=>/json/}
+ describe Projects::FindFileController, 'routing' do
+ it 'to #show' do
+ expect(get('/gitlab/gitlabhq/find_file/master')).to route_to('projects/find_file#show', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'master')
+ end
+
+ it 'to #list' do
+ expect(get('/gitlab/gitlabhq/files/master.json')).to route_to('projects/find_file#list', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'master', format: 'json')
+ end
+ end
+
+ describe Projects::BlobController, 'routing' do
+ it 'to #edit' do
+ expect(get('/gitlab/gitlabhq/edit/master/app/models/project.rb')).to(
+ route_to('projects/blob#edit',
+ namespace_id: 'gitlab', project_id: 'gitlabhq',
+ id: 'master/app/models/project.rb'))
+ end
+
+ it 'to #preview' do
+ expect(post('/gitlab/gitlabhq/preview/master/app/models/project.rb')).to(
+ route_to('projects/blob#preview',
+ namespace_id: 'gitlab', project_id: 'gitlabhq',
+ id: 'master/app/models/project.rb'))
+ end
+ end
+
+ # project_compare_index GET /:project_id/compare(.:format) compare#index {id: /[^\/]+/, project_id: /[^\/]+/}
+ # POST /:project_id/compare(.:format) compare#create {id: /[^\/]+/, project_id: /[^\/]+/}
+ # project_compare /:project_id/compare/:from...:to(.:format) compare#show {from: /.+/, to: /.+/, id: /[^\/]+/, project_id: /[^\/]+/}
+ describe Projects::CompareController, 'routing' do
+ it 'to #index' do
+ expect(get('/gitlab/gitlabhq/compare')).to route_to('projects/compare#index', namespace_id: 'gitlab', project_id: 'gitlabhq')
+ end
+
+ it 'to #compare' do
+ expect(post('/gitlab/gitlabhq/compare')).to route_to('projects/compare#create', namespace_id: 'gitlab', project_id: 'gitlabhq')
+ end
+
+ it 'to #show' do
+ expect(get('/gitlab/gitlabhq/compare/master...stable')).to route_to('projects/compare#show', namespace_id: 'gitlab', project_id: 'gitlabhq', from: 'master', to: 'stable')
+ expect(get('/gitlab/gitlabhq/compare/issue/1234...stable')).to route_to('projects/compare#show', namespace_id: 'gitlab', project_id: 'gitlabhq', from: 'issue/1234', to: 'stable')
+ end
+ end
+
+ describe Projects::NetworkController, 'routing' do
+ it 'to #show' do
+ expect(get('/gitlab/gitlabhq/network/master')).to route_to('projects/network#show', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'master')
+ expect(get('/gitlab/gitlabhq/network/ends-with.json')).to route_to('projects/network#show', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'ends-with.json')
+ expect(get('/gitlab/gitlabhq/network/master?format=json')).to route_to('projects/network#show', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'master', format: 'json')
+ end
+ end
+
+ describe Projects::GraphsController, 'routing' do
+ it 'to #show' do
+ expect(get('/gitlab/gitlabhq/graphs/master')).to route_to('projects/graphs#show', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'master')
+ expect(get('/gitlab/gitlabhq/graphs/ends-with.json')).to route_to('projects/graphs#show', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'ends-with.json')
+ expect(get('/gitlab/gitlabhq/graphs/master?format=json')).to route_to('projects/graphs#show', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'master', format: 'json')
+ end
+ end
+
+ describe Projects::ForksController, 'routing' do
+ it 'to #new' do
+ expect(get('/gitlab/gitlabhq/forks/new')).to route_to('projects/forks#new', namespace_id: 'gitlab', project_id: 'gitlabhq')
+ end
+
+ it 'to #create' do
+ expect(post('/gitlab/gitlabhq/forks')).to route_to('projects/forks#create', namespace_id: 'gitlab', project_id: 'gitlabhq')
+ end
+ end
+
+ # project_avatar DELETE /project/avatar(.:format) projects/avatars#destroy
+ describe Projects::AvatarsController, 'routing' do
+ it 'to #destroy' do
+ expect(delete('/gitlab/gitlabhq/avatar')).to route_to(
+ 'projects/avatars#destroy', namespace_id: 'gitlab', project_id: 'gitlabhq')
+ end
end
end
diff --git a/spec/routing/routing_spec.rb b/spec/routing/routing_spec.rb
index f15c45cbaac..9f6defe1450 100644
--- a/spec/routing/routing_spec.rb
+++ b/spec/routing/routing_spec.rb
@@ -9,7 +9,7 @@ require 'spec_helper'
# user_calendar_activities GET /u/:username/calendar_activities(.:format)
describe UsersController, "routing" do
it "to #show" do
- allow(User).to receive(:find_by).and_return(true)
+ allow_any_instance_of(UserUrlConstrainer).to receive(:matches?).and_return(true)
expect(get("/User")).to route_to('users#show', username: 'User')
end
@@ -195,6 +195,8 @@ describe Profiles::KeysController, "routing" do
# get all the ssh-keys of a user
it "to #get_keys" do
+ allow_any_instance_of(UserUrlConstrainer).to receive(:matches?).and_return(true)
+
expect(get("/foo.keys")).to route_to('profiles/keys#get_keys', username: 'foo')
end
end
@@ -263,13 +265,17 @@ end
describe "Groups", "routing" do
let(:name) { 'complex.group-namegit' }
+ before { allow_any_instance_of(GroupUrlConstrainer).to receive(:matches?).and_return(true) }
+
it "to #show" do
expect(get("/groups/#{name}")).to route_to('groups#show', id: name)
end
- it "also display group#show on the short path" do
- allow(Group).to receive(:find_by).and_return(true)
+ it "also supports nested groups" do
+ expect(get("/#{name}/#{name}")).to route_to('groups#show', id: "#{name}/#{name}")
+ end
+ it "also display group#show on the short path" do
expect(get("/#{name}")).to route_to('groups#show', id: name)
end
@@ -284,6 +290,10 @@ describe "Groups", "routing" do
it "to #members" do
expect(get("/groups/#{name}/group_members")).to route_to('groups/group_members#index', group_id: name)
end
+
+ it "also display group#show with slash in the path" do
+ expect(get('/group/subgroup')).to route_to('groups#show', id: 'group/subgroup')
+ end
end
describe HealthCheckController, 'routing' do
diff --git a/spec/serializers/analytics_build_serializer_spec.rb b/spec/serializers/analytics_build_serializer_spec.rb
index a0a9d9a5f12..f0551c78671 100644
--- a/spec/serializers/analytics_build_serializer_spec.rb
+++ b/spec/serializers/analytics_build_serializer_spec.rb
@@ -10,10 +10,6 @@ describe AnalyticsBuildSerializer do
let(:resource) { create(:ci_build) }
context 'when there is a single object provided' do
- it 'it generates payload for single object' do
- expect(json).to be_an_instance_of Hash
- end
-
it 'contains important elements of analyticsBuild' do
expect(json)
.to include(:name, :branch, :short_sha, :date, :total_time, :url, :author)
diff --git a/spec/serializers/analytics_issue_serializer_spec.rb b/spec/serializers/analytics_issue_serializer_spec.rb
index 2842e1ba52f..6afbb2df35c 100644
--- a/spec/serializers/analytics_issue_serializer_spec.rb
+++ b/spec/serializers/analytics_issue_serializer_spec.rb
@@ -22,10 +22,6 @@ describe AnalyticsIssueSerializer do
end
context 'when there is a single object provided' do
- it 'it generates payload for single object' do
- expect(json).to be_an_instance_of Hash
- end
-
it 'contains important elements of the issue' do
expect(json).to include(:title, :iid, :created_at, :total_time, :url, :author)
end
diff --git a/spec/serializers/analytics_merge_request_serializer_spec.rb b/spec/serializers/analytics_merge_request_serializer_spec.rb
index 564207984df..cdfae27193f 100644
--- a/spec/serializers/analytics_merge_request_serializer_spec.rb
+++ b/spec/serializers/analytics_merge_request_serializer_spec.rb
@@ -23,10 +23,6 @@ describe AnalyticsMergeRequestSerializer do
end
context 'when there is a single object provided' do
- it 'it generates payload for single object' do
- expect(json).to be_an_instance_of Hash
- end
-
it 'contains important elements of the merge request' do
expect(json).to include(:title, :iid, :created_at, :total_time, :url, :author, :state)
end
diff --git a/spec/serializers/environment_serializer_spec.rb b/spec/serializers/environment_serializer_spec.rb
index 8f95c9250b0..b7ed4eb0239 100644
--- a/spec/serializers/environment_serializer_spec.rb
+++ b/spec/serializers/environment_serializer_spec.rb
@@ -27,10 +27,6 @@ describe EnvironmentSerializer do
let(:deployable) { create(:ci_build) }
let(:resource) { deployment.environment }
- it 'it generates payload for single object' do
- expect(json).to be_an_instance_of Hash
- end
-
it 'contains important elements of environment' do
expect(json)
.to include(:name, :external_url, :environment_path, :last_deployment)
diff --git a/spec/services/issues/close_service_spec.rb b/spec/services/issues/close_service_spec.rb
index 4465f22a001..7a54373963e 100644
--- a/spec/services/issues/close_service_spec.rb
+++ b/spec/services/issues/close_service_spec.rb
@@ -62,7 +62,7 @@ describe Issues::CloseService, services: true do
it 'creates system note about issue reassign' do
note = issue.notes.last
- expect(note.note).to include "Status changed to closed"
+ expect(note.note).to include "closed"
end
it 'marks todos as done' do
diff --git a/spec/services/issues/move_service_spec.rb b/spec/services/issues/move_service_spec.rb
index f0ded06b785..c7de0d0c534 100644
--- a/spec/services/issues/move_service_spec.rb
+++ b/spec/services/issues/move_service_spec.rb
@@ -81,11 +81,11 @@ describe Issues::MoveService, services: true do
end
it 'adds system note to old issue at the end' do
- expect(old_issue.notes.last.note).to match /^Moved to/
+ expect(old_issue.notes.last.note).to start_with 'moved to'
end
it 'adds system note to new issue at the end' do
- expect(new_issue.notes.last.note).to match /^Moved from/
+ expect(new_issue.notes.last.note).to start_with 'moved from'
end
it 'closes old issue' do
@@ -151,7 +151,7 @@ describe Issues::MoveService, services: true do
end
it 'adds a system note about move after rewritten notes' do
- expect(system_notes.last.note).to match /^Moved from/
+ expect(system_notes.last.note).to match /^moved from/
end
it 'preserves orignal author of comment' do
diff --git a/spec/services/issues/update_service_spec.rb b/spec/services/issues/update_service_spec.rb
index 4777a90639e..4c878d748c0 100644
--- a/spec/services/issues/update_service_spec.rb
+++ b/spec/services/issues/update_service_spec.rb
@@ -91,24 +91,24 @@ describe Issues::UpdateService, services: true do
end
it 'creates system note about issue reassign' do
- note = find_note('Reassigned to')
+ note = find_note('assigned to')
expect(note).not_to be_nil
- expect(note.note).to include "Reassigned to \@#{user2.username}"
+ expect(note.note).to include "assigned to #{user2.to_reference}"
end
it 'creates system note about issue label edit' do
- note = find_note('Added ~')
+ note = find_note('added ~')
expect(note).not_to be_nil
- expect(note.note).to include "Added ~#{label.id} label"
+ expect(note.note).to include "added #{label.to_reference} label"
end
it 'creates system note about title change' do
- note = find_note('Changed title:')
+ note = find_note('changed title')
expect(note).not_to be_nil
- expect(note.note).to eq 'Changed title: **{-Old-} title** → **{+New+} title**'
+ expect(note.note).to eq 'changed title from **{-Old-} title** to **{+New+} title**'
end
end
end
@@ -128,10 +128,10 @@ describe Issues::UpdateService, services: true do
it 'creates system note about confidentiality change' do
update_issue(confidential: true)
- note = find_note('Made the issue confidential')
+ note = find_note('made the issue confidential')
expect(note).not_to be_nil
- expect(note.note).to eq 'Made the issue confidential'
+ expect(note.note).to eq 'made the issue confidential'
end
it 'executes confidential issue hooks' do
@@ -269,8 +269,8 @@ describe Issues::UpdateService, services: true do
before { update_issue(description: "- [x] Task 1\n- [X] Task 2") }
it 'creates system note about task status change' do
- note1 = find_note('Marked the task **Task 1** as completed')
- note2 = find_note('Marked the task **Task 2** as completed')
+ note1 = find_note('marked the task **Task 1** as completed')
+ note2 = find_note('marked the task **Task 2** as completed')
expect(note1).not_to be_nil
expect(note2).not_to be_nil
@@ -284,8 +284,8 @@ describe Issues::UpdateService, services: true do
end
it 'creates system note about task status change' do
- note1 = find_note('Marked the task **Task 1** as incomplete')
- note2 = find_note('Marked the task **Task 2** as incomplete')
+ note1 = find_note('marked the task **Task 1** as incomplete')
+ note2 = find_note('marked the task **Task 2** as incomplete')
expect(note1).not_to be_nil
expect(note2).not_to be_nil
@@ -299,7 +299,7 @@ describe Issues::UpdateService, services: true do
end
it 'does not create a system note' do
- note = find_note('Marked the task **Task 2** as incomplete')
+ note = find_note('marked the task **Task 2** as incomplete')
expect(note).to be_nil
end
@@ -312,7 +312,7 @@ describe Issues::UpdateService, services: true do
end
it 'does not create a system note referencing the position the old item' do
- note = find_note('Marked the task **Two** as incomplete')
+ note = find_note('marked the task **Two** as incomplete')
expect(note).to be_nil
end
diff --git a/spec/services/merge_requests/close_service_spec.rb b/spec/services/merge_requests/close_service_spec.rb
index 24c25e4350f..5f6a7716beb 100644
--- a/spec/services/merge_requests/close_service_spec.rb
+++ b/spec/services/merge_requests/close_service_spec.rb
@@ -42,7 +42,7 @@ describe MergeRequests::CloseService, services: true do
it 'creates system note about merge_request reassign' do
note = @merge_request.notes.last
- expect(note.note).to include 'Status changed to closed'
+ expect(note.note).to include 'closed'
end
it 'marks todos as done' do
diff --git a/spec/services/merge_requests/merge_service_spec.rb b/spec/services/merge_requests/merge_service_spec.rb
index 7db32a33c93..dff1781d2aa 100644
--- a/spec/services/merge_requests/merge_service_spec.rb
+++ b/spec/services/merge_requests/merge_service_spec.rb
@@ -34,7 +34,7 @@ describe MergeRequests::MergeService, services: true do
it 'creates system note about merge_request merge' do
note = merge_request.notes.last
- expect(note.note).to include 'Status changed to merged'
+ expect(note.note).to include 'merged'
end
end
diff --git a/spec/services/merge_requests/merge_when_build_succeeds_service_spec.rb b/spec/services/merge_requests/merge_when_build_succeeds_service_spec.rb
index 1f90efdbd6a..c0164138713 100644
--- a/spec/services/merge_requests/merge_when_build_succeeds_service_spec.rb
+++ b/spec/services/merge_requests/merge_when_build_succeeds_service_spec.rb
@@ -34,7 +34,7 @@ describe MergeRequests::MergeWhenBuildSucceedsService do
it 'creates a system note' do
note = merge_request.notes.last
- expect(note.note).to match /Enabled an automatic merge when the build for (\w+\/\w+@)?[0-9a-z]{8}/
+ expect(note.note).to match /enabled an automatic merge when the build for (\w+\/\w+@)?\h{8}/
end
end
@@ -113,7 +113,7 @@ describe MergeRequests::MergeWhenBuildSucceedsService do
it 'Posts a system note' do
note = mr_merge_if_green_enabled.notes.last
- expect(note.note).to include 'Canceled the automatic merge'
+ expect(note.note).to include 'canceled the automatic merge'
end
end
diff --git a/spec/services/merge_requests/refresh_service_spec.rb b/spec/services/merge_requests/refresh_service_spec.rb
index e515bc9f89c..bc340ff9d3c 100644
--- a/spec/services/merge_requests/refresh_service_spec.rb
+++ b/spec/services/merge_requests/refresh_service_spec.rb
@@ -76,10 +76,10 @@ describe MergeRequests::RefreshService, services: true do
reload_mrs
end
- it { expect(@merge_request.notes.last.note).to include('changed to merged') }
+ it { expect(@merge_request.notes.last.note).to include('merged') }
it { expect(@merge_request).to be_merged }
it { expect(@fork_merge_request).to be_merged }
- it { expect(@fork_merge_request.notes.last.note).to include('changed to merged') }
+ it { expect(@fork_merge_request.notes.last.note).to include('merged') }
it { expect(@build_failed_todo).to be_done }
it { expect(@fork_build_failed_todo).to be_done }
end
@@ -95,11 +95,11 @@ describe MergeRequests::RefreshService, services: true do
reload_mrs
end
- it { expect(@merge_request.notes.last.note).to include('changed to merged') }
+ it { expect(@merge_request.notes.last.note).to include('merged') }
it { expect(@merge_request).to be_merged }
it { expect(@merge_request.diffs.size).to be > 0 }
it { expect(@fork_merge_request).to be_merged }
- it { expect(@fork_merge_request.notes.last.note).to include('changed to merged') }
+ it { expect(@fork_merge_request.notes.last.note).to include('merged') }
it { expect(@build_failed_todo).to be_done }
it { expect(@fork_build_failed_todo).to be_done }
end
@@ -119,7 +119,7 @@ describe MergeRequests::RefreshService, services: true do
it { expect(@merge_request.notes).to be_empty }
it { expect(@merge_request).to be_open }
- it { expect(@fork_merge_request.notes.last.note).to include('Added 28 commits') }
+ it { expect(@fork_merge_request.notes.last.note).to include('added 28 commits') }
it { expect(@fork_merge_request).to be_open }
it { expect(@build_failed_todo).to be_pending }
it { expect(@fork_build_failed_todo).to be_pending }
@@ -146,7 +146,7 @@ describe MergeRequests::RefreshService, services: true do
reload_mrs
end
- it { expect(@merge_request.notes.last.note).to include('changed to merged') }
+ it { expect(@merge_request.notes.last.note).to include('merged') }
it { expect(@merge_request).to be_merged }
it { expect(@fork_merge_request).to be_open }
it { expect(@fork_merge_request.notes).to be_empty }
@@ -169,8 +169,8 @@ describe MergeRequests::RefreshService, services: true do
expect(@merge_request).to be_open
notes = @fork_merge_request.notes.reorder(:created_at).map(&:note)
- expect(notes[0]).to include('Restored source branch `master`')
- expect(notes[1]).to include('Added 28 commits')
+ expect(notes[0]).to include('restored source branch `master`')
+ expect(notes[1]).to include('added 28 commits')
expect(@fork_merge_request).to be_open
end
end
diff --git a/spec/services/merge_requests/reopen_service_spec.rb b/spec/services/merge_requests/reopen_service_spec.rb
index af7424a76a9..a99d4eac9bd 100644
--- a/spec/services/merge_requests/reopen_service_spec.rb
+++ b/spec/services/merge_requests/reopen_service_spec.rb
@@ -41,7 +41,7 @@ describe MergeRequests::ReopenService, services: true do
it 'creates system note about merge_request reopen' do
note = merge_request.notes.last
- expect(note.note).to include 'Status changed to reopened'
+ expect(note.note).to include 'reopened'
end
end
diff --git a/spec/services/merge_requests/update_service_spec.rb b/spec/services/merge_requests/update_service_spec.rb
index cb5d7cdb467..0bd6db1810a 100644
--- a/spec/services/merge_requests/update_service_spec.rb
+++ b/spec/services/merge_requests/update_service_spec.rb
@@ -79,31 +79,31 @@ describe MergeRequests::UpdateService, services: true do
end
it 'creates system note about merge_request reassign' do
- note = find_note('Reassigned to')
+ note = find_note('assigned to')
expect(note).not_to be_nil
- expect(note.note).to include "Reassigned to \@#{user2.username}"
+ expect(note.note).to include "assigned to #{user2.to_reference}"
end
it 'creates system note about merge_request label edit' do
- note = find_note('Added ~')
+ note = find_note('added ~')
expect(note).not_to be_nil
- expect(note.note).to include "Added ~#{label.id} label"
+ expect(note.note).to include "added #{label.to_reference} label"
end
it 'creates system note about title change' do
- note = find_note('Changed title:')
+ note = find_note('changed title')
expect(note).not_to be_nil
- expect(note.note).to eq 'Changed title: **{-Old-} title** → **{+New+} title**'
+ expect(note.note).to eq 'changed title from **{-Old-} title** to **{+New+} title**'
end
it 'creates system note about branch change' do
- note = find_note('Target')
+ note = find_note('changed target')
expect(note).not_to be_nil
- expect(note.note).to eq 'Target branch changed from `master` to `target`'
+ expect(note.note).to eq 'changed target branch from `master` to `target`'
end
context 'when not including source branch removal options' do
@@ -258,8 +258,8 @@ describe MergeRequests::UpdateService, services: true do
before { update_merge_request({ description: "- [x] Task 1\n- [X] Task 2" }) }
it 'creates system note about task status change' do
- note1 = find_note('Marked the task **Task 1** as completed')
- note2 = find_note('Marked the task **Task 2** as completed')
+ note1 = find_note('marked the task **Task 1** as completed')
+ note2 = find_note('marked the task **Task 2** as completed')
expect(note1).not_to be_nil
expect(note2).not_to be_nil
@@ -273,8 +273,8 @@ describe MergeRequests::UpdateService, services: true do
end
it 'creates system note about task status change' do
- note1 = find_note('Marked the task **Task 1** as incomplete')
- note2 = find_note('Marked the task **Task 2** as incomplete')
+ note1 = find_note('marked the task **Task 1** as incomplete')
+ note2 = find_note('marked the task **Task 2** as incomplete')
expect(note1).not_to be_nil
expect(note2).not_to be_nil
diff --git a/spec/services/system_note_service_spec.rb b/spec/services/system_note_service_spec.rb
index 2a5709c6322..4a8f6c321aa 100644
--- a/spec/services/system_note_service_spec.rb
+++ b/spec/services/system_note_service_spec.rb
@@ -50,7 +50,7 @@ describe SystemNoteService, services: true do
context 'without existing commits' do
it 'adds a message header' do
- expect(note_lines[0]).to eq "Added #{new_commits.size} commits:"
+ expect(note_lines[0]).to eq "added #{new_commits.size} commits"
end
it 'adds a message line for each commit' do
@@ -120,7 +120,7 @@ describe SystemNoteService, services: true do
context 'when assignee added' do
it 'sets the note text' do
- expect(subject.note).to eq "Reassigned to @#{assignee.username}"
+ expect(subject.note).to eq "assigned to @#{assignee.username}"
end
end
@@ -128,7 +128,7 @@ describe SystemNoteService, services: true do
let(:assignee) { nil }
it 'sets the note text' do
- expect(subject.note).to eq 'Assignee removed'
+ expect(subject.note).to eq 'removed assignee'
end
end
end
@@ -147,7 +147,7 @@ describe SystemNoteService, services: true do
let(:removed) { [] }
it 'sets the note text' do
- expect(subject.note).to eq "Added ~#{labels[0].id} ~#{labels[1].id} labels"
+ expect(subject.note).to eq "added ~#{labels[0].id} ~#{labels[1].id} labels"
end
end
@@ -156,7 +156,7 @@ describe SystemNoteService, services: true do
let(:removed) { labels }
it 'sets the note text' do
- expect(subject.note).to eq "Removed ~#{labels[0].id} ~#{labels[1].id} labels"
+ expect(subject.note).to eq "removed ~#{labels[0].id} ~#{labels[1].id} labels"
end
end
@@ -165,7 +165,7 @@ describe SystemNoteService, services: true do
let(:removed) { [labels[1]] }
it 'sets the note text' do
- expect(subject.note).to eq "Added ~#{labels[0].id} and removed ~#{labels[1].id} labels"
+ expect(subject.note).to eq "added ~#{labels[0].id} and removed ~#{labels[1].id} labels"
end
end
end
@@ -179,7 +179,7 @@ describe SystemNoteService, services: true do
context 'when milestone added' do
it 'sets the note text' do
- expect(subject.note).to eq "Milestone changed to #{milestone.to_reference}"
+ expect(subject.note).to eq "changed milestone to #{milestone.to_reference}"
end
end
@@ -187,7 +187,7 @@ describe SystemNoteService, services: true do
let(:milestone) { nil }
it 'sets the note text' do
- expect(subject.note).to eq 'Milestone removed'
+ expect(subject.note).to eq 'removed milestone'
end
end
end
@@ -204,13 +204,13 @@ describe SystemNoteService, services: true do
let(:source) { double('commit', gfm_reference: 'commit 123456') }
it 'sets the note text' do
- expect(subject.note).to eq "Status changed to #{status} by commit 123456"
+ expect(subject.note).to eq "#{status} via commit 123456"
end
end
context 'without a source' do
it 'sets the note text' do
- expect(subject.note).to eq "Status changed to #{status}"
+ expect(subject.note).to eq status
end
end
end
@@ -226,7 +226,7 @@ describe SystemNoteService, services: true do
it_behaves_like 'a system note'
it "posts the Merge When Build Succeeds system note" do
- expect(subject.note).to match /Enabled an automatic merge when the build for (\w+\/\w+@)?[0-9a-f]{40} succeeds/
+ expect(subject.note).to match /enabled an automatic merge when the build for (\w+\/\w+@)?\h{40} succeeds/
end
end
@@ -240,7 +240,7 @@ describe SystemNoteService, services: true do
it_behaves_like 'a system note'
it "posts the Merge When Build Succeeds system note" do
- expect(subject.note).to eq "Canceled the automatic merge"
+ expect(subject.note).to eq "canceled the automatic merge"
end
end
@@ -252,7 +252,7 @@ describe SystemNoteService, services: true do
it 'sets the note text' do
expect(subject.note).
- to eq "Changed title: **{-Old title-}** → **{+#{noteable.title}+}**"
+ to eq "changed title from **{-Old title-}** to **{+#{noteable.title}+}**"
end
end
end
@@ -264,7 +264,7 @@ describe SystemNoteService, services: true do
it_behaves_like 'a system note'
it 'sets the note text' do
- expect(subject.note).to eq 'Made the issue visible'
+ expect(subject.note).to eq 'made the issue visible to everyone'
end
end
end
@@ -278,7 +278,7 @@ describe SystemNoteService, services: true do
context 'when target branch name changed' do
it 'sets the note text' do
- expect(subject.note).to eq "Target branch changed from `#{old_branch}` to `#{new_branch}`"
+ expect(subject.note).to eq "changed target branch from `#{old_branch}` to `#{new_branch}`"
end
end
end
@@ -290,7 +290,7 @@ describe SystemNoteService, services: true do
context 'when source branch deleted' do
it 'sets the note text' do
- expect(subject.note).to eq "Deleted source branch `feature`"
+ expect(subject.note).to eq "deleted source branch `feature`"
end
end
end
@@ -302,7 +302,7 @@ describe SystemNoteService, services: true do
context 'when a branch is created from the new branch button' do
it 'sets the note text' do
- expect(subject.note).to match /\AStarted branch [`1-mepmep`]/
+ expect(subject.note).to match /\Acreated branch [`1-mepmep`]/
end
end
end
@@ -338,13 +338,13 @@ describe SystemNoteService, services: true do
let(:mentioner) { project2.repository.commit }
it 'references the mentioning commit' do
- expect(subject.note).to eq "Mentioned in commit #{mentioner.to_reference(project)}"
+ expect(subject.note).to eq "mentioned in commit #{mentioner.to_reference(project)}"
end
end
context 'from non-Commit' do
it 'references the mentioning object' do
- expect(subject.note).to eq "Mentioned in issue #{mentioner.to_reference(project)}"
+ expect(subject.note).to eq "mentioned in issue #{mentioner.to_reference(project)}"
end
end
end
@@ -354,13 +354,13 @@ describe SystemNoteService, services: true do
let(:mentioner) { project.repository.commit }
it 'references the mentioning commit' do
- expect(subject.note).to eq "Mentioned in commit #{mentioner.to_reference}"
+ expect(subject.note).to eq "mentioned in commit #{mentioner.to_reference}"
end
end
context 'from non-Commit' do
it 'references the mentioning object' do
- expect(subject.note).to eq "Mentioned in issue #{mentioner.to_reference}"
+ expect(subject.note).to eq "mentioned in issue #{mentioner.to_reference}"
end
end
end
@@ -370,7 +370,11 @@ describe SystemNoteService, services: true do
describe '.cross_reference?' do
it 'is truthy when text begins with expected text' do
- expect(described_class.cross_reference?('Mentioned in something')).to be_truthy
+ expect(described_class.cross_reference?('mentioned in something')).to be_truthy
+ end
+
+ it 'is truthy when text begins with legacy capitalized expected text' do
+ expect(described_class.cross_reference?('mentioned in something')).to be_truthy
end
it 'is falsey when text does not begin with expected text' do
@@ -433,6 +437,19 @@ describe SystemNoteService, services: true do
expect(described_class.cross_reference_exists?(noteable, commit1)).
to be_falsey
end
+
+ context 'legacy capitalized cross reference' do
+ before do
+ # Mention issue (noteable) from commit0
+ system_note = described_class.cross_reference(noteable, commit0, author)
+ system_note.update(note: system_note.note.capitalize)
+ end
+
+ it 'is truthy when already mentioned' do
+ expect(described_class.cross_reference_exists?(noteable, commit0)).
+ to be_truthy
+ end
+ end
end
context 'commit from commit' do
@@ -450,6 +467,19 @@ describe SystemNoteService, services: true do
expect(described_class.cross_reference_exists?(commit1, commit0)).
to be_falsey
end
+
+ context 'legacy capitalized cross reference' do
+ before do
+ # Mention commit1 from commit0
+ system_note = described_class.cross_reference(commit0, commit1, author)
+ system_note.update(note: system_note.note.capitalize)
+ end
+
+ it 'is truthy when already mentioned' do
+ expect(described_class.cross_reference_exists?(commit0, commit1)).
+ to be_truthy
+ end
+ end
end
context 'commit with cross-reference from fork' do
@@ -465,6 +495,18 @@ describe SystemNoteService, services: true do
expect(described_class.cross_reference_exists?(noteable, commit2)).
to be true
end
+
+ context 'legacy capitalized cross reference' do
+ before do
+ system_note = described_class.cross_reference(noteable, commit0, author2)
+ system_note.update(note: system_note.note.capitalize)
+ end
+
+ it 'is true when a fork mentions an external issue' do
+ expect(described_class.cross_reference_exists?(noteable, commit2)).
+ to be true
+ end
+ end
end
end
@@ -498,7 +540,7 @@ describe SystemNoteService, services: true do
it_behaves_like 'cross project mentionable'
it 'notifies about noteable being moved to' do
- expect(subject.note).to match /Moved to/
+ expect(subject.note).to match /moved to/
end
end
@@ -508,7 +550,7 @@ describe SystemNoteService, services: true do
it_behaves_like 'cross project mentionable'
it 'notifies about noteable being moved from' do
- expect(subject.note).to match /Moved from/
+ expect(subject.note).to match /moved from/
end
end
diff --git a/spec/support/carrierwave.rb b/spec/support/carrierwave.rb
index aa89afd8fb3..72af2c70324 100644
--- a/spec/support/carrierwave.rb
+++ b/spec/support/carrierwave.rb
@@ -1,7 +1,7 @@
CarrierWave.root = 'tmp/tests/uploads'
RSpec.configure do |config|
- config.after(:suite) do
+ config.after(:each) do
FileUtils.rm_rf('tmp/tests/uploads')
end
end
diff --git a/spec/support/setup_builds_storage.rb b/spec/support/setup_builds_storage.rb
index a4f21e95338..2e7c88bfc09 100644
--- a/spec/support/setup_builds_storage.rb
+++ b/spec/support/setup_builds_storage.rb
@@ -1,19 +1,18 @@
RSpec.configure do |config|
def builds_path
- Rails.root.join('tmp/builds')
+ Rails.root.join('tmp/tests/builds')
end
- config.before(:each) do
- FileUtils.mkdir_p(builds_path)
- FileUtils.touch(File.join(builds_path, ".gitkeep"))
+ config.before(:suite) do
Settings.gitlab_ci['builds_path'] = builds_path
end
- config.after(:suite) do
- Dir[File.join(builds_path, '*')].each do |path|
- next if File.basename(path) == '.gitkeep'
+ config.before(:all) do
+ FileUtils.mkdir_p(builds_path)
+ end
- FileUtils.rm_rf(path)
- end
+ config.before(:each) do
+ FileUtils.rm_rf(builds_path)
+ FileUtils.mkdir_p(builds_path)
end
end
diff --git a/spec/views/projects/builds/show.html.haml_spec.rb b/spec/views/projects/builds/show.html.haml_spec.rb
index e0c77201116..745d0c745bd 100644
--- a/spec/views/projects/builds/show.html.haml_spec.rb
+++ b/spec/views/projects/builds/show.html.haml_spec.rb
@@ -88,16 +88,46 @@ describe 'projects/builds/show', :view do
create(:ci_build, :running, environment: 'staging', pipeline: pipeline)
end
- let!(:environment) do
- create(:environment, name: 'staging', project: project)
- end
-
- it 'shows deployment message' do
- expected_text = 'This build is creating a deployment to staging'
- render
-
- expect(rendered).to have_css(
- '.environment-information', text: expected_text)
+ context 'when environment exists' do
+ let!(:environment) do
+ create(:environment, name: 'staging', project: project)
+ end
+
+ it 'shows deployment message' do
+ expected_text = 'This build is creating a deployment to staging'
+ render
+
+ expect(rendered).to have_css(
+ '.environment-information', text: expected_text)
+ end
+
+ context 'when it has deployment' do
+ let!(:deployment) do
+ create(:deployment, environment: environment)
+ end
+
+ it 'shows that deployment will be overwritten' do
+ expected_text = 'This build is creating a deployment to staging'
+ render
+
+ expect(rendered).to have_css(
+ '.environment-information', text: expected_text)
+ expect(rendered).to have_css(
+ '.environment-information', text: 'latest deployment')
+ end
+ end
+ end
+
+ context 'when environment does not exist' do
+ it 'shows deployment message' do
+ expected_text = 'This build is creating a deployment to staging'
+ render
+
+ expect(rendered).to have_css(
+ '.environment-information', text: expected_text)
+ expect(rendered).not_to have_css(
+ '.environment-information', text: 'latest deployment')
+ end
end
end
@@ -134,6 +164,8 @@ describe 'projects/builds/show', :view do
expect(rendered).to have_css(
'.environment-information', text: expected_text)
+ expect(rendered).not_to have_css(
+ '.environment-information', text: 'latest deployment')
end
end
end
diff --git a/spec/views/projects/edit.html.haml_spec.rb b/spec/views/projects/edit.html.haml_spec.rb
new file mode 100644
index 00000000000..d2575702ecc
--- /dev/null
+++ b/spec/views/projects/edit.html.haml_spec.rb
@@ -0,0 +1,24 @@
+require 'spec_helper'
+
+describe 'projects/edit' do
+ include Devise::Test::ControllerHelpers
+
+ let(:project) { create(:empty_project) }
+ let(:user) { create(:admin) }
+
+ before do
+ assign(:project, project)
+
+ allow(controller).to receive(:current_user).and_return(user)
+ allow(view).to receive_messages(current_user: user, can?: true)
+ allow(Gitlab.config.lfs).to receive(:enabled).and_return(true)
+ end
+
+ context 'LFS enabled setting' do
+ it 'displays the correct elements' do
+ render
+ expect(rendered).to have_select('project_lfs_enabled')
+ expect(rendered).to have_content('Git Large File Storage')
+ end
+ end
+end