summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLuke "Jared" Bennett <lbennett@gitlab.com>2017-04-05 14:48:00 +0100
committerLuke "Jared" Bennett <lbennett@gitlab.com>2017-04-05 14:48:00 +0100
commitebb89ebfc08a815836ec66bd4c89f72fcc4ea90b (patch)
treed6d8b62f722ca29b7d6b0a36f0d33b4b42d94924
parent8d5d43dbfb228e94330914b0072d2ca5ee2acf82 (diff)
parentd4349ba6c4960f50dce7b0beec5f309894dbada9 (diff)
downloadgitlab-ce-20838-getting-started-better-empty-state-for-milestones-view.tar.gz
Merge branch 'master' into 20838-getting-started-better-empty-state-for-milestones-view20838-getting-started-better-empty-state-for-milestones-view
-rw-r--r--.gitlab-ci.yml7
-rw-r--r--CHANGELOG.md17
-rw-r--r--GITALY_SERVER_VERSION2
-rw-r--r--Gemfile2
-rw-r--r--Gemfile.lock6
-rw-r--r--app/assets/javascripts/boards/components/board.js4
-rw-r--r--app/assets/javascripts/boards/components/board_list.js286
-rw-r--r--app/assets/javascripts/boards/components/board_new_issue.js4
-rw-r--r--app/assets/javascripts/environments/components/environment.js29
-rw-r--r--app/assets/javascripts/environments/components/environment_item.js30
-rw-r--r--app/assets/javascripts/environments/components/environments_table.js37
-rw-r--r--app/assets/javascripts/environments/services/environments_service.js5
-rw-r--r--app/assets/javascripts/environments/stores/environments_store.js70
-rw-r--r--app/assets/javascripts/lib/utils/number_utils.js34
-rw-r--r--app/assets/javascripts/main.js3
-rw-r--r--app/assets/javascripts/monitoring/prometheus_graph.js323
-rw-r--r--app/assets/javascripts/test_utils/index.js4
-rw-r--r--app/assets/javascripts/test_utils/simulate_drag.js262
-rw-r--r--app/assets/stylesheets/framework/blocks.scss4
-rw-r--r--app/assets/stylesheets/pages/boards.scss4
-rw-r--r--app/assets/stylesheets/pages/environments.scss10
-rw-r--r--app/assets/stylesheets/pages/projects.scss11
-rw-r--r--app/controllers/concerns/issuable_collections.rb3
-rw-r--r--app/controllers/groups/application_controller.rb1
-rw-r--r--app/controllers/import/base_controller.rb28
-rw-r--r--app/finders/issues_finder.rb2
-rw-r--r--app/finders/merge_requests_finder.rb2
-rw-r--r--app/models/ci/build.rb6
-rw-r--r--app/models/issue.rb4
-rw-r--r--app/models/merge_request_diff.rb18
-rw-r--r--app/models/project.rb2
-rw-r--r--app/models/project_services/kubernetes_service.rb43
-rw-r--r--app/models/project_services/mock_deployment_service.rb18
-rw-r--r--app/models/project_services/mock_monitoring_service.rb17
-rw-r--r--app/models/service.rb4
-rw-r--r--app/models/system_note_metadata.rb4
-rw-r--r--app/services/search/global_service.rb6
-rw-r--r--app/services/system_note_service.rb14
-rw-r--r--app/services/users/create_service.rb2
-rw-r--r--app/views/groups/merge_requests.html.haml30
-rw-r--r--app/views/groups/milestones/show.html.haml4
-rw-r--r--app/views/projects/boards/_show.html.haml2
-rw-r--r--app/views/projects/boards/components/_board_list.html.haml26
-rw-r--r--app/views/projects/edit.html.haml2
-rw-r--r--app/views/projects/environments/_external_url.html.haml1
-rw-r--r--app/views/projects/environments/_metrics_button.html.haml1
-rw-r--r--app/views/projects/environments/metrics.html.haml2
-rw-r--r--app/views/projects/environments/show.html.haml4
-rw-r--r--app/views/projects/labels/index.html.haml3
-rw-r--r--app/views/projects/merge_requests/_merge_requests.html.haml8
-rw-r--r--app/views/projects/merge_requests/index.html.haml25
-rw-r--r--app/views/projects/milestones/show.html.haml3
-rw-r--r--app/views/projects/new.html.haml24
-rw-r--r--app/views/projects/wikis/_form.html.haml4
-rw-r--r--app/views/shared/_merge_requests.html.haml2
-rw-r--r--app/views/shared/empty_states/_merge_requests.html.haml22
-rw-r--r--app/views/shared/empty_states/icons/_merge_requests.svg1
-rw-r--r--app/views/shared/icons/_activity.svg16
-rw-r--r--app/views/shared/icons/_commits.svg10
-rw-r--r--app/views/shared/icons/_contributionanalytics.svg17
-rw-r--r--app/views/shared/icons/_delta.svg3
-rw-r--r--app/views/shared/icons/_files.svg17
-rw-r--r--app/views/shared/icons/_icon_close.svg2
-rw-r--r--app/views/shared/icons/_icon_empty_groups.svg2
-rw-r--r--app/views/shared/icons/_icon_mr_issue.svg2
-rw-r--r--app/views/shared/icons/_icon_play.svg4
-rw-r--r--app/views/shared/icons/_icon_stopwatch.svg2
-rw-r--r--app/views/shared/icons/_icon_timer.svg2
-rw-r--r--app/views/shared/icons/_illustration_no_commits.svg2
-rw-r--r--app/views/shared/icons/_members.svg13
-rw-r--r--app/views/shared/icons/_milestones.svg15
-rw-r--r--app/views/shared/icons/_mr.svg13
-rw-r--r--app/views/shared/icons/_pipelines.svg10
-rw-r--r--app/views/shared/icons/_wiki.svg10
-rw-r--r--app/workers/process_commit_worker.rb2
-rw-r--r--changelogs/unreleased/20841-getting-started-better-empty-state-for-merge-requests-view.yml4
-rw-r--r--changelogs/unreleased/24187-set-git-terminal-prompt-env-var-in-initializer.yml4
-rw-r--r--changelogs/unreleased/26595-fix-issue-preselected-template.yml4
-rw-r--r--changelogs/unreleased/28732-expandable-folders.yml4
-rw-r--r--changelogs/unreleased/29034-fix-github-importer.yml4
-rw-r--r--changelogs/unreleased/29432-prevent-click-disabled-btn.yml4
-rw-r--r--changelogs/unreleased/29492-useless-queries.yml4
-rw-r--r--changelogs/unreleased/29541-fix-github-importer-deleted-fork.yml4
-rw-r--r--changelogs/unreleased/29670-jira-integration-documentation-improvment.yml4
-rw-r--r--changelogs/unreleased/29871-api-remove-merge-requests-comments-endpoints.yml4
-rw-r--r--changelogs/unreleased/30098-banzai-filter-mergerequestreferencefilter-has-an-n-1-query-problem.yml4
-rw-r--r--changelogs/unreleased/30264-fix-vue-pagination.yml5
-rw-r--r--changelogs/unreleased/30276-move-top-badges.yml4
-rw-r--r--changelogs/unreleased/30289-allow-users-to-import-github-projects-to-subgroups.yml4
-rw-r--r--changelogs/unreleased/30400-fix-blob-highlighting-in-search.yml4
-rw-r--r--changelogs/unreleased/add-mock-deployment-and-monitoring-service-for-development.yaml4
-rw-r--r--changelogs/unreleased/bug-api_milestone_merge_requests_scope.yml4
-rw-r--r--changelogs/unreleased/environment-performance-improvements.yml4
-rw-r--r--changelogs/unreleased/fix-gb-environments-folders-route.yml4
-rw-r--r--changelogs/unreleased/fix-import-fork.yml4
-rw-r--r--changelogs/unreleased/fix-import-namespace.yml4
-rw-r--r--changelogs/unreleased/fix_wiki_commit_message.yml4
-rw-r--r--changelogs/unreleased/forked-subquery-order.yml4
-rw-r--r--changelogs/unreleased/issue_91_ee_backport.yml4
-rw-r--r--changelogs/unreleased/make-ci-build-to-lock-on-status-change.yml4
-rw-r--r--changelogs/unreleased/remove_index_for_users-current_sign_in_at.yml4
-rw-r--r--changelogs/unreleased/sh-fix-destroy-user-race.yml5
-rw-r--r--changelogs/unreleased/sh-fix-ssh-keys-with-spaces.yml4
-rw-r--r--changelogs/unreleased/todo-update-order.yml4
-rw-r--r--changelogs/unreleased/zj-kube-service-auto-fill.yml4
-rw-r--r--config/application.rb1
-rw-r--r--config/webpack.config.js4
-rw-r--r--db/fixtures/development/19_environments.rb70
-rw-r--r--db/fixtures/development/20_nested_groups.rb (renamed from db/fixtures/development/19_nested_groups.rb)0
-rw-r--r--db/migrate/20170402231018_remove_index_for_users_current_sign_in_at.rb25
-rw-r--r--db/schema.rb3
-rw-r--r--doc/development/img/cache-hit.svg21
-rw-r--r--doc/development/img/cache-miss.svg24
-rw-r--r--doc/development/polling.md3
-rw-r--r--doc/install/requirements.md3
-rw-r--r--doc/update/patch_versions.md2
-rw-r--r--doc/user/project/integrations/img/jira_project_settings.pngbin0 -> 32791 bytes
-rw-r--r--doc/user/project/integrations/jira.md5
-rw-r--r--features/dashboard/dashboard.feature3
-rw-r--r--features/project/issues/issues.feature6
-rw-r--r--features/steps/project/fork.rb3
-rw-r--r--features/steps/project/forked_merge_requests.rb2
-rw-r--r--features/steps/project/merge_requests.rb2
-rw-r--r--features/steps/shared/project.rb4
-rw-r--r--lib/api/helpers/runner.rb6
-rw-r--r--lib/api/milestones.rb2
-rw-r--r--lib/api/runner.rb15
-rw-r--r--lib/api/services.rb8
-rw-r--r--lib/ci/api/builds.rb15
-rw-r--r--lib/ci/api/helpers.rb6
-rw-r--r--lib/gitlab/import_export/hash_util.rb25
-rw-r--r--lib/gitlab/import_export/import_export.yml2
-rw-r--r--lib/gitlab/import_export/importer.rb2
-rw-r--r--lib/gitlab/import_export/merge_request_parser.rb41
-rw-r--r--lib/gitlab/import_export/project_tree_restorer.rb2
-rw-r--r--lib/gitlab/import_export/relation_factory.rb17
-rw-r--r--lib/gitlab/project_search_results.rb2
-rw-r--r--lib/gitlab/search_results.rb21
-rw-r--r--lib/gitlab/shell.rb2
-rw-r--r--lib/gitlab/workhorse.rb10
-rw-r--r--spec/controllers/import/bitbucket_controller_spec.rb67
-rw-r--r--spec/controllers/import/gitlab_controller_spec.rb66
-rw-r--r--spec/features/admin/admin_health_check_spec.rb6
-rw-r--r--spec/features/dashboard/merge_requests_spec.rb32
-rw-r--r--spec/features/groups/empty_states_spec.rb70
-rw-r--r--spec/features/issues/filtered_search/filter_issues_spec.rb20
-rw-r--r--spec/features/merge_requests/create_new_mr_spec.rb8
-rw-r--r--spec/features/milestones/milestones_spec.rb6
-rw-r--r--spec/features/projects/environments/environments_spec.rb40
-rw-r--r--spec/features/projects/import_export/import_file_spec.rb8
-rw-r--r--spec/features/projects/merge_requests/list_spec.rb24
-rw-r--r--spec/features/projects/wiki/user_creates_wiki_page_spec.rb20
-rw-r--r--spec/features/projects/wiki/user_updates_wiki_page_spec.rb6
-rw-r--r--spec/features/search_spec.rb6
-rw-r--r--spec/fixtures/metrics.json1
-rw-r--r--spec/javascripts/boards/board_list_spec.js201
-rw-r--r--spec/javascripts/environments/environment_spec.js99
-rw-r--r--spec/javascripts/environments/environments_store_spec.js110
-rw-r--r--spec/javascripts/environments/mock_data.js16
-rw-r--r--spec/javascripts/lib/utils/number_utility_spec.js41
-rw-r--r--spec/javascripts/monitoring/prometheus_graph_spec.js10
-rw-r--r--spec/lib/gitlab/backend/shell_spec.rb9
-rw-r--r--spec/lib/gitlab/import_export/all_models.yml2
-rw-r--r--spec/lib/gitlab/import_export/fork_spec.rb49
-rw-r--r--spec/lib/gitlab/import_export/hash_util_spec.rb28
-rw-r--r--spec/lib/gitlab/import_export/merge_request_parser_spec.rb31
-rw-r--r--spec/lib/gitlab/import_export/project_tree_restorer_spec.rb6
-rw-r--r--spec/lib/gitlab/import_export/project_tree_saver_spec.rb4
-rw-r--r--spec/lib/gitlab/import_export/relation_factory_spec.rb2
-rw-r--r--spec/lib/gitlab/import_export/repo_saver_spec.rb (renamed from spec/lib/gitlab/import_export/repo_bundler_spec.rb)0
-rw-r--r--spec/lib/gitlab/o_auth/user_spec.rb9
-rw-r--r--spec/lib/gitlab/project_search_results_spec.rb8
-rw-r--r--spec/lib/gitlab/workhorse_spec.rb38
-rw-r--r--spec/models/issue_spec.rb8
-rw-r--r--spec/models/project_services/kubernetes_service_spec.rb89
-rw-r--r--spec/requests/api/milestones_spec.rb2
-rw-r--r--spec/requests/api/runner_spec.rb23
-rw-r--r--spec/services/system_note_service_spec.rb35
-rw-r--r--spec/services/users/create_service_spec.rb26
-rw-r--r--spec/support/controllers/githubish_import_controller_shared_examples.rb59
-rw-r--r--spec/support/drag_to_helper.rb4
-rw-r--r--spec/support/issuables_list_metadata_shared_examples.rb15
-rw-r--r--spec/support/matchers/query_matcher.rb33
-rw-r--r--spec/workers/process_commit_worker_spec.rb7
184 files changed, 2543 insertions, 914 deletions
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 476307e7076..66f8b6e6f9a 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -8,6 +8,7 @@ cache:
variables:
MYSQL_ALLOW_EMPTY_PASSWORD: "1"
RAILS_ENV: "test"
+ NODE_ENV: "test"
SIMPLECOV: "true"
SETUP_DB: "true"
USE_BUNDLE_INSTALL: "true"
@@ -129,9 +130,7 @@ setup-test-env:
stage: prepare
script:
- node --version
- - yarn --version
- yarn install --pure-lockfile
- - yarn check # ensure that yarn.lock matches package.json
- bundle exec rake gitlab:assets:compile
- bundle exec ruby -Ispec -e 'require "spec_helper" ; TestEnv.init'
artifacts:
@@ -296,8 +295,6 @@ docs:check:apilint:
image: "phusion/baseimage"
stage: test
<<: *dedicated-runner
- variables:
- GIT_DEPTH: "3"
cache: {}
dependencies: []
before_script: []
@@ -308,8 +305,6 @@ docs:check:links:
image: "registry.gitlab.com/gitlab-org/gitlab-build-images:nanoc-bootstrap-ruby-2.4-alpine"
stage: test
<<: *dedicated-runner
- variables:
- GIT_DEPTH: "3"
cache: {}
dependencies: []
before_script: []
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 734c72f5dd2..3e5475a2296 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,23 @@
documentation](doc/development/changelog.md) for instructions on adding your own
entry.
+## 9.0.3 (2017-04-05)
+
+- Fix name colision when importing GitHub pull requests from forked repositories. !9719
+- Fix GitHub Importer for PRs of deleted forked repositories. !9992
+- Fix environment folder route when special chars present in environment name. !10250
+- Improve Markdown rendering when a lot of merge requests are referenced. !10252
+- Allow users to import GitHub projects to subgroups.
+- Backport API changes needed to fix sticking in EE.
+- Remove unnecessary ORDER BY clause from `forked_to_project_id` subquery. (mhasbini)
+- Make CI build to use optimistic locking only on status change.
+- Fix race condition where a namespace would be deleted before a project was deleted.
+- Fix linking to new issue with selected template via url parameter.
+- Remove unnecessary ORDER BY clause when updating todos. (mhasbini)
+- API: Make the /notes endpoint work with noteable iid instead of id.
+- Fixes method not replacing URL parameters correctly and breaking pipelines pagination.
+- Move issue, mr, todos next to profile dropdown in top nav.
+
## 9.0.2 (2017-03-29)
- Correctly update paths when changing a child group.
diff --git a/GITALY_SERVER_VERSION b/GITALY_SERVER_VERSION
index 1d0ba9ea182..8f0916f768f 100644
--- a/GITALY_SERVER_VERSION
+++ b/GITALY_SERVER_VERSION
@@ -1 +1 @@
-0.4.0
+0.5.0
diff --git a/Gemfile b/Gemfile
index 910ab318227..6a45b3d7339 100644
--- a/Gemfile
+++ b/Gemfile
@@ -223,7 +223,7 @@ gem 'oj', '~> 2.17.4'
gem 'chronic', '~> 0.10.2'
gem 'chronic_duration', '~> 0.10.6'
-gem 'webpack-rails', '~> 0.9.9'
+gem 'webpack-rails', '~> 0.9.10'
gem 'rack-proxy', '~> 0.6.0'
gem 'sass-rails', '~> 5.0.6'
diff --git a/Gemfile.lock b/Gemfile.lock
index 304fc9f2bb3..50ca9af7a7a 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -823,8 +823,8 @@ GEM
addressable (>= 2.3.6)
crack (>= 0.3.2)
hashdiff
- webpack-rails (0.9.9)
- rails (>= 3.2.0)
+ webpack-rails (0.9.10)
+ railties (>= 3.2.0)
websocket-driver (0.6.3)
websocket-extensions (>= 0.1.0)
websocket-extensions (0.1.2)
@@ -1026,7 +1026,7 @@ DEPENDENCIES
virtus (~> 1.0.1)
vmstat (~> 2.3.0)
webmock (~> 1.24.0)
- webpack-rails (~> 0.9.9)
+ webpack-rails (~> 0.9.10)
wikicloth (= 0.8.1)
BUNDLED WITH
diff --git a/app/assets/javascripts/boards/components/board.js b/app/assets/javascripts/boards/components/board.js
index 35b3205cca7..93b8960da2e 100644
--- a/app/assets/javascripts/boards/components/board.js
+++ b/app/assets/javascripts/boards/components/board.js
@@ -1,7 +1,7 @@
/* eslint-disable comma-dangle, space-before-function-paren, one-var */
/* global Sortable */
-
import Vue from 'vue';
+import boardList from './board_list';
import boardBlankState from './board_blank_state';
require('./board_delete');
@@ -16,7 +16,7 @@ require('./board_list');
gl.issueBoards.Board = Vue.extend({
template: '#js-board-template',
components: {
- 'board-list': gl.issueBoards.BoardList,
+ boardList,
'board-delete': gl.issueBoards.BoardDelete,
boardBlankState,
},
diff --git a/app/assets/javascripts/boards/components/board_list.js b/app/assets/javascripts/boards/components/board_list.js
index 86e6c26e570..adbd82cb687 100644
--- a/app/assets/javascripts/boards/components/board_list.js
+++ b/app/assets/javascripts/boards/components/board_list.js
@@ -1,131 +1,197 @@
-/* eslint-disable comma-dangle, space-before-function-paren, max-len */
/* global Sortable */
-
-import Vue from 'vue';
import boardNewIssue from './board_new_issue';
import boardCard from './board_card';
+import eventHub from '../eventhub';
-(() => {
- const Store = gl.issueBoards.BoardsStore;
-
- window.gl = window.gl || {};
- window.gl.issueBoards = window.gl.issueBoards || {};
+const Store = gl.issueBoards.BoardsStore;
- gl.issueBoards.BoardList = Vue.extend({
- template: '#js-board-list-template',
- components: {
- boardCard,
- boardNewIssue,
+export default {
+ name: 'BoardList',
+ props: {
+ disabled: {
+ type: Boolean,
+ required: true,
},
- props: {
- disabled: Boolean,
- list: Object,
- issues: Array,
- loading: Boolean,
- issueLinkBase: String,
- rootPath: String,
+ list: {
+ type: Object,
+ required: true,
},
- data () {
- return {
- scrollOffset: 250,
- filters: Store.state.filters,
- showCount: false,
- showIssueForm: false
- };
+ issues: {
+ type: Array,
+ required: true,
},
- watch: {
- filters: {
- handler () {
- this.list.loadingMore = false;
- this.$refs.list.scrollTop = 0;
- },
- deep: true
- },
- issues () {
- this.$nextTick(() => {
- if (this.scrollHeight() <= this.listHeight() && this.list.issuesSize > this.list.issues.length) {
- this.list.page += 1;
- this.list.getIssues(false);
- }
+ loading: {
+ type: Boolean,
+ required: true,
+ },
+ issueLinkBase: {
+ type: String,
+ required: true,
+ },
+ rootPath: {
+ type: String,
+ required: true,
+ },
+ },
+ data() {
+ return {
+ scrollOffset: 250,
+ filters: Store.state.filters,
+ showCount: false,
+ showIssueForm: false,
+ };
+ },
+ components: {
+ boardCard,
+ boardNewIssue,
+ },
+ methods: {
+ listHeight() {
+ return this.$refs.list.getBoundingClientRect().height;
+ },
+ scrollHeight() {
+ return this.$refs.list.scrollHeight;
+ },
+ scrollTop() {
+ return this.$refs.list.scrollTop + this.listHeight();
+ },
+ loadNextPage() {
+ const getIssues = this.list.nextPage();
- if (this.scrollHeight() > Math.ceil(this.listHeight())) {
- this.showCount = true;
- } else {
- this.showCount = false;
- }
+ if (getIssues) {
+ this.list.loadingMore = true;
+ getIssues.then(() => {
+ this.list.loadingMore = false;
});
}
},
- methods: {
- listHeight () {
- return this.$refs.list.getBoundingClientRect().height;
- },
- scrollHeight () {
- return this.$refs.list.scrollHeight;
- },
- scrollTop () {
- return this.$refs.list.scrollTop + this.listHeight();
+ toggleForm() {
+ this.showIssueForm = !this.showIssueForm;
+ },
+ onScroll() {
+ if ((this.scrollTop() > this.scrollHeight() - this.scrollOffset) && !this.list.loadingMore) {
+ this.loadNextPage();
+ }
+ },
+ },
+ watch: {
+ filters: {
+ handler() {
+ this.list.loadingMore = false;
+ this.$refs.list.scrollTop = 0;
},
- loadNextPage () {
- const getIssues = this.list.nextPage();
+ deep: true,
+ },
+ issues() {
+ this.$nextTick(() => {
+ if (this.scrollHeight() <= this.listHeight() &&
+ this.list.issuesSize > this.list.issues.length) {
+ this.list.page += 1;
+ this.list.getIssues(false);
+ }
- if (getIssues) {
- this.list.loadingMore = true;
- getIssues.then(() => {
- this.list.loadingMore = false;
- });
+ if (this.scrollHeight() > Math.ceil(this.listHeight())) {
+ this.showCount = true;
+ } else {
+ this.showCount = false;
}
- },
- toggleForm() {
- this.showIssueForm = !this.showIssueForm;
- },
- },
- created() {
- gl.IssueBoardsApp.$on(`hide-issue-form-${this.list.id}`, this.toggleForm);
+ });
},
- mounted () {
- const options = gl.issueBoards.getBoardSortableDefaultOptions({
- scroll: document.querySelectorAll('.boards-list')[0],
- group: 'issues',
- disabled: this.disabled,
- filter: '.board-list-count, .is-disabled',
- dataIdAttr: 'data-issue-id',
- onStart: (e) => {
- const card = this.$refs.issue[e.oldIndex];
+ },
+ created() {
+ eventHub.$on(`hide-issue-form-${this.list.id}`, this.toggleForm);
+ },
+ mounted() {
+ const options = gl.issueBoards.getBoardSortableDefaultOptions({
+ scroll: document.querySelectorAll('.boards-list')[0],
+ group: 'issues',
+ disabled: this.disabled,
+ filter: '.board-list-count, .is-disabled',
+ dataIdAttr: 'data-issue-id',
+ onStart: (e) => {
+ const card = this.$refs.issue[e.oldIndex];
- card.showDetail = false;
- Store.moving.list = card.list;
- Store.moving.issue = Store.moving.list.findIssue(+e.item.dataset.issueId);
+ card.showDetail = false;
+ 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, e.newIndex);
+ gl.issueBoards.onStart();
+ },
+ onAdd: (e) => {
+ gl.issueBoards.BoardsStore
+ .moveIssueToList(Store.moving.list, this.list, Store.moving.issue, e.newIndex);
- this.$nextTick(() => {
- e.item.remove();
- });
- },
- onUpdate: (e) => {
- const sortedArray = this.sortable.toArray().filter(id => id !== '-1');
- gl.issueBoards.BoardsStore.moveIssueInList(this.list, Store.moving.issue, e.oldIndex, e.newIndex, sortedArray);
- },
- onMove(e) {
- return !e.related.classList.contains('board-list-count');
- }
- });
+ this.$nextTick(() => {
+ e.item.remove();
+ });
+ },
+ onUpdate: (e) => {
+ const sortedArray = this.sortable.toArray().filter(id => id !== '-1');
+ gl.issueBoards.BoardsStore
+ .moveIssueInList(this.list, Store.moving.issue, e.oldIndex, e.newIndex, sortedArray);
+ },
+ onMove(e) {
+ return !e.related.classList.contains('board-list-count');
+ },
+ });
- this.sortable = Sortable.create(this.$refs.list, options);
+ this.sortable = Sortable.create(this.$refs.list, options);
- // Scroll event on list to load more
- this.$refs.list.onscroll = () => {
- if ((this.scrollTop() > this.scrollHeight() - this.scrollOffset) && !this.list.loadingMore) {
- this.loadNextPage();
- }
- };
- },
- beforeDestroy() {
- gl.IssueBoardsApp.$off(`hide-issue-form-${this.list.id}`, this.toggleForm);
- },
- });
-})();
+ // Scroll event on list to load more
+ this.$refs.list.addEventListener('scroll', this.onScroll);
+ },
+ beforeDestroy() {
+ eventHub.$off(`hide-issue-form-${this.list.id}`, this.toggleForm);
+ this.$refs.list.removeEventListener('scroll', this.onScroll);
+ },
+ template: `
+ <div class="board-list-component">
+ <div
+ class="board-list-loading text-center"
+ aria-label="Loading issues"
+ v-if="loading">
+ <i
+ class="fa fa-spinner fa-spin"
+ aria-hidden="true">
+ </i>
+ </div>
+ <board-new-issue
+ :list="list"
+ v-if="list.type !== 'closed' && showIssueForm"/>
+ <ul
+ class="board-list"
+ v-show="!loading"
+ ref="list"
+ :data-board="list.id"
+ :class="{ 'is-smaller': showIssueForm }">
+ <board-card
+ v-for="(issue, index) in issues"
+ ref="issue"
+ :index="index"
+ :list="list"
+ :issue="issue"
+ :issue-link-base="issueLinkBase"
+ :root-path="rootPath"
+ :disabled="disabled"
+ :key="issue.id" />
+ <li
+ class="board-list-count text-center"
+ v-if="showCount"
+ data-id="-1">
+ <i
+ class="fa fa-spinner fa-spin"
+ aria-label="Loading more issues"
+ aria-hidden="true"
+ v-show="list.loadingMore">
+ </i>
+ <span v-if="list.issues.length === list.issuesSize">
+ Showing all issues
+ </span>
+ <span v-else>
+ Showing {{ list.issues.length }} of {{ list.issuesSize }} issues
+ </span>
+ </li>
+ </ul>
+ </div>
+ `,
+};
diff --git a/app/assets/javascripts/boards/components/board_new_issue.js b/app/assets/javascripts/boards/components/board_new_issue.js
index b88f59dd6d4..0fa85b6fe14 100644
--- a/app/assets/javascripts/boards/components/board_new_issue.js
+++ b/app/assets/javascripts/boards/components/board_new_issue.js
@@ -1,4 +1,6 @@
/* global ListIssue */
+import eventHub from '../eventhub';
+
const Store = gl.issueBoards.BoardsStore;
export default {
@@ -49,7 +51,7 @@ export default {
},
cancel() {
this.title = '';
- gl.IssueBoardsApp.$emit(`hide-issue-form-${this.list.id}`);
+ eventHub.$emit(`hide-issue-form-${this.list.id}`);
},
},
mounted() {
diff --git a/app/assets/javascripts/environments/components/environment.js b/app/assets/javascripts/environments/components/environment.js
index 51aab8460f6..0518422e475 100644
--- a/app/assets/javascripts/environments/components/environment.js
+++ b/app/assets/javascripts/environments/components/environment.js
@@ -24,6 +24,7 @@ export default Vue.component('environment-component', {
state: store.state,
visibility: 'available',
isLoading: false,
+ isLoadingFolderContent: false,
cssContainerClass: environmentsData.cssClass,
endpoint: environmentsData.environmentsDataEndpoint,
canCreateDeployment: environmentsData.canCreateDeployment,
@@ -68,15 +69,21 @@ export default Vue.component('environment-component', {
this.fetchEnvironments();
eventHub.$on('refreshEnvironments', this.fetchEnvironments);
+ eventHub.$on('toggleFolder', this.toggleFolder);
},
beforeDestroyed() {
eventHub.$off('refreshEnvironments');
+ eventHub.$off('toggleFolder');
},
methods: {
- toggleRow(model) {
- return this.store.toggleFolder(model.name);
+ toggleFolder(folder, folderUrl) {
+ this.store.toggleFolder(folder);
+
+ if (!folder.isOpen) {
+ this.fetchChildEnvironments(folder, folderUrl);
+ }
},
/**
@@ -117,6 +124,21 @@ export default Vue.component('environment-component', {
new Flash('An error occurred while fetching the environments.');
});
},
+
+ fetchChildEnvironments(folder, folderUrl) {
+ this.isLoadingFolderContent = true;
+
+ this.service.getFolderContent(folderUrl)
+ .then(resp => resp.json())
+ .then((response) => {
+ this.store.setfolderContent(folder, response.environments);
+ this.isLoadingFolderContent = false;
+ })
+ .catch(() => {
+ this.isLoadingFolderContent = false;
+ new Flash('An error occurred while fetching the environments.');
+ });
+ },
},
template: `
@@ -179,7 +201,8 @@ export default Vue.component('environment-component', {
:environments="state.environments"
:can-create-deployment="canCreateDeploymentParsed"
:can-read-environment="canReadEnvironmentParsed"
- :service="service"/>
+ :service="service"
+ :is-loading-folder-content="isLoadingFolderContent" />
</div>
<table-pagination v-if="state.paginationInformation && state.paginationInformation.totalPages > 1"
diff --git a/app/assets/javascripts/environments/components/environment_item.js b/app/assets/javascripts/environments/components/environment_item.js
index 9c196562c6c..e44d93a30c7 100644
--- a/app/assets/javascripts/environments/components/environment_item.js
+++ b/app/assets/javascripts/environments/components/environment_item.js
@@ -7,6 +7,7 @@ import RollbackComponent from './environment_rollback';
import TerminalButtonComponent from './environment_terminal_button';
import MonitoringButtonComponent from './environment_monitoring';
import CommitComponent from '../../vue_shared/components/commit';
+import eventHub from '../event_hub';
/**
* Envrionment Item Component
@@ -410,7 +411,6 @@ export default {
folderUrl() {
return `${window.location.pathname}/folders/${this.model.folderName}`;
},
-
},
/**
@@ -428,15 +428,37 @@ export default {
return true;
},
+ methods: {
+ onClickFolder() {
+ eventHub.$emit('toggleFolder', this.model, this.folderUrl);
+ },
+ },
+
template: `
- <tr>
+ <tr :class="{ 'js-child-row': model.isChildren }">
<td>
<a v-if="!model.isFolder"
class="environment-name"
+ :class="{ 'prepend-left-default': model.isChildren }"
:href="environmentPath">
{{model.name}}
</a>
- <a v-else class="folder-name" :href="folderUrl">
+ <span v-else
+ class="folder-name"
+ @click="onClickFolder"
+ role="button">
+
+ <span class="folder-icon">
+ <i
+ v-show="model.isOpen"
+ class="fa fa-caret-down"
+ aria-hidden="true" />
+ <i
+ v-show="!model.isOpen"
+ class="fa fa-caret-right"
+ aria-hidden="true"/>
+ </span>
+
<span class="folder-icon">
<i class="fa fa-folder" aria-hidden="true"></i>
</span>
@@ -448,7 +470,7 @@ export default {
<span class="badge">
{{model.size}}
</span>
- </a>
+ </span>
</td>
<td class="deployment-column">
diff --git a/app/assets/javascripts/environments/components/environments_table.js b/app/assets/javascripts/environments/components/environments_table.js
index 338dff40bc9..5e6af3a1d45 100644
--- a/app/assets/javascripts/environments/components/environments_table.js
+++ b/app/assets/javascripts/environments/components/environments_table.js
@@ -31,6 +31,18 @@ export default {
type: Object,
required: true,
},
+
+ isLoadingFolderContent: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
+ },
+
+ methods: {
+ folderUrl(model) {
+ return `${window.location.pathname}/folders/${model.folderName}`;
+ },
},
template: `
@@ -53,6 +65,31 @@ export default {
:can-create-deployment="canCreateDeployment"
:can-read-environment="canReadEnvironment"
:service="service"></tr>
+
+ <template v-if="model.isFolder && model.isOpen && model.children && model.children.length > 0">
+ <tr v-if="isLoadingFolderContent">
+ <td colspan="6" class="text-center">
+ <i class="fa fa-spin fa-spinner fa-2x" aria-hidden="true"/>
+ </td>
+ </tr>
+
+ <template v-else>
+ <tr is="environment-item"
+ v-for="children in model.children"
+ :model="children"
+ :can-create-deployment="canCreateDeployment"
+ :can-read-environment="canReadEnvironment"
+ :service="service"></tr>
+
+ <tr>
+ <td colspan="6" class="text-center">
+ <a :href="folderUrl(model)" class="btn btn-default">
+ Show all
+ </a>
+ </td>
+ </tr>
+ </template>
+ </template>
</template>
</tbody>
</table>
diff --git a/app/assets/javascripts/environments/services/environments_service.js b/app/assets/javascripts/environments/services/environments_service.js
index 07040bf0d73..8adb53ea86d 100644
--- a/app/assets/javascripts/environments/services/environments_service.js
+++ b/app/assets/javascripts/environments/services/environments_service.js
@@ -7,6 +7,7 @@ Vue.use(VueResource);
export default class EnvironmentsService {
constructor(endpoint) {
this.environments = Vue.resource(endpoint);
+ this.folderResults = 3;
}
get(scope, page) {
@@ -16,4 +17,8 @@ export default class EnvironmentsService {
postAction(endpoint) {
return Vue.http.post(endpoint, {}, { emulateJSON: true });
}
+
+ getFolderContent(folderUrl) {
+ return Vue.http.get(`${folderUrl}.json?per_page=${this.folderResults}`);
+ }
}
diff --git a/app/assets/javascripts/environments/stores/environments_store.js b/app/assets/javascripts/environments/stores/environments_store.js
index 3c3084f3b78..158e7922e3c 100644
--- a/app/assets/javascripts/environments/stores/environments_store.js
+++ b/app/assets/javascripts/environments/stores/environments_store.js
@@ -38,7 +38,12 @@ export default class EnvironmentsStore {
let filtered = {};
if (env.size > 1) {
- filtered = Object.assign({}, env, { isFolder: true, folderName: env.name });
+ filtered = Object.assign({}, env, {
+ isFolder: true,
+ folderName: env.name,
+ isOpen: false,
+ children: [],
+ });
}
if (env.latest) {
@@ -85,4 +90,67 @@ export default class EnvironmentsStore {
this.state.stoppedCounter = count;
return count;
}
+
+ /**
+ * Toggles folder open property for the given folder.
+ *
+ * @param {Object} folder
+ * @return {Array}
+ */
+ toggleFolder(folder) {
+ return this.updateFolder(folder, 'isOpen', !folder.isOpen);
+ }
+
+ /**
+ * Updates the folder with the received environments.
+ *
+ *
+ * @param {Object} folder Folder to update
+ * @param {Array} environments Received environments
+ * @return {Object}
+ */
+ setfolderContent(folder, environments) {
+ const updatedEnvironments = environments.map((env) => {
+ let updated = env;
+
+ if (env.latest) {
+ updated = Object.assign({}, env, env.latest);
+ delete updated.latest;
+ } else {
+ updated = env;
+ }
+
+ updated.isChildren = true;
+
+ return updated;
+ });
+
+ return this.updateFolder(folder, 'children', updatedEnvironments);
+ }
+
+ /**
+ * Given a folder a prop and a new value updates the correct folder.
+ *
+ * @param {Object} folder
+ * @param {String} prop
+ * @param {String|Boolean|Object|Array} newValue
+ * @return {Array}
+ */
+ updateFolder(folder, prop, newValue) {
+ const environments = this.state.environments;
+
+ const updatedEnvironments = environments.map((env) => {
+ const updateEnv = Object.assign({}, env);
+ if (env.isFolder && env.id === folder.id) {
+ updateEnv[prop] = newValue;
+ }
+
+ return updateEnv;
+ });
+
+ this.state.environments = updatedEnvironments;
+
+ return updatedEnvironments;
+ }
+
}
diff --git a/app/assets/javascripts/lib/utils/number_utils.js b/app/assets/javascripts/lib/utils/number_utils.js
new file mode 100644
index 00000000000..e2bf69ee52e
--- /dev/null
+++ b/app/assets/javascripts/lib/utils/number_utils.js
@@ -0,0 +1,34 @@
+/* eslint-disable import/prefer-default-export */
+
+/**
+ * Function that allows a number with an X amount of decimals
+ * to be formatted in the following fashion:
+ * * For 1 digit to the left of the decimal point and X digits to the right of it
+ * * * Show 3 digits to the right
+ * * For 2 digits to the left of the decimal point and X digits to the right of it
+ * * * Show 2 digits to the right
+*/
+export function formatRelevantDigits(number) {
+ let digitsLeft = '';
+ let relevantDigits = 0;
+ let formattedNumber = '';
+ if (!isNaN(Number(number))) {
+ digitsLeft = number.split('.')[0];
+ switch (digitsLeft.length) {
+ case 1:
+ relevantDigits = 3;
+ break;
+ case 2:
+ relevantDigits = 2;
+ break;
+ case 3:
+ relevantDigits = 1;
+ break;
+ default:
+ relevantDigits = 4;
+ break;
+ }
+ formattedNumber = Number(number).toFixed(relevantDigits);
+ }
+ return formattedNumber;
+}
diff --git a/app/assets/javascripts/main.js b/app/assets/javascripts/main.js
index 665a59f3183..5b50bc62876 100644
--- a/app/assets/javascripts/main.js
+++ b/app/assets/javascripts/main.js
@@ -187,6 +187,9 @@ import './visibility_select';
import './wikis';
import './zen_mode';
+// eslint-disable-next-line global-require
+if (process.env.NODE_ENV !== 'production') require('./test_utils/');
+
document.addEventListener('beforeunload', function () {
// Unbind scroll events
$(document).off('scroll');
diff --git a/app/assets/javascripts/monitoring/prometheus_graph.js b/app/assets/javascripts/monitoring/prometheus_graph.js
index 844a0785bc9..a6ffa0f59de 100644
--- a/app/assets/javascripts/monitoring/prometheus_graph.js
+++ b/app/assets/javascripts/monitoring/prometheus_graph.js
@@ -1,9 +1,9 @@
-/* eslint-disable no-new*/
+/* eslint-disable no-new */
/* global Flash */
import d3 from 'd3';
import statusCodes from '~/lib/utils/http_status';
-import '../lib/utils/common_utils';
+import { formatRelevantDigits } from '~/lib/utils/number_utils';
import '../flash';
const prometheusGraphsContainer = '.prometheus-graph';
@@ -21,19 +21,19 @@ class PrometheusGraph {
const parentContainerWidth = $(prometheusGraphsContainer).parent().width() +
extraAddedWidthParent;
this.originalWidth = parentContainerWidth;
- this.originalHeight = 400;
+ this.originalHeight = 330;
this.width = parentContainerWidth - this.margin.left - this.margin.right;
- this.height = 400 - this.margin.top - this.margin.bottom;
+ this.height = this.originalHeight - this.margin.top - this.margin.bottom;
this.backOffRequestCounter = 0;
this.configureGraph();
this.init();
}
createGraph() {
- Object.keys(this.data).forEach((key) => {
- const value = this.data[key];
- if (value.length > 0) {
- this.plotValues(value, key);
+ Object.keys(this.graphSpecificProperties).forEach((key) => {
+ const value = this.graphSpecificProperties[key];
+ if (value.data.length > 0) {
+ this.plotValues(key);
}
});
}
@@ -49,53 +49,56 @@ class PrometheusGraph {
});
}
- plotValues(valuesToPlot, key) {
+ plotValues(key) {
+ const graphSpecifics = this.graphSpecificProperties[key];
+
const x = d3.time.scale()
.range([0, this.width]);
const y = d3.scale.linear()
.range([this.height, 0]);
- const prometheusGraphContainer = `${prometheusGraphsContainer}[graph-type=${key}]`;
+ graphSpecifics.xScale = x;
+ graphSpecifics.yScale = y;
- const graphSpecifics = this.graphSpecificProperties[key];
+ const prometheusGraphContainer = `${prometheusGraphsContainer}[graph-type=${key}]`;
const chart = d3.select(prometheusGraphContainer)
- .attr('width', this.width + this.margin.left + this.margin.right)
- .attr('height', this.height + this.margin.bottom + this.margin.top)
- .append('g')
- .attr('transform', `translate(${this.margin.left},${this.margin.top})`);
+ .attr('width', this.width + this.margin.left + this.margin.right)
+ .attr('height', this.height + this.margin.bottom + this.margin.top)
+ .append('g')
+ .attr('transform', `translate(${this.margin.left},${this.margin.top})`);
const axisLabelContainer = d3.select(prometheusGraphContainer)
- .attr('width', this.originalWidth + this.marginLabelContainer.left + this.marginLabelContainer.right)
- .attr('height', this.originalHeight + this.marginLabelContainer.bottom + this.marginLabelContainer.top)
+ .attr('width', this.originalWidth)
+ .attr('height', this.originalHeight)
.append('g')
.attr('transform', `translate(${this.marginLabelContainer.left},${this.marginLabelContainer.top})`);
- x.domain(d3.extent(valuesToPlot, d => d.time));
- y.domain([0, d3.max(valuesToPlot.map(metricValue => metricValue.value))]);
+ x.domain(d3.extent(graphSpecifics.data, d => d.time));
+ y.domain([0, d3.max(graphSpecifics.data.map(metricValue => metricValue.value))]);
const xAxis = d3.svg.axis()
- .scale(x)
- .ticks(this.commonGraphProperties.axis_no_ticks)
- .orient('bottom');
+ .scale(x)
+ .ticks(this.commonGraphProperties.axis_no_ticks)
+ .orient('bottom');
const yAxis = d3.svg.axis()
- .scale(y)
- .ticks(this.commonGraphProperties.axis_no_ticks)
- .tickSize(-this.width)
- .orient('left');
+ .scale(y)
+ .ticks(this.commonGraphProperties.axis_no_ticks)
+ .tickSize(-this.width)
+ .orient('left');
this.createAxisLabelContainers(axisLabelContainer, key);
chart.append('g')
- .attr('class', 'x-axis')
- .attr('transform', `translate(0,${this.height})`)
- .call(xAxis);
+ .attr('class', 'x-axis')
+ .attr('transform', `translate(0,${this.height})`)
+ .call(xAxis);
chart.append('g')
- .attr('class', 'y-axis')
- .call(yAxis);
+ .attr('class', 'y-axis')
+ .call(yAxis);
const area = d3.svg.area()
.x(d => x(d.time))
@@ -108,13 +111,13 @@ class PrometheusGraph {
.y(d => y(d.value));
chart.append('path')
- .datum(valuesToPlot)
- .attr('d', area)
- .attr('class', 'metric-area')
- .attr('fill', graphSpecifics.area_fill_color);
+ .datum(graphSpecifics.data)
+ .attr('d', area)
+ .attr('class', 'metric-area')
+ .attr('fill', graphSpecifics.area_fill_color);
chart.append('path')
- .datum(valuesToPlot)
+ .datum(graphSpecifics.data)
.attr('class', 'metric-line')
.attr('stroke', graphSpecifics.line_color)
.attr('fill', 'none')
@@ -126,7 +129,7 @@ class PrometheusGraph {
.attr('class', 'prometheus-graph-overlay')
.attr('width', this.width)
.attr('height', this.height)
- .on('mousemove', this.handleMouseOverGraph.bind(this, x, y, valuesToPlot, chart, prometheusGraphContainer, key));
+ .on('mousemove', this.handleMouseOverGraph.bind(this, prometheusGraphContainer));
}
// The legends from the metric
@@ -134,128 +137,150 @@ class PrometheusGraph {
const graphSpecifics = this.graphSpecificProperties[key];
axisLabelContainer.append('line')
- .attr('class', 'label-x-axis-line')
- .attr('stroke', '#000000')
- .attr('stroke-width', '1')
- .attr({
- x1: 0,
- y1: this.originalHeight - this.marginLabelContainer.top,
- x2: this.originalWidth - this.margin.right,
- y2: this.originalHeight - this.marginLabelContainer.top,
- });
+ .attr('class', 'label-x-axis-line')
+ .attr('stroke', '#000000')
+ .attr('stroke-width', '1')
+ .attr({
+ x1: 10,
+ y1: this.originalHeight - this.margin.top,
+ x2: (this.originalWidth - this.margin.right) + 10,
+ y2: this.originalHeight - this.margin.top,
+ });
axisLabelContainer.append('line')
- .attr('class', 'label-y-axis-line')
- .attr('stroke', '#000000')
- .attr('stroke-width', '1')
- .attr({
- x1: 0,
- y1: 0,
- x2: 0,
- y2: this.originalHeight - this.marginLabelContainer.top,
- });
+ .attr('class', 'label-y-axis-line')
+ .attr('stroke', '#000000')
+ .attr('stroke-width', '1')
+ .attr({
+ x1: 10,
+ y1: 0,
+ x2: 10,
+ y2: this.originalHeight - this.margin.top,
+ });
+
+ axisLabelContainer.append('rect')
+ .attr('class', 'rect-axis-text')
+ .attr('x', 0)
+ .attr('y', 50)
+ .attr('width', 30)
+ .attr('height', 150);
axisLabelContainer.append('text')
- .attr('class', 'label-axis-text')
- .attr('text-anchor', 'middle')
- .attr('transform', `translate(15, ${(this.originalHeight - this.marginLabelContainer.top) / 2}) rotate(-90)`)
- .text(graphSpecifics.graph_legend_title);
+ .attr('class', 'label-axis-text')
+ .attr('text-anchor', 'middle')
+ .attr('transform', `translate(15, ${(this.originalHeight - this.margin.top) / 2}) rotate(-90)`)
+ .text(graphSpecifics.graph_legend_title);
axisLabelContainer.append('rect')
- .attr('class', 'rect-axis-text')
- .attr('x', (this.originalWidth / 2) - this.margin.right)
- .attr('y', this.originalHeight - this.marginLabelContainer.top - 20)
- .attr('width', 30)
- .attr('height', 80);
+ .attr('class', 'rect-axis-text')
+ .attr('x', (this.originalWidth / 2) - this.margin.right)
+ .attr('y', this.originalHeight - 100)
+ .attr('width', 30)
+ .attr('height', 80);
axisLabelContainer.append('text')
- .attr('class', 'label-axis-text')
- .attr('x', (this.originalWidth / 2) - this.margin.right)
- .attr('y', this.originalHeight - this.marginLabelContainer.top)
- .attr('dy', '.35em')
- .text('Time');
+ .attr('class', 'label-axis-text')
+ .attr('x', (this.originalWidth / 2) - this.margin.right)
+ .attr('y', this.originalHeight - this.margin.top)
+ .attr('dy', '.35em')
+ .text('Time');
// Legends
// Metric Usage
axisLabelContainer.append('rect')
- .attr('x', this.originalWidth - 170)
- .attr('y', (this.originalHeight / 2) - 60)
- .style('fill', graphSpecifics.area_fill_color)
- .attr('width', 20)
- .attr('height', 35);
+ .attr('x', this.originalWidth - 170)
+ .attr('y', (this.originalHeight / 2) - 60)
+ .style('fill', graphSpecifics.area_fill_color)
+ .attr('width', 20)
+ .attr('height', 35);
axisLabelContainer.append('text')
- .attr('class', 'label-axis-text')
- .attr('x', this.originalWidth - 140)
- .attr('y', (this.originalHeight / 2) - 50)
- .text('Average');
+ .attr('class', 'text-metric-title')
+ .attr('x', this.originalWidth - 140)
+ .attr('y', (this.originalHeight / 2) - 50)
+ .text('Average');
axisLabelContainer.append('text')
- .attr('class', 'text-metric-usage')
- .attr('x', this.originalWidth - 140)
- .attr('y', (this.originalHeight / 2) - 25);
+ .attr('class', 'text-metric-usage')
+ .attr('x', this.originalWidth - 140)
+ .attr('y', (this.originalHeight / 2) - 25);
}
- handleMouseOverGraph(x, y, valuesToPlot, chart, prometheusGraphContainer, key) {
+ handleMouseOverGraph(prometheusGraphContainer) {
const rectOverlay = document.querySelector(`${prometheusGraphContainer} .prometheus-graph-overlay`);
- const timeValueFromOverlay = x.invert(d3.mouse(rectOverlay)[0]);
- const timeValueIndex = bisectDate(valuesToPlot, timeValueFromOverlay, 1);
- const d0 = valuesToPlot[timeValueIndex - 1];
- const d1 = valuesToPlot[timeValueIndex];
- const currentData = timeValueFromOverlay - d0.time > d1.time - timeValueFromOverlay ? d1 : d0;
- const maxValueMetric = y(d3.max(valuesToPlot.map(metricValue => metricValue.value)));
- const currentTimeCoordinate = x(currentData.time);
- const graphSpecifics = this.graphSpecificProperties[key];
- // Remove the current selectors
- d3.selectAll(`${prometheusGraphContainer} .selected-metric-line`).remove();
- d3.selectAll(`${prometheusGraphContainer} .circle-metric`).remove();
- d3.selectAll(`${prometheusGraphContainer} .rect-text-metric`).remove();
- d3.selectAll(`${prometheusGraphContainer} .text-metric`).remove();
-
- chart.append('line')
- .attr('class', 'selected-metric-line')
- .attr({
- x1: currentTimeCoordinate,
- y1: y(0),
- x2: currentTimeCoordinate,
- y2: maxValueMetric,
- });
+ const currentXCoordinate = d3.mouse(rectOverlay)[0];
+
+ Object.keys(this.graphSpecificProperties).forEach((key) => {
+ const currentGraphProps = this.graphSpecificProperties[key];
+ const timeValueOverlay = currentGraphProps.xScale.invert(currentXCoordinate);
+ const overlayIndex = bisectDate(currentGraphProps.data, timeValueOverlay, 1);
+ const d0 = currentGraphProps.data[overlayIndex - 1];
+ const d1 = currentGraphProps.data[overlayIndex];
+ const evalTime = timeValueOverlay - d0.time > d1.time - timeValueOverlay;
+ const currentData = evalTime ? d1 : d0;
+ const currentTimeCoordinate = currentGraphProps.xScale(currentData.time);
+ const currentPrometheusGraphContainer = `${prometheusGraphsContainer}[graph-type=${key}]`;
+ const maxValueFromData = d3.max(currentGraphProps.data.map(metricValue => metricValue.value));
+ const maxMetricValue = currentGraphProps.yScale(maxValueFromData);
+
+ // Clear up all the pieces of the flag
+ d3.selectAll(`${currentPrometheusGraphContainer} .selected-metric-line`).remove();
+ d3.selectAll(`${currentPrometheusGraphContainer} .circle-metric`).remove();
+ d3.selectAll(`${currentPrometheusGraphContainer} .rect-text-metric`).remove();
+ d3.selectAll(`${currentPrometheusGraphContainer} .text-metric`).remove();
+
+ const currentChart = d3.select(currentPrometheusGraphContainer).select('g');
+ currentChart.append('line')
+ .attr('class', 'selected-metric-line')
+ .attr({
+ x1: currentTimeCoordinate,
+ y1: currentGraphProps.yScale(0),
+ x2: currentTimeCoordinate,
+ y2: maxMetricValue,
+ });
+
+ currentChart.append('circle')
+ .attr('class', 'circle-metric')
+ .attr('fill', currentGraphProps.line_color)
+ .attr('cx', currentTimeCoordinate)
+ .attr('cy', currentGraphProps.yScale(currentData.value))
+ .attr('r', this.commonGraphProperties.circle_radius_metric);
+
+ // The little box with text
+ const rectTextMetric = currentChart.append('g')
+ .attr('class', 'rect-text-metric')
+ .attr('translate', `(${currentTimeCoordinate}, ${currentGraphProps.yScale(currentData.value)})`);
+
+ rectTextMetric.append('rect')
+ .attr('class', 'rect-metric')
+ .attr('x', currentTimeCoordinate + 10)
+ .attr('y', maxMetricValue)
+ .attr('width', this.commonGraphProperties.rect_text_width)
+ .attr('height', this.commonGraphProperties.rect_text_height);
+
+ rectTextMetric.append('text')
+ .attr('class', 'text-metric')
+ .attr('x', currentTimeCoordinate + 35)
+ .attr('y', maxMetricValue + 35)
+ .text(timeFormat(currentData.time));
+
+ rectTextMetric.append('text')
+ .attr('class', 'text-metric-date')
+ .attr('x', currentTimeCoordinate + 15)
+ .attr('y', maxMetricValue + 15)
+ .text(dayFormat(currentData.time));
+
+ let currentMetricValue = formatRelevantDigits(currentData.value);
+ if (key === 'cpu_values') {
+ currentMetricValue = `${currentMetricValue}%`;
+ } else {
+ currentMetricValue = `${currentMetricValue} MB`;
+ }
- chart.append('circle')
- .attr('class', 'circle-metric')
- .attr('fill', graphSpecifics.line_color)
- .attr('cx', currentTimeCoordinate)
- .attr('cy', y(currentData.value))
- .attr('r', this.commonGraphProperties.circle_radius_metric);
-
- // The little box with text
- const rectTextMetric = chart.append('g')
- .attr('class', 'rect-text-metric')
- .attr('translate', `(${currentTimeCoordinate}, ${y(currentData.value)})`);
-
- rectTextMetric.append('rect')
- .attr('class', 'rect-metric')
- .attr('x', currentTimeCoordinate + 10)
- .attr('y', maxValueMetric)
- .attr('width', this.commonGraphProperties.rect_text_width)
- .attr('height', this.commonGraphProperties.rect_text_height);
-
- rectTextMetric.append('text')
- .attr('class', 'text-metric')
- .attr('x', currentTimeCoordinate + 35)
- .attr('y', maxValueMetric + 35)
- .text(timeFormat(currentData.time));
-
- rectTextMetric.append('text')
- .attr('class', 'text-metric-date')
- .attr('x', currentTimeCoordinate + 15)
- .attr('y', maxValueMetric + 15)
- .text(dayFormat(currentData.time));
-
- // Update the text
- d3.select(`${prometheusGraphContainer} .text-metric-usage`)
- .text(currentData.value.substring(0, 8));
+ d3.select(`${currentPrometheusGraphContainer} .text-metric-usage`)
+ .text(currentMetricValue);
+ });
}
configureGraph() {
@@ -263,12 +288,18 @@ class PrometheusGraph {
cpu_values: {
area_fill_color: '#edf3fc',
line_color: '#5b99f7',
- graph_legend_title: 'CPU utilization (%)',
+ graph_legend_title: 'CPU Usage (Cores)',
+ data: [],
+ xScale: {},
+ yScale: {},
},
memory_values: {
area_fill_color: '#fca326',
line_color: '#fc6d26',
- graph_legend_title: 'Memory usage (MB)',
+ graph_legend_title: 'Memory Usage (MB)',
+ data: [],
+ xScale: {},
+ yScale: {},
},
};
@@ -318,17 +349,17 @@ class PrometheusGraph {
}
transformData(metricsResponse) {
- const metricTypes = {};
Object.keys(metricsResponse.metrics).forEach((key) => {
if (key === 'cpu_values' || key === 'memory_values') {
const metricValues = (metricsResponse.metrics[key])[0];
- metricTypes[key] = metricValues.values.map(metric => ({
- time: new Date(metric[0] * 1000),
- value: metric[1],
- }));
+ if (typeof metricValues !== 'undefined') {
+ this.graphSpecificProperties[key].data = metricValues.values.map(metric => ({
+ time: new Date(metric[0] * 1000),
+ value: metric[1],
+ }));
+ }
}
});
- this.data = metricTypes;
}
}
diff --git a/app/assets/javascripts/test_utils/index.js b/app/assets/javascripts/test_utils/index.js
new file mode 100644
index 00000000000..ef401abce2d
--- /dev/null
+++ b/app/assets/javascripts/test_utils/index.js
@@ -0,0 +1,4 @@
+import simulateDrag from './simulate_drag';
+
+// Export to global space for rspec to use
+window.simulateDrag = simulateDrag;
diff --git a/app/assets/javascripts/test_utils/simulate_drag.js b/app/assets/javascripts/test_utils/simulate_drag.js
index d48f2404fa5..e39213cb098 100644
--- a/app/assets/javascripts/test_utils/simulate_drag.js
+++ b/app/assets/javascripts/test_utils/simulate_drag.js
@@ -1,143 +1,137 @@
-/* eslint-disable wrap-iife, func-names, strict, no-var, vars-on-top, no-param-reassign, object-shorthand, no-shadow, comma-dangle, prefer-template, consistent-return, no-mixed-operators, no-unused-vars, no-unused-expressions, prefer-arrow-callback, max-len */
-(function () {
- 'use strict';
-
- function simulateEvent(el, type, options) {
- var event;
- if (!el) return;
- var ownerDocument = el.ownerDocument;
-
- options = options || {};
-
- if (/^mouse/.test(type)) {
- event = ownerDocument.createEvent('MouseEvents');
- event.initMouseEvent(type, true, true, ownerDocument.defaultView,
- options.button, options.screenX, options.screenY, options.clientX, options.clientY,
- options.ctrlKey, options.altKey, options.shiftKey, options.metaKey, options.button, el);
- } else {
- event = ownerDocument.createEvent('CustomEvent');
-
- event.initCustomEvent(type, true, true, ownerDocument.defaultView,
- options.button, options.screenX, options.screenY, options.clientX, options.clientY,
- options.ctrlKey, options.altKey, options.shiftKey, options.metaKey, options.button, el);
-
- event.dataTransfer = {
- data: {},
-
- setData: function (type, val) {
- this.data[type] = val;
- },
-
- getData: function (type) {
- return this.data[type];
- }
- };
- }
-
- if (el.dispatchEvent) {
- el.dispatchEvent(event);
- } else if (el.fireEvent) {
- el.fireEvent('on' + type, event);
- }
-
- return event;
- }
-
- function isLast(target) {
- var el = typeof target.el === 'string' ? document.getElementById(target.el.substr(1)) : target.el;
- var children = el.children;
-
- return children.length - 1 === target.index;
+function simulateEvent(el, type, options = {}) {
+ let event;
+ if (!el) return null;
+
+ if (/^mouse/.test(type)) {
+ event = el.ownerDocument.createEvent('MouseEvents');
+ event.initMouseEvent(type, true, true, el.ownerDocument.defaultView,
+ options.button, options.screenX, options.screenY, options.clientX, options.clientY,
+ options.ctrlKey, options.altKey, options.shiftKey, options.metaKey, options.button, el);
+ } else {
+ event = el.ownerDocument.createEvent('CustomEvent');
+
+ event.initCustomEvent(type, true, true, el.ownerDocument.defaultView,
+ options.button, options.screenX, options.screenY, options.clientX, options.clientY,
+ options.ctrlKey, options.altKey, options.shiftKey, options.metaKey, options.button, el);
+
+ event.dataTransfer = {
+ data: {},
+
+ setData(key, val) {
+ this.data[key] = val;
+ },
+
+ getData(key) {
+ return this.data[key];
+ },
+ };
}
- function getTarget(target) {
- var el = typeof target.el === 'string' ? document.getElementById(target.el.substr(1)) : target.el;
- var children = el.children;
-
- return (
- children[target.index] ||
- children[target.index === 'first' ? 0 : -1] ||
- children[target.index === 'last' ? children.length - 1 : -1] ||
- el
- );
+ if (el.dispatchEvent) {
+ el.dispatchEvent(event);
+ } else if (el.fireEvent) {
+ el.fireEvent(`on${type}`, event);
}
- function getRect(el) {
- var rect = el.getBoundingClientRect();
- var width = rect.right - rect.left;
- var height = rect.bottom - rect.top + 10;
-
- return {
- x: rect.left,
- y: rect.top,
- cx: rect.left + width / 2,
- cy: rect.top + height / 2,
- w: width,
- h: height,
- hw: width / 2,
- wh: height / 2
- };
+ return event;
+}
+
+function isLast(target) {
+ const el = typeof target.el === 'string' ? document.getElementById(target.el.substr(1)) : target.el;
+ const children = el.children;
+
+ return children.length - 1 === target.index;
+}
+
+function getTarget(target) {
+ const el = typeof target.el === 'string' ? document.getElementById(target.el.substr(1)) : target.el;
+ const children = el.children;
+
+ return (
+ children[target.index] ||
+ children[target.index === 'first' ? 0 : -1] ||
+ children[target.index === 'last' ? children.length - 1 : -1] ||
+ el
+ );
+}
+
+function getRect(el) {
+ const rect = el.getBoundingClientRect();
+ const width = rect.right - rect.left;
+ const height = (rect.bottom - rect.top) + 10;
+
+ return {
+ x: rect.left,
+ y: rect.top,
+ cx: rect.left + (width / 2),
+ cy: rect.top + (height / 2),
+ w: width,
+ h: height,
+ hw: width / 2,
+ wh: height / 2,
+ };
+}
+
+export default function simulateDrag(options) {
+ const { to, from } = options;
+ to.el = to.el || from.el;
+
+ const fromEl = getTarget(from);
+ const toEl = getTarget(to);
+ const firstEl = getTarget({
+ el: to.el,
+ index: 'first',
+ });
+ const lastEl = getTarget({
+ el: options.to.el,
+ index: 'last',
+ });
+
+ const fromRect = getRect(fromEl);
+ const toRect = getRect(toEl);
+ const firstRect = getRect(firstEl);
+ const lastRect = getRect(lastEl);
+
+ const startTime = new Date().getTime();
+ const duration = options.duration || 1000;
+
+ simulateEvent(fromEl, 'mousedown', {
+ button: 0,
+ clientX: fromRect.cx,
+ clientY: fromRect.cy,
+ });
+
+ if (options.ontap) options.ontap();
+ window.SIMULATE_DRAG_ACTIVE = 1;
+
+ if (options.to.index === 0) {
+ toRect.cy = firstRect.y;
+ } else if (isLast(options.to)) {
+ toRect.cy = lastRect.y + lastRect.h + 50;
}
- function simulateDrag(options, callback) {
- options.to.el = options.to.el || options.from.el;
+ const dragInterval = setInterval(() => {
+ const progress = (new Date().getTime() - startTime) / duration;
+ const x = (fromRect.cx + ((toRect.cx - fromRect.cx) * progress));
+ const y = (fromRect.cy + ((toRect.cy - fromRect.cy) * progress));
+ const overEl = fromEl.ownerDocument.elementFromPoint(x, y);
- var fromEl = getTarget(options.from);
- var toEl = getTarget(options.to);
- var firstEl = getTarget({
- el: options.to.el,
- index: 'first'
+ simulateEvent(overEl, 'mousemove', {
+ clientX: x,
+ clientY: y,
});
- var lastEl = getTarget({
- el: options.to.el,
- index: 'last'
- });
- var scrollable = options.scrollable;
-
- var fromRect = getRect(fromEl);
- var toRect = getRect(toEl);
- var firstRect = getRect(firstEl);
- var lastRect = getRect(lastEl);
-
- var startTime = new Date().getTime();
- var duration = options.duration || 1000;
- simulateEvent(fromEl, 'mousedown', { button: 0 });
- options.ontap && options.ontap();
- window.SIMULATE_DRAG_ACTIVE = 1;
-
- if (options.to.index === 0) {
- toRect.cy = firstRect.y;
- } else if (isLast(options.to)) {
- toRect.cy = lastRect.y + lastRect.h + 50;
- }
- var dragInterval = setInterval(function loop() {
- var progress = (new Date().getTime() - startTime) / duration;
- var x = (fromRect.cx + (toRect.cx - fromRect.cx) * progress) - scrollable.scrollLeft;
- var y = (fromRect.cy + (toRect.cy - fromRect.cy) * progress) - scrollable.scrollTop;
- var overEl = fromEl.ownerDocument.elementFromPoint(x, y);
-
- simulateEvent(overEl, 'mousemove', {
- clientX: x,
- clientY: y
- });
-
- if (progress >= 1) {
- options.ondragend && options.ondragend();
- simulateEvent(toEl, 'mouseup');
- clearInterval(dragInterval);
- window.SIMULATE_DRAG_ACTIVE = 0;
- }
- }, 100);
-
- return {
- target: fromEl,
- fromList: fromEl.parentNode,
- toList: toEl.parentNode
- };
- }
-
- // Export
- window.simulateEvent = simulateEvent;
- window.simulateDrag = simulateDrag;
-})();
+ if (progress >= 1) {
+ if (options.ondragend) options.ondragend();
+ simulateEvent(toEl, 'mouseup');
+ clearInterval(dragInterval);
+ window.SIMULATE_DRAG_ACTIVE = 0;
+ }
+ }, 100);
+
+ return {
+ target: fromEl,
+ fromList: fromEl.parentNode,
+ toList: toEl.parentNode,
+ };
+}
diff --git a/app/assets/stylesheets/framework/blocks.scss b/app/assets/stylesheets/framework/blocks.scss
index 9a4129cdc8d..52425262925 100644
--- a/app/assets/stylesheets/framework/blocks.scss
+++ b/app/assets/stylesheets/framework/blocks.scss
@@ -292,6 +292,10 @@
}
@media(min-width: $screen-xs-max) {
+ &.merge-requests .text-content {
+ margin-top: 40px;
+ }
+
&.labels .text-content {
margin-top: 70px;
}
diff --git a/app/assets/stylesheets/pages/boards.scss b/app/assets/stylesheets/pages/boards.scss
index b6168a293e0..7c0fc1008d0 100644
--- a/app/assets/stylesheets/pages/boards.scss
+++ b/app/assets/stylesheets/pages/boards.scss
@@ -46,7 +46,7 @@
}
.issue-boards-page {
- .page-with-sidebar {
+ .content-wrapper {
padding-bottom: 0;
}
}
@@ -72,7 +72,7 @@
@media (min-width: $screen-sm-min) {
height: 475px; // Needed for PhantomJS
- height: calc(100vh - 220px);
+ height: calc(100vh - 222px);
min-height: 475px;
transition: width .2s;
diff --git a/app/assets/stylesheets/pages/environments.scss b/app/assets/stylesheets/pages/environments.scss
index 3d91e0b22d8..6faa3794c83 100644
--- a/app/assets/stylesheets/pages/environments.scss
+++ b/app/assets/stylesheets/pages/environments.scss
@@ -159,6 +159,16 @@
text {
fill: $stat-graph-axis-fill;
}
+
+ .label-axis-text,
+ .text-metric-usage {
+ fill: $black;
+ font-weight: 500;
+ }
+
+ .legend-axis-text {
+ fill: $black;
+ }
}
.x-axis path,
diff --git a/app/assets/stylesheets/pages/projects.scss b/app/assets/stylesheets/pages/projects.scss
index c2c2f371b87..0fa1f68e034 100644
--- a/app/assets/stylesheets/pages/projects.scss
+++ b/app/assets/stylesheets/pages/projects.scss
@@ -459,20 +459,13 @@ a.deploy-project-label {
flex-wrap: wrap;
.btn {
- margin: 0 10px 10px 0;
padding: 8px;
+ margin-left: 10px;
}
> div {
+ margin-bottom: 10px;
padding-left: 0;
-
- &:last-child {
- margin-bottom: 0;
-
- .btn {
- margin-right: 0;
- }
- }
}
}
}
diff --git a/app/controllers/concerns/issuable_collections.rb b/app/controllers/concerns/issuable_collections.rb
index 85ae4985e58..c8a501d7319 100644
--- a/app/controllers/concerns/issuable_collections.rb
+++ b/app/controllers/concerns/issuable_collections.rb
@@ -15,6 +15,9 @@ module IssuableCollections
# a new order into the collection.
# We cannot use reorder to not mess up the paginated collection.
issuable_ids = issuable_collection.map(&:id)
+
+ return {} if issuable_ids.empty?
+
issuable_note_count = Note.count_for_collection(issuable_ids, @collection_type)
issuable_votes_count = AwardEmoji.votes_for_collection(issuable_ids, @collection_type)
issuable_merge_requests_count =
diff --git a/app/controllers/groups/application_controller.rb b/app/controllers/groups/application_controller.rb
index c411c21bb80..8b69c18d689 100644
--- a/app/controllers/groups/application_controller.rb
+++ b/app/controllers/groups/application_controller.rb
@@ -10,6 +10,7 @@ class Groups::ApplicationController < ApplicationController
unless @group
id = params[:group_id] || params[:id]
@group = Group.find_by_full_path(id)
+ @group_merge_requests = MergeRequestsFinder.new(current_user, group_id: @group.id).execute
unless @group && can?(current_user, :read_group, @group)
@group = nil
diff --git a/app/controllers/import/base_controller.rb b/app/controllers/import/base_controller.rb
index eeee027ef2d..9de0297ecfd 100644
--- a/app/controllers/import/base_controller.rb
+++ b/app/controllers/import/base_controller.rb
@@ -1,17 +1,27 @@
class Import::BaseController < ApplicationController
private
- def find_or_create_namespace(name, owner)
- return current_user.namespace if name == owner
+ def find_or_create_namespace(names, owner)
+ return current_user.namespace if names == owner
return current_user.namespace unless current_user.can_create_group?
- begin
- name = params[:target_namespace].presence || name
- namespace = Group.create!(name: name, path: name, owner: current_user)
- namespace.add_owner(current_user)
- namespace
- rescue ActiveRecord::RecordNotUnique, ActiveRecord::RecordInvalid
- Namespace.find_by_full_path(name)
+ names = params[:target_namespace].presence || names
+ full_path_namespace = Namespace.find_by_full_path(names)
+
+ return full_path_namespace if full_path_namespace
+
+ names.split('/').inject(nil) do |parent, name|
+ begin
+ namespace = Group.create!(name: name,
+ path: name,
+ owner: current_user,
+ parent: parent)
+ namespace.add_owner(current_user)
+
+ namespace
+ rescue ActiveRecord::RecordNotUnique, ActiveRecord::RecordInvalid
+ Namespace.where(parent: parent).find_by_path_or_name(name)
+ end
end
end
end
diff --git a/app/finders/issues_finder.rb b/app/finders/issues_finder.rb
index 08713272947..76715e5970d 100644
--- a/app/finders/issues_finder.rb
+++ b/app/finders/issues_finder.rb
@@ -9,7 +9,7 @@
# state: 'open' or 'closed' or 'all'
# group_id: integer
# project_id: integer
-# milestone_id: integer
+# milestone_title: string
# assignee_id: integer
# search: string
# label_name: string
diff --git a/app/finders/merge_requests_finder.rb b/app/finders/merge_requests_finder.rb
index 1eec45d9cb5..42f0ebd774c 100644
--- a/app/finders/merge_requests_finder.rb
+++ b/app/finders/merge_requests_finder.rb
@@ -9,7 +9,7 @@
# state: 'open' or 'closed' or 'all'
# group_id: integer
# project_id: integer
-# milestone_id: integer
+# milestone_title: string
# assignee_id: integer
# search: string
# label_name: string
diff --git a/app/models/ci/build.rb b/app/models/ci/build.rb
index ad0be70c32a..8431c5f228c 100644
--- a/app/models/ci/build.rb
+++ b/app/models/ci/build.rb
@@ -540,6 +540,8 @@ module Ci
end
def dependencies
+ return [] if empty_dependencies?
+
depended_jobs = depends_on_builds
return depended_jobs unless options[:dependencies].present?
@@ -549,6 +551,10 @@ module Ci
end
end
+ def empty_dependencies?
+ options[:dependencies]&.empty?
+ end
+
private
def update_artifacts_size
diff --git a/app/models/issue.rb b/app/models/issue.rb
index 10a5d9d2a24..472796df9df 100644
--- a/app/models/issue.rb
+++ b/app/models/issue.rb
@@ -59,10 +59,6 @@ class Issue < ActiveRecord::Base
before_transition any => :closed do |issue|
issue.closed_at = Time.zone.now
end
-
- before_transition closed: any do |issue|
- issue.closed_at = nil
- end
end
def hook_attrs
diff --git a/app/models/merge_request_diff.rb b/app/models/merge_request_diff.rb
index baee00b8fcd..6ad56b842b2 100644
--- a/app/models/merge_request_diff.rb
+++ b/app/models/merge_request_diff.rb
@@ -177,6 +177,16 @@ class MergeRequestDiff < ActiveRecord::Base
st_commits.count
end
+ def utf8_st_diffs
+ return [] if st_diffs.blank?
+
+ st_diffs.map do |diff|
+ diff.each do |k, v|
+ diff[k] = encode_utf8(v) if v.respond_to?(:encoding)
+ end
+ end
+ end
+
private
# Old GitLab implementations may have generated diffs as ["--broken-diff"].
@@ -270,14 +280,6 @@ class MergeRequestDiff < ActiveRecord::Base
project.merge_base_commit(head_commit_sha, start_commit_sha).try(:sha)
end
- def utf8_st_diffs
- st_diffs.map do |diff|
- diff.each do |k, v|
- diff[k] = encode_utf8(v) if v.respond_to?(:encoding)
- end
- end
- end
-
#
# #save or #update_attributes providing changes on serialized attributes do a lot of
# serialization and deserialization calls resulting in bad performance.
diff --git a/app/models/project.rb b/app/models/project.rb
index 83660d8c431..12fd0668ff8 100644
--- a/app/models/project.rb
+++ b/app/models/project.rb
@@ -114,6 +114,8 @@ class Project < ActiveRecord::Base
has_one :kubernetes_service, dependent: :destroy, inverse_of: :project
has_one :prometheus_service, dependent: :destroy, inverse_of: :project
has_one :mock_ci_service, dependent: :destroy
+ has_one :mock_deployment_service, dependent: :destroy
+ has_one :mock_monitoring_service, dependent: :destroy
has_one :forked_project_link, dependent: :destroy, foreign_key: "forked_to_project_id"
has_one :forked_from_project, through: :forked_project_link
diff --git a/app/models/project_services/kubernetes_service.rb b/app/models/project_services/kubernetes_service.rb
index 02fbd5497fa..9c56518c991 100644
--- a/app/models/project_services/kubernetes_service.rb
+++ b/app/models/project_services/kubernetes_service.rb
@@ -22,22 +22,21 @@ class KubernetesService < DeploymentService
with_options presence: true, if: :activated? do
validates :api_url, url: true
validates :token
-
- validates :namespace,
- format: {
- with: Gitlab::Regex.kubernetes_namespace_regex,
- message: Gitlab::Regex.kubernetes_namespace_regex_message,
- },
- length: 1..63
end
+ validates :namespace,
+ allow_blank: true,
+ length: 1..63,
+ if: :activated?,
+ format: {
+ with: Gitlab::Regex.kubernetes_namespace_regex,
+ message: Gitlab::Regex.kubernetes_namespace_regex_message
+ }
+
after_save :clear_reactive_cache!
def initialize_properties
- if properties.nil?
- self.properties = {}
- self.namespace = "#{project.path}-#{project.id}" if project.present?
- end
+ self.properties = {} if properties.nil?
end
def title
@@ -62,7 +61,7 @@ class KubernetesService < DeploymentService
{ type: 'text',
name: 'namespace',
title: 'Kubernetes namespace',
- placeholder: 'Kubernetes namespace' },
+ placeholder: namespace_placeholder },
{ type: 'text',
name: 'api_url',
title: 'API URL',
@@ -92,7 +91,7 @@ class KubernetesService < DeploymentService
variables = [
{ key: 'KUBE_URL', value: api_url, public: true },
{ key: 'KUBE_TOKEN', value: token, public: false },
- { key: 'KUBE_NAMESPACE', value: namespace, public: true }
+ { key: 'KUBE_NAMESPACE', value: namespace_variable, public: true }
]
if ca_pem.present?
@@ -135,8 +134,26 @@ class KubernetesService < DeploymentService
{ pods: pods }
end
+ TEMPLATE_PLACEHOLDER = 'Kubernetes namespace'.freeze
+
private
+ def namespace_placeholder
+ default_namespace || TEMPLATE_PLACEHOLDER
+ end
+
+ def namespace_variable
+ if namespace.present?
+ namespace
+ else
+ default_namespace
+ end
+ end
+
+ def default_namespace
+ "#{project.path}-#{project.id}" if project.present?
+ end
+
def build_kubeclient!(api_path: 'api', api_version: 'v1')
raise "Incomplete settings" unless api_url && namespace && token
diff --git a/app/models/project_services/mock_deployment_service.rb b/app/models/project_services/mock_deployment_service.rb
new file mode 100644
index 00000000000..59a3811ce5d
--- /dev/null
+++ b/app/models/project_services/mock_deployment_service.rb
@@ -0,0 +1,18 @@
+class MockDeploymentService < DeploymentService
+ def title
+ 'Mock deployment'
+ end
+
+ def description
+ 'Mock deployment service'
+ end
+
+ def self.to_param
+ 'mock_deployment'
+ end
+
+ # No terminals support
+ def terminals(environment)
+ []
+ end
+end
diff --git a/app/models/project_services/mock_monitoring_service.rb b/app/models/project_services/mock_monitoring_service.rb
new file mode 100644
index 00000000000..dd04e04e198
--- /dev/null
+++ b/app/models/project_services/mock_monitoring_service.rb
@@ -0,0 +1,17 @@
+class MockMonitoringService < MonitoringService
+ def title
+ 'Mock monitoring'
+ end
+
+ def description
+ 'Mock monitoring service'
+ end
+
+ def self.to_param
+ 'mock_monitoring'
+ end
+
+ def metrics(environment)
+ JSON.parse(File.read(Rails.root + 'spec/fixtures/metrics.json'))
+ end
+end
diff --git a/app/models/service.rb b/app/models/service.rb
index 54550177744..5a0ec58d193 100644
--- a/app/models/service.rb
+++ b/app/models/service.rb
@@ -238,7 +238,9 @@ class Service < ActiveRecord::Base
slack
teamcity
]
- service_names << 'mock_ci' if Rails.env.development?
+ if Rails.env.development?
+ service_names += %w[mock_ci mock_deployment mock_monitoring]
+ end
service_names.sort_by(&:downcase)
end
diff --git a/app/models/system_note_metadata.rb b/app/models/system_note_metadata.rb
index 5cc66574941..1e6fc837a75 100644
--- a/app/models/system_note_metadata.rb
+++ b/app/models/system_note_metadata.rb
@@ -1,7 +1,7 @@
class SystemNoteMetadata < ActiveRecord::Base
ICON_TYPES = %w[
- commit merge confidentiality status label assignee cross_reference
- title time_tracking branch milestone discussion task moved
+ commit merge confidential visible label assignee cross_reference
+ title time_tracking branch milestone discussion task moved opened closed merged
].freeze
validates :note, presence: true
diff --git a/app/services/search/global_service.rb b/app/services/search/global_service.rb
index a3c655493a5..c1549df5ac6 100644
--- a/app/services/search/global_service.rb
+++ b/app/services/search/global_service.rb
@@ -18,7 +18,11 @@ module Search
end
def scope
- @scope ||= %w[issues merge_requests milestones].delete(params[:scope]) { 'projects' }
+ @scope ||= begin
+ allowed_scopes = %w[issues merge_requests milestones]
+
+ allowed_scopes.delete(params[:scope]) { 'projects' }
+ end
end
end
end
diff --git a/app/services/system_note_service.rb b/app/services/system_note_service.rb
index d3e502b66dd..35cfcc3682e 100644
--- a/app/services/system_note_service.rb
+++ b/app/services/system_note_service.rb
@@ -183,7 +183,9 @@ module SystemNoteService
body = status.dup
body << " via #{source.gfm_reference(project)}" if source
- create_note(NoteSummary.new(noteable, project, author, body, action: 'status'))
+ action = status == 'reopened' ? 'opened' : status
+
+ create_note(NoteSummary.new(noteable, project, author, body, action: action))
end
# Called when 'merge when pipeline succeeds' is executed
@@ -273,9 +275,15 @@ module SystemNoteService
#
# Returns the created Note object
def change_issue_confidentiality(issue, project, author)
- body = issue.confidential ? 'made the issue confidential' : 'made the issue visible to everyone'
+ if issue.confidential
+ body = 'made the issue confidential'
+ action = 'confidential'
+ else
+ body = 'made the issue visible to everyone'
+ action = 'visible'
+ end
- create_note(NoteSummary.new(issue, project, author, body, action: 'confidentiality'))
+ create_note(NoteSummary.new(issue, project, author, body, action: action))
end
# Called when a branch in Noteable is changed
diff --git a/app/services/users/create_service.rb b/app/services/users/create_service.rb
index 193fcd85896..a847a71a66a 100644
--- a/app/services/users/create_service.rb
+++ b/app/services/users/create_service.rb
@@ -62,6 +62,7 @@ module Users
:email,
:external,
:force_random_password,
+ :password_automatically_set,
:hide_no_password,
:hide_no_ssh_key,
:key_id,
@@ -85,6 +86,7 @@ module Users
[
:email,
:email_confirmation,
+ :password_automatically_set,
:name,
:password,
:username
diff --git a/app/views/groups/merge_requests.html.haml b/app/views/groups/merge_requests.html.haml
index 6ad76d23df5..8fe0bd149f3 100644
--- a/app/views/groups/merge_requests.html.haml
+++ b/app/views/groups/merge_requests.html.haml
@@ -1,18 +1,22 @@
- page_title "Merge Requests"
-.top-area
- = render 'shared/issuable/nav', type: :merge_requests
- - if current_user
- .nav-controls
- = render 'shared/new_project_item_select', path: 'merge_requests/new', label: "New Merge Request"
+- if @group_merge_requests.empty?
+ = render 'shared/empty_states/merge_requests', project_select_button: true
+- else
+ .top-area
+ = render 'shared/issuable/nav', type: :merge_requests
+ - if current_user
+ .nav-controls
+ = render 'shared/new_project_item_select', path: 'merge_requests/new', label: "New merge request"
-= render 'shared/issuable/filter', type: :merge_requests
+ = render 'shared/issuable/filter', type: :merge_requests
-.row-content-block.second-block
- Only merge requests from
- %strong= @group.name
- group are listed here.
- - if current_user
- To see all merge requests you should visit #{link_to 'dashboard', merge_requests_dashboard_path} page.
+ .row-content-block.second-block
+ Only merge requests from
+ %strong= @group.name
+ group are listed here.
+ - if current_user
+ To see all merge requests you should visit #{link_to 'dashboard', merge_requests_dashboard_path} page.
-= render 'shared/merge_requests'
+ .prepend-top-default
+ = render 'shared/merge_requests'
diff --git a/app/views/groups/milestones/show.html.haml b/app/views/groups/milestones/show.html.haml
index 8e83b2002b2..33e68bc766e 100644
--- a/app/views/groups/milestones/show.html.haml
+++ b/app/views/groups/milestones/show.html.haml
@@ -1,8 +1,4 @@
= render "header_title"
-
-- content_for :page_specific_javascripts do
- = page_specific_javascript_bundle_tag('simulate_drag') if Rails.env.test?
-
= render 'shared/milestones/top', milestone: @milestone, group: @group
= render 'shared/milestones/tabs', milestone: @milestone, show_project_name: true
= render 'shared/milestones/sidebar', milestone: @milestone, affix_offset: 102
diff --git a/app/views/projects/boards/_show.html.haml b/app/views/projects/boards/_show.html.haml
index added3f669b..7ca0ec8ed2b 100644
--- a/app/views/projects/boards/_show.html.haml
+++ b/app/views/projects/boards/_show.html.haml
@@ -6,10 +6,8 @@
= page_specific_javascript_bundle_tag('common_vue')
= page_specific_javascript_bundle_tag('filtered_search')
= page_specific_javascript_bundle_tag('boards')
- = page_specific_javascript_bundle_tag('simulate_drag') if Rails.env.test?
%script#js-board-template{ type: "text/x-template" }= render "projects/boards/components/board"
- %script#js-board-list-template{ type: "text/x-template" }= render "projects/boards/components/board_list"
%script#js-board-modal-filter{ type: "text/x-template" }= render "shared/issuable/search_bar", type: :boards_modal
= render "projects/issues/head"
diff --git a/app/views/projects/boards/components/_board_list.html.haml b/app/views/projects/boards/components/_board_list.html.haml
deleted file mode 100644
index 4a0b2110601..00000000000
--- a/app/views/projects/boards/components/_board_list.html.haml
+++ /dev/null
@@ -1,26 +0,0 @@
-.board-list-component
- .board-list-loading.text-center{ "v-if" => "loading" }
- = icon("spinner spin")
- - if can? current_user, :create_issue, @project
- %board-new-issue{ ":list" => "list",
- "v-if" => 'list.type !== "closed" && showIssueForm' }
- %ul.board-list{ "ref" => "list",
- "v-show" => "!loading",
- ":data-board" => "list.id",
- ":class" => '{ "is-smaller": showIssueForm }' }
- %board-card{ "v-for" => "(issue, index) in issues",
- "ref" => "issue",
- ":index" => "index",
- ":list" => "list",
- ":issue" => "issue",
- ":issue-link-base" => "issueLinkBase",
- ":root-path" => "rootPath",
- ":disabled" => "disabled",
- ":key" => "issue.id" }
- %li.board-list-count.text-center{ "v-if" => "showCount",
- "data-issue-id" => "-1" }
- = icon("spinner spin", "v-show" => "list.loadingMore" )
- %span{ "v-if" => "list.issues.length === list.issuesSize" }
- Showing all issues
- %span{ "v-else" => true }
- Showing {{ list.issues.length }} of {{ list.issuesSize }} issues
diff --git a/app/views/projects/edit.html.haml b/app/views/projects/edit.html.haml
index b78de092a60..160345cfaa5 100644
--- a/app/views/projects/edit.html.haml
+++ b/app/views/projects/edit.html.haml
@@ -238,6 +238,8 @@
%ul
%li Be careful. Renaming a project's repository can have unintended side effects.
%li You will need to update your local repositories to point to the new location.
+ - if @project.deployment_services.any?
+ %li Your deployment services will be broken, you will need to manually fix the services after renaming.
= f.submit 'Rename project', class: "btn btn-warning"
- if can?(current_user, :change_namespace, @project)
%hr
diff --git a/app/views/projects/environments/_external_url.html.haml b/app/views/projects/environments/_external_url.html.haml
index bf0f1819073..a82ef5ee5bb 100644
--- a/app/views/projects/environments/_external_url.html.haml
+++ b/app/views/projects/environments/_external_url.html.haml
@@ -1,3 +1,4 @@
- if environment.external_url && can?(current_user, :read_environment, environment)
= link_to environment.external_url, target: '_blank', rel: 'noopener noreferrer', class: 'btn external-url' do
= icon('external-link')
+ View deployment
diff --git a/app/views/projects/environments/_metrics_button.html.haml b/app/views/projects/environments/_metrics_button.html.haml
index acbac1869fd..e27281d6917 100644
--- a/app/views/projects/environments/_metrics_button.html.haml
+++ b/app/views/projects/environments/_metrics_button.html.haml
@@ -4,3 +4,4 @@
= link_to environment_metrics_path(environment), title: 'See metrics', class: 'btn metrics-button' do
= icon('area-chart')
+ Monitoring
diff --git a/app/views/projects/environments/metrics.html.haml b/app/views/projects/environments/metrics.html.haml
index 3b45162df52..92dc58cd38d 100644
--- a/app/views/projects/environments/metrics.html.haml
+++ b/app/views/projects/environments/metrics.html.haml
@@ -11,7 +11,7 @@
.col-sm-6
%h3.page-title
Environment:
- = @environment.name
+ = link_to @environment.name, environment_path(@environment)
.col-sm-6
.nav-controls
diff --git a/app/views/projects/environments/show.html.haml b/app/views/projects/environments/show.html.haml
index f463a429f65..ff6aaebda22 100644
--- a/app/views/projects/environments/show.html.haml
+++ b/app/views/projects/environments/show.html.haml
@@ -4,9 +4,9 @@
%div{ class: container_class }
.top-area.adjust
- .col-md-9
+ .col-md-7
%h3.page-title= @environment.name
- .col-md-3
+ .col-md-5
.nav-controls
= render 'projects/environments/metrics_button', environment: @environment
= render 'projects/environments/terminal_button', environment: @environment
diff --git a/app/views/projects/labels/index.html.haml b/app/views/projects/labels/index.html.haml
index 8d4a91cb64c..29f861c09c6 100644
--- a/app/views/projects/labels/index.html.haml
+++ b/app/views/projects/labels/index.html.haml
@@ -3,9 +3,6 @@
- hide_class = ''
= render "projects/issues/head"
-- content_for :page_specific_javascripts do
- = page_specific_javascript_bundle_tag('simulate_drag') if Rails.env.test?
-
- if @labels.exists? || @prioritized_labels.exists?
%div{ class: container_class }
.top-area.adjust
diff --git a/app/views/projects/merge_requests/_merge_requests.html.haml b/app/views/projects/merge_requests/_merge_requests.html.haml
index fe82f751f53..4e97f74dd6a 100644
--- a/app/views/projects/merge_requests/_merge_requests.html.haml
+++ b/app/views/projects/merge_requests/_merge_requests.html.haml
@@ -1,8 +1,8 @@
%ul.content-list.mr-list.issuable-list
- = render @merge_requests
- - if @merge_requests.blank?
- %li
- .nothing-here-block No merge requests to show
+ - if @merge_requests.exists?
+ = render @merge_requests
+ - else
+ = render 'shared/empty_states/merge_requests'
- if @merge_requests.present?
= paginate @merge_requests, theme: "gitlab"
diff --git a/app/views/projects/merge_requests/index.html.haml b/app/views/projects/merge_requests/index.html.haml
index 8a96c8dacf6..64f17ab34b1 100644
--- a/app/views/projects/merge_requests/index.html.haml
+++ b/app/views/projects/merge_requests/index.html.haml
@@ -7,16 +7,19 @@
- content_for :page_specific_javascripts do
= page_specific_javascript_bundle_tag('filtered_search')
-%div{ class: container_class }
- .top-area
- = render 'shared/issuable/nav', type: :merge_requests
- .nav-controls
- - merge_project = can?(current_user, :create_merge_request, @project) ? @project : (current_user && current_user.fork_of(@project))
- - if merge_project
- = link_to new_namespace_project_merge_request_path(merge_project.namespace, merge_project), class: "btn btn-new", title: "New Merge Request" do
- New Merge Request
+- if @project.merge_requests.exists?
+ %div{ class: container_class }
+ .top-area
+ = render 'shared/issuable/nav', type: :merge_requests
+ .nav-controls
+ - merge_project = can?(current_user, :create_merge_request, @project) ? @project : (current_user && current_user.fork_of(@project))
+ - if merge_project
+ = link_to new_namespace_project_merge_request_path(merge_project.namespace, merge_project), class: "btn btn-new", title: "New merge request" do
+ New merge request
- = render 'shared/issuable/search_bar', type: :merge_requests
+ = render 'shared/issuable/search_bar', type: :merge_requests
- .merge-requests-holder
- = render 'merge_requests'
+ .merge-requests-holder
+ = render 'merge_requests'
+- else
+ = render 'shared/empty_states/merge_requests', button_path: new_namespace_project_merge_request_path(@project.namespace, @project)
diff --git a/app/views/projects/milestones/show.html.haml b/app/views/projects/milestones/show.html.haml
index f612b5c7d6b..5249d752585 100644
--- a/app/views/projects/milestones/show.html.haml
+++ b/app/views/projects/milestones/show.html.haml
@@ -3,9 +3,6 @@
- page_description @milestone.description
= render "projects/issues/head"
-- content_for :page_specific_javascripts do
- = page_specific_javascript_bundle_tag('simulate_drag') if Rails.env.test?
-
%div{ class: container_class }
.detail-page-header.milestone-page-header
.status-box{ class: status_box_class(@milestone) }
diff --git a/app/views/projects/new.html.haml b/app/views/projects/new.html.haml
index 09ac1fd6794..0c7b53e5a9a 100644
--- a/app/views/projects/new.html.haml
+++ b/app/views/projects/new.html.haml
@@ -78,7 +78,7 @@
- if git_import_enabled?
%button.btn.js-toggle-button.import_git{ type: "button" }
= icon('git', text: 'Repo by URL')
- .import_gitlab_project
+ .import_gitlab_project.has-tooltip{ data: { container: 'body' } }
- if gitlab_project_import_enabled?
= link_to new_import_gitlab_project_path, class: 'btn btn_import_gitlab_project project-submit' do
= icon('gitlab', text: 'GitLab export')
@@ -109,6 +109,9 @@
%p Please wait a moment, this page will automatically refresh when ready.
:javascript
+ var importBtnTooltip = "Please enter a valid project name.";
+ var $importBtnWrapper = $('.import_gitlab_project');
+
$('.how_to_import_link').bind('click', function (e) {
e.preventDefault();
var import_modal = $(this).next(".modal").show();
@@ -123,15 +126,8 @@
$(".btn_import_gitlab_project").attr("href", _href + '?namespace_id=' + $("#project_namespace_id").val() + '&path=' + $("#project_path").val());
});
- $('.btn_import_gitlab_project').attr('disabled',true)
- $('.import_gitlab_project').attr('title', 'Project path and name required.');
-
- $('.import_gitlab_project').click(function( event ) {
- if($('.btn_import_gitlab_project').attr('disabled')) {
- event.preventDefault();
- new Flash("Please enter path and name for the project to be imported to.");
- }
- });
+ $('.btn_import_gitlab_project').attr('disabled', $('#project_path').val().trim().length === 0);
+ $importBtnWrapper.attr('title', importBtnTooltip);
$('#new_project').submit(function(){
var $path = $('#project_path');
@@ -139,13 +135,13 @@
});
$('#project_path').keyup(function(){
- if($(this).val().length !=0) {
+ if($(this).val().trim().length !== 0) {
$('.btn_import_gitlab_project').attr('disabled', false);
- $('.import_gitlab_project').attr('title','');
- $(".flash-container").html("")
+ $importBtnWrapper.attr('title','');
+ $importBtnWrapper.removeClass('has-tooltip');
} else {
$('.btn_import_gitlab_project').attr('disabled',true);
- $('.import_gitlab_project').attr('title', 'Project path and name required.');
+ $importBtnWrapper.addClass('has-tooltip');
}
});
diff --git a/app/views/projects/wikis/_form.html.haml b/app/views/projects/wikis/_form.html.haml
index c52527332bc..0d2cd4a7476 100644
--- a/app/views/projects/wikis/_form.html.haml
+++ b/app/views/projects/wikis/_form.html.haml
@@ -1,3 +1,5 @@
+- commit_message = @page.persisted? ? "Update #{@page.title}" : "Create #{@page.title}"
+
= form_for [@project.namespace.becomes(Namespace), @project, @page], method: @page.persisted? ? :put : :post, html: { class: 'form-horizontal wiki-form common-note-form prepend-top-default js-quick-submit' } do |f|
= form_errors(@page)
@@ -28,7 +30,7 @@
.form-group
= f.label :commit_message, class: 'control-label'
- .col-sm-10= f.text_field :message, class: 'form-control', rows: 18
+ .col-sm-10= f.text_field :message, class: 'form-control', rows: 18, value: commit_message
.form-actions
- if @page && @page.persisted?
diff --git a/app/views/shared/_merge_requests.html.haml b/app/views/shared/_merge_requests.html.haml
index b7982b7fe9b..eecbb32e90e 100644
--- a/app/views/shared/_merge_requests.html.haml
+++ b/app/views/shared/_merge_requests.html.haml
@@ -6,4 +6,4 @@
= paginate @merge_requests, theme: "gitlab"
- else
- .nothing-here-block No merge requests to show
+ = render 'shared/empty_states/merge_requests'
diff --git a/app/views/shared/empty_states/_merge_requests.html.haml b/app/views/shared/empty_states/_merge_requests.html.haml
new file mode 100644
index 00000000000..7f2f99f3406
--- /dev/null
+++ b/app/views/shared/empty_states/_merge_requests.html.haml
@@ -0,0 +1,22 @@
+- button_path = local_assigns.fetch(:button_path, false)
+- project_select_button = local_assigns.fetch(:project_select_button, false)
+- has_button = button_path || project_select_button
+
+.row.empty-state.merge-requests
+ .col-xs-12{ class: "#{'col-sm-6 pull-right' if has_button}" }
+ .svg-content
+ = render 'shared/empty_states/icons/merge_requests.svg'
+ .col-xs-12{ class: "#{'col-sm-6' if has_button}" }
+ .text-content
+ - if has_button
+ %h4
+ Merge requests are a place to propose changes you've made to a project and discuss those changes with others.
+ %p
+ Interested parties can even contribute by pushing commits if they want to.
+ - if project_select_button
+ = render 'shared/new_project_item_select', path: 'merge_requests/new', label: 'New merge request'
+ - else
+ = link_to 'New merge request', button_path, class: 'btn btn-new', title: 'New merge request', id: 'new_merge_request_link'
+ - else
+ %h4.text-center
+ There are no merge requests to show.
diff --git a/app/views/shared/empty_states/icons/_merge_requests.svg b/app/views/shared/empty_states/icons/_merge_requests.svg
new file mode 100644
index 00000000000..e77f6319a95
--- /dev/null
+++ b/app/views/shared/empty_states/icons/_merge_requests.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="755 221 385 225" xmlns:xlink="http://www.w3.org/1999/xlink"><defs><rect id="a" width="278" height="179" rx="10"/><mask id="d" width="278" height="179" x="0" y="0" fill="#fff"><use xlink:href="#a"/></mask><path id="b" d="M13.6 49H57c5.5 0 10-4.5 10-10V10c0-5.5-4.5-10-10-10H10C4.5 0 0 4.5 0 10v42c0 5.5 3.2 7 7.2 3l6.4-6z"/><mask id="e" width="67" height="57.2" x="0" y="0" fill="#fff"><use xlink:href="#b"/></mask><path id="c" d="M13.6 49H57c5.5 0 10-4.5 10-10V10c0-5.5-4.5-10-10-10H10C4.5 0 0 4.5 0 10v42c0 5.5 3.2 7 7.2 3l6.4-6z"/><mask id="f" width="67" height="57.2" x="0" y="0" fill="#fff"><use xlink:href="#c"/></mask></defs><g fill="none" fill-rule="evenodd"><g fill="#F9F9F9" transform="translate(752 227)"><rect width="120" height="22" x="30" rx="11"/><rect width="132" height="22" y="44" rx="11"/><rect width="190" height="22" x="208" y="66" rx="11"/><rect width="158" height="22" x="129" y="197" rx="11"/><rect width="158" height="22" x="66" y="154" rx="11"/><rect width="350" height="22" x="31" y="110" rx="11"/><path d="M153 22H21h21.5c6 0 11 5 11 11s-5 11-11 11H21h132-36.5c-6 0-11-5-11-11s5-11 11-11H153zm252 66H288h36.5c6 0 11 5 11 11s-5 11-11 11H288h117-36.5c-6 0-11-5-11-11s5-11 11-11H405zm-244 44H44h36.5c6 0 11 5 11 11s-5 11-11 11H44h117-36.5c-6 0-11-5-11-11s5-11 11-11H161zm75 44H119h21.5c6 0 11 5 11 11s-5 11-11 11H119h117-51.5c-6 0-11-5-11-11s5-11 11-11H236z"/></g><g transform="translate(812 240)"><use fill="#FFF" stroke="#EEE" stroke-width="8" mask="url(#d)" xlink:href="#a"/><path fill="#EEE" d="M4 29h271v4H4z"/><g transform="translate(34 60)"><rect width="6" height="2" y="1" fill="#B5A7DD" rx="1"/><rect width="15" height="4" x="15" fill="#EEE" rx="2"/><rect width="15" height="4" x="72" fill="#EEE" rx="2"/><rect width="15" height="4" x="39" y="22" fill="#EEE" rx="2"/><rect width="15" height="4" x="53" y="11" fill="#FC6D26" rx="2"/><rect width="20" height="4" x="48" fill="#FC6D26" opacity=".5" rx="2"/><rect width="20" height="4" x="15" y="22" fill="#EEE" rx="2"/><rect width="20" height="4" x="29" y="11" fill="#EEE" rx="2"/><rect width="10" height="4" x="34" fill="#FC6D26" rx="2"/><rect width="10" height="4" x="15" y="11" fill="#EEE" rx="2"/><rect width="6" height="2" y="12" fill="#B5A7DD" rx="1"/><rect width="6" height="2" y="23" fill="#B5A7DD" rx="1"/></g><g transform="translate(34 93)"><rect width="6" height="2" y="1" fill="#B5A7DD" rx="1"/><rect width="15" height="4" x="15" fill="#FC6D26" rx="2"/><rect width="15" height="4" x="72" fill="#EEE" rx="2"/><rect width="15" height="4" x="39" y="22" fill="#FC6D26" opacity=".5" rx="2"/><rect width="15" height="4" x="53" y="11" fill="#EEE" rx="2"/><rect width="20" height="4" x="48" fill="#FC6D26" rx="2"/><rect width="20" height="4" x="15" y="22" fill="#FC6D26" rx="2"/><rect width="20" height="4" x="29" y="11" fill="#EEE" rx="2"/><rect width="10" height="4" x="34" fill="#FC6D26" opacity=".5" rx="2"/><rect width="10" height="4" x="15" y="11" fill="#EEE" rx="2"/><rect width="6" height="2" y="12" fill="#B5A7DD" rx="1"/><rect width="6" height="2" y="23" fill="#B5A7DD" rx="1"/></g><g transform="translate(34 126)"><rect width="6" height="2" y="1" fill="#B5A7DD" rx="1"/><rect width="15" height="4" x="15" fill="#EEE" rx="2"/><rect width="15" height="4" x="72" fill="#EEE" rx="2"/><rect width="15" height="4" x="39" y="22" fill="#EEE" rx="2"/><rect width="15" height="4" x="53" y="11" fill="#EEE" rx="2"/><rect width="20" height="4" x="48" fill="#FC6D26" rx="2"/><rect width="20" height="4" x="15" y="22" fill="#EEE" rx="2"/><rect width="20" height="4" x="29" y="11" fill="#EEE" rx="2"/><rect width="10" height="4" x="34" fill="#FC6D26" opacity=".5" rx="2"/><rect width="10" height="4" x="15" y="11" fill="#EEE" rx="2"/><rect width="6" height="2" y="12" fill="#B5A7DD" rx="1"/><rect width="6" height="2" y="23" fill="#B5A7DD" rx="1"/></g><g transform="translate(157 59)"><rect width="6" height="2" y="1" fill="#FDE5D8" rx="1"/><rect width="15" height="4" x="15" fill="#EEE" rx="2"/><rect width="15" height="4" x="72" fill="#EEE" rx="2"/><rect width="15" height="4" x="39" y="22" fill="#6B4FBB" opacity=".5" rx="2"/><rect width="15" height="4" x="53" y="11" fill="#6B4FBB" rx="2"/><rect width="20" height="4" x="48" fill="#6B4FBB" opacity=".5" rx="2"/><rect width="20" height="4" x="15" y="22" fill="#6B4FBB" rx="2"/><rect width="20" height="4" x="29" y="11" fill="#EEE" rx="2"/><rect width="10" height="4" x="34" fill="#6B4FBB" rx="2"/><rect width="10" height="4" x="15" y="11" fill="#EEE" rx="2"/><rect width="6" height="2" y="12" fill="#FDE5D8" rx="1"/><rect width="6" height="2" y="23" fill="#FDE5D8" rx="1"/><rect width="6" height="2" y="34" fill="#FDE5D8" rx="1"/><rect width="15" height="4" x="15" y="33" fill="#EEE" rx="2"/><rect width="15" height="4" x="58" y="22" fill="#EEE" rx="2"/><rect width="15" height="4" x="39" y="55" fill="#6B4FBB" opacity=".5" rx="2"/><rect width="15" height="4" x="29" y="44" fill="#6B4FBB" rx="2"/><rect width="20" height="4" x="48" y="33" fill="#6B4FBB" rx="2"/><rect width="20" height="4" x="15" y="55" fill="#EEE" rx="2"/><rect width="10" height="4" x="34" y="33" fill="#EEE" rx="2"/><rect width="10" height="4" x="15" y="44" fill="#EEE" rx="2"/><rect width="10" height="4" x="48" y="44" fill="#EEE" rx="2"/><rect width="10" height="4" x="62" y="44" fill="#EEE" rx="2"/><rect width="10" height="4" x="77" y="22" fill="#EEE" rx="2"/><rect width="6" height="2" y="45" fill="#FDE5D8" rx="1"/><rect width="6" height="2" y="56" fill="#FDE5D8" rx="1"/><rect width="6" height="2" y="67" fill="#FDE5D8" rx="1"/><rect width="15" height="4" x="15" y="66" fill="#6B4FBB" rx="2"/><rect width="15" height="4" x="39" y="88" fill="#EEE" rx="2"/><rect width="15" height="4" x="53" y="77" fill="#6B4FBB" opacity=".5" rx="2"/><rect width="20" height="4" x="15" y="88" fill="#EEE" rx="2"/><rect width="20" height="4" x="29" y="77" fill="#6B4FBB" rx="2"/><rect width="10" height="4" x="34" y="66" fill="#EEE" rx="2"/><rect width="10" height="4" x="72" y="77" fill="#EEE" rx="2"/><rect width="10" height="4" x="15" y="77" fill="#EEE" rx="2"/><rect width="6" height="2" y="78" fill="#FDE5D8" rx="1"/><rect width="6" height="2" y="89" fill="#FDE5D8" rx="1"/></g></g><g transform="translate(1057 221)"><use fill="#FFF" stroke="#FDE5D8" stroke-width="8" mask="url(#e)" xlink:href="#b"/><rect width="29" height="3" x="14" y="14" fill="#FDB692" rx="1.5"/><rect width="39" height="3" x="14" y="23" fill="#FDB692" rx="1.5"/><rect width="29" height="3" x="14" y="32" fill="#FDB692" rx="1.5"/></g><g transform="translate(1046 285)"><circle cx="16" cy="15" r="15" fill="#FFF7F4" stroke="#FC6D26" stroke-width="3"/><path stroke="#FC6D26" stroke-width="2" d="M0 14h1c5 0 9.2-2.7 11.4-6.7M14 1V0"/><path stroke="#FC6D26" stroke-width="2" d="M7.8 3c3 4.3 7.8 7 13.2 7 3.3 0 6.3-1 9-2.7"/><circle cx="10.5" cy="17.5" r="1.5" fill="#FC6D26"/><circle cx="21.5" cy="17.5" r="1.5" fill="#FC6D26"/></g><g transform="translate(825 370)"><circle cx="15" cy="16" r="15" fill="#F4F1FA" stroke="#6B4FBB" stroke-width="3"/><path fill="#6B4FBB" d="M25 7h2.7C25 2.8 20.4 0 15 0 9.6 0 5 2.8 2.3 7H5l2.5-3L10 7l2.5-3L15 7l2.5-3L20 7l2.5-3L25 7z"/><circle cx="9.5" cy="17.5" r="1.5" fill="#6B4FBB"/><circle cx="20.5" cy="17.5" r="1.5" fill="#6B4FBB"/></g><g transform="matrix(-1 0 0 1 840 306)"><use fill="#FFF" stroke="#E2DCF2" stroke-width="8" mask="url(#f)" xlink:href="#c"/><rect width="29" height="3" x="24" y="14" fill="#6B4FBB" opacity=".5" rx="1.5"/><rect width="19" height="3" x="34" y="23" fill="#6B4FBB" opacity=".5" rx="1.5"/><rect width="19" height="3" x="34" y="32" fill="#6B4FBB" opacity=".5" rx="1.5"/></g></g></svg>
diff --git a/app/views/shared/icons/_activity.svg b/app/views/shared/icons/_activity.svg
deleted file mode 100644
index d465504b154..00000000000
--- a/app/views/shared/icons/_activity.svg
+++ /dev/null
@@ -1,16 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<svg width="16px" height="16px" viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
- <!-- Generator: Sketch 3.8.3 (29802) - http://www.bohemiancoding.com/sketch -->
- <title>path-1</title>
- <desc>Created with Sketch.</desc>
- <defs></defs>
- <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
- <g id="_activity" fill="#7E7D7D">
- <g id="Page-1">
- <g id="path-1">
- <path d="M5,0 C4.448,0 4,0.448 4,1 L4,3 L1,3 C0.448,3 0,3.448 0,4 L0,9 C0,9.552 0.448,10 1,10 L5,10 L5,8 L11,8 L11,10 L15,10 C15.552,10 16,9.552 16,9 L16,4 C16,3.448 15.552,3 15,3 L12,3 L12,1 C12,0.448 11.552,0 11,0 L5,0 L5,0 L5,0 L5,0 Z M6,2.5 C6,2.224 6.224,2 6.5,2 L9.5,2 C9.776,2 10,2.224 10,2.5 C10,2.776 9.776,3 9.5,3 L6.5,3 C6.224,3 6,2.776 6,2.5 L6,2.5 L6,2.5 L6,2.5 Z M6,11 L10.001,11 L10.001,9 L6,9 L6,11 L6,11 L6,11 L6,11 Z M11,11 L11,12 L5,12 L5,11 L1,11 C0.448,11 0,11.448 0,12 L0,15 C0,15.552 0.448,16 1,16 L15,16 C15.552,16 16,15.552 16,15 L16,12 C16,11.448 15.552,11 15,11 L11,11 L11,11 L11,11 L11,11 Z"></path>
- </g>
- </g>
- </g>
- </g>
-</svg> \ No newline at end of file
diff --git a/app/views/shared/icons/_commits.svg b/app/views/shared/icons/_commits.svg
deleted file mode 100644
index ba9bb89935e..00000000000
--- a/app/views/shared/icons/_commits.svg
+++ /dev/null
@@ -1,10 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<svg width="16px" height="16px" viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
- <!-- Generator: Sketch 3.8.3 (29802) - http://www.bohemiancoding.com/sketch -->
- <title>Pasted Image 240</title>
- <desc>Created with Sketch.</desc>
- <defs></defs>
- <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
- <path d="M3,8 C3,5.951 4.236,4.194 6,3.422 L6,0 L1,0 C0.448,0 0,0.448 0,1 L0,15 C0,15.552 0.448,16 1,16 L6,16 L6,12.578 C4.236,11.806 3,10.049 3,8 M7,12.899 L7,16 L9,16 L9,12.899 C8.677,12.965 8.343,13 8,13 C7.657,13 7.323,12.965 7,12.899 M15,0 L10,0 L10,3.422 C11.764,4.194 13,5.951 13,8 C13,10.049 11.764,11.806 10,12.578 L10,16 L15,16 C15.552,16 16,15.552 16,15 L16,1 C16,0.448 15.552,0 15,0 M10,8 C10,9.105 9.105,10 8,10 C6.895,10 6,9.105 6,8 C6,6.895 6.895,6 8,6 C9.105,6 10,6.895 10,8 M4,8 C4,10.209 5.791,12 8,12 C10.209,12 12,10.209 12,8 C12,5.791 10.209,4 8,4 C5.791,4 4,5.791 4,8 M9,3.101 L9,0 L7,0 L7,3.101 C7.323,3.035 7.657,3 8,3 C8.343,3 8.677,3.035 9,3.101" id="Pasted-Image-240" fill="#7E7D7D"></path>
- </g>
-</svg> \ No newline at end of file
diff --git a/app/views/shared/icons/_contributionanalytics.svg b/app/views/shared/icons/_contributionanalytics.svg
deleted file mode 100644
index adf09a14964..00000000000
--- a/app/views/shared/icons/_contributionanalytics.svg
+++ /dev/null
@@ -1,17 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<svg width="16px" height="16px" viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
- <!-- Generator: Sketch 3.7.2 (28276) - http://www.bohemiancoding.com/sketch -->
- <title>Group</title>
- <desc>Created with Sketch.</desc>
- <defs></defs>
- <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
- <g id="Group">
- <path d="M8,0 C3.581,0 0,3.581 0,8 C0,12.419 3.581,16 8,16 C12.419,16 16,12.419 16,8 C16,3.581 12.419,0 8,0 M8,2 C11.308,2 14,4.692 14,8 C14,11.308 11.308,14 8,14 C4.692,14 2,11.308 2,8 C2,4.692 4.692,2 8,2" id="Fill-1" fill="#7E7C7C"></path>
- <polygon id="Stroke-6" fill="#7E7C7C" points="2.0197351 9.86809696 6.4567351 6.52409696 5.79233671 6.46815759 9.53233671 10.4271576 9.87070552 10.78534 10.2338016 10.4522494 15.0258016 6.05624938 14.3497984 5.31935062 9.55779844 9.71535062 10.2592633 9.74044241 6.51926329 5.78144241 6.21208651 5.45627854 5.8548649 5.72550304 1.4178649 9.06950304"></polygon>
- <path d="M7.0313,6.3928 C7.0313,6.9448 6.5833,7.3928 6.0313,7.3928 C5.4793,7.3928 5.0313,6.9448 5.0313,6.3928 C5.0313,5.8408 5.4793,5.3928 6.0313,5.3928 C6.5833,5.3928 7.0313,5.8408 7.0313,6.3928" id="Fill-8" fill="#FEFEFE"></path>
- <path d="M6.5313,6.3928 C6.5313,6.66865763 6.30715763,6.8928 6.0313,6.8928 C5.75544237,6.8928 5.5313,6.66865763 5.5313,6.3928 C5.5313,6.11694237 5.75544237,5.8928 6.0313,5.8928 C6.30715763,5.8928 6.5313,6.11694237 6.5313,6.3928 L6.5313,6.3928 Z M7.5313,6.3928 C7.5313,5.56465763 6.85944237,4.8928 6.0313,4.8928 C5.20315763,4.8928 4.5313,5.56465763 4.5313,6.3928 C4.5313,7.22094237 5.20315763,7.8928 6.0313,7.8928 C6.85944237,7.8928 7.5313,7.22094237 7.5313,6.3928 L7.5313,6.3928 Z" id="Stroke-10" fill="#7E7C7C"></path>
- <path d="M10.8854,9.8715 C10.8854,10.4235 10.4374,10.8715 9.8854,10.8715 C9.3334,10.8715 8.8854,10.4235 8.8854,9.8715 C8.8854,9.3195 9.3334,8.8715 9.8854,8.8715 C10.4374,8.8715 10.8854,9.3195 10.8854,9.8715" id="Fill-12" fill="#FEFEFE"></path>
- <path d="M10.3854,9.8715 C10.3854,10.1473576 10.1612576,10.3715 9.8854,10.3715 C9.60954237,10.3715 9.3854,10.1473576 9.3854,9.8715 C9.3854,9.59564237 9.60954237,9.3715 9.8854,9.3715 C10.1612576,9.3715 10.3854,9.59564237 10.3854,9.8715 L10.3854,9.8715 Z M11.3854,9.8715 C11.3854,9.04335763 10.7135424,8.3715 9.8854,8.3715 C9.05725763,8.3715 8.3854,9.04335763 8.3854,9.8715 C8.3854,10.6996424 9.05725763,11.3715 9.8854,11.3715 C10.7135424,11.3715 11.3854,10.6996424 11.3854,9.8715 L11.3854,9.8715 Z" id="Stroke-14" fill="#7E7C7C"></path>
- </g>
- </g>
-</svg> \ No newline at end of file
diff --git a/app/views/shared/icons/_delta.svg b/app/views/shared/icons/_delta.svg
deleted file mode 100644
index 7c0c0d3999c..00000000000
--- a/app/views/shared/icons/_delta.svg
+++ /dev/null
@@ -1,3 +0,0 @@
-<svg width="14px" height="10px" viewBox="322 21 14 10" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
- <path d="M330.078605,22.8166945 L335.259532,29.6235062 C335.615145,30.0907182 335.412062,30.4694683 334.822641,30.4694683 L331.657805,30.4694683 L324.04678,30.4694683 C323.449879,30.4694683 323.260751,30.0822112 323.609889,29.6235062 L328.790816,22.8166945 C329.146429,22.3494825 329.729467,22.3579895 330.078605,22.8166945 Z" id="delta" stroke="#5C5C5C" stroke-width="1" fill="none"></path>
-</svg>
diff --git a/app/views/shared/icons/_files.svg b/app/views/shared/icons/_files.svg
deleted file mode 100644
index fc378d81e40..00000000000
--- a/app/views/shared/icons/_files.svg
+++ /dev/null
@@ -1,17 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<svg width="16px" height="16px" viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
- <!-- Generator: Sketch 3.8.3 (29802) - http://www.bohemiancoding.com/sketch -->
- <title>Pasted Image 237</title>
- <desc>Created with Sketch.</desc>
- <defs></defs>
- <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
- <g id="Pasted-Image-237">
- <path d="M15.1111,16 C15.6021,16 16.0001,15.602 16.0001,15.111 L16.0001,4.444 C15.5341,3.983 12.0671,0.378 11.5551,0 L0.8891,0 C0.3981,0 0.0001,0.398 0.0001,0.889 L0.0001,15.111 C0.0001,15.602 0.3981,16 0.8891,16 L15.1111,16 M14.0001,14.111 L1.8891,14.111 L1.8891,2 L10.8131,2 C11.4451,2.42 13.5811,4.555 14.0001,5.187 L14.0001,14.111" id="Fill-1" fill="#7E7D7D"></path>
- <path d="M0.889,0 C0.398,0 0,0.398 0,0.889 L0,15.111 C0,15.602 0.398,16 0.889,16 L15.111,16 C15.602,16 16,15.602 16,15.111 L16,4.445 C15.534,3.983 12.068,0.377 11.555,0 L0.889,0 L0.889,0 Z M1.889,2 L10.813,2 C11.446,2.42 13.581,4.554 14,5.187 L14,14.111 L1.889,14.111 L1.889,2 L1.889,2 Z" id="Clip-4"></path>
- <polygon id="Fill-6" fill="#7E7D7D" points="9 7 11 7 11 2 9 2"></polygon>
- <polygon id="Clip-9" points="9 7 11 7 11 2.001 9 2.001"></polygon>
- <polygon id="Fill-11" fill="#7E7D7D" points="10 7 15.444 7 15.444 5 10 5"></polygon>
- <polygon id="Clip-14" points="10 7 15.444 7 15.444 5 10 5"></polygon>
- </g>
- </g>
-</svg> \ No newline at end of file
diff --git a/app/views/shared/icons/_icon_close.svg b/app/views/shared/icons/_icon_close.svg
index 9d62012518b..59a6cb32d18 100644
--- a/app/views/shared/icons/_icon_close.svg
+++ b/app/views/shared/icons/_icon_close.svg
@@ -1 +1 @@
-<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 15 15"><path d="M9,7.5l5.83-5.91a.48.48,0,0,0,0-.69L14.11.15a.46.46,0,0,0-.68,0l-5.93,6L1.57.15a.46.46,0,0,0-.68,0L.15.9a.48.48,0,0,0,0,.69L6,7.5.15,13.41a.48.48,0,0,0,0,.69l.74.75a.46.46,0,0,0,.68,0l5.93-6,5.93,6a.46.46,0,0,0,.68,0l.74-.75a.48.48,0,0,0,0-.69Z"/></svg> \ No newline at end of file
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 15 15"><path d="M9,7.5l5.83-5.91a.48.48,0,0,0,0-.69L14.11.15a.46.46,0,0,0-.68,0l-5.93,6L1.57.15a.46.46,0,0,0-.68,0L.15.9a.48.48,0,0,0,0,.69L6,7.5.15,13.41a.48.48,0,0,0,0,.69l.74.75a.46.46,0,0,0,.68,0l5.93-6,5.93,6a.46.46,0,0,0,.68,0l.74-.75a.48.48,0,0,0,0-.69Z"/></svg>
diff --git a/app/views/shared/icons/_icon_empty_groups.svg b/app/views/shared/icons/_icon_empty_groups.svg
index 9228be05f03..cf378145e59 100644
--- a/app/views/shared/icons/_icon_empty_groups.svg
+++ b/app/views/shared/icons/_icon_empty_groups.svg
@@ -1 +1 @@
-<svg width="249" height="368" viewBox="891 156 249 368" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><defs><rect id="a" width="131" height="162" rx="10"/><mask id="e" x="0" y="0" width="131" height="162" fill="#fff"><use xlink:href="#a"/></mask><path d="M223.616 127.958V108.96c0-4.416-3.584-8-8.005-8h-23.985c-2.778 0-5.98 2.014-7.18 4.5l-5.07 10.5h-49.763c-5.527 0-9.996 4.475-9.996 9.997v53.005c0 5.513 4.475 9.997 9.996 9.997h84.01c5.525 0 9.994-4.477 9.994-9.998v-51.004z" id="b"/><mask id="f" x="0" y="0" width="104" height="88" fill="#fff"><use xlink:href="#b"/></mask><path d="M47 25h.996C53.52 25 58 29.472 58 34.99v20.02C58 60.526 53.52 65 47.996 65H10.004C4.48 65 0 60.528 0 55.01V34.99C0 29.474 4.48 25 10.004 25H11v-7c0-9.94 8.06-18 18-18s18 8.06 18 18v7zm-6 0H17v-7c0-6.627 5.373-12 12-12s12 5.373 12 12v7z" id="c"/><mask id="g" x="0" y="0" width="58" height="65" fill="#fff"><use xlink:href="#c"/></mask><path d="M0 10.008C0 4.48 4.476 0 10 0h218c5.523 0 10 4.473 10 10.008v140.94c0 5.53-4.062 11.882-9.08 14.196l-100.84 46.5c-5.015 2.31-13.142 2.312-18.16 0l-100.84-46.5C4.064 162.832 0 156.484 0 150.95V10.007z" id="d"/><mask id="h" x="0" y="0" width="238" height="213.417" fill="#fff"><use xlink:href="#d"/></mask></defs><g fill="none" fill-rule="evenodd" transform="translate(891 156)"><g transform="rotate(8 -266.528 490.3)"><use stroke="#E5E5E5" mask="url(#e)" stroke-width="8" fill="#FFF" xlink:href="#a"/><rect fill="#FC8A51" x="20" y="31" width="12" height="4" rx="2"/><rect fill="#FC8A51" x="60" y="31" width="12" height="4" rx="2"/><rect fill="#FDE5D8" x="36" y="31" width="20" height="4" rx="2"/><rect fill="#6B4FBB" x="20" y="65" width="20" height="4" rx="2"/><rect fill="#FDE5D8" x="44" y="65" width="20" height="4" rx="2"/><rect fill="#FC8A51" x="36" y="80" width="20" height="4" rx="2"/><rect fill="#FDE5D8" x="20" y="80" width="12" height="4" rx="2"/><rect fill="#FDE5D8" x="20" y="48" width="12" height="4" rx="2"/><rect fill="#FC8A51" x="36" y="48" width="12" height="4" rx="2"/><rect fill="#FDE5D8" x="60" y="80" width="12" height="4" rx="2"/><rect fill="#6B4FBB" x="52" y="48" width="12" height="4" rx="2"/><rect fill="#FDE5D8" x="68" y="48" width="12" height="4" rx="2"/></g><use stroke="#B5A7DD" mask="url(#f)" stroke-width="8" fill="#FFF" transform="rotate(5 171.616 144.96)" xlink:href="#b"/><path d="M58 132c0-9.94 8.06-18 18-18s18 8.06 18 18-8.06 18-18 18-18-8.06-18-18z" fill="#C1E7D0"/><path d="M90.143 132c0-7.81-6.332-14.143-14.143-14.143-7.81 0-14.143 6.332-14.143 14.143 0 7.81 6.332 14.143 14.143 14.143 7.81 0 14.143-6.332 14.143-14.143z" fill="#FFF"/><path d="M74.686 133.875l-3.18-3.18c-.29-.29-.77-.296-1.06-.005l-1.55 1.55c-.287.287-.29.766.004 1.06l4.92 4.92c.504.504 1.32.504 1.823 0l.654-.653 7.804-7.804c.3-.3.29-.77-.005-1.067l-1.578-1.58c-.302-.3-.775-.298-1.068-.004l-6.764 6.763z" fill="#31AF64"/><path d="M4 66c0-9.94 8.06-18 18-18s18 8.06 18 18-8.06 18-18 18S4 75.94 4 66z" fill="#D5ECF7"/><path d="M36.143 66c0-7.81-6.332-14.143-14.143-14.143-7.81 0-14.143 6.332-14.143 14.143 0 7.81 6.332 14.143 14.143 14.143 7.81 0 14.143-6.332 14.143-14.143z" fill="#FFF"/><path d="M22 55.714c5.68 0 10.286 4.605 10.286 10.286 0 5.68-4.605 10.286-10.286 10.286-3.45 0-6.505-1.7-8.37-4.307L22 66V55.714z" fill="#2D9FD8"/><g transform="rotate(-8 748.533 18.147)"><use stroke="#FDE5D8" mask="url(#g)" stroke-width="8" fill="#FFF" xlink:href="#c"/><path d="M31 46.584c1.766-.772 3-2.534 3-4.584 0-2.76-2.24-5-5-5s-5 2.24-5 5c0 2.05 1.234 3.812 3 4.584v3.42c0 1.1.895 1.996 2 1.996 1.112 0 2-.894 2-1.997v-3.42z" fill="#FC8A51"/></g><g transform="translate(0 154)"><use stroke="#E5E5E5" mask="url(#h)" stroke-width="8" fill="#FFF" xlink:href="#d"/><g opacity=".3"><path d="M141.837 104.53l-2.56-7.993-5.074-15.843c-.26-.815-1.398-.815-1.66 0l-5.074 15.843h-16.85l-5.075-15.843c-.26-.815-1.398-.815-1.66 0l-5.073 15.843-2.56 7.993c-.234.73.022 1.528.633 1.98l22.16 16.33 22.16-16.33c.61-.452.866-1.25.632-1.98" fill="#A1A1A1"/><path fill="#5C5C5C" d="M119.044 122.84l8.425-26.303h-16.85l8.424 26.304"/><path fill="#787878" d="M119.044 122.84l-8.425-26.303H98.81l20.232 26.304"/><path fill="#787878" d="M119.044 122.84l8.425-26.303h11.807l-20.233 26.304"/><path d="M98.812 96.537l-2.56 7.993c-.234.73.022 1.528.633 1.98l22.16 16.33L98.81 96.538z" fill="#A1A1A1"/><path d="M98.812 96.537h11.807l-5.075-15.843c-.26-.815-1.398-.815-1.66 0l-5.073 15.843z" fill="#5C5C5C"/><path d="M139.277 96.537l2.56 7.993c.234.73-.022 1.528-.634 1.98l-22.16 16.33 20.234-26.303z" fill="#A1A1A1"/><path d="M139.277 96.537H127.47l5.074-15.843c.26-.815 1.398-.815 1.66 0l5.073 15.843z" fill="#5C5C5C"/></g><path d="M57 18.29c1.105 0 2-.818 2-1.828 0-1.01-.895-1.83-2-1.83H41c-1.105 0-2 .82-2 1.83 0 1.01.895 1.83 2 1.83h16zm36 0c1.105 0 2-.818 2-1.828 0-1.01-.895-1.83-2-1.83H77c-1.105 0-2 .82-2 1.83 0 1.01.895 1.83 2 1.83h16zm36 0c1.105 0 2-.818 2-1.828 0-1.01-.895-1.83-2-1.83h-16c-1.105 0-2 .82-2 1.83 0 1.01.895 1.83 2 1.83h16zm36 0c1.105 0 2-.818 2-1.828 0-1.01-.895-1.83-2-1.83h-16c-1.105 0-2 .82-2 1.83 0 1.01.895 1.83 2 1.83h16zm36 0c1.105 0 2-.818 2-1.828 0-1.01-.895-1.83-2-1.83h-16c-1.105 0-2 .82-2 1.83 0 1.01.895 1.83 2 1.83h16zm17 24.693c0 1.01.895 1.83 2 1.83s2-.82 2-1.83V28.35c0-1.01-.895-1.83-2-1.83s-2 .82-2 1.83v14.633zm-202 0c0 1.01.895 1.83 2 1.83s2-.82 2-1.83V28.35c0-1.01-.895-1.83-2-1.83s-2 .82-2 1.83v14.633zm202 32.923c0 1.01.895 1.83 2 1.83s2-.82 2-1.83V61.274c0-1.01-.895-1.83-2-1.83s-2 .82-2 1.83v14.632zm-202 0c0 1.01.895 1.83 2 1.83s2-.82 2-1.83V61.274c0-1.01-.895-1.83-2-1.83s-2 .82-2 1.83v14.632zm202 32.923c0 1.01.895 1.828 2 1.828s2-.82 2-1.83V94.2c0-1.012-.895-1.83-2-1.83s-2 .818-2 1.83v14.63zm-202 0c0 1.01.895 1.828 2 1.828s2-.82 2-1.83V94.2c0-1.012-.895-1.83-2-1.83s-2 .818-2 1.83v14.63zm202 32.922c0 1.01.895 1.83 2 1.83s2-.82 2-1.83V127.12c0-1.01-.895-1.83-2-1.83s-2 .82-2 1.83v14.632zm-202 0c0 1.01.895 1.83 2 1.83s2-.82 2-1.83V127.12c0-1.01-.895-1.83-2-1.83s-2 .82-2 1.83v14.632zm179.023 19.555c-.988.452-1.388 1.55-.894 2.454.493.904 1.694 1.27 2.682.82l14.31-6.545c.99-.452 1.39-1.55.896-2.454-.494-.902-1.696-1.27-2.684-.817l-14.31 6.544zm-32.2 14.723c-.987.452-1.388 1.55-.894 2.454.493.904 1.695 1.27 2.683.818l14.31-6.544c.99-.45 1.39-1.55.895-2.454-.494-.903-1.695-1.27-2.683-.818l-14.31 6.544zm-32.2 14.724c-.987.45-1.387 1.55-.893 2.454.494.903 1.695 1.27 2.683.818l14.31-6.544c.99-.452 1.39-1.55.896-2.454-.495-.904-1.697-1.27-2.685-.818l-14.31 6.544zm-23.67-2.023l-12.186-5.57c-.987-.452-2.19-.086-2.683.817-.494.904-.093 2.003.895 2.454l12.185 5.573c.754.345 1.57.645 2.438.898 1.052.307 2.177-.224 2.513-1.187.335-.962-.246-1.99-1.298-2.298-.677-.197-1.302-.426-1.864-.684zM62.57 168.437c-.988-.452-2.19-.086-2.683.818-.494.903-.094 2.002.894 2.454l14.31 6.544c.988.45 2.19.085 2.683-.818.494-.904.094-2.003-.894-2.454l-14.312-6.544zm-32.2-14.723c-.988-.452-2.19-.086-2.683.818-.494.904-.093 2.003.895 2.454l14.31 6.544c.988.452 2.19.086 2.684-.818.494-.903.093-2.002-.895-2.454l-14.312-6.543z" fill="#EEE"/></g><g><path d="M104 18c0-9.94 8.06-18 18-18s18 8.06 18 18-8.06 18-18 18-18-8.06-18-18z" fill="#FADFD9"/><path d="M136.143 18c0-7.81-6.332-14.143-14.143-14.143-7.81 0-14.143 6.332-14.143 14.143 0 7.81 6.332 14.143 14.143 14.143 7.81 0 14.143-6.332 14.143-14.143z" fill="#FFF"/><path d="M119.43 8.994c0-.707.57-1.28 1.283-1.28h2.574c.71 0 1.284.57 1.284 1.28v10.298c0 .706-.57 1.28-1.283 1.28h-2.574c-.71 0-1.284-.57-1.284-1.28V8.994zm0 15.433c0-.71.57-1.284 1.283-1.284h2.574c.71 0 1.284.57 1.284 1.284V27c0 .71-.57 1.286-1.283 1.286h-2.574c-.71 0-1.284-.57-1.284-1.285v-2.573z" fill="#E75E40"/></g><g><path d="M213 89c0-9.94 8.06-18 18-18s18 8.06 18 18-8.06 18-18 18-18-8.06-18-18z" fill="#F6D4DC"/><path d="M245.143 89c0-7.81-6.332-14.143-14.143-14.143-7.81 0-14.143 6.332-14.143 14.143 0 7.81 6.332 14.143 14.143 14.143 7.81 0 14.143-6.332 14.143-14.143z" fill="#FFF"/><path d="M231 86.348l-3.603-3.602c-.288-.29-.766-.286-1.063.01l-1.578 1.578c-.3.302-.3.773-.01 1.063L228.348 89l-3.602 3.603c-.29.288-.286.766.01 1.063l1.578 1.578c.302.3.773.3 1.063.01L231 91.652l3.603 3.602c.288.29.766.286 1.063-.01l1.578-1.578c.3-.302.3-.773.01-1.063L233.652 89l3.602-3.603c.29-.288.286-.766-.01-1.063l-1.578-1.578c-.302-.3-.773-.3-1.063-.01L231 86.348z" fill="#D22852"/></g></g></svg> \ No newline at end of file
+<svg width="249" height="368" viewBox="891 156 249 368" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><defs><rect id="a" width="131" height="162" rx="10"/><mask id="e" x="0" y="0" width="131" height="162" fill="#fff"><use xlink:href="#a"/></mask><path d="M223.616 127.958V108.96c0-4.416-3.584-8-8.005-8h-23.985c-2.778 0-5.98 2.014-7.18 4.5l-5.07 10.5h-49.763c-5.527 0-9.996 4.475-9.996 9.997v53.005c0 5.513 4.475 9.997 9.996 9.997h84.01c5.525 0 9.994-4.477 9.994-9.998v-51.004z" id="b"/><mask id="f" x="0" y="0" width="104" height="88" fill="#fff"><use xlink:href="#b"/></mask><path d="M47 25h.996C53.52 25 58 29.472 58 34.99v20.02C58 60.526 53.52 65 47.996 65H10.004C4.48 65 0 60.528 0 55.01V34.99C0 29.474 4.48 25 10.004 25H11v-7c0-9.94 8.06-18 18-18s18 8.06 18 18v7zm-6 0H17v-7c0-6.627 5.373-12 12-12s12 5.373 12 12v7z" id="c"/><mask id="g" x="0" y="0" width="58" height="65" fill="#fff"><use xlink:href="#c"/></mask><path d="M0 10.008C0 4.48 4.476 0 10 0h218c5.523 0 10 4.473 10 10.008v140.94c0 5.53-4.062 11.882-9.08 14.196l-100.84 46.5c-5.015 2.31-13.142 2.312-18.16 0l-100.84-46.5C4.064 162.832 0 156.484 0 150.95V10.007z" id="d"/><mask id="h" x="0" y="0" width="238" height="213.417" fill="#fff"><use xlink:href="#d"/></mask></defs><g fill="none" fill-rule="evenodd" transform="translate(891 156)"><g transform="rotate(8 -266.528 490.3)"><use stroke="#E5E5E5" mask="url(#e)" stroke-width="8" fill="#FFF" xlink:href="#a"/><rect fill="#FC8A51" x="20" y="31" width="12" height="4" rx="2"/><rect fill="#FC8A51" x="60" y="31" width="12" height="4" rx="2"/><rect fill="#FDE5D8" x="36" y="31" width="20" height="4" rx="2"/><rect fill="#6B4FBB" x="20" y="65" width="20" height="4" rx="2"/><rect fill="#FDE5D8" x="44" y="65" width="20" height="4" rx="2"/><rect fill="#FC8A51" x="36" y="80" width="20" height="4" rx="2"/><rect fill="#FDE5D8" x="20" y="80" width="12" height="4" rx="2"/><rect fill="#FDE5D8" x="20" y="48" width="12" height="4" rx="2"/><rect fill="#FC8A51" x="36" y="48" width="12" height="4" rx="2"/><rect fill="#FDE5D8" x="60" y="80" width="12" height="4" rx="2"/><rect fill="#6B4FBB" x="52" y="48" width="12" height="4" rx="2"/><rect fill="#FDE5D8" x="68" y="48" width="12" height="4" rx="2"/></g><use stroke="#B5A7DD" mask="url(#f)" stroke-width="8" fill="#FFF" transform="rotate(5 171.616 144.96)" xlink:href="#b"/><path d="M58 132c0-9.94 8.06-18 18-18s18 8.06 18 18-8.06 18-18 18-18-8.06-18-18z" fill="#C1E7D0"/><path d="M90.143 132c0-7.81-6.332-14.143-14.143-14.143-7.81 0-14.143 6.332-14.143 14.143 0 7.81 6.332 14.143 14.143 14.143 7.81 0 14.143-6.332 14.143-14.143z" fill="#FFF"/><path d="M74.686 133.875l-3.18-3.18c-.29-.29-.77-.296-1.06-.005l-1.55 1.55c-.287.287-.29.766.004 1.06l4.92 4.92c.504.504 1.32.504 1.823 0l.654-.653 7.804-7.804c.3-.3.29-.77-.005-1.067l-1.578-1.58c-.302-.3-.775-.298-1.068-.004l-6.764 6.763z" fill="#31AF64"/><path d="M4 66c0-9.94 8.06-18 18-18s18 8.06 18 18-8.06 18-18 18S4 75.94 4 66z" fill="#D5ECF7"/><path d="M36.143 66c0-7.81-6.332-14.143-14.143-14.143-7.81 0-14.143 6.332-14.143 14.143 0 7.81 6.332 14.143 14.143 14.143 7.81 0 14.143-6.332 14.143-14.143z" fill="#FFF"/><path d="M22 55.714c5.68 0 10.286 4.605 10.286 10.286 0 5.68-4.605 10.286-10.286 10.286-3.45 0-6.505-1.7-8.37-4.307L22 66V55.714z" fill="#2D9FD8"/><g transform="rotate(-8 748.533 18.147)"><use stroke="#FDE5D8" mask="url(#g)" stroke-width="8" fill="#FFF" xlink:href="#c"/><path d="M31 46.584c1.766-.772 3-2.534 3-4.584 0-2.76-2.24-5-5-5s-5 2.24-5 5c0 2.05 1.234 3.812 3 4.584v3.42c0 1.1.895 1.996 2 1.996 1.112 0 2-.894 2-1.997v-3.42z" fill="#FC8A51"/></g><g transform="translate(0 154)"><use stroke="#E5E5E5" mask="url(#h)" stroke-width="8" fill="#FFF" xlink:href="#d"/><g opacity=".3"><path d="M141.837 104.53l-2.56-7.993-5.074-15.843c-.26-.815-1.398-.815-1.66 0l-5.074 15.843h-16.85l-5.075-15.843c-.26-.815-1.398-.815-1.66 0l-5.073 15.843-2.56 7.993c-.234.73.022 1.528.633 1.98l22.16 16.33 22.16-16.33c.61-.452.866-1.25.632-1.98" fill="#A1A1A1"/><path fill="#5C5C5C" d="M119.044 122.84l8.425-26.303h-16.85l8.424 26.304"/><path fill="#787878" d="M119.044 122.84l-8.425-26.303H98.81l20.232 26.304"/><path fill="#787878" d="M119.044 122.84l8.425-26.303h11.807l-20.233 26.304"/><path d="M98.812 96.537l-2.56 7.993c-.234.73.022 1.528.633 1.98l22.16 16.33L98.81 96.538z" fill="#A1A1A1"/><path d="M98.812 96.537h11.807l-5.075-15.843c-.26-.815-1.398-.815-1.66 0l-5.073 15.843z" fill="#5C5C5C"/><path d="M139.277 96.537l2.56 7.993c.234.73-.022 1.528-.634 1.98l-22.16 16.33 20.234-26.303z" fill="#A1A1A1"/><path d="M139.277 96.537H127.47l5.074-15.843c.26-.815 1.398-.815 1.66 0l5.073 15.843z" fill="#5C5C5C"/></g><path d="M57 18.29c1.105 0 2-.818 2-1.828 0-1.01-.895-1.83-2-1.83H41c-1.105 0-2 .82-2 1.83 0 1.01.895 1.83 2 1.83h16zm36 0c1.105 0 2-.818 2-1.828 0-1.01-.895-1.83-2-1.83H77c-1.105 0-2 .82-2 1.83 0 1.01.895 1.83 2 1.83h16zm36 0c1.105 0 2-.818 2-1.828 0-1.01-.895-1.83-2-1.83h-16c-1.105 0-2 .82-2 1.83 0 1.01.895 1.83 2 1.83h16zm36 0c1.105 0 2-.818 2-1.828 0-1.01-.895-1.83-2-1.83h-16c-1.105 0-2 .82-2 1.83 0 1.01.895 1.83 2 1.83h16zm36 0c1.105 0 2-.818 2-1.828 0-1.01-.895-1.83-2-1.83h-16c-1.105 0-2 .82-2 1.83 0 1.01.895 1.83 2 1.83h16zm17 24.693c0 1.01.895 1.83 2 1.83s2-.82 2-1.83V28.35c0-1.01-.895-1.83-2-1.83s-2 .82-2 1.83v14.633zm-202 0c0 1.01.895 1.83 2 1.83s2-.82 2-1.83V28.35c0-1.01-.895-1.83-2-1.83s-2 .82-2 1.83v14.633zm202 32.923c0 1.01.895 1.83 2 1.83s2-.82 2-1.83V61.274c0-1.01-.895-1.83-2-1.83s-2 .82-2 1.83v14.632zm-202 0c0 1.01.895 1.83 2 1.83s2-.82 2-1.83V61.274c0-1.01-.895-1.83-2-1.83s-2 .82-2 1.83v14.632zm202 32.923c0 1.01.895 1.828 2 1.828s2-.82 2-1.83V94.2c0-1.012-.895-1.83-2-1.83s-2 .818-2 1.83v14.63zm-202 0c0 1.01.895 1.828 2 1.828s2-.82 2-1.83V94.2c0-1.012-.895-1.83-2-1.83s-2 .818-2 1.83v14.63zm202 32.922c0 1.01.895 1.83 2 1.83s2-.82 2-1.83V127.12c0-1.01-.895-1.83-2-1.83s-2 .82-2 1.83v14.632zm-202 0c0 1.01.895 1.83 2 1.83s2-.82 2-1.83V127.12c0-1.01-.895-1.83-2-1.83s-2 .82-2 1.83v14.632zm179.023 19.555c-.988.452-1.388 1.55-.894 2.454.493.904 1.694 1.27 2.682.82l14.31-6.545c.99-.452 1.39-1.55.896-2.454-.494-.902-1.696-1.27-2.684-.817l-14.31 6.544zm-32.2 14.723c-.987.452-1.388 1.55-.894 2.454.493.904 1.695 1.27 2.683.818l14.31-6.544c.99-.45 1.39-1.55.895-2.454-.494-.903-1.695-1.27-2.683-.818l-14.31 6.544zm-32.2 14.724c-.987.45-1.387 1.55-.893 2.454.494.903 1.695 1.27 2.683.818l14.31-6.544c.99-.452 1.39-1.55.896-2.454-.495-.904-1.697-1.27-2.685-.818l-14.31 6.544zm-23.67-2.023l-12.186-5.57c-.987-.452-2.19-.086-2.683.817-.494.904-.093 2.003.895 2.454l12.185 5.573c.754.345 1.57.645 2.438.898 1.052.307 2.177-.224 2.513-1.187.335-.962-.246-1.99-1.298-2.298-.677-.197-1.302-.426-1.864-.684zM62.57 168.437c-.988-.452-2.19-.086-2.683.818-.494.903-.094 2.002.894 2.454l14.31 6.544c.988.45 2.19.085 2.683-.818.494-.904.094-2.003-.894-2.454l-14.312-6.544zm-32.2-14.723c-.988-.452-2.19-.086-2.683.818-.494.904-.093 2.003.895 2.454l14.31 6.544c.988.452 2.19.086 2.684-.818.494-.903.093-2.002-.895-2.454l-14.312-6.543z" fill="#EEE"/></g><g><path d="M104 18c0-9.94 8.06-18 18-18s18 8.06 18 18-8.06 18-18 18-18-8.06-18-18z" fill="#FADFD9"/><path d="M136.143 18c0-7.81-6.332-14.143-14.143-14.143-7.81 0-14.143 6.332-14.143 14.143 0 7.81 6.332 14.143 14.143 14.143 7.81 0 14.143-6.332 14.143-14.143z" fill="#FFF"/><path d="M119.43 8.994c0-.707.57-1.28 1.283-1.28h2.574c.71 0 1.284.57 1.284 1.28v10.298c0 .706-.57 1.28-1.283 1.28h-2.574c-.71 0-1.284-.57-1.284-1.28V8.994zm0 15.433c0-.71.57-1.284 1.283-1.284h2.574c.71 0 1.284.57 1.284 1.284V27c0 .71-.57 1.286-1.283 1.286h-2.574c-.71 0-1.284-.57-1.284-1.285v-2.573z" fill="#E75E40"/></g><g><path d="M213 89c0-9.94 8.06-18 18-18s18 8.06 18 18-8.06 18-18 18-18-8.06-18-18z" fill="#F6D4DC"/><path d="M245.143 89c0-7.81-6.332-14.143-14.143-14.143-7.81 0-14.143 6.332-14.143 14.143 0 7.81 6.332 14.143 14.143 14.143 7.81 0 14.143-6.332 14.143-14.143z" fill="#FFF"/><path d="M231 86.348l-3.603-3.602c-.288-.29-.766-.286-1.063.01l-1.578 1.578c-.3.302-.3.773-.01 1.063L228.348 89l-3.602 3.603c-.29.288-.286.766.01 1.063l1.578 1.578c.302.3.773.3 1.063.01L231 91.652l3.603 3.602c.288.29.766.286 1.063-.01l1.578-1.578c.3-.302.3-.773.01-1.063L233.652 89l3.602-3.603c.29-.288.286-.766-.01-1.063l-1.578-1.578c-.302-.3-.773-.3-1.063-.01L231 86.348z" fill="#D22852"/></g></g></svg>
diff --git a/app/views/shared/icons/_icon_mr_issue.svg b/app/views/shared/icons/_icon_mr_issue.svg
index ae219a3ded2..a56af9c556c 100644
--- a/app/views/shared/icons/_icon_mr_issue.svg
+++ b/app/views/shared/icons/_icon_mr_issue.svg
@@ -1 +1 @@
-<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><g fill-rule="evenodd"><path d="m8.411 1.012c-.136-.008-.273-.012-.411-.012-3.866 0-7 3.134-7 7 0 3.866 3.134 7 7 7 3.866 0 7-3.134 7-7 0-.138-.004-.275-.012-.411-.464.201-.964.334-1.488.386 0 .008 0 .016 0 .025 0 3.038-2.462 5.5-5.5 5.5-3.038 0-5.5-2.462-5.5-5.5 0-3.038 2.462-5.5 5.5-5.5.008 0 .016 0 .025 0 .052-.524.185-1.024.386-1.488"/><path d="m12 2h-1.01c-.54 0-.991.448-.991 1 0 .556.444 1 .991 1h1.01v1.01c0 .54.448.991 1 .991.556 0 1-.444 1-.991v-1.01h1.01c.54 0 .991-.448.991-1 0-.556-.444-1-.991-1h-1.01v-1.01c0-.54-.448-.991-1-.991-.556 0-1 .444-1 .991v1.01m-5 4.01c0-.557.444-1.01 1-1.01.552 0 1 .443 1 1.01v1.981c0 .557-.444 1.01-1 1.01-.552 0-1-.443-1-1.01v-1.981m1 5.991c.552 0 1-.448 1-1 0-.552-.448-1-1-1-.552 0-1 .448-1 1 0 .552.448 1 1 1"/></g></svg> \ No newline at end of file
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><g fill-rule="evenodd"><path d="m8.411 1.012c-.136-.008-.273-.012-.411-.012-3.866 0-7 3.134-7 7 0 3.866 3.134 7 7 7 3.866 0 7-3.134 7-7 0-.138-.004-.275-.012-.411-.464.201-.964.334-1.488.386 0 .008 0 .016 0 .025 0 3.038-2.462 5.5-5.5 5.5-3.038 0-5.5-2.462-5.5-5.5 0-3.038 2.462-5.5 5.5-5.5.008 0 .016 0 .025 0 .052-.524.185-1.024.386-1.488"/><path d="m12 2h-1.01c-.54 0-.991.448-.991 1 0 .556.444 1 .991 1h1.01v1.01c0 .54.448.991 1 .991.556 0 1-.444 1-.991v-1.01h1.01c.54 0 .991-.448.991-1 0-.556-.444-1-.991-1h-1.01v-1.01c0-.54-.448-.991-1-.991-.556 0-1 .444-1 .991v1.01m-5 4.01c0-.557.444-1.01 1-1.01.552 0 1 .443 1 1.01v1.981c0 .557-.444 1.01-1 1.01-.552 0-1-.443-1-1.01v-1.981m1 5.991c.552 0 1-.448 1-1 0-.552-.448-1-1-1-.552 0-1 .448-1 1 0 .552.448 1 1 1"/></g></svg>
diff --git a/app/views/shared/icons/_icon_play.svg b/app/views/shared/icons/_icon_play.svg
index e965afa9a56..4c69fc99a9e 100644
--- a/app/views/shared/icons/_icon_play.svg
+++ b/app/views/shared/icons/_icon_play.svg
@@ -1,3 +1 @@
-<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 10 11" class="icon-play">
- <path fill-rule="evenodd" d="m9.283 6.47l-7.564 4.254c-.949.534-1.719.266-1.719-.576v-9.292c0-.852.756-1.117 1.719-.576l7.564 4.254c.949.534.963 1.392 0 1.934"/>
- </svg> \ No newline at end of file
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 10 11" class="icon-play"><path fill-rule="evenodd" d="m9.283 6.47l-7.564 4.254c-.949.534-1.719.266-1.719-.576v-9.292c0-.852.756-1.117 1.719-.576l7.564 4.254c.949.534.963 1.392 0 1.934"/></svg>
diff --git a/app/views/shared/icons/_icon_stopwatch.svg b/app/views/shared/icons/_icon_stopwatch.svg
index f20de04538e..6c2a8b2773f 100644
--- a/app/views/shared/icons/_icon_stopwatch.svg
+++ b/app/views/shared/icons/_icon_stopwatch.svg
@@ -1 +1 @@
-<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 12 14" enable-background="new 0 0 12 14"><path d="m11.5 2.4l-1.3-1.1-1 1.1 1.4 1.1.9-1.1"/><path d="m6.8 2v-.5h.5v-1.5h-2.6v1.5h.5v.5c-2.9.4-5.2 2.9-5.2 6 0 3.3 2.7 6 6 6s6-2.7 6-6c0-3-2.3-5.6-5.2-6m-.8 10.5c-2.5 0-4.5-2-4.5-4.5s2-4.5 4.5-4.5 4.5 2 4.5 4.5-2 4.5-4.5 4.5"/><path d="m6.2 8.9h-.5c-.1 0-.2-.1-.2-.2v-3.5c0-.1.1-.2.2-.2h.5c.1 0 .2.1.2.2v3.5c0 .1-.1.2-.2.2"/></svg> \ No newline at end of file
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 12 14" enable-background="new 0 0 12 14"><path d="m11.5 2.4l-1.3-1.1-1 1.1 1.4 1.1.9-1.1"/><path d="m6.8 2v-.5h.5v-1.5h-2.6v1.5h.5v.5c-2.9.4-5.2 2.9-5.2 6 0 3.3 2.7 6 6 6s6-2.7 6-6c0-3-2.3-5.6-5.2-6m-.8 10.5c-2.5 0-4.5-2-4.5-4.5s2-4.5 4.5-4.5 4.5 2 4.5 4.5-2 4.5-4.5 4.5"/><path d="m6.2 8.9h-.5c-.1 0-.2-.1-.2-.2v-3.5c0-.1.1-.2.2-.2h.5c.1 0 .2.1.2.2v3.5c0 .1-.1.2-.2.2"/></svg>
diff --git a/app/views/shared/icons/_icon_timer.svg b/app/views/shared/icons/_icon_timer.svg
index 0b1e5804427..572a31ebcca 100644
--- a/app/views/shared/icons/_icon_timer.svg
+++ b/app/views/shared/icons/_icon_timer.svg
@@ -1 +1 @@
-<svg xmlns="http://www.w3.org/2000/svg" width="40" height="40" viewBox="0 0 40 40"><g fill="#8F8F8F" fill-rule="evenodd"><path d="M29.513 10.134A15.922 15.922 0 0 0 23 7.28V6h2.993C26.55 6 27 5.552 27 5V2a1 1 0 0 0-1.007-1H14.007C13.45 1 13 1.448 13 2v3a1 1 0 0 0 1.007 1H17v1.28C9.597 8.686 4 15.19 4 23c0 8.837 7.163 16 16 16s16-7.163 16-16c0-3.461-1.099-6.665-2.967-9.283l1.327-1.58a2.498 2.498 0 0 0-.303-3.53 2.499 2.499 0 0 0-3.528.315l-1.016 1.212zM20 34c6.075 0 11-4.925 11-11s-4.925-11-11-11S9 16.925 9 23s4.925 11 11 11z"/><path d="M19 21h-4.002c-.552 0-.998.452-.998 1.01v1.98c0 .567.447 1.01.998 1.01h7.004c.274 0 .521-.111.701-.291a.979.979 0 0 0 .297-.704v-8.01c0-.54-.452-.995-1.01-.995h-1.98a.997.997 0 0 0-1.01.995V21z"/></g></svg> \ No newline at end of file
+<svg xmlns="http://www.w3.org/2000/svg" width="40" height="40" viewBox="0 0 40 40"><g fill="#8F8F8F" fill-rule="evenodd"><path d="M29.513 10.134A15.922 15.922 0 0 0 23 7.28V6h2.993C26.55 6 27 5.552 27 5V2a1 1 0 0 0-1.007-1H14.007C13.45 1 13 1.448 13 2v3a1 1 0 0 0 1.007 1H17v1.28C9.597 8.686 4 15.19 4 23c0 8.837 7.163 16 16 16s16-7.163 16-16c0-3.461-1.099-6.665-2.967-9.283l1.327-1.58a2.498 2.498 0 0 0-.303-3.53 2.499 2.499 0 0 0-3.528.315l-1.016 1.212zM20 34c6.075 0 11-4.925 11-11s-4.925-11-11-11S9 16.925 9 23s4.925 11 11 11z"/><path d="M19 21h-4.002c-.552 0-.998.452-.998 1.01v1.98c0 .567.447 1.01.998 1.01h7.004c.274 0 .521-.111.701-.291a.979.979 0 0 0 .297-.704v-8.01c0-.54-.452-.995-1.01-.995h-1.98a.997.997 0 0 0-1.01.995V21z"/></g></svg>
diff --git a/app/views/shared/icons/_illustration_no_commits.svg b/app/views/shared/icons/_illustration_no_commits.svg
index 4f9d9add60d..34f177d7efa 100644
--- a/app/views/shared/icons/_illustration_no_commits.svg
+++ b/app/views/shared/icons/_illustration_no_commits.svg
@@ -1 +1 @@
-<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 168 107" xmlns:xlink="http://www.w3.org/1999/xlink"><g fill="#eee" fill-rule="evenodd"><path d="m4.01 2h1.102c.552 0 1-.448 1-1 0-.552-.448-1-1-1h-1.102c-2.218 0-4.01 1.788-4.01 4 0 .552.448 1 1 1 .552 0 1-.448 1-1 0-1.108.892-2 2.01-2m12.702 0c.552 0 1-.448 1-1 0-.552-.448-1-1-1h-5.7c-.552 0-1 .448-1 1 0 .552.448 1 1 1h5.7m11.6 0c.552 0 1-.448 1-1 0-.552-.448-1-1-1h-5.7c-.552 0-1 .448-1 1 0 .552.448 1 1 1h5.7m11.6 0c.552 0 1-.448 1-1 0-.552-.448-1-1-1h-5.7c-.552 0-1 .448-1 1 0 .552.448 1 1 1h5.7m11.6 0c.552 0 1-.448 1-1 0-.552-.448-1-1-1h-5.7c-.552 0-1 .448-1 1 0 .552.448 1 1 1h5.7m11.6 0c.552 0 1-.448 1-1 0-.552-.448-1-1-1h-5.7c-.552 0-1 .448-1 1 0 .552.448 1 1 1h5.7m11.6 0c.552 0 1-.448 1-1 0-.552-.448-1-1-1h-5.7c-.552 0-1 .448-1 1 0 .552.448 1 1 1h5.7m11.6 0c.552 0 1-.448 1-1 0-.552-.448-1-1-1h-5.7c-.552 0-1 .448-1 1 0 .552.448 1 1 1h5.7m11.6 0c.552 0 1-.448 1-1 0-.552-.448-1-1-1h-5.7c-.552 0-1 .448-1 1 0 .552.448 1 1 1h5.7m11.6 0c.552 0 1-.448 1-1 0-.552-.448-1-1-1h-5.7c-.552 0-1 .448-1 1 0 .552.448 1 1 1h5.7m11.6 0c.552 0 1-.448 1-1 0-.552-.448-1-1-1h-5.7c-.552 0-1 .448-1 1 0 .552.448 1 1 1h5.7m11.6 0c.552 0 1-.448 1-1 0-.552-.448-1-1-1h-5.7c-.552 0-1 .448-1 1 0 .552.448 1 1 1h5.7m11.6 0c.552 0 1-.448 1-1 0-.552-.448-1-1-1h-5.7c-.552 0-1 .448-1 1 0 .552.448 1 1 1h5.7m11.6 0c.552 0 1-.448 1-1 0-.552-.448-1-1-1h-5.7c-.552 0-1 .448-1 1 0 .552.448 1 1 1h5.7m8.088 0c.822 0 1.554.503 1.86 1.254.208.512.791.758 1.303.55.512-.208.758-.791.55-1.303-.609-1.497-2.069-2.5-3.712-2.5h-2.188c-.552 0-1 .448-1 1 0 .552.448 1 1 1h2.188m2.01 12.518c0 .552.448 1 1 1 .552 0 1-.448 1-1v-5.7c0-.552-.448-1-1-1-.552 0-1 .448-1 1v5.7m0 11.6c0 .552.448 1 1 1 .552 0 1-.448 1-1v-5.7c0-.552-.448-1-1-1-.552 0-1 .448-1 1v5.7m0 11.6c0 .552.448 1 1 1 .552 0 1-.448 1-1v-5.7c0-.552-.448-1-1-1-.552 0-1 .448-1 1v5.7m0 6.282c0 1.108-.892 2-2.01 2h-.72c-.552 0-1 .448-1 1 0 .552.448 1 1 1h.72c2.218 0 4.01-1.788 4.01-4v-.382c0-.552-.448-1-1-1-.552 0-1 .448-1 1v.382m-14.325 2c-.552 0-1 .448-1 1 0 .552.448 1 1 1h5.7c.552 0 1-.448 1-1 0-.552-.448-1-1-1h-5.7m-11.6 0c-.552 0-1 .448-1 1 0 .552.448 1 1 1h5.7c.552 0 1-.448 1-1 0-.552-.448-1-1-1h-5.7m-11.6 0c-.552 0-1 .448-1 1 0 .552.448 1 1 1h5.7c.552 0 1-.448 1-1 0-.552-.448-1-1-1h-5.7m-11.6 0c-.552 0-1 .448-1 1 0 .552.448 1 1 1h5.7c.552 0 1-.448 1-1 0-.552-.448-1-1-1h-5.7m-11.6 0c-.552 0-1 .448-1 1 0 .552.448 1 1 1h5.7c.552 0 1-.448 1-1 0-.552-.448-1-1-1h-5.7m-11.6 0c-.552 0-1 .448-1 1 0 .552.448 1 1 1h5.7c.552 0 1-.448 1-1 0-.552-.448-1-1-1h-5.7m-11.6 0c-.552 0-1 .448-1 1 0 .552.448 1 1 1h5.7c.552 0 1-.448 1-1 0-.552-.448-1-1-1h-5.7m-11.6 0c-.552 0-1 .448-1 1 0 .552.448 1 1 1h5.7c.552 0 1-.448 1-1 0-.552-.448-1-1-1h-5.7m-11.6 0c-.552 0-1 .448-1 1 0 .552.448 1 1 1h5.7c.552 0 1-.448 1-1 0-.552-.448-1-1-1h-5.7m-11.6 0c-.552 0-1 .448-1 1 0 .552.448 1 1 1h5.7c.552 0 1-.448 1-1 0-.552-.448-1-1-1h-5.7m-11.6 0c-.552 0-1 .448-1 1 0 .552.448 1 1 1h5.7c.552 0 1-.448 1-1 0-.552-.448-1-1-1h-5.7m-11.6 0c-.552 0-1 .448-1 1 0 .552.448 1 1 1h5.7c.552 0 1-.448 1-1 0-.552-.448-1-1-1h-5.7m-11.6 0c-.552 0-1 .448-1 1 0 .552.448 1 1 1h5.7c.552 0 1-.448 1-1 0-.552-.448-1-1-1h-5.7m-8.47 0c-.755 0-1.438-.424-1.782-1.085-.255-.49-.859-.681-1.349-.426-.49.255-.681.859-.426 1.349.684 1.316 2.046 2.162 3.556 2.162h2.57c.552 0 1-.448 1-1 0-.552-.448-1-1-1h-2.57m-2.01-12.136c0-.552-.448-1-1-1-.552 0-1 .448-1 1v5.7c0 .552.448 1 1 1 .552 0 1-.448 1-1v-5.7m0-11.6c0-.552-.448-1-1-1-.552 0-1 .448-1 1v5.7c0 .552.448 1 1 1 .552 0 1-.448 1-1v-5.7m0-11.6c0-.552-.448-1-1-1-.552 0-1 .448-1 1v5.7c0 .552.448 1 1 1 .552 0 1-.448 1-1v-5.7m0-6.664c0-.552-.448-1-1-1-.552 0-1 .448-1 1v.764c0 .552.448 1 1 1 .552 0 1-.448 1-1v-.764" id="0"/><circle cx="21" cy="24" r="10"/><rect width="33" height="3" x="37" y="18" rx="1.5" id="1"/><rect width="53" height="3" x="37" y="27" rx="1.5" id="2"/><path d="m131 29c0 .552.447.999.996.999h22.01c.545 0 .996-.451.996-.999v-9c0-.552-.447-.999-.996-.999h-22.01c-.545 0-.996.451-.996.999v9m.996-12h22.01c1.655 0 2.996 1.344 2.996 2.999v9c0 1.657-1.35 2.999-2.996 2.999h-22.01c-1.655 0-2.996-1.344-2.996-2.999v-9c0-1.657 1.35-2.999 2.996-2.999" id="3"/><g transform="translate(0 59)"><use xlink:href="#0"/><circle cx="21" cy="24" r="10"/><use xlink:href="#1"/><use xlink:href="#2"/><use xlink:href="#3"/></g></g></svg> \ No newline at end of file
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 168 107" xmlns:xlink="http://www.w3.org/1999/xlink"><g fill="#eee" fill-rule="evenodd"><path d="m4.01 2h1.102c.552 0 1-.448 1-1 0-.552-.448-1-1-1h-1.102c-2.218 0-4.01 1.788-4.01 4 0 .552.448 1 1 1 .552 0 1-.448 1-1 0-1.108.892-2 2.01-2m12.702 0c.552 0 1-.448 1-1 0-.552-.448-1-1-1h-5.7c-.552 0-1 .448-1 1 0 .552.448 1 1 1h5.7m11.6 0c.552 0 1-.448 1-1 0-.552-.448-1-1-1h-5.7c-.552 0-1 .448-1 1 0 .552.448 1 1 1h5.7m11.6 0c.552 0 1-.448 1-1 0-.552-.448-1-1-1h-5.7c-.552 0-1 .448-1 1 0 .552.448 1 1 1h5.7m11.6 0c.552 0 1-.448 1-1 0-.552-.448-1-1-1h-5.7c-.552 0-1 .448-1 1 0 .552.448 1 1 1h5.7m11.6 0c.552 0 1-.448 1-1 0-.552-.448-1-1-1h-5.7c-.552 0-1 .448-1 1 0 .552.448 1 1 1h5.7m11.6 0c.552 0 1-.448 1-1 0-.552-.448-1-1-1h-5.7c-.552 0-1 .448-1 1 0 .552.448 1 1 1h5.7m11.6 0c.552 0 1-.448 1-1 0-.552-.448-1-1-1h-5.7c-.552 0-1 .448-1 1 0 .552.448 1 1 1h5.7m11.6 0c.552 0 1-.448 1-1 0-.552-.448-1-1-1h-5.7c-.552 0-1 .448-1 1 0 .552.448 1 1 1h5.7m11.6 0c.552 0 1-.448 1-1 0-.552-.448-1-1-1h-5.7c-.552 0-1 .448-1 1 0 .552.448 1 1 1h5.7m11.6 0c.552 0 1-.448 1-1 0-.552-.448-1-1-1h-5.7c-.552 0-1 .448-1 1 0 .552.448 1 1 1h5.7m11.6 0c.552 0 1-.448 1-1 0-.552-.448-1-1-1h-5.7c-.552 0-1 .448-1 1 0 .552.448 1 1 1h5.7m11.6 0c.552 0 1-.448 1-1 0-.552-.448-1-1-1h-5.7c-.552 0-1 .448-1 1 0 .552.448 1 1 1h5.7m11.6 0c.552 0 1-.448 1-1 0-.552-.448-1-1-1h-5.7c-.552 0-1 .448-1 1 0 .552.448 1 1 1h5.7m8.088 0c.822 0 1.554.503 1.86 1.254.208.512.791.758 1.303.55.512-.208.758-.791.55-1.303-.609-1.497-2.069-2.5-3.712-2.5h-2.188c-.552 0-1 .448-1 1 0 .552.448 1 1 1h2.188m2.01 12.518c0 .552.448 1 1 1 .552 0 1-.448 1-1v-5.7c0-.552-.448-1-1-1-.552 0-1 .448-1 1v5.7m0 11.6c0 .552.448 1 1 1 .552 0 1-.448 1-1v-5.7c0-.552-.448-1-1-1-.552 0-1 .448-1 1v5.7m0 11.6c0 .552.448 1 1 1 .552 0 1-.448 1-1v-5.7c0-.552-.448-1-1-1-.552 0-1 .448-1 1v5.7m0 6.282c0 1.108-.892 2-2.01 2h-.72c-.552 0-1 .448-1 1 0 .552.448 1 1 1h.72c2.218 0 4.01-1.788 4.01-4v-.382c0-.552-.448-1-1-1-.552 0-1 .448-1 1v.382m-14.325 2c-.552 0-1 .448-1 1 0 .552.448 1 1 1h5.7c.552 0 1-.448 1-1 0-.552-.448-1-1-1h-5.7m-11.6 0c-.552 0-1 .448-1 1 0 .552.448 1 1 1h5.7c.552 0 1-.448 1-1 0-.552-.448-1-1-1h-5.7m-11.6 0c-.552 0-1 .448-1 1 0 .552.448 1 1 1h5.7c.552 0 1-.448 1-1 0-.552-.448-1-1-1h-5.7m-11.6 0c-.552 0-1 .448-1 1 0 .552.448 1 1 1h5.7c.552 0 1-.448 1-1 0-.552-.448-1-1-1h-5.7m-11.6 0c-.552 0-1 .448-1 1 0 .552.448 1 1 1h5.7c.552 0 1-.448 1-1 0-.552-.448-1-1-1h-5.7m-11.6 0c-.552 0-1 .448-1 1 0 .552.448 1 1 1h5.7c.552 0 1-.448 1-1 0-.552-.448-1-1-1h-5.7m-11.6 0c-.552 0-1 .448-1 1 0 .552.448 1 1 1h5.7c.552 0 1-.448 1-1 0-.552-.448-1-1-1h-5.7m-11.6 0c-.552 0-1 .448-1 1 0 .552.448 1 1 1h5.7c.552 0 1-.448 1-1 0-.552-.448-1-1-1h-5.7m-11.6 0c-.552 0-1 .448-1 1 0 .552.448 1 1 1h5.7c.552 0 1-.448 1-1 0-.552-.448-1-1-1h-5.7m-11.6 0c-.552 0-1 .448-1 1 0 .552.448 1 1 1h5.7c.552 0 1-.448 1-1 0-.552-.448-1-1-1h-5.7m-11.6 0c-.552 0-1 .448-1 1 0 .552.448 1 1 1h5.7c.552 0 1-.448 1-1 0-.552-.448-1-1-1h-5.7m-11.6 0c-.552 0-1 .448-1 1 0 .552.448 1 1 1h5.7c.552 0 1-.448 1-1 0-.552-.448-1-1-1h-5.7m-11.6 0c-.552 0-1 .448-1 1 0 .552.448 1 1 1h5.7c.552 0 1-.448 1-1 0-.552-.448-1-1-1h-5.7m-8.47 0c-.755 0-1.438-.424-1.782-1.085-.255-.49-.859-.681-1.349-.426-.49.255-.681.859-.426 1.349.684 1.316 2.046 2.162 3.556 2.162h2.57c.552 0 1-.448 1-1 0-.552-.448-1-1-1h-2.57m-2.01-12.136c0-.552-.448-1-1-1-.552 0-1 .448-1 1v5.7c0 .552.448 1 1 1 .552 0 1-.448 1-1v-5.7m0-11.6c0-.552-.448-1-1-1-.552 0-1 .448-1 1v5.7c0 .552.448 1 1 1 .552 0 1-.448 1-1v-5.7m0-11.6c0-.552-.448-1-1-1-.552 0-1 .448-1 1v5.7c0 .552.448 1 1 1 .552 0 1-.448 1-1v-5.7m0-6.664c0-.552-.448-1-1-1-.552 0-1 .448-1 1v.764c0 .552.448 1 1 1 .552 0 1-.448 1-1v-.764" id="0"/><circle cx="21" cy="24" r="10"/><rect width="33" height="3" x="37" y="18" rx="1.5" id="1"/><rect width="53" height="3" x="37" y="27" rx="1.5" id="2"/><path d="m131 29c0 .552.447.999.996.999h22.01c.545 0 .996-.451.996-.999v-9c0-.552-.447-.999-.996-.999h-22.01c-.545 0-.996.451-.996.999v9m.996-12h22.01c1.655 0 2.996 1.344 2.996 2.999v9c0 1.657-1.35 2.999-2.996 2.999h-22.01c-1.655 0-2.996-1.344-2.996-2.999v-9c0-1.657 1.35-2.999 2.996-2.999" id="3"/><g transform="translate(0 59)"><use xlink:href="#0"/><circle cx="21" cy="24" r="10"/><use xlink:href="#1"/><use xlink:href="#2"/><use xlink:href="#3"/></g></g></svg>
diff --git a/app/views/shared/icons/_members.svg b/app/views/shared/icons/_members.svg
deleted file mode 100644
index f8043b31fe8..00000000000
--- a/app/views/shared/icons/_members.svg
+++ /dev/null
@@ -1,13 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<svg width="22px" height="16px" viewBox="0 0 22 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
- <!-- Generator: Sketch 3.7.2 (28276) - http://www.bohemiancoding.com/sketch -->
- <title>Group</title>
- <desc>Created with Sketch.</desc>
- <defs></defs>
- <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
- <g id="Group" fill="#7E7C7C">
- <path d="M6.4357,11.8588 C7.1487,11.2798 7.8797,10.7808 8.5357,10.3708 C8.5837,10.3008 8.6187,10.2338 8.6187,10.1768 L8.6187,8.8088 C8.9197,8.5218 9.0927,8.1248 9.0927,7.7028 L9.0927,5.3748 C9.0927,3.9478 7.9187,2.7858 6.4757,2.7858 L5.9687,2.7858 C4.5247,2.7858 3.3507,3.9478 3.3507,5.3748 L3.3507,7.7028 C3.3507,8.1248 3.5247,8.5218 3.8247,8.8088 L3.8247,10.5838 C3.2537,10.8738 1.8797,11.6198 0.5967,12.6618 C0.2177,12.9698 -0.0003,13.4258 -0.0003,13.9138 L-0.0003,15.5088 C-0.0003,15.5438 0.0857,15.7668 0.3467,15.7778 C1.3257,15.8198 3.8417,15.8328 5.9617,15.9038 C5.8337,15.8148 5.7447,15.6748 5.7447,15.5088 L5.7447,13.5498 C5.7447,12.9848 5.9967,12.2158 6.4357,11.8588" id="Fill-1"></path>
- <path d="M21.3092,12.1 C19.6932,10.787 17.9592,9.86 17.3042,9.53 L17.3042,7.235 C17.6722,6.9 17.8862,6.428 17.8862,5.925 L17.8862,3.066 C17.8862,1.376 16.4952,0 14.7852,0 L14.1632,0 C12.4532,0 11.0622,1.376 11.0622,3.066 L11.0622,5.925 C11.0622,6.428 11.2752,6.9 11.6442,7.235 L11.6442,9.53 C10.9892,9.86 9.2542,10.787 7.6392,12.1 C7.2002,12.457 6.9482,12.985 6.9482,13.55 L6.9482,15.509 C6.9482,15.78 7.1702,16 7.4442,16 L14.1172,16 L14.1172,11.704 C12.6812,11.595 11.5652,10.853 11.5652,9.945 C11.5652,9.804 11.5982,9.669 11.6482,9.538 C11.9502,10.326 13.0982,10.913 14.4762,10.913 C15.8532,10.913 17.0012,10.326 17.3032,9.538 C17.3532,9.669 17.3862,9.804 17.3862,9.945 C17.3862,10.793 16.4152,11.5 15.1172,11.679 L15.1172,16 L21.5032,16 C21.7772,16 22.0002,15.78 22.0002,15.509 L22.0002,13.55 C22.0002,12.985 21.7482,12.457 21.3092,12.1" id="Fill-4"></path>
- </g>
- </g>
-</svg> \ No newline at end of file
diff --git a/app/views/shared/icons/_milestones.svg b/app/views/shared/icons/_milestones.svg
deleted file mode 100644
index 3d62ecc0631..00000000000
--- a/app/views/shared/icons/_milestones.svg
+++ /dev/null
@@ -1,15 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<svg width="16px" height="17px" viewBox="0 0 16 17" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
- <!-- Generator: Sketch 3.7.2 (28276) - http://www.bohemiancoding.com/sketch -->
- <title>Group</title>
- <desc>Created with Sketch.</desc>
- <defs></defs>
- <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
- <g id="Group" fill="#7E7C7C">
- <path d="M15.1111,1 L0.8891,1 C0.3981,1 0.0001,1.446 0.0001,1.996 L0.0001,15.945 C0.0001,16.495 0.3981,16.941 0.8891,16.941 L15.1111,16.941 C15.6021,16.941 16.0001,16.495 16.0001,15.945 L16.0001,1.996 C16.0001,1.446 15.6021,1 15.1111,1 L15.1111,1 L15.1111,1 Z M14.0001,6.0002 L14.0001,14.949 L2.0001,14.949 L2.0001,6.0002 L14.0001,6.0002 Z M14.0001,4.0002 L14.0001,2.993 L2.0001,2.993 L2.0001,4.0002 L14.0001,4.0002 Z" id="Combined-Shape"></path>
- <polygon id="Fill-11" points="3 2.0002 5 2.0002 5 0.0002 3 0.0002"></polygon>
- <polygon id="Fill-16" points="11 2.0002 13 2.0002 13 0.0002 11 0.0002"></polygon>
- <path d="M5.37709616,11.5511984 L6.92309616,12.7821984 C7.35112915,13.123019 7.97359761,13.0565604 8.32002627,12.6330535 L10.7740263,9.63305349 C11.1237073,9.20557058 11.0606364,8.57555475 10.6331535,8.22587373 C10.2056706,7.87619272 9.57565475,7.93926361 9.22597373,8.36674651 L6.77197373,11.3667465 L8.16890384,11.2176016 L6.62290384,9.98660159 C6.19085236,9.6425813 5.56172188,9.71394467 5.21770159,10.1459962 C4.8736813,10.5780476 4.94504467,11.2071781 5.37709616,11.5511984 L5.37709616,11.5511984 Z" id="Stroke-21"></path>
- </g>
- </g>
-</svg> \ No newline at end of file
diff --git a/app/views/shared/icons/_mr.svg b/app/views/shared/icons/_mr.svg
deleted file mode 100644
index dd3dbcc4473..00000000000
--- a/app/views/shared/icons/_mr.svg
+++ /dev/null
@@ -1,13 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<svg width="16px" height="16px" viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
- <!-- Generator: Sketch 3.7.2 (28276) - http://www.bohemiancoding.com/sketch -->
- <title>Group</title>
- <desc>Created with Sketch.</desc>
- <defs></defs>
- <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
- <g id="Group" fill="#7E7C7C">
- <path d="M15.1111,0 L0.8891,0 C0.3981,0 0.0001,0.446 0.0001,0.996 L0.0001,14.945 C0.0001,15.495 0.3981,15.941 0.8891,15.941 L15.1111,15.941 C15.6021,15.941 16.0001,15.495 16.0001,14.945 L16.0001,0.996 C16.0001,0.446 15.6021,0 15.1111,0 L15.1111,0 L15.1111,0 Z M2.0001,13.949 L14.0001,13.949 L14.0001,1.993 L2.0001,1.993 L2.0001,13.949 Z M2,5.0002 L14,5.0002 L14,3.0002 L2,3.0002 L2,5.0002 Z" id="Combined-Shape"></path>
- <path d="M8.547,12.0002 L12,12.0002 L12,10.0002 L8.547,10.0002 L8.547,12.0002 Z M5.2029,12 L3.9999,10.867 L5.2029,9.501 L3.9999,8.181 L5.2029,7 L7.4529,9.499 L5.2029,12 Z" id="Combined-Shape"></path>
- </g>
- </g>
-</svg> \ No newline at end of file
diff --git a/app/views/shared/icons/_pipelines.svg b/app/views/shared/icons/_pipelines.svg
deleted file mode 100644
index 794e8a27025..00000000000
--- a/app/views/shared/icons/_pipelines.svg
+++ /dev/null
@@ -1,10 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<svg width="16px" height="16px" viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
- <!-- Generator: Sketch 3.8.3 (29802) - http://www.bohemiancoding.com/sketch -->
- <title>Pasted Image 246</title>
- <desc>Created with Sketch.</desc>
- <defs></defs>
- <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
- <path d="M12.5,14 C11.672,14 11,13.328 11,12.5 C11,11.672 11.672,11 12.5,11 C13.328,11 14,11.672 14,12.5 C14,13.328 13.328,14 12.5,14 M12.5,9 L3.5,9 C1.567,9 0,10.567 0,12.5 C0,14.433 1.567,16 3.5,16 L12.5,16 C14.433,16 16,14.433 16,12.5 C16,10.567 14.433,9 12.5,9 M3.5,2 C4.328,2 5,2.672 5,3.5 C5,4.328 4.328,5 3.5,5 C2.672,5 2,4.328 2,3.5 C2,2.672 2.672,2 3.5,2 M3.5,7 L12.5,7 C14.433,7 16,5.433 16,3.5 C16,1.567 14.433,0 12.5,0 L3.5,0 C1.567,0 0,1.567 0,3.5 C0,5.433 1.567,7 3.5,7" id="Pasted-Image-246" fill="#303030"></path>
- </g>
-</svg> \ No newline at end of file
diff --git a/app/views/shared/icons/_wiki.svg b/app/views/shared/icons/_wiki.svg
deleted file mode 100644
index 182d91e23aa..00000000000
--- a/app/views/shared/icons/_wiki.svg
+++ /dev/null
@@ -1,10 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<svg width="16px" height="16px" viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
- <!-- Generator: Sketch 3.8.3 (29802) - http://www.bohemiancoding.com/sketch -->
- <title>Pasted Image 241</title>
- <desc>Created with Sketch.</desc>
- <defs></defs>
- <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
- <path d="M2.004,12.9999459 L3.939,12.9999459 L3.939,4.99994585 L2.004,4.99994585 L2.004,12.9999459 Z M7.017,9.99994585 L13.018,9.99994585 L13.018,8.99994585 L7.017,8.99994585 L7.017,9.99994585 Z M7.017,7.99994585 L13.018,7.99994585 L13.018,6.99994585 L7.017,6.99994585 L7.017,7.99994585 Z M7.017,5.99994585 L13.018,5.99994585 L13.018,4.99994585 L7.017,4.99994585 L7.017,5.99994585 Z M14.754,-5.41499267e-05 L4.938,-5.41499267e-05 C4.386,-5.41499267e-05 3.938,0.44794585 3.938,0.99994585 L3.938,2.99994585 L1,2.99994585 C0.448,2.99994585 0,3.44794585 0,3.99994585 L0,12.9999459 C0.037,13.4999459 -0.25,16.0509459 3.938,15.9999459 L12.408,15.9999459 C12.408,15.9999459 15.754,15.9169459 15.754,13.9999459 L15.754,0.99994585 C15.754,0.44794585 15.306,-5.41499267e-05 14.754,-5.41499267e-05 L14.754,-5.41499267e-05 Z" id="Pasted-Image-241" fill="#7E7D7D"></path>
- </g>
-</svg> \ No newline at end of file
diff --git a/app/workers/process_commit_worker.rb b/app/workers/process_commit_worker.rb
index e9a5bd7f24e..2f7967cf531 100644
--- a/app/workers/process_commit_worker.rb
+++ b/app/workers/process_commit_worker.rb
@@ -53,6 +53,8 @@ class ProcessCommitWorker
def update_issue_metrics(commit, author)
mentioned_issues = commit.all_references(author).issues
+ return if mentioned_issues.empty?
+
Issue::Metrics.where(issue_id: mentioned_issues.map(&:id), first_mentioned_in_commit_at: nil).
update_all(first_mentioned_in_commit_at: commit.committed_date)
end
diff --git a/changelogs/unreleased/20841-getting-started-better-empty-state-for-merge-requests-view.yml b/changelogs/unreleased/20841-getting-started-better-empty-state-for-merge-requests-view.yml
new file mode 100644
index 00000000000..34909c06df3
--- /dev/null
+++ b/changelogs/unreleased/20841-getting-started-better-empty-state-for-merge-requests-view.yml
@@ -0,0 +1,4 @@
+---
+title: Added merge requests empty state
+merge_request: 7342
+author:
diff --git a/changelogs/unreleased/24187-set-git-terminal-prompt-env-var-in-initializer.yml b/changelogs/unreleased/24187-set-git-terminal-prompt-env-var-in-initializer.yml
new file mode 100644
index 00000000000..7fe5c8a84af
--- /dev/null
+++ b/changelogs/unreleased/24187-set-git-terminal-prompt-env-var-in-initializer.yml
@@ -0,0 +1,4 @@
+---
+title: Set GIT_TERMINAL_PROMPT env variable in initializer
+merge_request: 10372
+author:
diff --git a/changelogs/unreleased/26595-fix-issue-preselected-template.yml b/changelogs/unreleased/26595-fix-issue-preselected-template.yml
deleted file mode 100644
index a94765f8f2a..00000000000
--- a/changelogs/unreleased/26595-fix-issue-preselected-template.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Fix linking to new issue with selected template via url parameter
-merge_request:
-author:
diff --git a/changelogs/unreleased/28732-expandable-folders.yml b/changelogs/unreleased/28732-expandable-folders.yml
new file mode 100644
index 00000000000..9ae30ba6253
--- /dev/null
+++ b/changelogs/unreleased/28732-expandable-folders.yml
@@ -0,0 +1,4 @@
+---
+title: Add back expandable folder behavior
+merge_request:
+author:
diff --git a/changelogs/unreleased/29034-fix-github-importer.yml b/changelogs/unreleased/29034-fix-github-importer.yml
deleted file mode 100644
index 6d08db3d55d..00000000000
--- a/changelogs/unreleased/29034-fix-github-importer.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Fix name colision when importing GitHub pull requests from forked repositories
-merge_request: 9719
-author:
diff --git a/changelogs/unreleased/29432-prevent-click-disabled-btn.yml b/changelogs/unreleased/29432-prevent-click-disabled-btn.yml
new file mode 100644
index 00000000000..f30570cf68b
--- /dev/null
+++ b/changelogs/unreleased/29432-prevent-click-disabled-btn.yml
@@ -0,0 +1,4 @@
+---
+title: Fix project title validation, prevent clicking on disabled button
+merge_request: 9931
+author:
diff --git a/changelogs/unreleased/29492-useless-queries.yml b/changelogs/unreleased/29492-useless-queries.yml
new file mode 100644
index 00000000000..266a04be352
--- /dev/null
+++ b/changelogs/unreleased/29492-useless-queries.yml
@@ -0,0 +1,4 @@
+---
+title: Remove useless queries with false conditions (e.g 1=0)
+merge_request: 10141
+author: mhasbini
diff --git a/changelogs/unreleased/29541-fix-github-importer-deleted-fork.yml b/changelogs/unreleased/29541-fix-github-importer-deleted-fork.yml
deleted file mode 100644
index fcb5798a619..00000000000
--- a/changelogs/unreleased/29541-fix-github-importer-deleted-fork.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Fix GitHub Importer for PRs of deleted forked repositories
-merge_request: 9992
-author:
diff --git a/changelogs/unreleased/29670-jira-integration-documentation-improvment.yml b/changelogs/unreleased/29670-jira-integration-documentation-improvment.yml
new file mode 100644
index 00000000000..8975f0b6ef3
--- /dev/null
+++ b/changelogs/unreleased/29670-jira-integration-documentation-improvment.yml
@@ -0,0 +1,4 @@
+---
+title: Added clarification to the Jira integration documentation.
+merge_request: 10066
+author: Matthew Bender
diff --git a/changelogs/unreleased/29871-api-remove-merge-requests-comments-endpoints.yml b/changelogs/unreleased/29871-api-remove-merge-requests-comments-endpoints.yml
deleted file mode 100644
index e3fb62d53b6..00000000000
--- a/changelogs/unreleased/29871-api-remove-merge-requests-comments-endpoints.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: 'API: Make the /notes endpoint work with noteable iid instead of id'
-merge_request:
-author:
diff --git a/changelogs/unreleased/30098-banzai-filter-mergerequestreferencefilter-has-an-n-1-query-problem.yml b/changelogs/unreleased/30098-banzai-filter-mergerequestreferencefilter-has-an-n-1-query-problem.yml
deleted file mode 100644
index f3f4e065aef..00000000000
--- a/changelogs/unreleased/30098-banzai-filter-mergerequestreferencefilter-has-an-n-1-query-problem.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Improve Markdown rendering when a lot of merge requests are referenced
-merge_request: 10252
-author:
diff --git a/changelogs/unreleased/30264-fix-vue-pagination.yml b/changelogs/unreleased/30264-fix-vue-pagination.yml
deleted file mode 100644
index d5846e52bcf..00000000000
--- a/changelogs/unreleased/30264-fix-vue-pagination.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fixes method not replacing URL parameters correctly and breaking pipelines
- pagination
-merge_request:
-author:
diff --git a/changelogs/unreleased/30276-move-top-badges.yml b/changelogs/unreleased/30276-move-top-badges.yml
deleted file mode 100644
index 68a8ab359fd..00000000000
--- a/changelogs/unreleased/30276-move-top-badges.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Move issue, mr, todos next to profile dropdown in top nav
-merge_request:
-author:
diff --git a/changelogs/unreleased/30289-allow-users-to-import-github-projects-to-subgroups.yml b/changelogs/unreleased/30289-allow-users-to-import-github-projects-to-subgroups.yml
deleted file mode 100644
index a33382a85e3..00000000000
--- a/changelogs/unreleased/30289-allow-users-to-import-github-projects-to-subgroups.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Allow users to import GitHub projects to subgroups
-merge_request:
-author:
diff --git a/changelogs/unreleased/30400-fix-blob-highlighting-in-search.yml b/changelogs/unreleased/30400-fix-blob-highlighting-in-search.yml
new file mode 100644
index 00000000000..942258450c0
--- /dev/null
+++ b/changelogs/unreleased/30400-fix-blob-highlighting-in-search.yml
@@ -0,0 +1,4 @@
+---
+title: Fix blob highlighting in search
+merge_request: 10420
+author:
diff --git a/changelogs/unreleased/add-mock-deployment-and-monitoring-service-for-development.yaml b/changelogs/unreleased/add-mock-deployment-and-monitoring-service-for-development.yaml
new file mode 100644
index 00000000000..4c81d21a94b
--- /dev/null
+++ b/changelogs/unreleased/add-mock-deployment-and-monitoring-service-for-development.yaml
@@ -0,0 +1,4 @@
+---
+title: Added mock deployment and monitoring service with environments fixtures
+merge_request:
+author:
diff --git a/changelogs/unreleased/bug-api_milestone_merge_requests_scope.yml b/changelogs/unreleased/bug-api_milestone_merge_requests_scope.yml
new file mode 100644
index 00000000000..a1e1c29165e
--- /dev/null
+++ b/changelogs/unreleased/bug-api_milestone_merge_requests_scope.yml
@@ -0,0 +1,4 @@
+---
+title: Fixes milestone/merge_requests endpoint to actually scope the result
+merge_request:
+author: Joren De Groof
diff --git a/changelogs/unreleased/environment-performance-improvements.yml b/changelogs/unreleased/environment-performance-improvements.yml
new file mode 100644
index 00000000000..43e8f0afcee
--- /dev/null
+++ b/changelogs/unreleased/environment-performance-improvements.yml
@@ -0,0 +1,4 @@
+---
+title: Improved UX for the environments metrics view
+merge_request: 9946
+author:
diff --git a/changelogs/unreleased/fix-gb-environments-folders-route.yml b/changelogs/unreleased/fix-gb-environments-folders-route.yml
deleted file mode 100644
index fd9d9e6f168..00000000000
--- a/changelogs/unreleased/fix-gb-environments-folders-route.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Fix environment folder route when special chars present in environment name
-merge_request: 10250
-author:
diff --git a/changelogs/unreleased/fix-import-fork.yml b/changelogs/unreleased/fix-import-fork.yml
new file mode 100644
index 00000000000..ff8dd131995
--- /dev/null
+++ b/changelogs/unreleased/fix-import-fork.yml
@@ -0,0 +1,4 @@
+---
+title: Fix Import/Export MR diffs not showing and missing forked MRs
+merge_request:
+author:
diff --git a/changelogs/unreleased/fix-import-namespace.yml b/changelogs/unreleased/fix-import-namespace.yml
new file mode 100644
index 00000000000..9a2fa5e425f
--- /dev/null
+++ b/changelogs/unreleased/fix-import-namespace.yml
@@ -0,0 +1,4 @@
+---
+title: Create subgroups if they don't exist while importing projects
+merge_request:
+author:
diff --git a/changelogs/unreleased/fix_wiki_commit_message.yml b/changelogs/unreleased/fix_wiki_commit_message.yml
new file mode 100644
index 00000000000..e5cd398b4b5
--- /dev/null
+++ b/changelogs/unreleased/fix_wiki_commit_message.yml
@@ -0,0 +1,4 @@
+---
+title: Fix wiki commit message
+merge_request: 10464
+author: blackst0ne
diff --git a/changelogs/unreleased/forked-subquery-order.yml b/changelogs/unreleased/forked-subquery-order.yml
deleted file mode 100644
index 06fb8236783..00000000000
--- a/changelogs/unreleased/forked-subquery-order.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Remove unnecessary ORDER BY clause from `forked_to_project_id` subquery
-merge_request:
-author: mhasbini
diff --git a/changelogs/unreleased/issue_91_ee_backport.yml b/changelogs/unreleased/issue_91_ee_backport.yml
new file mode 100644
index 00000000000..17bc0e435f3
--- /dev/null
+++ b/changelogs/unreleased/issue_91_ee_backport.yml
@@ -0,0 +1,4 @@
+---
+title: Do not set closed_at to nil when issue is reopened
+merge_request:
+author:
diff --git a/changelogs/unreleased/make-ci-build-to-lock-on-status-change.yml b/changelogs/unreleased/make-ci-build-to-lock-on-status-change.yml
deleted file mode 100644
index b0c5eb4ed17..00000000000
--- a/changelogs/unreleased/make-ci-build-to-lock-on-status-change.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Make CI build to use optimistic locking only on status change
-merge_request:
-author:
diff --git a/changelogs/unreleased/remove_index_for_users-current_sign_in_at.yml b/changelogs/unreleased/remove_index_for_users-current_sign_in_at.yml
new file mode 100644
index 00000000000..ec3a2c8e2bf
--- /dev/null
+++ b/changelogs/unreleased/remove_index_for_users-current_sign_in_at.yml
@@ -0,0 +1,4 @@
+---
+title: Remove index for users.current sign in at
+merge_request: 10401
+author: blackst0ne
diff --git a/changelogs/unreleased/sh-fix-destroy-user-race.yml b/changelogs/unreleased/sh-fix-destroy-user-race.yml
deleted file mode 100644
index 4d650b51ada..00000000000
--- a/changelogs/unreleased/sh-fix-destroy-user-race.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fix race condition where a namespace would be deleted before a project was
- deleted
-merge_request:
-author:
diff --git a/changelogs/unreleased/sh-fix-ssh-keys-with-spaces.yml b/changelogs/unreleased/sh-fix-ssh-keys-with-spaces.yml
new file mode 100644
index 00000000000..fe75d7e1156
--- /dev/null
+++ b/changelogs/unreleased/sh-fix-ssh-keys-with-spaces.yml
@@ -0,0 +1,4 @@
+---
+title: Handle SSH keys that have multiple spaces between each marker
+merge_request:
+author:
diff --git a/changelogs/unreleased/todo-update-order.yml b/changelogs/unreleased/todo-update-order.yml
deleted file mode 100644
index 2046b6df11e..00000000000
--- a/changelogs/unreleased/todo-update-order.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Remove unnecessary ORDER BY clause when updating todos
-merge_request:
-author: mhasbini
diff --git a/changelogs/unreleased/zj-kube-service-auto-fill.yml b/changelogs/unreleased/zj-kube-service-auto-fill.yml
new file mode 100644
index 00000000000..7a2c7a5085b
--- /dev/null
+++ b/changelogs/unreleased/zj-kube-service-auto-fill.yml
@@ -0,0 +1,4 @@
+---
+title: Don't fill in the default kubernetes namespace
+merge_request:
+author:
diff --git a/config/application.rb b/config/application.rb
index f9f01b66473..f2ecc4ce77c 100644
--- a/config/application.rb
+++ b/config/application.rb
@@ -150,6 +150,7 @@ module Gitlab
# This is needed for gitlab-shell
ENV['GITLAB_PATH_OUTSIDE_HOOK'] = ENV['PATH']
+ ENV['GIT_TERMINAL_PROMPT'] = '0'
config.generators do |g|
g.factory_girl false
diff --git a/config/webpack.config.js b/config/webpack.config.js
index 70d98b022c1..36c09c14d56 100644
--- a/config/webpack.config.js
+++ b/config/webpack.config.js
@@ -23,7 +23,6 @@ var config = {
main: './main.js',
blob: './blob_edit/blob_bundle.js',
boards: './boards/boards_bundle.js',
- simulate_drag: './test_utils/simulate_drag.js',
cycle_analytics: './cycle_analytics/cycle_analytics_bundle.js',
commit_pipelines: './commit/pipelines/pipelines_bundle.js',
diff_notes: './diff_notes/diff_notes_bundle.js',
@@ -53,7 +52,7 @@ var config = {
filename: IS_PRODUCTION ? '[name].[chunkhash].bundle.js' : '[name].bundle.js'
},
- devtool: 'inline-source-map',
+ devtool: 'cheap-module-source-map',
module: {
rules: [
@@ -164,6 +163,7 @@ if (IS_PRODUCTION) {
}
if (IS_DEV_SERVER) {
+ config.devtool = 'cheap-module-eval-source-map';
config.devServer = {
port: DEV_SERVER_PORT,
headers: { 'Access-Control-Allow-Origin': '*' },
diff --git a/db/fixtures/development/19_environments.rb b/db/fixtures/development/19_environments.rb
new file mode 100644
index 00000000000..93214b9d3e7
--- /dev/null
+++ b/db/fixtures/development/19_environments.rb
@@ -0,0 +1,70 @@
+require './spec/support/sidekiq'
+
+class Gitlab::Seeder::Environments
+ def initialize(project)
+ @project = project
+ end
+
+ def seed!
+ @project.create_mock_deployment_service!(active: true)
+ @project.create_mock_monitoring_service!(active: true)
+
+ create_master_deployments!('production')
+ create_master_deployments!('staging')
+ create_merge_request_review_deployments!
+ end
+
+ private
+
+ def create_master_deployments!(name)
+ @project.repository.commits('master', limit: 4).map do |commit|
+ create_deployment!(
+ @project,
+ name,
+ 'master',
+ commit.id
+ )
+ end
+ end
+
+ def create_merge_request_review_deployments!
+ @project.merge_requests.sample(4).map do |merge_request|
+ next unless merge_request.diff_head_sha
+
+ create_deployment!(
+ merge_request.source_project,
+ "review/#{merge_request.source_branch}",
+ merge_request.source_branch,
+ merge_request.diff_head_sha
+ )
+ end
+ end
+
+ def create_deployment!(project, name, ref, sha)
+ environment = find_or_create_environment!(project, name)
+ environment.deployments.create!(
+ project: project,
+ ref: ref,
+ sha: sha,
+ tag: false,
+ deployable: find_deployable(project, name)
+ )
+ end
+
+ def find_or_create_environment!(project, name)
+ project.environments.find_or_create_by!(name: name).tap do |environment|
+ environment.update(external_url: "https://google.com/#{name}")
+ end
+ end
+
+ def find_deployable(project, environment)
+ project.builds.where(environment: environment).sample
+ end
+end
+
+Gitlab::Seeder.quiet do
+ Project.all.sample(5).each do |project|
+ project_environments = Gitlab::Seeder::Environments.new(project)
+ project_environments.seed!
+ end
+end
diff --git a/db/fixtures/development/19_nested_groups.rb b/db/fixtures/development/20_nested_groups.rb
index d8dddc3fee9..d8dddc3fee9 100644
--- a/db/fixtures/development/19_nested_groups.rb
+++ b/db/fixtures/development/20_nested_groups.rb
diff --git a/db/migrate/20170402231018_remove_index_for_users_current_sign_in_at.rb b/db/migrate/20170402231018_remove_index_for_users_current_sign_in_at.rb
new file mode 100644
index 00000000000..8316ee9eb9f
--- /dev/null
+++ b/db/migrate/20170402231018_remove_index_for_users_current_sign_in_at.rb
@@ -0,0 +1,25 @@
+# See http://doc.gitlab.com/ce/development/migration_style_guide.html
+# for more information on how to write migrations for GitLab.
+
+class RemoveIndexForUsersCurrentSignInAt < ActiveRecord::Migration
+ include Gitlab::Database::MigrationHelpers
+
+ # Set this constant to true if this migration requires downtime.
+ DOWNTIME = false
+
+ disable_ddl_transaction!
+
+ def up
+ if index_exists? :users, :current_sign_in_at
+ if Gitlab::Database.postgresql?
+ execute 'DROP INDEX CONCURRENTLY index_users_on_current_sign_in_at;'
+ else
+ remove_index :users, :current_sign_in_at
+ end
+ end
+ end
+
+ def down
+ add_concurrent_index :users, :current_sign_in_at
+ end
+end
diff --git a/db/schema.rb b/db/schema.rb
index 19aca4b941e..ccf18d07179 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -11,7 +11,7 @@
#
# It's strongly recommended that you check this file into your version control system.
-ActiveRecord::Schema.define(version: 20170329124448) do
+ActiveRecord::Schema.define(version: 20170402231018) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
@@ -1252,7 +1252,6 @@ ActiveRecord::Schema.define(version: 20170329124448) do
add_index "users", ["authentication_token"], name: "index_users_on_authentication_token", unique: true, using: :btree
add_index "users", ["confirmation_token"], name: "index_users_on_confirmation_token", unique: true, using: :btree
add_index "users", ["created_at"], name: "index_users_on_created_at", using: :btree
- add_index "users", ["current_sign_in_at"], name: "index_users_on_current_sign_in_at", using: :btree
add_index "users", ["email"], name: "index_users_on_email", unique: true, using: :btree
add_index "users", ["email"], name: "index_users_on_email_trigram", using: :gin, opclasses: {"email"=>"gin_trgm_ops"}
add_index "users", ["ghost"], name: "index_users_on_ghost", using: :btree
diff --git a/doc/development/img/cache-hit.svg b/doc/development/img/cache-hit.svg
new file mode 100644
index 00000000000..1c37693df2d
--- /dev/null
+++ b/doc/development/img/cache-hit.svg
@@ -0,0 +1,21 @@
+<svg version="1.1" id="mscgen_js-svg-__svg" class="mscgen_js-svg-__svg" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="976" height="310" viewBox="0 0 976 310"><desc>
+
+# Generated by mscgen_js - https://sverweij.github.io/mscgen_js
+msc {
+ # options
+ hscale="1.5";
+
+ # entities
+ c [label="Client", textbgcolor="lime"],
+ rails [label="Rails", textbgcolor="cyan"],
+ etag [label="EtagCaching", textbgcolor="orange"],
+ redis [label="Redis", textbgcolor="white"];
+
+ # arcs
+ c =&gt; rails [label="GET /projects/5/pipelines"];
+ rails =&gt; etag [label="GET /projects/5/pipelines"];
+ etag =&gt; redis [label="read(key = 'GET &lt;Etag&gt;')"];
+ redis =&gt; etag [label="cache hit", linecolor="green", textcolor="green"];
+ |||;
+ etag =&gt; c [label="304 Not Modified", linecolor="blue", textcolor="blue"];
+}</desc><defs><style type="text/css">svg.mscgen_js-svg-__svg{font-family:Helvetica,sans-serif;font-size:12px;font-weight:normal;font-style:normal;text-decoration:none;background-color:white;stroke:black;stroke-width:2;color:black}.mscgen_js-svg-__svg path, .mscgen_js-svg-__svg rect{fill:none;color:black;stroke:black}.mscgen_js-svg-__svg .label-text-background{fill:white;stroke:white;stroke-width:0}.mscgen_js-svg-__svg .bglayer{fill:white;stroke:white;stroke-width:0}.mscgen_js-svg-__svg line{stroke:black}.mscgen_js-svg-__svg .return, .mscgen_js-svg-__svg .comment{stroke-dasharray:5,3}.mscgen_js-svg-__svg .inline_expression_divider{stroke-dasharray:10,5}.mscgen_js-svg-__svg text{color:inherit;stroke:none;text-anchor:middle}.mscgen_js-svg-__svg text.entity-text{text-decoration:underline}.mscgen_js-svg-__svg text.anchor-start{text-anchor:start}.mscgen_js-svg-__svg .arrow-marker{overflow:visible}.mscgen_js-svg-__svg .arrow-style{stroke-width:1}.mscgen_js-svg-__svg .arcrow, .mscgen_js-svg-__svg .arcrowomit, .mscgen_js-svg-__svg .emphasised{stroke-linecap:butt}.mscgen_js-svg-__svg .arcrowomit{stroke-dasharray:2,2;}.mscgen_js-svg-__svg .box, .mscgen_js-svg-__svg .entity{fill:white;stroke-linejoin:round}.mscgen_js-svg-__svg .inherit{stroke:inherit;color:inherit}.mscgen_js-svg-__svg .inherit-fill{fill:inherit}.mscgen_js-svg-__svg .watermark{stroke:black;color:black;fill:black;font-size:48pt;font-weight:bold;opacity:0.14}</style><marker orient="auto" id="mscgen_js-svg-__svgmethod-black" class="arrow-marker" viewBox="0 0 10 10" refX="9" refY="3" markerUnits="strokeWidth" markerWidth="10" markerHeight="10"><polygon points="1,1 9,3 1,5" class="arrow-style" stroke="black" fill="black"></polygon></marker><marker orient="auto" id="mscgen_js-svg-__svgmethod-l-black" class="arrow-marker" viewBox="0 0 10 10" refX="9" refY="3" markerUnits="strokeWidth" markerWidth="10" markerHeight="10"><polygon points="17,1 9,3 17,5" class="arrow-style" stroke="black" fill="black"></polygon></marker><marker orient="auto" id="mscgen_js-svg-__svgmethod-blue" class="arrow-marker" viewBox="0 0 10 10" refX="9" refY="3" markerUnits="strokeWidth" markerWidth="10" markerHeight="10"><polygon points="1,1 9,3 1,5" class="arrow-style" stroke="blue" fill="blue"></polygon></marker><marker orient="auto" id="mscgen_js-svg-__svgmethod-l-blue" class="arrow-marker" viewBox="0 0 10 10" refX="9" refY="3" markerUnits="strokeWidth" markerWidth="10" markerHeight="10"><polygon points="17,1 9,3 17,5" class="arrow-style" stroke="blue" fill="blue"></polygon></marker><marker orient="auto" id="mscgen_js-svg-__svgmethod-green" class="arrow-marker" viewBox="0 0 10 10" refX="9" refY="3" markerUnits="strokeWidth" markerWidth="10" markerHeight="10"><polygon points="1,1 9,3 1,5" class="arrow-style" stroke="green" fill="green"></polygon></marker><marker orient="auto" id="mscgen_js-svg-__svgmethod-l-green" class="arrow-marker" viewBox="0 0 10 10" refX="9" refY="3" markerUnits="strokeWidth" markerWidth="10" markerHeight="10"><polygon points="17,1 9,3 17,5" class="arrow-style" stroke="green" fill="green"></polygon></marker></defs><g id="mscgen_js-svg-__svg__body" transform="translate(53,3) scale(1,1)"><g id="mscgen_js-svg-__svg__background"><rect width="976" height="310" x="-53" y="-3" class="bglayer"></rect></g><g id="mscgen_js-svg-__svg__arcspanlayer"></g><g id="mscgen_js-svg-__svg__lifelinelayer"><line x1="75" y1="38" x2="75" y2="76" class="arcrow"></line><line x1="315" y1="38" x2="315" y2="76" class="arcrow"></line><line x1="555" y1="38" x2="555" y2="76" class="arcrow"></line><line x1="795" y1="38" x2="795" y2="76" class="arcrow"></line><line x1="75" y1="76" x2="75" y2="114" class="arcrow"></line><line x1="315" y1="76" x2="315" y2="114" class="arcrow"></line><line x1="555" y1="76" x2="555" y2="114" class="arcrow"></line><line x1="795" y1="76" x2="795" y2="114" class="arcrow"></line><line x1="75" y1="114" x2="75" y2="152" class="arcrow"></line><line x1="315" y1="114" x2="315" y2="152" class="arcrow"></line><line x1="555" y1="114" x2="555" y2="152" class="arcrow"></line><line x1="795" y1="114" x2="795" y2="152" class="arcrow"></line><line x1="75" y1="152" x2="75" y2="190" class="arcrow"></line><line x1="315" y1="152" x2="315" y2="190" class="arcrow"></line><line x1="555" y1="152" x2="555" y2="190" class="arcrow"></line><line x1="795" y1="152" x2="795" y2="190" class="arcrow"></line><line x1="75" y1="190" x2="75" y2="228" class="arcrow"></line><line x1="315" y1="190" x2="315" y2="228" class="arcrow"></line><line x1="555" y1="190" x2="555" y2="228" class="arcrow"></line><line x1="795" y1="190" x2="795" y2="228" class="arcrow"></line><line x1="75" y1="228" x2="75" y2="266" class="arcrow"></line><line x1="315" y1="228" x2="315" y2="266" class="arcrow"></line><line x1="555" y1="228" x2="555" y2="266" class="arcrow"></line><line x1="795" y1="228" x2="795" y2="266" class="arcrow"></line><line x1="75" y1="266" x2="75" y2="304" class="arcrow"></line><line x1="315" y1="266" x2="315" y2="304" class="arcrow"></line><line x1="555" y1="266" x2="555" y2="304" class="arcrow"></line><line x1="795" y1="266" x2="795" y2="304" class="arcrow"></line></g><g id="mscgen_js-svg-__svg__sequencelayer"><g id="mscgen_js-svg-__svgentities"><g><rect width="150" height="38" class="entity" style="fill:lime;"></rect><g><text x="75" y="22.5" class="entity-text "><tspan>Client</tspan></text></g></g><g><rect width="150" height="38" x="240" class="entity" style="fill:cyan;"></rect><g><text x="315" y="22.5" class="entity-text "><tspan>Rails</tspan></text></g></g><g><rect width="150" height="38" x="480" class="entity" style="fill:orange;"></rect><g><text x="555" y="22.5" class="entity-text "><tspan>EtagCaching</tspan></text></g></g><g><rect width="150" height="38" x="720" class="entity" style="fill:white;"></rect><g><text x="795" y="22.5" class="entity-text "><tspan>Redis</tspan></text></g></g></g><g><line x1="75" y1="95" x2="315" y2="95" class="arc directional method" style="stroke:black" marker-end="url(#mscgen_js-svg-__svgmethod-black)"></line><g><rect width="134.06" height="14" x="127.97" y="79.5" class="label-text-background"></rect><text x="195" y="90.5" class="directional-text method-text "><tspan>GET /projects/5/pipelines</tspan></text></g></g><g><line x1="315" y1="133" x2="555" y2="133" class="arc directional method" style="stroke:black" marker-end="url(#mscgen_js-svg-__svgmethod-black)"></line><g><rect width="134.06" height="14" x="367.97" y="117.5" class="label-text-background"></rect><text x="435" y="128.5" class="directional-text method-text "><tspan>GET /projects/5/pipelines</tspan></text></g></g><g><line x1="555" y1="171" x2="795" y2="171" class="arc directional method" style="stroke:black" marker-end="url(#mscgen_js-svg-__svgmethod-black)"></line><g><rect width="135.64" height="14" x="607.17" y="155.5" class="label-text-background"></rect><text x="675" y="166.5" class="directional-text method-text "><tspan>read(key = 'GET &lt;Etag&gt;')</tspan></text></g></g><g><line x1="795" y1="209" x2="555" y2="209" class="arc directional method" style="stroke:green" marker-end="url(#mscgen_js-svg-__svgmethod-green)"></line><g><rect width="48.02" height="14" x="650.98" y="193.5" class="label-text-background"></rect><text x="675" y="204.5" class="directional-text method-text " style="fill:green;"><tspan>cache hit</tspan></text></g></g><g></g><g><line x1="555" y1="285" x2="75" y2="285" class="arc directional method" style="stroke:blue" marker-end="url(#mscgen_js-svg-__svgmethod-blue)"></line><g><rect width="90.72" height="14" x="269.63" y="269.5" class="label-text-background"></rect><text x="315" y="280.5" class="directional-text method-text " style="fill:blue;"><tspan>304 Not Modified</tspan></text></g></g></g><g id="mscgen_js-svg-__svg__notelayer"></g><g id="mscgen_js-svg-__svg__watermark"></g></g></svg> \ No newline at end of file
diff --git a/doc/development/img/cache-miss.svg b/doc/development/img/cache-miss.svg
new file mode 100644
index 00000000000..8429e6a1918
--- /dev/null
+++ b/doc/development/img/cache-miss.svg
@@ -0,0 +1,24 @@
+<svg version="1.1" id="mscgen_js-svg-__svg" class="mscgen_js-svg-__svg" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="976" height="386" viewBox="0 0 976 386"><desc>
+
+# Generated by mscgen_js - https://sverweij.github.io/mscgen_js
+msc {
+ # options
+ hscale="1.5";
+
+ # entities
+ c [label="Client", textbgcolor="lime"],
+ rails [label="Rails", textbgcolor="cyan"],
+ etag [label="EtagCaching", textbgcolor="orange"],
+ redis [label="Redis", textbgcolor="white"];
+
+ # arcs
+ c =&gt; rails [label="GET /projects/5/pipelines"];
+ rails =&gt; etag [label="GET /projects/5/pipelines"];
+ etag =&gt; redis [label="read(key = 'GET &lt;Etag&gt;')"];
+ redis =&gt; etag [label="cache miss", linecolor="red", textcolor="red"];
+ |||;
+ etag =&gt; redis [label="write('&lt;New Etag&gt;')"];
+ etag =&gt; rails [label="GET /projects/5/pipelines"];
+ rails =&gt; c [label="JSON response w/ ETag", linecolor="blue", textcolor="blue"];
+}
+</desc><defs><style type="text/css">svg.mscgen_js-svg-__svg{font-family:Helvetica,sans-serif;font-size:12px;font-weight:normal;font-style:normal;text-decoration:none;background-color:white;stroke:black;stroke-width:2}.mscgen_js-svg-__svg path, .mscgen_js-svg-__svg rect{fill:none}.mscgen_js-svg-__svg .label-text-background{fill:white;stroke:white;stroke-width:0}.mscgen_js-svg-__svg .bglayer{fill:white;stroke:white;stroke-width:0}.mscgen_js-svg-__svg line{}.mscgen_js-svg-__svg .return, .mscgen_js-svg-__svg .comment{stroke-dasharray:5,3}.mscgen_js-svg-__svg .inline_expression_divider{stroke-dasharray:10,5}.mscgen_js-svg-__svg text{color:inherit;stroke:none;text-anchor:middle}.mscgen_js-svg-__svg text.entity-text{text-decoration:underline}.mscgen_js-svg-__svg text.anchor-start{text-anchor:start}.mscgen_js-svg-__svg .arrow-marker{overflow:visible}.mscgen_js-svg-__svg .arrow-style{stroke-width:1}.mscgen_js-svg-__svg .arcrow, .mscgen_js-svg-__svg .arcrowomit, .mscgen_js-svg-__svg .emphasised{stroke-linecap:butt}.mscgen_js-svg-__svg .arcrowomit{stroke-dasharray:2,2}.mscgen_js-svg-__svg .box, .mscgen_js-svg-__svg .entity{fill:white;stroke-linejoin:round}.mscgen_js-svg-__svg .inherit{stroke:inherit;color:inherit}.mscgen_js-svg-__svg .inherit-fill{fill:inherit}.mscgen_js-svg-__svg .watermark{font-size:48pt;font-weight:bold;opacity:0.14}</style><marker orient="auto" id="mscgen_js-svg-__svgmethod-black" class="arrow-marker" viewBox="0 0 10 10" refX="9" refY="3" markerUnits="strokeWidth" markerWidth="10" markerHeight="10"><polygon points="1,1 9,3 1,5" class="arrow-style" stroke="black" fill="black"></polygon></marker><marker orient="auto" id="mscgen_js-svg-__svgmethod-l-black" class="arrow-marker" viewBox="0 0 10 10" refX="9" refY="3" markerUnits="strokeWidth" markerWidth="10" markerHeight="10"><polygon points="17,1 9,3 17,5" class="arrow-style" stroke="black" fill="black"></polygon></marker><marker orient="auto" id="mscgen_js-svg-__svgmethod-blue" class="arrow-marker" viewBox="0 0 10 10" refX="9" refY="3" markerUnits="strokeWidth" markerWidth="10" markerHeight="10"><polygon points="1,1 9,3 1,5" class="arrow-style" stroke="blue" fill="blue"></polygon></marker><marker orient="auto" id="mscgen_js-svg-__svgmethod-l-blue" class="arrow-marker" viewBox="0 0 10 10" refX="9" refY="3" markerUnits="strokeWidth" markerWidth="10" markerHeight="10"><polygon points="17,1 9,3 17,5" class="arrow-style" stroke="blue" fill="blue"></polygon></marker><marker orient="auto" id="mscgen_js-svg-__svgmethod-red" class="arrow-marker" viewBox="0 0 10 10" refX="9" refY="3" markerUnits="strokeWidth" markerWidth="10" markerHeight="10"><polygon points="1,1 9,3 1,5" class="arrow-style" stroke="red" fill="red"></polygon></marker><marker orient="auto" id="mscgen_js-svg-__svgmethod-l-red" class="arrow-marker" viewBox="0 0 10 10" refX="9" refY="3" markerUnits="strokeWidth" markerWidth="10" markerHeight="10"><polygon points="17,1 9,3 17,5" class="arrow-style" stroke="red" fill="red"></polygon></marker></defs><g id="mscgen_js-svg-__svg__body" transform="translate(53,3) scale(1,1)"><g id="mscgen_js-svg-__svg__background"><rect width="976" height="386" x="-53" y="-3" class="bglayer"></rect></g><g id="mscgen_js-svg-__svg__arcspanlayer"></g><g id="mscgen_js-svg-__svg__lifelinelayer"><line x1="75" y1="38" x2="75" y2="76" class="arcrow"></line><line x1="315" y1="38" x2="315" y2="76" class="arcrow"></line><line x1="555" y1="38" x2="555" y2="76" class="arcrow"></line><line x1="795" y1="38" x2="795" y2="76" class="arcrow"></line><line x1="75" y1="76" x2="75" y2="114" class="arcrow"></line><line x1="315" y1="76" x2="315" y2="114" class="arcrow"></line><line x1="555" y1="76" x2="555" y2="114" class="arcrow"></line><line x1="795" y1="76" x2="795" y2="114" class="arcrow"></line><line x1="75" y1="114" x2="75" y2="152" class="arcrow"></line><line x1="315" y1="114" x2="315" y2="152" class="arcrow"></line><line x1="555" y1="114" x2="555" y2="152" class="arcrow"></line><line x1="795" y1="114" x2="795" y2="152" class="arcrow"></line><line x1="75" y1="152" x2="75" y2="190" class="arcrow"></line><line x1="315" y1="152" x2="315" y2="190" class="arcrow"></line><line x1="555" y1="152" x2="555" y2="190" class="arcrow"></line><line x1="795" y1="152" x2="795" y2="190" class="arcrow"></line><line x1="75" y1="190" x2="75" y2="228" class="arcrow"></line><line x1="315" y1="190" x2="315" y2="228" class="arcrow"></line><line x1="555" y1="190" x2="555" y2="228" class="arcrow"></line><line x1="795" y1="190" x2="795" y2="228" class="arcrow"></line><line x1="75" y1="228" x2="75" y2="266" class="arcrow"></line><line x1="315" y1="228" x2="315" y2="266" class="arcrow"></line><line x1="555" y1="228" x2="555" y2="266" class="arcrow"></line><line x1="795" y1="228" x2="795" y2="266" class="arcrow"></line><line x1="75" y1="266" x2="75" y2="304" class="arcrow"></line><line x1="315" y1="266" x2="315" y2="304" class="arcrow"></line><line x1="555" y1="266" x2="555" y2="304" class="arcrow"></line><line x1="795" y1="266" x2="795" y2="304" class="arcrow"></line><line x1="75" y1="304" x2="75" y2="342" class="arcrow"></line><line x1="315" y1="304" x2="315" y2="342" class="arcrow"></line><line x1="555" y1="304" x2="555" y2="342" class="arcrow"></line><line x1="795" y1="304" x2="795" y2="342" class="arcrow"></line><line x1="75" y1="342" x2="75" y2="380" class="arcrow"></line><line x1="315" y1="342" x2="315" y2="380" class="arcrow"></line><line x1="555" y1="342" x2="555" y2="380" class="arcrow"></line><line x1="795" y1="342" x2="795" y2="380" class="arcrow"></line></g><g id="mscgen_js-svg-__svg__sequencelayer"><g id="mscgen_js-svg-__svgentities"><g><rect width="150" height="38" class="entity" style="fill:lime;"></rect><g><text x="75" y="22.5" class="entity-text "><tspan>Client</tspan></text></g></g><g><rect width="150" height="38" x="240" class="entity" style="fill:cyan;"></rect><g><text x="315" y="22.5" class="entity-text "><tspan>Rails</tspan></text></g></g><g><rect width="150" height="38" x="480" class="entity" style="fill:orange;"></rect><g><text x="555" y="22.5" class="entity-text "><tspan>EtagCaching</tspan></text></g></g><g><rect width="150" height="38" x="720" class="entity" style="fill:white;"></rect><g><text x="795" y="22.5" class="entity-text "><tspan>Redis</tspan></text></g></g></g><g><line x1="75" y1="95" x2="315" y2="95" class="arc directional method" style="stroke:black" marker-end="url(#mscgen_js-svg-__svgmethod-black)"></line><g><rect width="134.06" height="14" x="127.97" y="79.5" class="label-text-background"></rect><text x="195" y="90.5" class="directional-text method-text "><tspan>GET /projects/5/pipelines</tspan></text></g></g><g><line x1="315" y1="133" x2="555" y2="133" class="arc directional method" style="stroke:black" marker-end="url(#mscgen_js-svg-__svgmethod-black)"></line><g><rect width="134.06" height="14" x="367.97" y="117.5" class="label-text-background"></rect><text x="435" y="128.5" class="directional-text method-text "><tspan>GET /projects/5/pipelines</tspan></text></g></g><g><line x1="555" y1="171" x2="795" y2="171" class="arc directional method" style="stroke:black" marker-end="url(#mscgen_js-svg-__svgmethod-black)"></line><g><rect width="135.64" height="14" x="607.17" y="155.5" class="label-text-background"></rect><text x="675" y="166.5" class="directional-text method-text "><tspan>read(key = 'GET &lt;Etag&gt;')</tspan></text></g></g><g><line x1="795" y1="209" x2="555" y2="209" class="arc directional method" style="stroke:red" marker-end="url(#mscgen_js-svg-__svgmethod-red)"></line><g><rect width="60.02" height="14" x="644.98" y="193.5" class="label-text-background"></rect><text x="675" y="204.5" class="directional-text method-text " style="fill:red;"><tspan>cache miss</tspan></text></g></g><g></g><g><line x1="555" y1="285" x2="795" y2="285" class="arc directional method" style="stroke:black" marker-end="url(#mscgen_js-svg-__svgmethod-black)"></line><g><rect width="103.94" height="14" x="623.02" y="269.5" class="label-text-background"></rect><text x="675" y="280.5" class="directional-text method-text "><tspan>write('&lt;New Etag&gt;')</tspan></text></g></g><g><line x1="555" y1="323" x2="315" y2="323" class="arc directional method" style="stroke:black" marker-end="url(#mscgen_js-svg-__svgmethod-black)"></line><g><rect width="134.06" height="14" x="367.97" y="307.5" class="label-text-background"></rect><text x="435" y="318.5" class="directional-text method-text "><tspan>GET /projects/5/pipelines</tspan></text></g></g><g><line x1="315" y1="361" x2="75" y2="361" class="arc directional method" style="stroke:blue" marker-end="url(#mscgen_js-svg-__svgmethod-blue)"></line><g><rect width="130.72" height="14" x="129.63" y="345.5" class="label-text-background"></rect><text x="195" y="356.5" class="directional-text method-text " style="fill:blue;"><tspan>JSON response w/ ETag</tspan></text></g></g></g><g id="mscgen_js-svg-__svg__notelayer"></g><g id="mscgen_js-svg-__svg__watermark"></g></g></svg> \ No newline at end of file
diff --git a/doc/development/polling.md b/doc/development/polling.md
index a7f2962acf0..e5a717f712b 100644
--- a/doc/development/polling.md
+++ b/doc/development/polling.md
@@ -22,6 +22,9 @@ Instead you should use polling mechanism with ETag caching in Redis.
## How it works
+![Cache miss](img/cache-miss.svg)
+![Cache hit](img/cache-hit.svg)
+
1. Whenever a resource changes we generate a random value and store it in
Redis.
1. When a client makes a request we set the `ETag` response header to the value
diff --git a/doc/install/requirements.md b/doc/install/requirements.md
index 7b586138f42..35586091f74 100644
--- a/doc/install/requirements.md
+++ b/doc/install/requirements.md
@@ -129,6 +129,9 @@ If you want to run the database separately, expect a size of about 1 MB per user
### PostgreSQL Requirements
+As of GitLab 9.0, PostgreSQL 9.6 is recommended. Lower versions of PostgreSQL
+may work but primary testing and developement takes place using PostgreSQL 9.6.
+
Users using PostgreSQL must ensure the `pg_trgm` extension is loaded into every
GitLab database. This extension can be enabled (using a PostgreSQL super user)
by running the following query for every database:
diff --git a/doc/update/patch_versions.md b/doc/update/patch_versions.md
index 154a0f817da..1c493599cf8 100644
--- a/doc/update/patch_versions.md
+++ b/doc/update/patch_versions.md
@@ -57,7 +57,7 @@ sudo -u git -H bundle clean
sudo -u git -H bundle exec rake db:migrate RAILS_ENV=production
# Clean up assets and cache
-sudo -u git -H bundle exec rake gitlab:assets:clean gitlab:assets:compile cache:clear RAILS_ENV=production
+sudo -u git -H bundle exec rake yarn:install gitlab:assets:clean gitlab:assets:compile cache:clear RAILS_ENV=production
```
### 4. Update gitlab-workhorse to the corresponding version
diff --git a/doc/user/project/integrations/img/jira_project_settings.png b/doc/user/project/integrations/img/jira_project_settings.png
new file mode 100644
index 00000000000..cb6a6ba14ce
--- /dev/null
+++ b/doc/user/project/integrations/img/jira_project_settings.png
Binary files differ
diff --git a/doc/user/project/integrations/jira.md b/doc/user/project/integrations/jira.md
index 4c64d1e0907..e02f81fd972 100644
--- a/doc/user/project/integrations/jira.md
+++ b/doc/user/project/integrations/jira.md
@@ -157,6 +157,11 @@ the same goal:
where `PROJECT-1` is the issue ID of the JIRA project.
+>**Note:**
+- Only commits and merges into the project's default branch (usually **master**) will
+ close an issue in Jira. You can change your projects default branch under
+ [project settings](img/jira_project_settings.png).
+
### JIRA issue closing example
Let's consider the following example:
diff --git a/features/dashboard/dashboard.feature b/features/dashboard/dashboard.feature
index b1d5e4a7acb..1af4d46dec9 100644
--- a/features/dashboard/dashboard.feature
+++ b/features/dashboard/dashboard.feature
@@ -63,7 +63,8 @@ Feature: Dashboard
@javascript
Scenario: Visiting Project's merge requests after sorting
- Given I visit dashboard merge requests page
+ Given project "Shop" has a "Bugfix MR" merge request open
+ And I visit dashboard merge requests page
And I sort the list by "Oldest updated"
And I visit project "Shop" merge requests page
Then The list should be sorted by "Oldest updated"
diff --git a/features/project/issues/issues.feature b/features/project/issues/issues.feature
index b2b4fe72220..27fa67c1843 100644
--- a/features/project/issues/issues.feature
+++ b/features/project/issues/issues.feature
@@ -56,14 +56,16 @@ Feature: Project Issues
@javascript
Scenario: Visiting Merge Requests after being sorted the list
- Given I visit project "Shop" issues page
+ Given project "Shop" has a "Bugfix MR" merge request open
+ And I visit project "Shop" issues page
And I sort the list by "Oldest updated"
And I visit project "Shop" merge requests page
Then The list should be sorted by "Oldest updated"
@javascript
Scenario: Visiting Merge Requests from a differente Project after sorting
- Given I visit project "Shop" merge requests page
+ Given project "Shop" has a "Bugfix MR" merge request open
+ And I visit project "Shop" merge requests page
And I sort the list by "Oldest updated"
And I visit dashboard merge requests page
Then The list should be sorted by "Oldest updated"
diff --git a/features/steps/project/fork.rb b/features/steps/project/fork.rb
index 79db9728227..7591e7d5612 100644
--- a/features/steps/project/fork.rb
+++ b/features/steps/project/fork.rb
@@ -42,8 +42,7 @@ class Spinach::Features::ProjectFork < Spinach::FeatureSteps
end
step 'I click link "New merge request"' do
- expect(page).to have_content(/new merge request/i)
- click_link "New Merge Request"
+ page.has_link?('New Merge Request') ? click_link("New Merge Request") : click_link('New merge request')
end
step 'I should see the new merge request page for my namespace' do
diff --git a/features/steps/project/forked_merge_requests.rb b/features/steps/project/forked_merge_requests.rb
index c0827ff8fc7..ef1bb453615 100644
--- a/features/steps/project/forked_merge_requests.rb
+++ b/features/steps/project/forked_merge_requests.rb
@@ -16,7 +16,7 @@ class Spinach::Features::ProjectForkedMergeRequests < Spinach::FeatureSteps
end
step 'I click link "New Merge Request"' do
- click_link "New Merge Request"
+ page.has_link?('New Merge Request') ? click_link("New Merge Request") : click_link('New merge request')
end
step 'I should see merge request "Merge Request On Forked Project"' do
diff --git a/features/steps/project/merge_requests.rb b/features/steps/project/merge_requests.rb
index c9c4f537fad..5510c65265a 100644
--- a/features/steps/project/merge_requests.rb
+++ b/features/steps/project/merge_requests.rb
@@ -14,7 +14,7 @@ class Spinach::Features::ProjectMergeRequests < Spinach::FeatureSteps
end
step 'I click link "New Merge Request"' do
- click_link "New Merge Request"
+ page.has_link?('New Merge Request') ? click_link("New Merge Request") : click_link('New merge request')
end
step 'I click link "Bug NS-04"' do
diff --git a/features/steps/shared/project.rb b/features/steps/shared/project.rb
index 345a28f27dc..782009a32a7 100644
--- a/features/steps/shared/project.rb
+++ b/features/steps/shared/project.rb
@@ -273,6 +273,10 @@ module SharedProject
@project.update(public_builds: false)
end
+ step 'project "Shop" has a "Bugfix MR" merge request open' do
+ create(:merge_request, title: "Bugfix MR", target_project: project, source_project: project, author: project.users.first)
+ end
+
def user_owns_project(user_name:, project_name:, visibility: :private)
user = user_exists(user_name, username: user_name.gsub(/\s/, '').underscore)
project = Project.find_by(name: project_name)
diff --git a/lib/api/helpers/runner.rb b/lib/api/helpers/runner.rb
index 74848a6e144..1369b021ea4 100644
--- a/lib/api/helpers/runner.rb
+++ b/lib/api/helpers/runner.rb
@@ -50,10 +50,14 @@ module API
forbidden!('Job has been erased!') if job.erased?
end
- def authenticate_job!(job)
+ def authenticate_job!
+ job = Ci::Build.find_by_id(params[:id])
+
validate_job!(job) do
forbidden! unless job_token_valid?(job)
end
+
+ job
end
def job_token_valid?(job)
diff --git a/lib/api/milestones.rb b/lib/api/milestones.rb
index e7ab82f08db..a3ea619a2fb 100644
--- a/lib/api/milestones.rb
+++ b/lib/api/milestones.rb
@@ -139,7 +139,7 @@ module API
finder_params = {
project_id: user_project.id,
- milestone_id: milestone.id,
+ milestone_title: milestone.title,
sort: 'position_asc'
}
diff --git a/lib/api/runner.rb b/lib/api/runner.rb
index 4c9db2c8716..d288369e362 100644
--- a/lib/api/runner.rb
+++ b/lib/api/runner.rb
@@ -113,8 +113,7 @@ module API
optional :state, type: String, desc: %q(Job's status: success, failed)
end
put '/:id' do
- job = Ci::Build.find_by_id(params[:id])
- authenticate_job!(job)
+ job = authenticate_job!
job.update_attributes(trace: params[:trace]) if params[:trace]
@@ -140,8 +139,7 @@ module API
optional :token, type: String, desc: %q(Job's authentication token)
end
patch '/:id/trace' do
- job = Ci::Build.find_by_id(params[:id])
- authenticate_job!(job)
+ job = authenticate_job!
error!('400 Missing header Content-Range', 400) unless request.headers.has_key?('Content-Range')
content_range = request.headers['Content-Range']
@@ -175,8 +173,7 @@ module API
require_gitlab_workhorse!
Gitlab::Workhorse.verify_api_request!(headers)
- job = Ci::Build.find_by_id(params[:id])
- authenticate_job!(job)
+ job = authenticate_job!
forbidden!('Job is not running') unless job.running?
if params[:filesize]
@@ -212,8 +209,7 @@ module API
not_allowed! unless Gitlab.config.artifacts.enabled
require_gitlab_workhorse!
- job = Ci::Build.find_by_id(params[:id])
- authenticate_job!(job)
+ job = authenticate_job!
forbidden!('Job is not running!') unless job.running?
artifacts_upload_path = ArtifactUploader.artifacts_upload_path
@@ -245,8 +241,7 @@ module API
optional :token, type: String, desc: %q(Job's authentication token)
end
get '/:id/artifacts' do
- job = Ci::Build.find_by_id(params[:id])
- authenticate_job!(job)
+ job = authenticate_job!
artifacts_file = job.artifacts_file
unless artifacts_file.file_storage?
diff --git a/lib/api/services.rb b/lib/api/services.rb
index 4e0c9cb1f63..6802a99311e 100644
--- a/lib/api/services.rb
+++ b/lib/api/services.rb
@@ -562,8 +562,14 @@ module API
desc: 'URL to the mock service'
}
]
+ services['mock-deployment'] = []
+ services['mock-monitoring'] = []
- service_classes << MockCiService
+ service_classes += [
+ MockCiService,
+ MockDeploymentService,
+ MockMonitoringService,
+ ]
end
trigger_services = {
diff --git a/lib/ci/api/builds.rb b/lib/ci/api/builds.rb
index 746e76a1b1f..95cc6308c3b 100644
--- a/lib/ci/api/builds.rb
+++ b/lib/ci/api/builds.rb
@@ -86,8 +86,7 @@ module Ci
# Example Request:
# PATCH /builds/:id/trace.txt
patch ":id/trace.txt" do
- build = Ci::Build.find_by_id(params[:id])
- authenticate_build!(build)
+ build = authenticate_build!
error!('400 Missing header Content-Range', 400) unless request.headers.has_key?('Content-Range')
content_range = request.headers['Content-Range']
@@ -117,8 +116,7 @@ module Ci
require_gitlab_workhorse!
Gitlab::Workhorse.verify_api_request!(headers)
not_allowed! unless Gitlab.config.artifacts.enabled
- build = Ci::Build.find_by_id(params[:id])
- authenticate_build!(build)
+ build = authenticate_build!
forbidden!('build is not running') unless build.running?
if params[:filesize]
@@ -154,8 +152,7 @@ module Ci
post ":id/artifacts" do
require_gitlab_workhorse!
not_allowed! unless Gitlab.config.artifacts.enabled
- build = Ci::Build.find_by_id(params[:id])
- authenticate_build!(build)
+ build = authenticate_build!
forbidden!('Build is not running!') unless build.running?
artifacts_upload_path = ArtifactUploader.artifacts_upload_path
@@ -189,8 +186,7 @@ module Ci
# Example Request:
# GET /builds/:id/artifacts
get ":id/artifacts" do
- build = Ci::Build.find_by_id(params[:id])
- authenticate_build!(build)
+ build = authenticate_build!
artifacts_file = build.artifacts_file
unless artifacts_file.file_storage?
@@ -214,8 +210,7 @@ module Ci
# Example Request:
# DELETE /builds/:id/artifacts
delete ":id/artifacts" do
- build = Ci::Build.find_by_id(params[:id])
- authenticate_build!(build)
+ build = authenticate_build!
status(200)
build.erase_artifacts!
diff --git a/lib/ci/api/helpers.rb b/lib/ci/api/helpers.rb
index 996990b464f..5109dc9670f 100644
--- a/lib/ci/api/helpers.rb
+++ b/lib/ci/api/helpers.rb
@@ -13,10 +13,14 @@ module Ci
forbidden! unless current_runner
end
- def authenticate_build!(build)
+ def authenticate_build!
+ build = Ci::Build.find_by_id(params[:id])
+
validate_build!(build) do
forbidden! unless build_token_valid?(build)
end
+
+ build
end
def validate_build!(build)
diff --git a/lib/gitlab/import_export/hash_util.rb b/lib/gitlab/import_export/hash_util.rb
new file mode 100644
index 00000000000..d4adeeb3797
--- /dev/null
+++ b/lib/gitlab/import_export/hash_util.rb
@@ -0,0 +1,25 @@
+module Gitlab
+ module ImportExport
+ class HashUtil
+ def self.deep_symbolize_array!(array)
+ return if array.blank?
+
+ array.map! do |hash|
+ hash.deep_symbolize_keys!
+
+ yield(hash) if block_given?
+
+ hash
+ end
+ end
+
+ def self.deep_symbolize_array_with_date!(array)
+ self.deep_symbolize_array!(array) do |hash|
+ hash.select { |k, _v| k.to_s.end_with?('_date') }.each do |key, value|
+ hash[key] = Time.zone.parse(value)
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/import_export/import_export.yml b/lib/gitlab/import_export/import_export.yml
index ab74c8782f6..f69288f7d67 100644
--- a/lib/gitlab/import_export/import_export.yml
+++ b/lib/gitlab/import_export/import_export.yml
@@ -89,3 +89,5 @@ methods:
- :type
merge_request_diff:
- :utf8_st_diffs
+ merge_requests:
+ - :diff_head_sha
diff --git a/lib/gitlab/import_export/importer.rb b/lib/gitlab/import_export/importer.rb
index 063ce74ecad..fbdd74788bc 100644
--- a/lib/gitlab/import_export/importer.rb
+++ b/lib/gitlab/import_export/importer.rb
@@ -9,7 +9,7 @@ module Gitlab
end
def execute
- if import_file && check_version! && [project_tree, avatar_restorer, repo_restorer, wiki_restorer, uploads_restorer].all?(&:restore)
+ if import_file && check_version! && [repo_restorer, wiki_restorer, project_tree, avatar_restorer, uploads_restorer].all?(&:restore)
project_tree.restored_project
else
raise Projects::ImportService::Error.new(@shared.errors.join(', '))
diff --git a/lib/gitlab/import_export/merge_request_parser.rb b/lib/gitlab/import_export/merge_request_parser.rb
new file mode 100644
index 00000000000..c20adc20bfd
--- /dev/null
+++ b/lib/gitlab/import_export/merge_request_parser.rb
@@ -0,0 +1,41 @@
+module Gitlab
+ module ImportExport
+ class MergeRequestParser
+ FORKED_PROJECT_ID = -1
+
+ def initialize(project, diff_head_sha, merge_request, relation_hash)
+ @project = project
+ @diff_head_sha = diff_head_sha
+ @merge_request = merge_request
+ @relation_hash = relation_hash
+ end
+
+ def parse!
+ if fork_merge_request? && @diff_head_sha
+ @merge_request.source_project_id = @relation_hash['project_id']
+
+ fetch_ref unless branch_exists?(@merge_request.source_branch)
+ create_target_branch unless branch_exists?(@merge_request.target_branch)
+ end
+
+ @merge_request
+ end
+
+ def create_target_branch
+ @project.repository.create_branch(@merge_request.target_branch, @merge_request.target_branch_sha)
+ end
+
+ def fetch_ref
+ @project.repository.fetch_ref(@project.repository.path, @diff_head_sha, @merge_request.source_branch)
+ end
+
+ def branch_exists?(branch_name)
+ @project.repository.branch_exists?(branch_name)
+ end
+
+ def fork_merge_request?
+ @relation_hash['source_project_id'] == FORKED_PROJECT_ID
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/import_export/project_tree_restorer.rb b/lib/gitlab/import_export/project_tree_restorer.rb
index cda6ddf0443..df21ff22216 100644
--- a/lib/gitlab/import_export/project_tree_restorer.rb
+++ b/lib/gitlab/import_export/project_tree_restorer.rb
@@ -119,7 +119,7 @@ module Gitlab
relation_hash: parsed_relation_hash(relation_hash),
members_mapper: members_mapper,
user: @user,
- project_id: restored_project.id)
+ project: restored_project)
end.compact
relation_hash_list.is_a?(Array) ? relation_array : relation_array.first
diff --git a/lib/gitlab/import_export/relation_factory.rb b/lib/gitlab/import_export/relation_factory.rb
index d44563333a5..fb43e7ccdbb 100644
--- a/lib/gitlab/import_export/relation_factory.rb
+++ b/lib/gitlab/import_export/relation_factory.rb
@@ -29,11 +29,12 @@ module Gitlab
new(*args).create
end
- def initialize(relation_sym:, relation_hash:, members_mapper:, user:, project_id:)
+ def initialize(relation_sym:, relation_hash:, members_mapper:, user:, project:)
@relation_name = OVERRIDES[relation_sym] || relation_sym
- @relation_hash = relation_hash.except('noteable_id').merge('project_id' => project_id)
+ @relation_hash = relation_hash.except('noteable_id').merge('project_id' => project.id)
@members_mapper = members_mapper
@user = user
+ @project = project
@imported_object_retries = 0
end
@@ -66,7 +67,7 @@ module Gitlab
remove_encrypted_attributes!
@relation_hash['data'].deep_symbolize_keys! if @relation_name == :events && @relation_hash['data']
- set_st_diffs if @relation_name == :merge_request_diff
+ set_st_diff_commits if @relation_name == :merge_request_diff
end
def update_user_references
@@ -105,6 +106,8 @@ module Gitlab
imported_object do |object|
object.commit_id = nil
end
+ elsif @relation_name == :merge_requests
+ MergeRequestParser.new(@project, @relation_hash.delete('diff_head_sha'), imported_object, @relation_hash).parse!
else
imported_object
end
@@ -115,7 +118,7 @@ module Gitlab
# If source and target are the same, populate them with the new project ID.
if @relation_hash['source_project_id']
- @relation_hash['source_project_id'] = same_source_and_target? ? project_id : -1
+ @relation_hash['source_project_id'] = same_source_and_target? ? project_id : MergeRequestParser::FORKED_PROJECT_ID
end
# project_id may not be part of the export, but we always need to populate it if required.
@@ -166,6 +169,7 @@ module Gitlab
def imported_object
yield(existing_or_new_object) if block_given?
existing_or_new_object.importing = true if existing_or_new_object.respond_to?(:importing)
+
existing_or_new_object
rescue ActiveRecord::RecordNotUnique
# as the operation is not atomic, retry in the unlikely scenario an INSERT is
@@ -188,8 +192,11 @@ module Gitlab
relation_class: relation_class)
end
- def set_st_diffs
+ def set_st_diff_commits
@relation_hash['st_diffs'] = @relation_hash.delete('utf8_st_diffs')
+
+ HashUtil.deep_symbolize_array!(@relation_hash['st_diffs'])
+ HashUtil.deep_symbolize_array_with_date!(@relation_hash['st_commits'])
end
def existing_or_new_object
diff --git a/lib/gitlab/project_search_results.rb b/lib/gitlab/project_search_results.rb
index db325c00705..0b8959f2fb9 100644
--- a/lib/gitlab/project_search_results.rb
+++ b/lib/gitlab/project_search_results.rb
@@ -62,7 +62,7 @@ module Gitlab
data << line.sub(ref, '').sub(filename, '').sub(/^:-\d+-/, '').sub(/^::\d+:/, '')
end
- OpenStruct.new(
+ FoundBlob.new(
filename: filename,
basename: basename,
ref: ref,
diff --git a/lib/gitlab/search_results.rb b/lib/gitlab/search_results.rb
index ccfa517e04b..efe8095beea 100644
--- a/lib/gitlab/search_results.rb
+++ b/lib/gitlab/search_results.rb
@@ -1,5 +1,26 @@
module Gitlab
class SearchResults
+ class FoundBlob
+ attr_reader :id, :filename, :basename, :ref, :startline, :data
+
+ def initialize(opts = {})
+ @id = opts.fetch(:id, nil)
+ @filename = opts.fetch(:filename, nil)
+ @basename = opts.fetch(:basename, nil)
+ @ref = opts.fetch(:ref, nil)
+ @startline = opts.fetch(:startline, nil)
+ @data = opts.fetch(:data, nil)
+ end
+
+ def path
+ filename
+ end
+
+ def no_highlighting?
+ false
+ end
+ end
+
attr_reader :current_user, :query
# Limit search results by passed projects
diff --git a/lib/gitlab/shell.rb b/lib/gitlab/shell.rb
index b631ef11ce7..36a871e5bbc 100644
--- a/lib/gitlab/shell.rb
+++ b/lib/gitlab/shell.rb
@@ -35,7 +35,7 @@ module Gitlab
end
def strip_key(key)
- key.split(/ /)[0, 2].join(' ')
+ key.split(/[ ]+/)[0, 2].join(' ')
end
private
diff --git a/lib/gitlab/workhorse.rb b/lib/gitlab/workhorse.rb
index d0637f8b394..08011301d3c 100644
--- a/lib/gitlab/workhorse.rb
+++ b/lib/gitlab/workhorse.rb
@@ -35,7 +35,8 @@ module Gitlab
feature_enabled = case action.to_s
when 'git_receive_pack'
- Gitlab::GitalyClient.feature_enabled?(:post_receive_pack)
+ # Disabled for now, see https://gitlab.com/gitlab-org/gitaly/issues/172
+ false
when 'git_upload_pack'
Gitlab::GitalyClient.feature_enabled?(:post_upload_pack)
when 'info_refs'
@@ -44,7 +45,12 @@ module Gitlab
raise "Unsupported action: #{action}"
end
- params[:GitalySocketPath] = URI(address).path if feature_enabled
+ if feature_enabled
+ params[:GitalyAddress] = address
+ # TODO deprecate GitalySocketPath once GITLAB_WORKHORSE_VERSION points
+ # to a version that supports GitalyAddress.
+ params[:GitalySocketPath] = URI(address).path
+ end
end
params
diff --git a/spec/controllers/import/bitbucket_controller_spec.rb b/spec/controllers/import/bitbucket_controller_spec.rb
index 51f23e4eeb9..010e3180ea4 100644
--- a/spec/controllers/import/bitbucket_controller_spec.rb
+++ b/spec/controllers/import/bitbucket_controller_spec.rb
@@ -200,5 +200,72 @@ describe Import::BitbucketController do
end
end
end
+
+ context 'user has chosen an existing nested namespace and name for the project' do
+ let(:parent_namespace) { create(:namespace, name: 'foo', owner: user) }
+ let(:nested_namespace) { create(:namespace, name: 'bar', parent: parent_namespace, owner: user) }
+ let(:test_name) { 'test_name' }
+
+ it 'takes the selected namespace and name' do
+ expect(Gitlab::BitbucketImport::ProjectCreator).
+ to receive(:new).with(bitbucket_repo, test_name, nested_namespace, user, access_params).
+ and_return(double(execute: true))
+
+ post :create, { target_namespace: nested_namespace.full_path, new_name: test_name, format: :js }
+ end
+ end
+
+ context 'user has chosen a non-existent nested namespaces and name for the project' do
+ let(:test_name) { 'test_name' }
+
+ it 'takes the selected namespace and name' do
+ expect(Gitlab::BitbucketImport::ProjectCreator).
+ to receive(:new).with(bitbucket_repo, test_name, kind_of(Namespace), user, access_params).
+ and_return(double(execute: true))
+
+ post :create, { target_namespace: 'foo/bar', new_name: test_name, format: :js }
+ end
+
+ it 'creates the namespaces' do
+ allow(Gitlab::BitbucketImport::ProjectCreator).
+ to receive(:new).with(bitbucket_repo, test_name, kind_of(Namespace), user, access_params).
+ and_return(double(execute: true))
+
+ expect { post :create, { target_namespace: 'foo/bar', new_name: test_name, format: :js } }
+ .to change { Namespace.count }.by(2)
+ end
+
+ it 'new namespace has the right parent' do
+ allow(Gitlab::BitbucketImport::ProjectCreator).
+ to receive(:new).with(bitbucket_repo, test_name, kind_of(Namespace), user, access_params).
+ and_return(double(execute: true))
+
+ post :create, { target_namespace: 'foo/bar', new_name: test_name, format: :js }
+
+ expect(Namespace.find_by_path_or_name('bar').parent.path).to eq('foo')
+ end
+ end
+
+ context 'user has chosen existent and non-existent nested namespaces and name for the project' do
+ let(:test_name) { 'test_name' }
+ let!(:parent_namespace) { create(:namespace, name: 'foo', owner: user) }
+
+ it 'takes the selected namespace and name' do
+ expect(Gitlab::BitbucketImport::ProjectCreator).
+ to receive(:new).with(bitbucket_repo, test_name, kind_of(Namespace), user, access_params).
+ and_return(double(execute: true))
+
+ post :create, { target_namespace: 'foo/foobar/bar', new_name: test_name, format: :js }
+ end
+
+ it 'creates the namespaces' do
+ allow(Gitlab::BitbucketImport::ProjectCreator).
+ to receive(:new).with(bitbucket_repo, test_name, kind_of(Namespace), user, access_params).
+ and_return(double(execute: true))
+
+ expect { post :create, { target_namespace: 'foo/foobar/bar', new_name: test_name, format: :js } }
+ .to change { Namespace.count }.by(2)
+ end
+ end
end
end
diff --git a/spec/controllers/import/gitlab_controller_spec.rb b/spec/controllers/import/gitlab_controller_spec.rb
index 3f73ea000ae..2dbb89219d0 100644
--- a/spec/controllers/import/gitlab_controller_spec.rb
+++ b/spec/controllers/import/gitlab_controller_spec.rb
@@ -174,6 +174,72 @@ describe Import::GitlabController do
end
end
end
+
+ context 'user has chosen an existing nested namespace for the project' do
+ let(:parent_namespace) { create(:namespace, name: 'foo', owner: user) }
+ let(:nested_namespace) { create(:namespace, name: 'bar', parent: parent_namespace, owner: user) }
+
+ it 'takes the selected namespace and name' do
+ expect(Gitlab::GitlabImport::ProjectCreator).
+ to receive(:new).with(gitlab_repo, nested_namespace, user, access_params).
+ and_return(double(execute: true))
+
+ post :create, { target_namespace: nested_namespace.full_path, format: :js }
+ end
+ end
+
+ context 'user has chosen a non-existent nested namespaces for the project' do
+ let(:test_name) { 'test_name' }
+
+ it 'takes the selected namespace and name' do
+ expect(Gitlab::GitlabImport::ProjectCreator).
+ to receive(:new).with(gitlab_repo, kind_of(Namespace), user, access_params).
+ and_return(double(execute: true))
+
+ post :create, { target_namespace: 'foo/bar', format: :js }
+ end
+
+ it 'creates the namespaces' do
+ allow(Gitlab::GitlabImport::ProjectCreator).
+ to receive(:new).with(gitlab_repo, kind_of(Namespace), user, access_params).
+ and_return(double(execute: true))
+
+ expect { post :create, { target_namespace: 'foo/bar', format: :js } }
+ .to change { Namespace.count }.by(2)
+ end
+
+ it 'new namespace has the right parent' do
+ allow(Gitlab::GitlabImport::ProjectCreator).
+ to receive(:new).with(gitlab_repo, kind_of(Namespace), user, access_params).
+ and_return(double(execute: true))
+
+ post :create, { target_namespace: 'foo/bar', format: :js }
+
+ expect(Namespace.find_by_path_or_name('bar').parent.path).to eq('foo')
+ end
+ end
+
+ context 'user has chosen existent and non-existent nested namespaces and name for the project' do
+ let(:test_name) { 'test_name' }
+ let!(:parent_namespace) { create(:namespace, name: 'foo', owner: user) }
+
+ it 'takes the selected namespace and name' do
+ expect(Gitlab::GitlabImport::ProjectCreator).
+ to receive(:new).with(gitlab_repo, kind_of(Namespace), user, access_params).
+ and_return(double(execute: true))
+
+ post :create, { target_namespace: 'foo/foobar/bar', format: :js }
+ end
+
+ it 'creates the namespaces' do
+ allow(Gitlab::GitlabImport::ProjectCreator).
+ to receive(:new).with(gitlab_repo, kind_of(Namespace), user, access_params).
+ and_return(double(execute: true))
+
+ expect { post :create, { target_namespace: 'foo/foobar/bar', format: :js } }
+ .to change { Namespace.count }.by(2)
+ end
+ end
end
end
end
diff --git a/spec/features/admin/admin_health_check_spec.rb b/spec/features/admin/admin_health_check_spec.rb
index f7e49a56deb..523afa2318f 100644
--- a/spec/features/admin/admin_health_check_spec.rb
+++ b/spec/features/admin/admin_health_check_spec.rb
@@ -2,7 +2,6 @@ require 'spec_helper'
feature "Admin Health Check", feature: true do
include StubENV
- include WaitForAjax
before do
stub_env('IN_MEMORY_APPLICATION_SETTINGS', 'false')
@@ -24,11 +23,12 @@ feature "Admin Health Check", feature: true do
expect(page).to have_selector('#health-check-token', text: token)
end
- describe 'reload access token', js: true do
+ describe 'reload access token' do
it 'changes the access token' do
orig_token = current_application_settings.health_check_access_token
click_button 'Reset health check access token'
- wait_for_ajax
+
+ expect(page).to have_content('New health check access token has been generated!')
expect(find('#health-check-token').text).not_to eq orig_token
end
end
diff --git a/spec/features/dashboard/merge_requests_spec.rb b/spec/features/dashboard/merge_requests_spec.rb
new file mode 100644
index 00000000000..508ca38d7e5
--- /dev/null
+++ b/spec/features/dashboard/merge_requests_spec.rb
@@ -0,0 +1,32 @@
+require 'spec_helper'
+
+describe 'Dashboard Merge Requests' do
+ let(:current_user) { create :user }
+ let(:project) do
+ create(:empty_project) do |project|
+ project.add_master(current_user)
+ end
+ end
+
+ before do
+ login_as(current_user)
+ end
+
+ it 'should show an empty state' do
+ visit merge_requests_dashboard_path(assignee_id: current_user.id)
+
+ expect(page).to have_selector('.empty-state')
+ end
+
+ context 'if there are merge requests' do
+ before do
+ create(:merge_request, assignee: current_user, source_project: project)
+
+ visit merge_requests_dashboard_path(assignee_id: current_user.id)
+ end
+
+ it 'should not show an empty state' do
+ expect(page).not_to have_selector('.empty-state')
+ end
+ end
+end
diff --git a/spec/features/groups/empty_states_spec.rb b/spec/features/groups/empty_states_spec.rb
new file mode 100644
index 00000000000..fef8e41bffe
--- /dev/null
+++ b/spec/features/groups/empty_states_spec.rb
@@ -0,0 +1,70 @@
+require 'spec_helper'
+
+feature 'Groups Merge Requests Empty States' do
+ let(:group) { create(:group) }
+ let(:user) { create(:group_member, :developer, user: create(:user), group: group ).user }
+
+ before do
+ login_as(user)
+ end
+
+ context 'group has a project' do
+ let(:project) { create(:empty_project, namespace: group) }
+
+ before do
+ project.add_master(user)
+ end
+
+ context 'the project has a merge request' do
+ before do
+ create(:merge_request, source_project: project)
+
+ visit merge_requests_group_path(group)
+ end
+
+ it 'should not display an empty state' do
+ expect(page).not_to have_selector('.empty-state')
+ end
+ end
+
+ context 'the project has no merge requests', :js do
+ before do
+ visit merge_requests_group_path(group)
+ end
+
+ it 'should display an empty state' do
+ expect(page).to have_selector('.empty-state')
+ end
+
+ it 'should show a new merge request button' do
+ within '.empty-state' do
+ expect(page).to have_content('New merge request')
+ end
+ end
+
+ it 'the new merge request button opens a project dropdown' do
+ within '.empty-state' do
+ find('.new-project-item-select-button').click
+ end
+
+ expect(page).to have_selector('.ajax-project-dropdown')
+ end
+ end
+ end
+
+ context 'group without a project' do
+ before do
+ visit merge_requests_group_path(group)
+ end
+
+ it 'should display an empty state' do
+ expect(page).to have_selector('.empty-state')
+ end
+
+ it 'should not show a new merge request button' do
+ within '.empty-state' do
+ expect(page).not_to have_link('New merge request')
+ end
+ end
+ end
+end
diff --git a/spec/features/issues/filtered_search/filter_issues_spec.rb b/spec/features/issues/filtered_search/filter_issues_spec.rb
index 004e335dd38..2f880c926e7 100644
--- a/spec/features/issues/filtered_search/filter_issues_spec.rb
+++ b/spec/features/issues/filtered_search/filter_issues_spec.rb
@@ -1,6 +1,7 @@
require 'spec_helper'
describe 'Filter issues', js: true, feature: true do
+ include Devise::Test::IntegrationHelpers
include FilteredSearchHelpers
include WaitForAjax
@@ -42,16 +43,17 @@ describe 'Filter issues', js: true, feature: true do
project.team << [user2, :master]
group.add_developer(user)
group.add_developer(user2)
- login_as(user)
- create(:issue, project: project)
- create(:issue, title: "Bug report 1", project: project)
- create(:issue, title: "Bug report 2", project: project)
- create(:issue, title: "issue with 'single quotes'", project: project)
- create(:issue, title: "issue with \"double quotes\"", project: project)
- create(:issue, title: "issue with !@\#{$%^&*()-+", project: project)
- create(:issue, title: "issue by assignee", project: project, milestone: milestone, author: user, assignee: user)
- create(:issue, title: "issue by assignee with searchTerm", project: project, milestone: milestone, author: user, assignee: user)
+ sign_in(user)
+
+ create(:issue, project: project)
+ create(:issue, project: project, title: "Bug report 1")
+ create(:issue, project: project, title: "Bug report 2")
+ create(:issue, project: project, title: "issue with 'single quotes'")
+ create(:issue, project: project, title: "issue with \"double quotes\"")
+ create(:issue, project: project, title: "issue with !@\#{$%^&*()-+")
+ create(:issue, project: project, title: "issue by assignee", milestone: milestone, author: user, assignee: user)
+ create(:issue, project: project, title: "issue by assignee with searchTerm", milestone: milestone, author: user, assignee: user)
issue = create(:issue,
title: "Bug 2",
diff --git a/spec/features/merge_requests/create_new_mr_spec.rb b/spec/features/merge_requests/create_new_mr_spec.rb
index f1ad4a55246..f36781167fb 100644
--- a/spec/features/merge_requests/create_new_mr_spec.rb
+++ b/spec/features/merge_requests/create_new_mr_spec.rb
@@ -15,7 +15,7 @@ feature 'Create New Merge Request', feature: true, js: true do
it 'selects the source branch sha when a tag with the same name exists' do
visit namespace_project_merge_requests_path(project.namespace, project)
- click_link 'New Merge Request'
+ click_link 'New merge request'
expect(page).to have_content('Source branch')
expect(page).to have_content('Target branch')
@@ -27,8 +27,8 @@ feature 'Create New Merge Request', feature: true, js: true do
it 'selects the target branch sha when a tag with the same name exists' do
visit namespace_project_merge_requests_path(project.namespace, project)
-
- click_link 'New Merge Request'
+
+ click_link 'New merge request'
expect(page).to have_content('Source branch')
expect(page).to have_content('Target branch')
@@ -42,7 +42,7 @@ feature 'Create New Merge Request', feature: true, js: true do
it 'generates a diff for an orphaned branch' do
visit namespace_project_merge_requests_path(project.namespace, project)
- click_link 'New Merge Request'
+ page.has_link?('New Merge Request') ? click_link("New Merge Request") : click_link('New merge request')
expect(page).to have_content('Source branch')
expect(page).to have_content('Target branch')
diff --git a/spec/features/milestones/milestones_spec.rb b/spec/features/milestones/milestones_spec.rb
index 8de9942c54e..2fa3e72ab08 100644
--- a/spec/features/milestones/milestones_spec.rb
+++ b/spec/features/milestones/milestones_spec.rb
@@ -76,6 +76,7 @@ describe 'Milestone draggable', feature: true, js: true do
create(:issue, params.merge(title: 'Foo', project: project, milestone: milestone))
visit namespace_project_milestone_path(project.namespace, project, milestone)
+ scroll_into_view('.milestone-content')
drag_to(selector: '.issues-sortable-list', list_to_index: 1)
wait_for_ajax
@@ -86,8 +87,13 @@ describe 'Milestone draggable', feature: true, js: true do
visit namespace_project_milestone_path(project.namespace, project, milestone)
page.find("a[href='#tab-merge-requests']").click
+ scroll_into_view('.milestone-content')
drag_to(selector: '.merge_requests-sortable-list', list_to_index: 1)
wait_for_ajax
end
+
+ def scroll_into_view(selector)
+ page.evaluate_script("document.querySelector('#{selector}').scrollIntoView();")
+ end
end
diff --git a/spec/features/projects/environments/environments_spec.rb b/spec/features/projects/environments/environments_spec.rb
index 641e2cf7402..cf393afccbb 100644
--- a/spec/features/projects/environments/environments_spec.rb
+++ b/spec/features/projects/environments/environments_spec.rb
@@ -23,6 +23,46 @@ feature 'Environments page', :feature, :js do
expect(page).to have_link('Available')
expect(page).to have_link('Stopped')
end
+
+ describe 'with one available environment' do
+ given(:environment) { create(:environment, project: project, state: :available) }
+
+ describe 'in available tab page' do
+ it 'should show one environment' do
+ visit namespace_project_environments_path(project.namespace, project, scope: 'available')
+ expect(page).to have_css('.environments-container')
+ expect(page.all('tbody > tr').length).to eq(1)
+ end
+ end
+
+ describe 'in stopped tab page' do
+ it 'should show no environments' do
+ visit namespace_project_environments_path(project.namespace, project, scope: 'stopped')
+ expect(page).to have_css('.environments-container')
+ expect(page).to have_content('You don\'t have any environments right now')
+ end
+ end
+ end
+
+ describe 'with one stopped environment' do
+ given(:environment) { create(:environment, project: project, state: :stopped) }
+
+ describe 'in available tab page' do
+ it 'should show no environments' do
+ visit namespace_project_environments_path(project.namespace, project, scope: 'available')
+ expect(page).to have_css('.environments-container')
+ expect(page).to have_content('You don\'t have any environments right now')
+ end
+ end
+
+ describe 'in stopped tab page' do
+ it 'should show one environment' do
+ visit namespace_project_environments_path(project.namespace, project, scope: 'stopped')
+ expect(page).to have_css('.environments-container')
+ expect(page.all('tbody > tr').length).to eq(1)
+ end
+ end
+ end
end
context 'without environments' do
diff --git a/spec/features/projects/import_export/import_file_spec.rb b/spec/features/projects/import_export/import_file_spec.rb
index 2d1106ea3e8..583f479ec18 100644
--- a/spec/features/projects/import_export/import_file_spec.rb
+++ b/spec/features/projects/import_export/import_file_spec.rb
@@ -69,12 +69,8 @@ feature 'Import/Export - project import integration test', feature: true, js: tr
select2(namespace.id, from: '#project_namespace_id')
- # click on disabled element
- find(:link, 'GitLab export').trigger('click')
-
- page.within('.flash-container') do
- expect(page).to have_content('Please enter path and name')
- end
+ # Check for tooltip disabled import button
+ expect(find('.import_gitlab_project')['title']).to eq('Please enter a valid project name.')
end
end
diff --git a/spec/features/projects/merge_requests/list_spec.rb b/spec/features/projects/merge_requests/list_spec.rb
index 5dd58ad66a7..7e8a796c55d 100644
--- a/spec/features/projects/merge_requests/list_spec.rb
+++ b/spec/features/projects/merge_requests/list_spec.rb
@@ -17,4 +17,28 @@ feature 'Merge Requests List' do
expect(page).not_to have_selector('.js-new-board-list')
end
+
+ it 'should show an empty state' do
+ visit namespace_project_merge_requests_path(project.namespace, project)
+
+ expect(page).to have_selector('.empty-state')
+ end
+
+ it 'empty state should have a create merge request button' do
+ visit namespace_project_merge_requests_path(project.namespace, project)
+
+ expect(page).to have_link 'New merge request', href: new_namespace_project_merge_request_path(project.namespace, project)
+ end
+
+ context 'if there are merge requests' do
+ before do
+ create(:merge_request, assignee: user, source_project: project)
+
+ visit namespace_project_merge_requests_path(project.namespace, project)
+ end
+
+ it 'should not show an empty state' do
+ expect(page).not_to have_selector('.empty-state')
+ end
+ end
end
diff --git a/spec/features/projects/wiki/user_creates_wiki_page_spec.rb b/spec/features/projects/wiki/user_creates_wiki_page_spec.rb
index fff8b9f3447..7bdaafd1beb 100644
--- a/spec/features/projects/wiki/user_creates_wiki_page_spec.rb
+++ b/spec/features/projects/wiki/user_creates_wiki_page_spec.rb
@@ -15,6 +15,10 @@ feature 'Projects > Wiki > User creates wiki page', feature: true do
let(:project) { create(:project, namespace: user.namespace) }
context 'when wiki is empty' do
+ scenario 'commit message field has value "Create home"' do
+ expect(page).to have_field('wiki[message]', with: 'Create home')
+ end
+
scenario 'directly from the wiki home page' do
fill_in :wiki_content, with: 'My awesome wiki!'
click_button 'Create page'
@@ -37,6 +41,9 @@ feature 'Projects > Wiki > User creates wiki page', feature: true do
fill_in :new_wiki_path, with: 'foo'
click_button 'Create Page'
+ # Commit message field should have correct value.
+ expect(page).to have_field('wiki[message]', with: 'Create foo')
+
fill_in :wiki_content, with: 'My awesome wiki!'
click_button 'Create page'
@@ -51,6 +58,9 @@ feature 'Projects > Wiki > User creates wiki page', feature: true do
fill_in :new_wiki_path, with: 'Spaces in the name'
click_button 'Create Page'
+ # Commit message field should have correct value.
+ expect(page).to have_field('wiki[message]', with: 'Create spaces in the name')
+
fill_in :wiki_content, with: 'My awesome wiki!'
click_button 'Create page'
@@ -65,6 +75,9 @@ feature 'Projects > Wiki > User creates wiki page', feature: true do
fill_in :new_wiki_path, with: 'hyphens-in-the-name'
click_button 'Create Page'
+ # Commit message field should have correct value.
+ expect(page).to have_field('wiki[message]', with: 'Create hyphens in the name')
+
fill_in :wiki_content, with: 'My awesome wiki!'
click_button 'Create page'
@@ -80,6 +93,10 @@ feature 'Projects > Wiki > User creates wiki page', feature: true do
let(:project) { create(:project, namespace: create(:group, :public)) }
context 'when wiki is empty' do
+ scenario 'commit message field has value "Create home"' do
+ expect(page).to have_field('wiki[message]', with: 'Create home')
+ end
+
scenario 'directly from the wiki home page' do
fill_in :wiki_content, with: 'My awesome wiki!'
click_button 'Create page'
@@ -101,6 +118,9 @@ feature 'Projects > Wiki > User creates wiki page', feature: true do
fill_in :new_wiki_path, with: 'foo'
click_button 'Create Page'
+ # Commit message field should have correct value.
+ expect(page).to have_field('wiki[message]', with: 'Create foo')
+
fill_in :wiki_content, with: 'My awesome wiki!'
click_button 'Create page'
diff --git a/spec/features/projects/wiki/user_updates_wiki_page_spec.rb b/spec/features/projects/wiki/user_updates_wiki_page_spec.rb
index aedc0333cb9..86cf520ea80 100644
--- a/spec/features/projects/wiki/user_updates_wiki_page_spec.rb
+++ b/spec/features/projects/wiki/user_updates_wiki_page_spec.rb
@@ -19,6 +19,9 @@ feature 'Projects > Wiki > User updates wiki page', feature: true do
scenario 'success when the wiki content is not empty' do
click_link 'Edit'
+ # Commit message field should have correct value.
+ expect(page).to have_field('wiki[message]', with: 'Update home')
+
fill_in :wiki_content, with: 'My awesome wiki!'
click_button 'Save changes'
@@ -48,6 +51,9 @@ feature 'Projects > Wiki > User updates wiki page', feature: true do
scenario 'the home page' do
click_link 'Edit'
+ # Commit message field should have correct value.
+ expect(page).to have_field('wiki[message]', with: 'Update home')
+
fill_in :wiki_content, with: 'My awesome wiki!'
click_button 'Save changes'
diff --git a/spec/features/search_spec.rb b/spec/features/search_spec.rb
index a6560a81096..e8ad28a00f0 100644
--- a/spec/features/search_spec.rb
+++ b/spec/features/search_spec.rb
@@ -119,13 +119,15 @@ describe "Search", feature: true do
visit namespace_project_path(project.namespace, project)
page.within '.search' do
- fill_in 'search', with: 'def'
+ fill_in 'search', with: 'application.js'
click_button 'Go'
end
click_link "Code"
expect(page).to have_selector('.file-content .code')
+
+ expect(page).to have_selector("span.line[lang='javascript']")
end
end
@@ -162,6 +164,8 @@ describe "Search", feature: true do
end
context 'click the links in the category search dropdown', js: true do
+ let!(:merge_request) { create(:merge_request, source_project: project, author: user, assignee: user) }
+
before do
page.find('#search').click
end
diff --git a/spec/fixtures/metrics.json b/spec/fixtures/metrics.json
new file mode 100644
index 00000000000..06427adce57
--- /dev/null
+++ b/spec/fixtures/metrics.json
@@ -0,0 +1 @@
+{"success":true,"metrics":{"memory_values":[{"metric":{},"values":[[1490935421.33,"9.832775297619047"],[1490935481.33,"9.8359375"],[1490935541.33,"9.837983630952381"],[1490935601.33,"9.840401785714286"],[1490935661.33,"9.84375"],[1490935721.33,"9.846168154761905"],[1490935781.33,"9.849516369047619"],[1490935841.33,"9.85249255952381"],[1490935901.33,"9.855096726190476"],[1490935961.33,"9.845796130952381"],[1490936021.33,"9.847284226190476"],[1490936081.33,"9.84468005952381"],[1490936141.33,"9.847470238095237"],[1490936201.33,"9.850818452380953"],[1490936261.33,"9.852864583333334"],[1490936321.33,"9.854910714285714"],[1490936381.33,"9.857700892857142"],[1490936441.33,"9.865513392857142"],[1490936501.33,"9.874813988095237"],[1490936561.33,"9.866071428571429"],[1490936621.33,"9.849330357142858"],[1490936681.33,"9.841331845238095"],[1490936741.33,"9.853236607142858"],[1490936801.33,"9.839657738095237"],[1490936861.33,"9.841517857142858"],[1490936921.33,"9.852864583333334"],[1490936981.33,"9.851376488095237"],[1490937041.33,"9.837611607142858"],[1490937101.33,"9.840401785714286"],[1490937161.33,"9.843377976190476"],[1490937221.33,"9.845796130952381"],[1490937281.33,"9.84858630952381"],[1490937341.33,"9.866071428571429"],[1490937401.33,"9.852864583333334"],[1490937461.33,"9.855840773809524"],[1490937521.33,"9.837797619047619"],[1490937581.33,"9.840959821428571"],[1490937641.33,"9.848958333333334"],[1490937701.33,"9.844308035714286"],[1490937761.33,"9.845982142857142"],[1490937821.33,"9.83984375"],[1490937881.33,"9.830171130952381"],[1490937941.33,"9.83686755952381"],[1490938001.33,"9.834263392857142"],[1490938061.33,"9.836309523809524"],[1490938121.33,"9.83984375"],[1490938181.33,"9.832775297619047"],[1490938241.33,"9.818266369047619"],[1490938301.33,"9.820126488095237"],[1490938361.33,"9.824032738095237"],[1490938421.33,"9.826078869047619"],[1490938481.33,"9.817708333333334"],[1490938541.33,"9.811755952380953"],[1490938601.33,"9.811197916666666"],[1490938661.33,"9.81156994047619"],[1490938721.33,"9.812313988095237"],[1490938781.33,"9.813058035714286"],[1490938841.33,"9.81343005952381"],[1490938901.33,"9.81547619047619"],[1490938961.33,"9.818824404761905"],[1490939021.33,"9.819754464285714"],[1490939081.33,"9.820684523809524"],[1490939141.33,"9.824776785714286"],[1490939201.33,"9.826078869047619"],[1490939261.33,"9.828311011904763"],[1490939321.33,"9.820870535714286"],[1490939381.33,"9.823846726190476"],[1490939441.33,"9.824404761904763"],[1490939501.33,"9.82905505952381"],[1490939561.33,"9.832775297619047"],[1490939621.33,"9.835565476190476"],[1490939681.33,"9.833333333333334"],[1490939741.33,"9.835379464285714"],[1490939801.33,"9.837239583333334"],[1490939861.33,"9.839285714285714"],[1490939921.33,"9.829613095238095"],[1490939981.33,"9.832403273809524"],[1490940041.33,"9.835751488095237"],[1490940101.33,"9.837797619047619"],[1490940161.33,"9.840959821428571"],[1490940221.33,"9.84375"],[1490940281.33,"9.846354166666666"],[1490940341.33,"9.853980654761905"],[1490940401.33,"9.852678571428571"],[1490940461.33,"9.861979166666666"],[1490940521.33,"9.857700892857142"],[1490940581.33,"9.861793154761905"],[1490940641.33,"9.86421130952381"],[1490940701.33,"9.867001488095237"],[1490940761.33,"9.867931547619047"],[1490940821.33,"9.859933035714286"],[1490940881.33,"9.86235119047619"],[1490940941.33,"9.865141369047619"],[1490941001.33,"9.866443452380953"],[1490941061.33,"9.868861607142858"],[1490941121.33,"9.871465773809524"],[1490941181.33,"9.873511904761905"],[1490941241.33,"9.875558035714286"],[1490941301.33,"9.87797619047619"],[1490941361.33,"9.881324404761905"],[1490941421.33,"9.888392857142858"],[1490941481.33,"9.888392857142858"],[1490941541.33,"9.89546130952381"],[1490941601.33,"9.898065476190476"],[1490941661.33,"9.885044642857142"],[1490941721.33,"9.872395833333334"],[1490941781.33,"9.870349702380953"],[1490941841.33,"9.873325892857142"],[1490941901.33,"9.875558035714286"],[1490941961.33,"9.878534226190476"],[1490942021.33,"9.87983630952381"],[1490942081.33,"9.884300595238095"],[1490942141.33,"9.891927083333334"],[1490942201.33,"9.890252976190476"],[1490942261.33,"9.891927083333334"],[1490942321.33,"9.893787202380953"],[1490942381.33,"9.892113095238095"],[1490942441.33,"9.900111607142858"],[1490942501.33,"9.893415178571429"],[1490942561.33,"9.895647321428571"],[1490942621.33,"9.889322916666666"],[1490942681.33,"9.883556547619047"],[1490942741.33,"9.885602678571429"],[1490942801.33,"9.88764880952381"],[1490942861.33,"9.898623511904763"],[1490942921.33,"9.89453125"],[1490942981.33,"9.885044642857142"],[1490943041.33,"9.874813988095237"],[1490943101.33,"9.880766369047619"],[1490943161.33,"9.868675595238095"],[1490943221.33,"9.864769345238095"],[1490943281.33,"9.852864583333334"],[1490943341.33,"9.855096726190476"],[1490943401.33,"9.857514880952381"],[1490943461.33,"9.859747023809524"],[1490943521.33,"9.861793154761905"],[1490943581.33,"9.864025297619047"],[1490943641.33,"9.857514880952381"],[1490943701.33,"9.859002976190476"],[1490943761.33,"9.860677083333334"],[1490943821.33,"9.864025297619047"],[1490943881.33,"9.86625744047619"],[1490943941.33,"9.873325892857142"],[1490944001.33,"9.876674107142858"],[1490944061.33,"9.888950892857142"],[1490944121.33,"9.878534226190476"],[1490944181.33,"9.880766369047619"],[1490944241.33,"9.884858630952381"],[1490944301.33,"9.870535714285714"],[1490944361.33,"9.864769345238095"],[1490944421.33,"9.851190476190476"],[1490944481.33,"9.85249255952381"],[1490944541.33,"9.85844494047619"],[1490944601.33,"9.855840773809524"],[1490944661.33,"9.868303571428571"],[1490944721.33,"9.859188988095237"],[1490944781.33,"9.860491071428571"],[1490944841.33,"9.863467261904763"],[1490944901.33,"9.864025297619047"],[1490944961.33,"9.857514880952381"],[1490945021.33,"9.843377976190476"],[1490945081.33,"9.836123511904763"],[1490945141.33,"9.837983630952381"],[1490945201.33,"9.84077380952381"],[1490945261.33,"9.847284226190476"],[1490945321.33,"9.849702380952381"],[1490945381.33,"9.827380952380953"],[1490945441.33,"9.82124255952381"],[1490945501.33,"9.822916666666666"],[1490945561.33,"9.824962797619047"],[1490945621.33,"9.814546130952381"],[1490945681.33,"9.805989583333334"],[1490945741.33,"9.791294642857142"],[1490945801.33,"9.786458333333334"],[1490945861.33,"9.77641369047619"],[1490945921.33,"9.76655505952381"],[1490945981.33,"9.76953125"],[1490946041.33,"9.742745535714286"],[1490946101.33,"9.753162202380953"],[1490946161.33,"9.739583333333334"],[1490946221.33,"9.742931547619047"],[1490946281.33,"9.743489583333334"],[1490946341.33,"9.746837797619047"],[1490946401.33,"9.749255952380953"],[1490946461.33,"9.737165178571429"],[1490946521.33,"9.739583333333334"],[1490946581.33,"9.74311755952381"],[1490946641.33,"9.751302083333334"],[1490946701.33,"9.761346726190476"],[1490946761.33,"9.747953869047619"],[1490946821.33,"9.75093005952381"],[1490946881.33,"9.755580357142858"],[1490946941.33,"9.759858630952381"],[1490947001.33,"9.761904761904763"],[1490947061.33,"9.77641369047619"],[1490947121.33,"9.768787202380953"],[1490947181.33,"9.772879464285714"],[1490947241.33,"9.777715773809524"],[1490947301.33,"9.779947916666666"],[1490947361.33,"9.772135416666666"],[1490947421.33,"9.77641369047619"],[1490947481.33,"9.783668154761905"],[1490947541.33,"9.780505952380953"],[1490947601.33,"9.777157738095237"],[1490947661.33,"9.759114583333334"],[1490947721.33,"9.761532738095237"],[1490947781.33,"9.763392857142858"],[1490947841.33,"9.765252976190476"],[1490947901.33,"9.760602678571429"],[1490947961.33,"9.751488095238095"],[1490948021.33,"9.757998511904763"],[1490948081.33,"9.759486607142858"],[1490948141.33,"9.754650297619047"],[1490948201.33,"9.728050595238095"],[1490948261.33,"9.73530505952381"],[1490948321.33,"9.718005952380953"],[1490948381.33,"9.732142857142858"],[1490948441.33,"9.725260416666666"],[1490948501.33,"9.728422619047619"],[1490948561.33,"9.72953869047619"],[1490948621.33,"9.733072916666666"],[1490948681.33,"9.736421130952381"],[1490948741.33,"9.749627976190476"],[1490948801.33,"9.740141369047619"],[1490948861.33,"9.74311755952381"],[1490948921.33,"9.736607142857142"],[1490948981.33,"9.744233630952381"],[1490949041.33,"9.723772321428571"],[1490949101.33,"9.731956845238095"],[1490949161.33,"9.732514880952381"],[1490949221.33,"9.734747023809524"],[1490949281.33,"9.737723214285714"],[1490949341.33,"9.737909226190476"],[1490949401.33,"9.742373511904763"],[1490949461.33,"9.744977678571429"],[1490949521.33,"9.748139880952381"],[1490949581.33,"9.751302083333334"],[1490949641.33,"9.757440476190476"],[1490949701.33,"9.756324404761905"],[1490949761.33,"9.749813988095237"],[1490949821.33,"9.739025297619047"],[1490949881.33,"9.726004464285714"],[1490949941.33,"9.728236607142858"],[1490950001.33,"9.732514880952381"],[1490950061.33,"9.735119047619047"],[1490950121.33,"9.737165178571429"],[1490950181.33,"9.739025297619047"],[1490950241.33,"9.740513392857142"],[1490950301.33,"9.749441964285714"],[1490950361.33,"9.736979166666666"],[1490950421.33,"9.741629464285714"],[1490950481.33,"9.743303571428571"],[1490950541.33,"9.74609375"],[1490950601.33,"9.75093005952381"],[1490950661.33,"9.724330357142858"],[1490950721.33,"9.726748511904763"],[1490950781.33,"9.733258928571429"],[1490950841.33,"9.744233630952381"],[1490950901.33,"9.734375"],[1490950961.33,"9.737537202380953"],[1490951021.33,"9.741071428571429"],[1490951081.33,"9.757254464285714"],[1490951141.33,"9.760044642857142"],[1490951201.33,"9.755952380952381"],[1490951261.33,"9.745349702380953"],[1490951321.33,"9.746651785714286"],[1490951381.33,"9.749441964285714"],[1490951441.33,"9.751674107142858"],[1490951501.33,"9.757998511904763"],[1490951561.33,"9.756510416666666"],[1490951621.33,"9.76264880952381"],[1490951681.33,"9.765625"],[1490951741.33,"9.757254464285714"],[1490951801.33,"9.751674107142858"],[1490951861.33,"9.754278273809524"],[1490951921.33,"9.744233630952381"],[1490951981.33,"9.745349702380953"],[1490952041.33,"9.748883928571429"],[1490952101.33,"9.753162202380953"],[1490952161.33,"9.747953869047619"],[1490952221.33,"9.750186011904763"],[1490952281.33,"9.751116071428571"],[1490952341.33,"9.753162202380953"],[1490952401.33,"9.758928571428571"],[1490952461.33,"9.758928571428571"],[1490952521.33,"9.755394345238095"],[1490952581.33,"9.758928571428571"],[1490952641.33,"9.761160714285714"],[1490952701.33,"9.763206845238095"],[1490952761.33,"9.767857142857142"],[1490952821.33,"9.765438988095237"],[1490952881.33,"9.768229166666666"],[1490952941.33,"9.780877976190476"],[1490953001.33,"9.77250744047619"],[1490953061.33,"9.784412202380953"],[1490953121.33,"9.77827380952381"],[1490953181.33,"9.781063988095237"],[1490953241.33,"9.783668154761905"],[1490953301.33,"9.787016369047619"],[1490953361.33,"9.784970238095237"],[1490953421.33,"9.787946428571429"],[1490953481.33,"9.788690476190476"],[1490953541.33,"9.790922619047619"],[1490953601.33,"9.792596726190476"],[1490953661.33,"9.79594494047619"],[1490953721.33,"9.79780505952381"],[1490953781.33,"9.800223214285714"],[1490953841.33,"9.794828869047619"],[1490953901.33,"9.799293154761905"],[1490953961.33,"9.801525297619047"],[1490954021.33,"9.786458333333334"],[1490954081.33,"9.773809523809524"],[1490954141.33,"9.767485119047619"],[1490954201.33,"9.760044642857142"],[1490954261.33,"9.751116071428571"],[1490954321.33,"9.752790178571429"],[1490954381.33,"9.753162202380953"],[1490954441.33,"9.744419642857142"],[1490954501.33,"9.73921130952381"],[1490954561.33,"9.74125744047619"],[1490954621.33,"9.743303571428571"],[1490954681.33,"9.745535714285714"],[1490954741.33,"9.746837797619047"],[1490954801.33,"9.749255952380953"],[1490954861.33,"9.744419642857142"],[1490954921.33,"9.745349702380953"],[1490954981.33,"9.74702380952381"],[1490955041.33,"9.738467261904763"],[1490955101.33,"9.740141369047619"],[1490955161.33,"9.747767857142858"],[1490955221.33,"9.750372023809524"],[1490955281.33,"9.747767857142858"],[1490955341.33,"9.739025297619047"],[1490955401.33,"9.745349702380953"],[1490955461.33,"9.730282738095237"],[1490955521.33,"9.73139880952381"],[1490955581.33,"9.722842261904763"],[1490955641.33,"9.725818452380953"],[1490955701.33,"9.72749255952381"],[1490955761.33,"9.72953869047619"],[1490955821.33,"9.731956845238095"],[1490955881.33,"9.735677083333334"],[1490955941.33,"9.738467261904763"],[1490956001.33,"9.735863095238095"],[1490956061.33,"9.743675595238095"],[1490956121.33,"9.730840773809524"],[1490956181.33,"9.734747023809524"],[1490956241.33,"9.736235119047619"],[1490956301.33,"9.736607142857142"],[1490956361.33,"9.73921130952381"],[1490956421.33,"9.742001488095237"],[1490956481.33,"9.743675595238095"],[1490956541.33,"9.744977678571429"],[1490956601.33,"9.748697916666666"],[1490956661.33,"9.760602678571429"],[1490956721.33,"9.751302083333334"],[1490956781.33,"9.754278273809524"],[1490956841.33,"9.756324404761905"],[1490956901.33,"9.758370535714286"],[1490956961.33,"9.760416666666666"],[1490957021.33,"9.763020833333334"],[1490957081.33,"9.766183035714286"],[1490957141.33,"9.764508928571429"],[1490957201.33,"9.767299107142858"],[1490957261.33,"9.768787202380953"],[1490957321.33,"9.771019345238095"],[1490957381.33,"9.773623511904763"],[1490957441.33,"9.775111607142858"],[1490957501.33,"9.779389880952381"],[1490957561.33,"9.780691964285714"],[1490957621.33,"9.788690476190476"],[1490957681.33,"9.794828869047619"],[1490957741.33,"9.779203869047619"],[1490957801.33,"9.787016369047619"],[1490957861.33,"9.783854166666666"],[1490957921.33,"9.78515625"],[1490957981.33,"9.786644345238095"],[1490958041.33,"9.787946428571429"],[1490958101.33,"9.800409226190476"],[1490958161.33,"9.787202380952381"],[1490958221.33,"9.789806547619047"],[1490958281.33,"9.791852678571429"],[1490958341.33,"9.788876488095237"],[1490958401.33,"9.78515625"],[1490958461.33,"9.7890625"],[1490958521.33,"9.791108630952381"],[1490958581.33,"9.792596726190476"],[1490958641.33,"9.794828869047619"],[1490958701.33,"9.793154761904763"],[1490958761.33,"9.799293154761905"],[1490958821.33,"9.797247023809524"],[1490958881.33,"9.794084821428571"],[1490958941.33,"9.796875"],[1490959001.33,"9.763950892857142"],[1490959061.33,"9.765997023809524"],[1490959121.33,"9.767671130952381"],[1490959181.33,"9.77046130952381"],[1490959241.33,"9.773809523809524"],[1490959301.33,"9.765252976190476"],[1490959361.33,"9.767485119047619"],[1490959421.33,"9.76953125"],[1490959481.33,"9.774553571428571"],[1490959541.33,"9.77734375"],[1490959601.33,"9.778459821428571"],[1490959661.33,"9.780877976190476"],[1490959721.33,"9.783296130952381"],[1490959781.33,"9.794828869047619"],[1490959841.33,"9.787016369047619"],[1490959901.33,"9.798735119047619"],[1490959961.33,"9.803013392857142"],[1490960021.33,"9.801525297619047"],[1490960081.33,"9.804873511904763"],[1490960141.33,"9.80078125"],[1490960201.33,"9.80375744047619"],[1490960261.33,"9.805059523809524"],[1490960321.33,"9.807849702380953"],[1490960381.33,"9.810825892857142"],[1490960441.33,"9.813058035714286"],[1490960501.33,"9.813616071428571"],[1490960561.33,"9.815104166666666"],[1490960621.33,"9.81733630952381"],[1490960681.33,"9.812872023809524"],[1490960741.33,"9.814546130952381"],[1490960801.33,"9.808035714285714"],[1490960861.33,"9.810081845238095"],[1490960921.33,"9.813058035714286"],[1490960981.33,"9.825892857142858"],[1490961041.33,"9.816964285714286"],[1490961101.33,"9.82421875"],[1490961161.33,"9.80952380952381"],[1490961221.33,"9.804315476190476"],[1490961281.33,"9.797619047619047"],[1490961341.33,"9.80078125"],[1490961401.33,"9.802827380952381"],[1490961461.33,"9.803199404761905"],[1490961521.33,"9.80952380952381"],[1490961581.33,"9.806919642857142"],[1490961641.33,"9.808779761904763"],[1490961701.33,"9.811197916666666"],[1490961761.33,"9.813244047619047"],[1490961821.33,"9.815662202380953"],[1490961881.33,"9.819940476190476"],[1490961941.33,"9.822172619047619"],[1490962001.33,"9.82328869047619"],[1490962061.33,"9.826822916666666"],[1490962121.33,"9.829241071428571"],[1490962181.33,"9.832589285714286"],[1490962241.33,"9.835565476190476"],[1490962301.33,"9.839471726190476"],[1490962361.33,"9.825520833333334"],[1490962421.33,"9.829427083333334"],[1490962481.33,"9.832217261904763"],[1490962541.33,"9.839285714285714"],[1490962601.33,"9.837611607142858"],[1490962661.33,"9.841145833333334"],[1490962721.33,"9.834077380952381"],[1490962781.33,"9.837239583333334"],[1490962841.33,"9.841703869047619"],[1490962901.33,"9.844308035714286"],[1490962961.33,"9.838727678571429"],[1490963021.33,"9.840587797619047"],[1490963081.33,"9.849516369047619"],[1490963141.33,"9.845238095238095"],[1490963201.33,"9.84375"],[1490963261.33,"9.838541666666666"],[1490963321.33,"9.841889880952381"],[1490963381.33,"9.846354166666666"],[1490963441.33,"9.832403273809524"],[1490963501.33,"9.833891369047619"],[1490963561.33,"9.808221726190476"],[1490963621.33,"9.812686011904763"],[1490963681.33,"9.814918154761905"],[1490963741.33,"9.817708333333334"],[1490963801.33,"9.80561755952381"],[1490963861.33,"9.80859375"],[1490963921.33,"9.811197916666666"],[1490963981.33,"9.802269345238095"],[1490964041.33,"9.798177083333334"],[1490964101.33,"9.80078125"],[1490964161.33,"9.815104166666666"],[1490964221.33,"9.806361607142858"]]}],"memory_current":[{"metric":{},"value":[1490964221.593,"9.806361607142858"]}],"cpu_values":[{"metric":{},"values":[[1490935421.446,"0.011520035833333402"],[1490935481.446,"0.010738020634921052"],[1490935541.446,"0.011830812658730162"],[1490935601.446,"0.011666519206349292"],[1490935661.446,"0.012397734365079505"],[1490935721.446,"0.012264678253967905"],[1490935781.446,"0.011701125396825458"],[1490935841.446,"0.011413869087301435"],[1490935901.446,"0.011355704404762157"],[1490935961.446,"0.01295611777777756"],[1490936021.446,"0.012283088253968812"],[1490936081.446,"0.011711742103174674"],[1490936141.446,"0.011066851150792879"],[1490936201.446,"0.011525933611111726"],[1490936261.446,"0.012260294246031015"],[1490936321.446,"0.011917795238095285"],[1490936381.446,"0.011402582301587626"],[1490936441.446,"0.012311798253968057"],[1490936501.446,"0.011604295476191046"],[1490936561.446,"0.012329014206349137"],[1490936621.446,"0.011401263769840977"],[1490936681.446,"0.012310593492063392"],[1490936741.446,"0.01244334305555575"],[1490936801.446,"0.01176146669320973"],[1490936861.446,"0.011186474629011792"],[1490936921.446,"0.013234800079365536"],[1490936981.446,"0.01217435722222217"],[1490937041.446,"0.011211570753967583"],[1490937101.446,"0.012066252420634934"],[1490937161.446,"0.012175381944444839"],[1490937221.446,"0.011215347936507976"],[1490937281.446,"0.012909065515873003"],[1490937341.446,"0.011718783452381023"],[1490937401.446,"0.011740557499999828"],[1490937461.446,"0.012024899960317205"],[1490937521.446,"0.011518551626984471"],[1490937581.446,"0.013295429607829826"],[1490937641.446,"0.013578758822130006"],[1490937701.446,"0.01170811908668783"],[1490937761.446,"0.011867610238095478"],[1490937821.446,"0.012601599007937034"],[1490937881.446,"0.011028959285714405"],[1490937941.446,"0.011972864523808899"],[1490938001.446,"0.012236090515873134"],[1490938061.446,"0.012468855793650629"],[1490938121.446,"0.012324049999999686"],[1490938181.446,"0.012271810317460288"],[1490938241.446,"0.013109732103174912"],[1490938301.446,"0.01201708535714284"],[1490938361.446,"0.01198280035714318"],[1490938421.446,"0.011631491547618469"],[1490938481.446,"0.012698120317460778"],[1490938541.446,"0.011908042499999686"],[1490938601.446,"0.012941332460317123"],[1490938661.446,"0.012009558055555753"],[1490938721.446,"0.011749238293651211"],[1490938781.446,"0.012597720873015857"],[1490938841.446,"0.012128174365079517"],[1490938901.446,"0.013411003452380428"],[1490938961.446,"0.012712377896825132"],[1490939021.446,"0.0126730261111118"],[1490939081.446,"0.012196438134920173"],[1490939141.446,"0.011617917341270696"],[1490939201.446,"0.012271590992062863"],[1490939261.446,"0.01196238253968261"],[1490939321.446,"0.012446522619048245"],[1490939381.446,"0.013146698134919643"],[1490939441.446,"0.013160663611111774"],[1490939501.446,"0.012921960039682278"],[1490939561.446,"0.012100972380952405"],[1490939621.446,"0.01235039095238153"],[1490939681.446,"0.013303590992062684"],[1490939741.446,"0.012064513055556225"],[1490939801.446,"0.011846763531745252"],[1490939861.446,"0.012280224007936782"],[1490939921.446,"0.012305159166666833"],[1490939981.446,"0.012107076111110887"],[1490940041.446,"0.013109447341269884"],[1490940101.446,"0.011668830198412932"],[1490940161.446,"0.011757771468254286"],[1490940221.446,"0.013607426447330252"],[1490940281.446,"0.012069082212503184"],[1490940341.446,"0.012702448174603309"],[1490940401.446,"0.012915864642857006"],[1490940461.446,"0.012882558941478554"],[1490940521.446,"0.01180430288917485"],[1490940581.446,"0.012561457142856586"],[1490940641.446,"0.013117287261905215"],[1490940701.446,"0.0119707260317455"],[1490940761.446,"0.012110876587301957"],[1490940821.446,"0.012900523174603096"],[1490940881.446,"0.012405300317460836"],[1490940941.446,"0.013397718690476127"],[1490941001.446,"0.011853019404761512"],[1490941061.446,"0.011410178968254279"],[1490941121.446,"0.01385021210317412"],[1490941181.446,"0.012158262658730703"],[1490941241.446,"0.012590782142857021"],[1490941301.446,"0.011902994444444289"],[1490941361.446,"0.012597971468253468"],[1490941421.446,"0.013460530436508394"],[1490941481.446,"0.012871132936507318"],[1490941541.446,"0.012321937023810644"],[1490941601.446,"0.012861435992063004"],[1490941661.446,"0.011904687658730493"],[1490941721.446,"0.013068603849206292"],[1490941781.446,"0.011558027420635053"],[1490941841.446,"0.011785108134920095"],[1490941901.446,"0.013018491984126938"],[1490941961.446,"0.012803318611111494"],[1490942021.446,"0.011276595873015969"],[1490942081.446,"0.012407365753968128"],[1490942141.446,"0.01261537746031769"],[1490942201.446,"0.011981626507936492"],[1490942261.446,"0.011779192579364465"],[1490942321.446,"0.012944439365080001"],[1490942381.446,"0.012563845515873258"],[1490942441.446,"0.012490993809523204"],[1490942501.446,"0.011721826547619399"],[1490942561.446,"0.012376904523809195"],[1490942621.446,"0.012627997539682608"],[1490942681.446,"0.012353236984126971"],[1490942741.446,"0.012143749162511788"],[1490942801.446,"0.01210106380777602"],[1490942861.446,"0.01323092650793727"],[1490942921.446,"0.01217811805555557"],[1490942981.446,"0.011703709655399819"],[1490943041.446,"0.01140056596399108"],[1490943101.446,"0.011589462460317477"],[1490943161.446,"0.011424534784915178"],[1490943221.446,"0.011720420858480131"],[1490943281.446,"0.011956359603174035"],[1490943341.446,"0.011627974444444375"],[1490943401.446,"0.012056417142857899"],[1490943461.446,"0.012875421865079256"],[1490943521.446,"0.011447757222222438"],[1490943581.446,"0.011686728412698438"],[1490943641.446,"0.012264428214285543"],[1490943701.446,"0.011396086150793258"],[1490943761.446,"0.012637377857143453"],[1490943821.446,"0.012229487817460189"],[1490943881.446,"0.012519327516820155"],[1490943941.446,"0.011632154440677021"],[1490944001.446,"0.0127011905614214"],[1490944061.446,"0.012041664776432408"],[1490944121.446,"0.011550796183789442"],[1490944181.446,"0.012340807579364546"],[1490944241.446,"0.012514561706348858"],[1490944301.446,"0.011591095515873378"],[1490944361.446,"0.011562522896825472"],[1490944421.446,"0.012653687499999684"],[1490944481.446,"0.012597878095237767"],[1490944541.446,"0.011373836746032411"],[1490944601.446,"0.011489111309523512"],[1490944661.446,"0.012365606547618906"],[1490944721.446,"0.011246835793650788"],[1490944781.446,"0.011556645833333596"],[1490944841.446,"0.0114839880952384"],[1490944901.446,"0.011559932103174322"],[1490944961.446,"0.011456621547618827"],[1490945021.446,"0.011137903531746323"],[1490945081.446,"0.011371503134920238"],[1490945141.446,"0.01262392527777806"],[1490945201.446,"0.011231213571428417"],[1490945261.446,"0.011834045595238011"],[1490945321.446,"0.011222574087301793"],[1490945381.446,"0.01139294579365124"],[1490945441.446,"0.011876671865079205"],[1490945501.446,"0.012003088888888104"],[1490945561.446,"0.011232171746032069"],[1490945621.446,"0.01189458067460394"],[1490945681.446,"0.011593709801586787"],[1490945741.446,"0.01179023611111146"],[1490945801.446,"0.012056340952381187"],[1490945861.446,"0.011755026706348978"],[1490945921.446,"0.011906753412698057"],[1490945981.446,"0.011362850850868408"],[1490946041.446,"0.011567284784873766"],[1490946101.446,"0.01159940924603172"],[1490946161.446,"0.01169248444646143"],[1490946221.446,"0.011294826570231075"],[1490946281.446,"0.011797972936507535"],[1490946341.446,"0.011732454126984091"],[1490946401.446,"0.011992103412699077"],[1490946461.446,"0.011787900634920185"],[1490946521.446,"0.01170581265873045"],[1490946581.446,"0.011391009603175007"],[1490946641.446,"0.01205839841269773"],[1490946701.446,"0.01188169805555573"],[1490946761.446,"0.011459351746031153"],[1490946821.446,"0.012089251071429255"],[1490946881.446,"0.011159798611111122"],[1490946941.446,"0.012261993650793439"],[1490947001.446,"0.011150941865079526"],[1490947061.446,"0.011784560238095428"],[1490947121.446,"0.01146369333333352"],[1490947181.446,"0.011946112341269969"],[1490947241.446,"0.012244168452380742"],[1490947301.446,"0.01108276087301507"],[1490947361.446,"0.011391418571428976"],[1490947421.446,"0.012042411525379642"],[1490947481.446,"0.012082919141039653"],[1490947541.446,"0.011615924682540189"],[1490947601.446,"0.01218819496031727"],[1490947661.446,"0.011292488293650517"],[1490947721.446,"0.011232974365079479"],[1490947781.446,"0.011638264880952223"],[1490947841.446,"0.0115353722619047"],[1490947901.446,"0.011426710952381045"],[1490947961.446,"0.0121381246428574"],[1490948021.446,"0.011812514087301832"],[1490948081.446,"0.012050580317459442"],[1490948141.446,"0.011855329166666742"],[1490948201.446,"0.011649919960317898"],[1490948261.446,"0.01163187396825391"],[1490948321.446,"0.011266725634920935"],[1490948381.446,"0.011934722460317146"],[1490948441.446,"0.011368148333333088"],[1490948501.446,"0.011662377698413048"],[1490948561.446,"0.011039417341269188"],[1490948621.446,"0.012176113174603589"],[1490948681.446,"0.011265313531746158"],[1490948741.446,"0.01158711781746033"],[1490948801.446,"0.011557390912698215"],[1490948861.446,"0.012131684804188454"],[1490948921.446,"0.011474324082027133"],[1490948981.446,"0.011376334484127639"],[1490949041.446,"0.011627233571428175"],[1490949101.446,"0.012499916785714077"],[1490949161.446,"0.011920621706348947"],[1490949221.446,"0.011574053410790661"],[1490949281.446,"0.011837460242165967"],[1490949341.446,"0.011227153174603937"],[1490949401.446,"0.011635896944444115"],[1490949461.446,"0.011701339047618983"],[1490949521.446,"0.011847283650793895"],[1490949581.446,"0.0116057894841271"],[1490949641.446,"0.011789695753968094"],[1490949701.446,"0.011279284841269992"],[1490949761.446,"0.011470807460317041"],[1490949821.446,"0.012172255515873568"],[1490949881.446,"0.011721892103174175"],[1490949941.446,"0.010727560317460336"],[1490950001.446,"0.011509186269841303"],[1490950061.446,"0.01188623087301566"],[1490950121.446,"0.011476948452380968"],[1490950181.446,"0.01211593166666722"],[1490950241.446,"0.011757469444444444"],[1490950301.446,"0.011519936865079109"],[1490950361.446,"0.01165834781746044"],[1490950421.446,"0.010831068928571068"],[1490950481.446,"0.011977692023809912"],[1490950541.446,"0.011828264880952136"],[1490950601.446,"0.01191921916666625"],[1490950661.446,"0.011901336547619379"],[1490950721.446,"0.011776620238095158"],[1490950781.446,"0.011911536031746153"],[1490950841.446,"0.011467936309523809"],[1490950901.446,"0.012163667023809579"],[1490950961.446,"0.0116551746825399"],[1490951021.446,"0.011799408095237739"],[1490951081.446,"0.011845631309524084"],[1490951141.446,"0.011289116626983809"],[1490951201.446,"0.012258327777777984"],[1490951261.446,"0.012265819682539036"],[1490951321.446,"0.011346034166667811"],[1490951381.446,"0.011996446111110597"],[1490951441.446,"0.011511485714285046"],[1490951501.446,"0.011980616349206635"],[1490951561.446,"0.011565376031746316"],[1490951621.446,"0.010918043373016443"],[1490951681.446,"0.011479107380951632"],[1490951741.446,"0.012467024051997748"],[1490951801.446,"0.01235313125400671"],[1490951861.446,"0.012167793061507889"],[1490951921.446,"0.01249734373015914"],[1490951981.446,"0.011414617499999877"],[1490952041.446,"0.012559693849205949"],[1490952101.446,"0.012135384801587835"],[1490952161.446,"0.01195310698412663"],[1490952221.446,"0.011996730515873409"],[1490952281.446,"0.012245181626984071"],[1490952341.446,"0.01172794166666644"],[1490952401.446,"0.012153839325397124"],[1490952461.446,"0.01287662682539674"],[1490952521.446,"0.011412833611110576"],[1490952581.446,"0.0115385753968256"],[1490952641.446,"0.011953797142857927"],[1490952701.446,"0.012210606230158325"],[1490952761.446,"0.012193429836568915"],[1490952821.446,"0.01175164000191546"],[1490952881.446,"0.011686968928571266"],[1490952941.446,"0.01204885615079335"],[1490953001.446,"0.010858237182540066"],[1490953061.446,"0.012570554523809901"],[1490953121.446,"0.011606933412697877"],[1490953181.446,"0.011895175039682713"],[1490953241.446,"0.011877423888888992"],[1490953301.446,"0.01134354857142876"],[1490953361.446,"0.011999752857142089"],[1490953421.446,"0.011927079960317739"],[1490953481.446,"0.01172722273809559"],[1490953541.446,"0.0114388174999997"],[1490953601.446,"0.012584772738095138"],[1490953661.446,"0.011858990837323214"],[1490953721.446,"0.011489406427467985"],[1490953781.446,"0.011673106071428765"],[1490953841.446,"0.012389803452380168"],[1490953901.446,"0.010877735714285755"],[1490953961.446,"0.012098601984127518"],[1490954021.446,"0.011876002539682478"],[1490954081.446,"0.0119792138492057"],[1490954141.446,"0.01116768142857198"],[1490954201.446,"0.011819058452381173"],[1490954261.446,"0.011543723055555002"],[1490954321.446,"0.011877097777778114"],[1490954381.446,"0.011255818690476465"],[1490954441.446,"0.011544411269840424"],[1490954501.446,"0.011844739246031948"],[1490954561.446,"0.012498686626984624"],[1490954621.446,"0.011012790753967753"],[1490954681.446,"0.011763483769841236"],[1490954741.446,"0.011742064880952764"],[1490954801.446,"0.011329697023809454"],[1490954861.446,"0.011616721150793869"],[1490954921.446,"0.011935843650793056"],[1490954981.446,"0.012041806150794254"],[1490955041.446,"0.011776362817460298"],[1490955101.446,"0.011507964920634838"],[1490955161.446,"0.012249892380951723"],[1490955221.446,"0.011680689451964254"],[1490955281.446,"0.011966289381797203"],[1490955341.446,"0.011113054447726804"],[1490955401.446,"0.012155607703748966"],[1490955461.446,"0.011851554722222412"],[1490955521.446,"0.011899298531746077"],[1490955581.446,"0.01202313674603201"],[1490955641.446,"0.011739823253968055"],[1490955701.446,"0.011866135595237215"],[1490955761.446,"0.012171682563083994"],[1490955821.446,"0.01125473955952014"],[1490955881.446,"0.011791852817460289"],[1490955941.446,"0.011389896547619342"],[1490956001.446,"0.011801524404761971"],[1490956061.446,"0.011788201388888577"],[1490956121.446,"0.011472721388889214"],[1490956181.446,"0.012352298174603236"],[1490956241.446,"0.011831984404761721"],[1490956301.446,"0.0114478640476188"],[1490956361.446,"0.012315896944444986"],[1490956421.446,"0.01184387992063444"],[1490956481.446,"0.0108170579365078"],[1490956541.446,"0.012441825119047971"],[1490956601.446,"0.011650502579365023"],[1490956661.446,"0.011244622936507553"],[1490956721.446,"0.01138462460317496"],[1490956781.446,"0.012361013348424437"],[1490956841.446,"0.011687763677888905"],[1490956901.446,"0.011387440952381297"],[1490956961.446,"0.012246620039682158"],[1490957021.446,"0.010769535198412467"],[1490957081.446,"0.012311013690477024"],[1490957141.446,"0.011455958968253554"],[1490957201.446,"0.012126715198413286"],[1490957261.446,"0.011078292499999627"],[1490957321.446,"0.012041933253967746"],[1490957381.446,"0.01147051317460329"],[1490957441.446,"0.01173451460317538"],[1490957501.446,"0.011660740317459825"],[1490957561.446,"0.011851131269840753"],[1490957621.446,"0.012117949444444812"],[1490957681.446,"0.011214277301587397"],[1490957741.446,"0.011935565277777841"],[1490957801.446,"0.011180848809523986"],[1490957861.446,"0.011540955039682404"],[1490957921.446,"0.011678924523809829"],[1490957981.446,"0.01175049698412655"],[1490958041.446,"0.01179233821428546"],[1490958101.446,"0.011217207341269743"],[1490958161.446,"0.011623496111110998"],[1490958221.446,"0.011751017182540137"],[1490958281.446,"0.011548055515872839"],[1490958341.446,"0.01157145297619062"],[1490958401.446,"0.011809365079364814"],[1490958461.446,"0.011367088134920926"],[1490958521.446,"0.011220626785714515"],[1490958581.446,"0.012502413531745657"],[1490958641.446,"0.011674712222222085"],[1490958701.446,"0.010840117777778147"],[1490958761.446,"0.01169669242063464"],[1490958821.446,"0.01206404448412709"],[1490958881.446,"0.011476003253967956"],[1490958941.446,"0.011927363650794281"],[1490959001.446,"0.011834540039682623"],[1490959061.446,"0.011952310396106811"],[1490959121.446,"0.011641002569963536"],[1490959181.446,"0.011215335912698408"],[1490959241.446,"0.011801235515873079"],[1490959301.446,"0.012109150079365269"],[1490959361.446,"0.011696530238095701"],[1490959421.446,"0.01188721699431308"],[1490959481.446,"0.011013023946272025"],[1490959541.446,"0.011927455988174854"],[1490959601.446,"0.011773952156046168"],[1490959661.446,"0.011311449525742057"],[1490959721.446,"0.011926485873016056"],[1490959781.446,"0.012208613174603443"],[1490959841.446,"0.011077256706349554"],[1490959901.446,"0.012141572896825473"],[1490959961.446,"0.011884196547619123"],[1490960021.446,"0.01182910611111061"],[1490960081.446,"0.011089906190476237"],[1490960141.446,"0.011485851349206303"],[1490960201.446,"0.011621675079365073"],[1490960261.446,"0.011420984246031282"],[1490960321.446,"0.011702707664224543"],[1490960381.446,"0.011122996101531552"],[1490960441.446,"0.011923133293650747"],[1490960501.446,"0.012209551587301823"],[1490960561.446,"0.011541768293650705"],[1490960621.446,"0.01133343007936486"],[1490960681.446,"0.011718844880952742"],[1490960741.446,"0.01170618126984048"],[1490960801.446,"0.01158023575396868"],[1490960861.446,"0.012154581865079351"],[1490960921.446,"0.011287024246031918"],[1490960981.446,"0.012035483412697787"],[1490961041.446,"0.01206407186508005"],[1490961101.446,"0.011742228333332922"],[1490961161.446,"0.011460450952381294"],[1490961221.446,"0.011752177539682223"],[1490961281.446,"0.012416623373015778"],[1490961341.446,"0.01134374146825419"],[1490961401.446,"0.011742214642857577"],[1490961461.446,"0.01157076337301528"],[1490961521.446,"0.011251291190475883"],[1490961581.446,"0.010835279404761772"],[1490961641.446,"0.012082314722223412"],[1490961701.446,"0.011244282817460054"],[1490961761.446,"0.012600352738094536"],[1490961821.446,"0.011595374841270692"],[1490961881.446,"0.012047435158729298"],[1490961941.446,"0.012117879285714984"],[1490962001.446,"0.011105805912698236"],[1490962061.446,"0.011228379365079935"],[1490962121.446,"0.012051188888888457"],[1490962181.446,"0.011811605198411965"],[1490962241.446,"0.011438638690477312"],[1490962301.446,"0.011535638928571016"],[1490962361.446,"0.011846252277212543"],[1490962421.446,"0.011137096779830425"],[1490962481.446,"0.011301488807399701"],[1490962541.446,"0.011706436349206364"],[1490962601.446,"0.011607870952381014"],[1490962661.446,"0.01165941666666676"],[1490962721.446,"0.011457761706349363"],[1490962781.446,"0.012004376428571304"],[1490962841.446,"0.012380191230158676"],[1490962901.446,"0.011650816111111262"],[1490962961.446,"0.011339834484126858"],[1490963021.446,"0.011815001031746352"],[1490963081.446,"0.01215702742063424"],[1490963141.446,"0.011112767612387399"],[1490963201.446,"0.011991515394890143"],[1490963261.446,"0.011573327579365182"],[1490963321.446,"0.011559778809523533"],[1490963381.446,"0.012400119444444207"],[1490963441.446,"0.011127036507936056"],[1490963501.446,"0.012095518055556944"],[1490963561.446,"0.011203742460316668"],[1490963621.446,"0.012493672738095584"],[1490963681.446,"0.012086427023809085"],[1490963741.446,"0.01073350408730215"],[1490963801.446,"0.011784052619047683"],[1490963861.446,"0.011817165277777068"],[1490963921.446,"0.01162805619047661"],[1490963981.446,"0.01141054027777739"],[1490964041.446,"0.012398790952381392"],[1490964101.446,"0.011081906428571691"],[1490964161.446,"0.012049610714285322"],[1490964221.446,"0.011764468492063805"]]}],"cpu_current":[{"metric":{},"value":[1490964221.765,"0.011764468492063801"]}]},"last_update":"2017-03-31T12:43:41.618Z"}
diff --git a/spec/javascripts/boards/board_list_spec.js b/spec/javascripts/boards/board_list_spec.js
new file mode 100644
index 00000000000..3f598887603
--- /dev/null
+++ b/spec/javascripts/boards/board_list_spec.js
@@ -0,0 +1,201 @@
+/* global BoardService */
+/* global boardsMockInterceptor */
+/* global List */
+/* global listObj */
+/* global ListIssue */
+import Vue from 'vue';
+import _ from 'underscore';
+import Sortable from 'vendor/Sortable';
+import BoardList from '~/boards/components/board_list';
+import eventHub from '~/boards/eventhub';
+import '~/boards/mixins/sortable_default_options';
+import '~/boards/models/issue';
+import '~/boards/models/list';
+import '~/boards/stores/boards_store';
+import './mock_data';
+
+window.Sortable = Sortable;
+
+describe('Board list component', () => {
+ let component;
+
+ beforeEach((done) => {
+ const el = document.createElement('div');
+
+ document.body.appendChild(el);
+ Vue.http.interceptors.push(boardsMockInterceptor);
+ gl.boardService = new BoardService('/test/issue-boards/board', '', '1');
+ gl.issueBoards.BoardsStore.create();
+ gl.IssueBoardsApp = new Vue();
+
+ const BoardListComp = Vue.extend(BoardList);
+ const list = new List(listObj);
+ const issue = new ListIssue({
+ title: 'Testing',
+ iid: 1,
+ confidential: false,
+ labels: [],
+ });
+ list.issuesSize = 1;
+ list.issues.push(issue);
+
+ component = new BoardListComp({
+ el,
+ propsData: {
+ disabled: false,
+ list,
+ issues: list.issues,
+ loading: false,
+ issueLinkBase: '/issues',
+ rootPath: '/',
+ },
+ }).$mount();
+
+ Vue.nextTick(() => {
+ done();
+ });
+ });
+
+ afterEach(() => {
+ Vue.http.interceptors = _.without(Vue.http.interceptors, boardsMockInterceptor);
+ });
+
+ it('renders component', () => {
+ expect(
+ component.$el.classList.contains('board-list-component'),
+ ).toBe(true);
+ });
+
+ it('renders loading icon', (done) => {
+ component.loading = true;
+
+ Vue.nextTick(() => {
+ expect(
+ component.$el.querySelector('.board-list-loading'),
+ ).not.toBeNull();
+
+ done();
+ });
+ });
+
+ it('renders issues', () => {
+ expect(
+ component.$el.querySelectorAll('.card').length,
+ ).toBe(1);
+ });
+
+ it('sets data attribute with issue id', () => {
+ expect(
+ component.$el.querySelector('.card').getAttribute('data-issue-id'),
+ ).toBe('1');
+ });
+
+ it('shows new issue form', (done) => {
+ component.toggleForm();
+
+ Vue.nextTick(() => {
+ expect(
+ component.$el.querySelector('.board-new-issue-form'),
+ ).not.toBeNull();
+
+ expect(
+ component.$el.querySelector('.is-smaller'),
+ ).not.toBeNull();
+
+ done();
+ });
+ });
+
+ it('shows new issue form after eventhub event', (done) => {
+ eventHub.$emit(`hide-issue-form-${component.list.id}`);
+
+ Vue.nextTick(() => {
+ expect(
+ component.$el.querySelector('.board-new-issue-form'),
+ ).not.toBeNull();
+
+ expect(
+ component.$el.querySelector('.is-smaller'),
+ ).not.toBeNull();
+
+ done();
+ });
+ });
+
+ it('does not show new issue form for closed list', (done) => {
+ component.list.type = 'closed';
+ component.toggleForm();
+
+ Vue.nextTick(() => {
+ expect(
+ component.$el.querySelector('.board-new-issue-form'),
+ ).toBeNull();
+
+ done();
+ });
+ });
+
+ it('shows count list item', (done) => {
+ component.showCount = true;
+
+ Vue.nextTick(() => {
+ expect(
+ component.$el.querySelector('.board-list-count'),
+ ).not.toBeNull();
+
+ expect(
+ component.$el.querySelector('.board-list-count').textContent.trim(),
+ ).toBe('Showing all issues');
+
+ done();
+ });
+ });
+
+ it('shows how many more issues to load', (done) => {
+ component.showCount = true;
+ component.list.issuesSize = 20;
+
+ Vue.nextTick(() => {
+ expect(
+ component.$el.querySelector('.board-list-count').textContent.trim(),
+ ).toBe('Showing 1 of 20 issues');
+
+ done();
+ });
+ });
+
+ it('loads more issues after scrolling', (done) => {
+ spyOn(component.list, 'nextPage');
+ component.$refs.list.style.height = '100px';
+ component.$refs.list.style.overflow = 'scroll';
+
+ for (let i = 0; i < 19; i += 1) {
+ const issue = component.list.issues[0];
+ issue.id += 1;
+ component.list.issues.push(issue);
+ }
+
+ Vue.nextTick(() => {
+ component.$refs.list.scrollTop = 20000;
+
+ setTimeout(() => {
+ expect(component.list.nextPage).toHaveBeenCalled();
+
+ done();
+ });
+ });
+ });
+
+ it('shows loading more spinner', (done) => {
+ component.showCount = true;
+ component.list.loadingMore = true;
+
+ Vue.nextTick(() => {
+ expect(
+ component.$el.querySelector('.board-list-count .fa-spinner'),
+ ).not.toBeNull();
+
+ done();
+ });
+ });
+});
diff --git a/spec/javascripts/environments/environment_spec.js b/spec/javascripts/environments/environment_spec.js
index 9bcf617fcd8..4431baa4b96 100644
--- a/spec/javascripts/environments/environment_spec.js
+++ b/spec/javascripts/environments/environment_spec.js
@@ -1,7 +1,7 @@
import Vue from 'vue';
import '~/flash';
import EnvironmentsComponent from '~/environments/components/environment';
-import { environment } from './mock_data';
+import { environment, folder } from './mock_data';
describe('Environment', () => {
preloadFixtures('static/environments/environments.html.raw');
@@ -179,4 +179,101 @@ describe('Environment', () => {
}, 0);
});
});
+
+ describe('expandable folders', () => {
+ const environmentsResponseInterceptor = (request, next) => {
+ next(request.respondWith(JSON.stringify({
+ environments: [folder],
+ stopped_count: 0,
+ available_count: 1,
+ }), {
+ status: 200,
+ headers: {
+ 'X-nExt-pAge': '2',
+ 'x-page': '1',
+ 'X-Per-Page': '1',
+ 'X-Prev-Page': '',
+ 'X-TOTAL': '37',
+ 'X-Total-Pages': '2',
+ },
+ }));
+ };
+
+ beforeEach(() => {
+ Vue.http.interceptors.push(environmentsResponseInterceptor);
+ component = new EnvironmentsComponent({
+ el: document.querySelector('#environments-list-view'),
+ });
+ });
+
+ afterEach(() => {
+ Vue.http.interceptors = _.without(
+ Vue.http.interceptors, environmentsResponseInterceptor,
+ );
+ });
+
+ it('should open a closed folder', (done) => {
+ setTimeout(() => {
+ component.$el.querySelector('.folder-name').click();
+
+ Vue.nextTick(() => {
+ expect(
+ component.$el.querySelector('.folder-icon i.fa-caret-right').getAttribute('style'),
+ ).toContain('display: none');
+ expect(
+ component.$el.querySelector('.folder-icon i.fa-caret-down').getAttribute('style'),
+ ).not.toContain('display: none');
+ done();
+ });
+ });
+ });
+
+ it('should close an opened folder', (done) => {
+ setTimeout(() => {
+ // open folder
+ component.$el.querySelector('.folder-name').click();
+
+ Vue.nextTick(() => {
+ // close folder
+ component.$el.querySelector('.folder-name').click();
+
+ Vue.nextTick(() => {
+ expect(
+ component.$el.querySelector('.folder-icon i.fa-caret-down').getAttribute('style'),
+ ).toContain('display: none');
+ expect(
+ component.$el.querySelector('.folder-icon i.fa-caret-right').getAttribute('style'),
+ ).not.toContain('display: none');
+ done();
+ });
+ });
+ });
+ });
+
+ it('should show children environments and a button to show all environments', (done) => {
+ setTimeout(() => {
+ // open folder
+ component.$el.querySelector('.folder-name').click();
+
+ Vue.nextTick(() => {
+ const folderInterceptor = (request, next) => {
+ next(request.respondWith(JSON.stringify({
+ environments: [environment],
+ }), { status: 200 }));
+ };
+
+ Vue.http.interceptors.push(folderInterceptor);
+
+ // wait for next async request
+ setTimeout(() => {
+ expect(component.$el.querySelectorAll('.js-child-row').length).toEqual(1);
+ expect(component.$el.querySelector('td.text-center > a.btn').textContent).toContain('Show all');
+
+ Vue.http.interceptors = _.without(Vue.http.interceptors, folderInterceptor);
+ done();
+ });
+ });
+ });
+ });
+ });
});
diff --git a/spec/javascripts/environments/environments_store_spec.js b/spec/javascripts/environments/environments_store_spec.js
index 115d84b50f5..f617c4bdffe 100644
--- a/spec/javascripts/environments/environments_store_spec.js
+++ b/spec/javascripts/environments/environments_store_spec.js
@@ -1,38 +1,106 @@
import Store from '~/environments/stores/environments_store';
import { environmentsList, serverData } from './mock_data';
-(() => {
- describe('Store', () => {
- let store;
+describe('Store', () => {
+ let store;
- beforeEach(() => {
- store = new Store();
- });
+ beforeEach(() => {
+ store = new Store();
+ });
- it('should start with a blank state', () => {
- expect(store.state.environments.length).toEqual(0);
- expect(store.state.stoppedCounter).toEqual(0);
- expect(store.state.availableCounter).toEqual(0);
- expect(store.state.paginationInformation).toEqual({});
- });
+ it('should start with a blank state', () => {
+ expect(store.state.environments.length).toEqual(0);
+ expect(store.state.stoppedCounter).toEqual(0);
+ expect(store.state.availableCounter).toEqual(0);
+ expect(store.state.paginationInformation).toEqual({});
+ });
+ it('should store environments', () => {
+ store.storeEnvironments(serverData);
+ expect(store.state.environments.length).toEqual(serverData.length);
+ expect(store.state.environments[0]).toEqual(environmentsList[0]);
+ });
+
+ it('should store available count', () => {
+ store.storeAvailableCount(2);
+ expect(store.state.availableCounter).toEqual(2);
+ });
+
+ it('should store stopped count', () => {
+ store.storeStoppedCount(2);
+ expect(store.state.stoppedCounter).toEqual(2);
+ });
+
+ describe('store environments', () => {
it('should store environments', () => {
store.storeEnvironments(serverData);
expect(store.state.environments.length).toEqual(serverData.length);
- expect(store.state.environments[0]).toEqual(environmentsList[0]);
});
- it('should store available count', () => {
- store.storeAvailableCount(2);
- expect(store.state.availableCounter).toEqual(2);
+ it('should add folder keys when environment is a folder', () => {
+ const environment = {
+ name: 'bar',
+ size: 3,
+ id: 2,
+ };
+
+ store.storeEnvironments([environment]);
+ expect(store.state.environments[0].isFolder).toEqual(true);
+ expect(store.state.environments[0].folderName).toEqual('bar');
+ });
+
+ it('should extract content of `latest` key when provided', () => {
+ const environment = {
+ name: 'bar',
+ size: 3,
+ id: 2,
+ latest: {
+ last_deployment: {},
+ isStoppable: true,
+ },
+ };
+
+ store.storeEnvironments([environment]);
+ expect(store.state.environments[0].last_deployment).toEqual({});
+ expect(store.state.environments[0].isStoppable).toEqual(true);
});
- it('should store stopped count', () => {
- store.storeStoppedCount(2);
- expect(store.state.stoppedCounter).toEqual(2);
+ it('should store latest.name when the environment is not a folder', () => {
+ store.storeEnvironments(serverData);
+ expect(store.state.environments[0].name).toEqual(serverData[0].latest.name);
});
- it('should store pagination information', () => {
+ it('should store root level name when environment is a folder', () => {
+ store.storeEnvironments(serverData);
+ expect(store.state.environments[1].folderName).toEqual(serverData[1].name);
+ });
+ });
+
+ describe('toggleFolder', () => {
+ it('should toggle folder', () => {
+ store.storeEnvironments(serverData);
+
+ store.toggleFolder(store.state.environments[1]);
+ expect(store.state.environments[1].isOpen).toEqual(true);
+
+ store.toggleFolder(store.state.environments[1]);
+ expect(store.state.environments[1].isOpen).toEqual(false);
+ });
+ });
+
+ describe('setfolderContent', () => {
+ it('should store folder content', () => {
+ store.storeEnvironments(serverData);
+
+ store.setfolderContent(store.state.environments[1], serverData);
+
+ expect(store.state.environments[1].children.length).toEqual(serverData.length);
+ expect(store.state.environments[1].children[0].isChildren).toEqual(true);
+ });
+ });
+
+ describe('store pagination', () => {
+ it('should store normalized and integer pagination information', () => {
const pagination = {
'X-nExt-pAge': '2',
'X-page': '1',
@@ -55,4 +123,4 @@ import { environmentsList, serverData } from './mock_data';
expect(store.state.paginationInformation).toEqual(expectedResult);
});
});
-})();
+});
diff --git a/spec/javascripts/environments/mock_data.js b/spec/javascripts/environments/mock_data.js
index 30861481cc5..15e11aa686b 100644
--- a/spec/javascripts/environments/mock_data.js
+++ b/spec/javascripts/environments/mock_data.js
@@ -84,3 +84,19 @@ export const environment = {
updated_at: '2017-01-31T10:53:46.894Z',
},
};
+
+export const folder = {
+ folderName: 'build',
+ size: 5,
+ id: 12,
+ name: 'build/update-README',
+ state: 'available',
+ external_url: null,
+ environment_type: 'build',
+ last_deployment: null,
+ 'stop_action?': false,
+ environment_path: '/root/review-app/environments/12',
+ stop_path: '/root/review-app/environments/12/stop',
+ created_at: '2017-02-01T19:42:18.400Z',
+ updated_at: '2017-02-01T19:42:18.400Z',
+};
diff --git a/spec/javascripts/lib/utils/number_utility_spec.js b/spec/javascripts/lib/utils/number_utility_spec.js
new file mode 100644
index 00000000000..5fde8be9123
--- /dev/null
+++ b/spec/javascripts/lib/utils/number_utility_spec.js
@@ -0,0 +1,41 @@
+import { formatRelevantDigits } from '~/lib/utils/number_utils';
+
+describe('Number Utils', () => {
+ describe('formatRelevantDigits', () => {
+ it('returns an empty string when the number is NaN', () => {
+ expect(formatRelevantDigits('fail')).toBe('');
+ });
+
+ it('returns 4 decimals when there is 4 plus digits to the left', () => {
+ const formattedNumber = formatRelevantDigits('1000.1234567');
+ const rightFromDecimal = formattedNumber.split('.')[1];
+ const leftFromDecimal = formattedNumber.split('.')[0];
+ expect(rightFromDecimal.length).toBe(4);
+ expect(leftFromDecimal.length).toBe(4);
+ });
+
+ it('returns 3 decimals when there is 1 digit to the left', () => {
+ const formattedNumber = formatRelevantDigits('0.1234567');
+ const rightFromDecimal = formattedNumber.split('.')[1];
+ const leftFromDecimal = formattedNumber.split('.')[0];
+ expect(rightFromDecimal.length).toBe(3);
+ expect(leftFromDecimal.length).toBe(1);
+ });
+
+ it('returns 2 decimals when there is 2 digits to the left', () => {
+ const formattedNumber = formatRelevantDigits('10.1234567');
+ const rightFromDecimal = formattedNumber.split('.')[1];
+ const leftFromDecimal = formattedNumber.split('.')[0];
+ expect(rightFromDecimal.length).toBe(2);
+ expect(leftFromDecimal.length).toBe(2);
+ });
+
+ it('returns 1 decimal when there is 3 digits to the left', () => {
+ const formattedNumber = formatRelevantDigits('100.1234567');
+ const rightFromDecimal = formattedNumber.split('.')[1];
+ const leftFromDecimal = formattedNumber.split('.')[0];
+ expect(rightFromDecimal.length).toBe(1);
+ expect(leftFromDecimal.length).toBe(3);
+ });
+ });
+});
diff --git a/spec/javascripts/monitoring/prometheus_graph_spec.js b/spec/javascripts/monitoring/prometheus_graph_spec.js
index a3c1c5e1b7c..c2bcd9c0f7c 100644
--- a/spec/javascripts/monitoring/prometheus_graph_spec.js
+++ b/spec/javascripts/monitoring/prometheus_graph_spec.js
@@ -37,9 +37,11 @@ describe('PrometheusGraph', () => {
it('transforms the data', () => {
this.prometheusGraph.init(prometheusMockData.metrics);
- expect(this.prometheusGraph.data).toBeDefined();
- expect(this.prometheusGraph.data.cpu_values.length).toBe(121);
- expect(this.prometheusGraph.data.memory_values.length).toBe(121);
+ Object.keys(this.prometheusGraph.graphSpecificProperties, (key) => {
+ const graphProps = this.prometheusGraph.graphSpecificProperties[key];
+ expect(graphProps.data).toBeDefined();
+ expect(graphProps.data.length).toBe(121);
+ });
});
it('creates two graphs', () => {
@@ -68,7 +70,7 @@ describe('PrometheusGraph', () => {
expect($prometheusGraphContents.find('.label-y-axis-line')).toBeDefined();
expect($prometheusGraphContents.find('.label-axis-text')).toBeDefined();
expect($prometheusGraphContents.find('.rect-axis-text')).toBeDefined();
- expect($axisLabelContainer.find('rect').length).toBe(2);
+ expect($axisLabelContainer.find('rect').length).toBe(3);
expect($axisLabelContainer.find('text').length).toBe(4);
});
});
diff --git a/spec/lib/gitlab/backend/shell_spec.rb b/spec/lib/gitlab/backend/shell_spec.rb
index 4b08a02ec73..6675d26734e 100644
--- a/spec/lib/gitlab/backend/shell_spec.rb
+++ b/spec/lib/gitlab/backend/shell_spec.rb
@@ -69,6 +69,15 @@ describe Gitlab::Shell, lib: true do
expect(io).to have_received(:puts).with("key-42\tssh-rsa foo")
end
+ it 'handles multiple spaces in the key' do
+ io = spy(:io)
+ adder = described_class.new(io)
+
+ adder.add_key('key-42', "ssh-rsa foo")
+
+ expect(io).to have_received(:puts).with("key-42\tssh-rsa foo")
+ end
+
it 'raises an exception if the key contains a tab' do
expect do
described_class.new(StringIO.new).add_key('key-42', "ssh-rsa\tfoobar")
diff --git a/spec/lib/gitlab/import_export/all_models.yml b/spec/lib/gitlab/import_export/all_models.yml
index 002cffd3062..24654bf6afd 100644
--- a/spec/lib/gitlab/import_export/all_models.yml
+++ b/spec/lib/gitlab/import_export/all_models.yml
@@ -156,6 +156,8 @@ project:
- external_wiki_service
- kubernetes_service
- mock_ci_service
+- mock_deployment_service
+- mock_monitoring_service
- forked_project_link
- forked_from_project
- forked_project_links
diff --git a/spec/lib/gitlab/import_export/fork_spec.rb b/spec/lib/gitlab/import_export/fork_spec.rb
new file mode 100644
index 00000000000..c5ce06afd73
--- /dev/null
+++ b/spec/lib/gitlab/import_export/fork_spec.rb
@@ -0,0 +1,49 @@
+require 'spec_helper'
+
+describe 'forked project import', services: true do
+ let(:user) { create(:user) }
+ let!(:project_with_repo) { create(:project, :test_repo, name: 'test-repo-restorer', path: 'test-repo-restorer') }
+ let!(:project) { create(:empty_project) }
+ let(:export_path) { "#{Dir.tmpdir}/project_tree_saver_spec" }
+ let(:shared) { Gitlab::ImportExport::Shared.new(relative_path: project.path_with_namespace) }
+ let(:forked_from_project) { create(:project) }
+ let(:fork_link) { create(:forked_project_link, forked_from_project: project_with_repo) }
+ let(:repo_saver) { Gitlab::ImportExport::RepoSaver.new(project: project_with_repo, shared: shared) }
+ let(:bundle_path) { File.join(shared.export_path, Gitlab::ImportExport.project_bundle_filename) }
+
+ let(:repo_restorer) do
+ Gitlab::ImportExport::RepoRestorer.new(path_to_bundle: bundle_path, shared: shared, project: project)
+ end
+
+ let!(:merge_request) do
+ create(:merge_request, source_project: fork_link.forked_to_project, target_project: project_with_repo)
+ end
+
+ let(:saver) do
+ Gitlab::ImportExport::ProjectTreeSaver.new(project: project_with_repo, current_user: user, shared: shared)
+ end
+
+ let(:restorer) do
+ Gitlab::ImportExport::ProjectTreeRestorer.new(user: user, shared: shared, project: project)
+ end
+
+ before do
+ allow_any_instance_of(Gitlab::ImportExport).to receive(:storage_path).and_return(export_path)
+
+ saver.save
+ repo_saver.save
+
+ repo_restorer.restore
+ restorer.restore
+ end
+
+ after do
+ FileUtils.rm_rf(export_path)
+ FileUtils.rm_rf(project_with_repo.repository.path_to_repo)
+ FileUtils.rm_rf(project.repository.path_to_repo)
+ end
+
+ it 'can access the MR' do
+ expect(project.merge_requests.first.ensure_ref_fetched.first).to include('refs/merge-requests/1/head')
+ end
+end
diff --git a/spec/lib/gitlab/import_export/hash_util_spec.rb b/spec/lib/gitlab/import_export/hash_util_spec.rb
new file mode 100644
index 00000000000..1c3a0b23ece
--- /dev/null
+++ b/spec/lib/gitlab/import_export/hash_util_spec.rb
@@ -0,0 +1,28 @@
+require 'spec_helper'
+
+describe Gitlab::ImportExport::HashUtil, lib: true do
+ let(:stringified_array) { [{ 'test' => 1 }] }
+ let(:stringified_array_with_date) { [{ 'test_date' => '2016-04-06 06:17:44 +0200' }] }
+
+ describe '.deep_symbolize_array!' do
+ it 'symbolizes keys' do
+ expect { described_class.deep_symbolize_array!(stringified_array) }.to change {
+ stringified_array.first.keys.first
+ }.from('test').to(:test)
+ end
+ end
+
+ describe '.deep_symbolize_array_with_date!' do
+ it 'symbolizes keys' do
+ expect { described_class.deep_symbolize_array_with_date!(stringified_array_with_date) }.to change {
+ stringified_array_with_date.first.keys.first
+ }.from('test_date').to(:test_date)
+ end
+
+ it 'transforms date strings into Time objects' do
+ expect { described_class.deep_symbolize_array_with_date!(stringified_array_with_date) }.to change {
+ stringified_array_with_date.first.values.first.class
+ }.from(String).to(ActiveSupport::TimeWithZone)
+ end
+ end
+end
diff --git a/spec/lib/gitlab/import_export/merge_request_parser_spec.rb b/spec/lib/gitlab/import_export/merge_request_parser_spec.rb
new file mode 100644
index 00000000000..349be4596b6
--- /dev/null
+++ b/spec/lib/gitlab/import_export/merge_request_parser_spec.rb
@@ -0,0 +1,31 @@
+require 'spec_helper'
+
+describe Gitlab::ImportExport::MergeRequestParser do
+ let(:user) { create(:user) }
+ let!(:project) { create(:project, :test_repo, name: 'test-repo-restorer', path: 'test-repo-restorer') }
+ let(:forked_from_project) { create(:project) }
+ let(:fork_link) { create(:forked_project_link, forked_from_project: project) }
+
+ let!(:merge_request) do
+ create(:merge_request, source_project: fork_link.forked_to_project, target_project: project)
+ end
+
+ let(:parsed_merge_request) do
+ described_class.new(project,
+ merge_request.diff_head_sha,
+ merge_request,
+ merge_request.as_json).parse!
+ end
+
+ after do
+ FileUtils.rm_rf(project.repository.path_to_repo)
+ end
+
+ it 'has a source branch' do
+ expect(project.repository.branch_exists?(parsed_merge_request.source_branch)).to be true
+ end
+
+ it 'has a target branch' do
+ expect(project.repository.branch_exists?(parsed_merge_request.target_branch)).to be true
+ end
+end
diff --git a/spec/lib/gitlab/import_export/project_tree_restorer_spec.rb b/spec/lib/gitlab/import_export/project_tree_restorer_spec.rb
index c36f12dbd82..af9c25acb02 100644
--- a/spec/lib/gitlab/import_export/project_tree_restorer_spec.rb
+++ b/spec/lib/gitlab/import_export/project_tree_restorer_spec.rb
@@ -82,6 +82,12 @@ describe Gitlab::ImportExport::ProjectTreeRestorer, services: true do
expect(MergeRequestDiff.where.not(st_diffs: nil).count).to eq(9)
end
+ it 'has the correct time for merge request st_commits' do
+ st_commits = MergeRequestDiff.where.not(st_commits: nil).first.st_commits
+
+ expect(st_commits.first[:committed_date]).to be_kind_of(Time)
+ end
+
it 'has labels associated to label links, associated to issues' do
expect(Label.first.label_links.first.target).not_to be_nil
end
diff --git a/spec/lib/gitlab/import_export/project_tree_saver_spec.rb b/spec/lib/gitlab/import_export/project_tree_saver_spec.rb
index 012c22ec5ad..d2d89e3b019 100644
--- a/spec/lib/gitlab/import_export/project_tree_saver_spec.rb
+++ b/spec/lib/gitlab/import_export/project_tree_saver_spec.rb
@@ -79,6 +79,10 @@ describe Gitlab::ImportExport::ProjectTreeSaver, services: true do
expect(saved_project_json['merge_requests'].first['merge_request_diff']).not_to be_empty
end
+ it 'has merge requests diff st_diffs' do
+ expect(saved_project_json['merge_requests'].first['merge_request_diff']['utf8_st_diffs']).not_to be_nil
+ end
+
it 'has merge requests comments' do
expect(saved_project_json['merge_requests'].first['notes']).not_to be_empty
end
diff --git a/spec/lib/gitlab/import_export/relation_factory_spec.rb b/spec/lib/gitlab/import_export/relation_factory_spec.rb
index 57e412b0cef..fcc23a75ca1 100644
--- a/spec/lib/gitlab/import_export/relation_factory_spec.rb
+++ b/spec/lib/gitlab/import_export/relation_factory_spec.rb
@@ -9,7 +9,7 @@ describe Gitlab::ImportExport::RelationFactory, lib: true do
relation_hash: relation_hash,
members_mapper: members_mapper,
user: user,
- project_id: project.id)
+ project: project)
end
context 'hook object' do
diff --git a/spec/lib/gitlab/import_export/repo_bundler_spec.rb b/spec/lib/gitlab/import_export/repo_saver_spec.rb
index a7f4e11271e..a7f4e11271e 100644
--- a/spec/lib/gitlab/import_export/repo_bundler_spec.rb
+++ b/spec/lib/gitlab/import_export/repo_saver_spec.rb
diff --git a/spec/lib/gitlab/o_auth/user_spec.rb b/spec/lib/gitlab/o_auth/user_spec.rb
index 6c84a4c8b73..8f09266c3b3 100644
--- a/spec/lib/gitlab/o_auth/user_spec.rb
+++ b/spec/lib/gitlab/o_auth/user_spec.rb
@@ -40,6 +40,15 @@ describe Gitlab::OAuth::User, lib: true do
let(:provider) { 'twitter' }
describe 'signup' do
+ it 'marks user as having password_automatically_set' do
+ stub_omniauth_config(allow_single_sign_on: ['twitter'], external_providers: ['twitter'])
+
+ oauth_user.save
+
+ expect(gl_user).to be_persisted
+ expect(gl_user).to be_password_automatically_set
+ end
+
shared_examples 'to verify compliance with allow_single_sign_on' do
context 'provider is marked as external' do
it 'marks user as external' do
diff --git a/spec/lib/gitlab/project_search_results_spec.rb b/spec/lib/gitlab/project_search_results_spec.rb
index 9a8096208db..e0ebea63eb4 100644
--- a/spec/lib/gitlab/project_search_results_spec.rb
+++ b/spec/lib/gitlab/project_search_results_spec.rb
@@ -41,8 +41,10 @@ describe Gitlab::ProjectSearchResults, lib: true do
subject { described_class.parse_search_result(search_result) }
- it "returns a valid OpenStruct object" do
- is_expected.to be_an OpenStruct
+ it "returns a valid FoundBlob" do
+ is_expected.to be_an Gitlab::SearchResults::FoundBlob
+ expect(subject.id).to be_nil
+ expect(subject.path).to eq('CHANGELOG')
expect(subject.filename).to eq('CHANGELOG')
expect(subject.basename).to eq('CHANGELOG')
expect(subject.ref).to eq('master')
@@ -53,6 +55,7 @@ describe Gitlab::ProjectSearchResults, lib: true do
context "when filename has extension" do
let(:search_result) { "master:CONTRIBUTE.md:5:- [Contribute to GitLab](#contribute-to-gitlab)\n" }
+ it { expect(subject.path).to eq('CONTRIBUTE.md') }
it { expect(subject.filename).to eq('CONTRIBUTE.md') }
it { expect(subject.basename).to eq('CONTRIBUTE') }
end
@@ -60,6 +63,7 @@ describe Gitlab::ProjectSearchResults, lib: true do
context "when file under directory" do
let(:search_result) { "master:a/b/c.md:5:a b c\n" }
+ it { expect(subject.path).to eq('a/b/c.md') }
it { expect(subject.filename).to eq('a/b/c.md') }
it { expect(subject.basename).to eq('a/b/c') }
end
diff --git a/spec/lib/gitlab/workhorse_spec.rb b/spec/lib/gitlab/workhorse_spec.rb
index 3bd2a3238fe..9fbcb1fee69 100644
--- a/spec/lib/gitlab/workhorse_spec.rb
+++ b/spec/lib/gitlab/workhorse_spec.rb
@@ -188,8 +188,10 @@ describe Gitlab::Workhorse, lib: true do
context 'when Gitaly is enabled' do
let(:gitaly_params) do
+ address = Gitlab::GitalyClient.get_address('default')
{
- GitalySocketPath: URI(Gitlab::GitalyClient.get_address('default')).path,
+ GitalySocketPath: URI(address).path,
+ GitalyAddress: address,
}
end
@@ -207,31 +209,33 @@ describe Gitlab::Workhorse, lib: true do
expect(subject).to include(repo_param)
end
- {
- git_receive_pack: :post_receive_pack,
- git_upload_pack: :post_upload_pack
- }.each do |action_name, feature_flag|
- context "when #{action_name} action is passed" do
- let(:action) { action_name }
+ context "when git_upload_pack action is passed" do
+ let(:action) { 'git_upload_pack' }
+ let(:feature_flag) { :post_upload_pack }
- context 'when action is enabled by feature flag' do
- it 'includes Gitaly params in the returned value' do
- allow(Gitlab::GitalyClient).to receive(:feature_enabled?).with(feature_flag).and_return(true)
+ context 'when action is enabled by feature flag' do
+ it 'includes Gitaly params in the returned value' do
+ allow(Gitlab::GitalyClient).to receive(:feature_enabled?).with(feature_flag).and_return(true)
- expect(subject).to include(gitaly_params)
- end
+ expect(subject).to include(gitaly_params)
end
+ end
- context 'when action is not enabled by feature flag' do
- it 'does not include Gitaly params in the returned value' do
- allow(Gitlab::GitalyClient).to receive(:feature_enabled?).with(feature_flag).and_return(false)
+ context 'when action is not enabled by feature flag' do
+ it 'does not include Gitaly params in the returned value' do
+ allow(Gitlab::GitalyClient).to receive(:feature_enabled?).with(feature_flag).and_return(false)
- expect(subject).not_to include(gitaly_params)
- end
+ expect(subject).not_to include(gitaly_params)
end
end
end
+ context "when git_receive_pack action is passed" do
+ let(:action) { 'git_receive_pack' }
+
+ it { expect(subject).not_to include(gitaly_params) }
+ end
+
context "when info_refs action is passed" do
let(:action) { 'info_refs' }
diff --git a/spec/models/issue_spec.rb b/spec/models/issue_spec.rb
index b8584301baa..4bdd46a581d 100644
--- a/spec/models/issue_spec.rb
+++ b/spec/models/issue_spec.rb
@@ -51,14 +51,6 @@ describe Issue, models: true do
expect(issue.closed_at).to eq(now)
end
-
- it 'sets closed_at to nil when issue is reopened' do
- issue = create(:issue, state: 'closed')
-
- issue.reopen
-
- expect(issue.closed_at).to be_nil
- end
end
describe '#to_reference' do
diff --git a/spec/models/project_services/kubernetes_service_spec.rb b/spec/models/project_services/kubernetes_service_spec.rb
index bf7950ef1c9..e69eb0098dd 100644
--- a/spec/models/project_services/kubernetes_service_spec.rb
+++ b/spec/models/project_services/kubernetes_service_spec.rb
@@ -4,7 +4,7 @@ describe KubernetesService, models: true, caching: true do
include KubernetesHelpers
include ReactiveCachingHelpers
- let(:project) { create(:kubernetes_project) }
+ let(:project) { build_stubbed(:kubernetes_project) }
let(:service) { project.kubernetes_service }
# We use Kubeclient to interactive with the Kubernetes API. It will
@@ -32,7 +32,8 @@ describe KubernetesService, models: true, caching: true do
describe 'Validations' do
context 'when service is active' do
before { subject.active = true }
- it { is_expected.to validate_presence_of(:namespace) }
+
+ it { is_expected.not_to validate_presence_of(:namespace) }
it { is_expected.to validate_presence_of(:api_url) }
it { is_expected.to validate_presence_of(:token) }
@@ -55,7 +56,7 @@ describe KubernetesService, models: true, caching: true do
'a.b' => false,
'a*b' => false,
}.each do |namespace, validity|
- it "should validate #{namespace} as #{validity ? 'valid' : 'invalid'}" do
+ it "validates #{namespace} as #{validity ? 'valid' : 'invalid'}" do
subject.namespace = namespace
expect(subject.valid?).to eq(validity)
@@ -66,24 +67,40 @@ describe KubernetesService, models: true, caching: true do
context 'when service is inactive' do
before { subject.active = false }
- it { is_expected.not_to validate_presence_of(:namespace) }
+
it { is_expected.not_to validate_presence_of(:api_url) }
it { is_expected.not_to validate_presence_of(:token) }
end
end
describe '#initialize_properties' do
- context 'with a project' do
- let(:namespace_name) { "#{project.path}-#{project.id}" }
+ context 'without a project' do
+ it 'leaves the namespace unset' do
+ expect(described_class.new.namespace).to be_nil
+ end
+ end
+ end
+
+ describe '#fields' do
+ let(:kube_namespace) do
+ subject.fields.find { |h| h[:name] == 'namespace' }
+ end
+
+ context 'as template' do
+ before { subject.template = true }
- it 'defaults to the project name with ID' do
- expect(described_class.new(project: project).namespace).to eq(namespace_name)
+ it 'sets the namespace to the default' do
+ expect(kube_namespace).not_to be_nil
+ expect(kube_namespace[:placeholder]).to eq(subject.class::TEMPLATE_PLACEHOLDER)
end
end
- context 'without a project' do
- it 'leaves the namespace unset' do
- expect(described_class.new.namespace).to be_nil
+ context 'with associated project' do
+ before { subject.project = project }
+
+ it 'sets the namespace to the default' do
+ expect(kube_namespace).not_to be_nil
+ expect(kube_namespace[:placeholder]).to match(/\A#{Gitlab::Regex::PATH_REGEX_STR}-\d+\z/)
end
end
end
@@ -138,38 +155,40 @@ describe KubernetesService, models: true, caching: true do
before do
subject.api_url = 'https://kube.domain.com'
subject.token = 'token'
- subject.namespace = 'my-project'
subject.ca_pem = 'CA PEM DATA'
+ subject.project = project
end
- it 'sets KUBE_URL' do
- expect(subject.predefined_variables).to include(
- { key: 'KUBE_URL', value: 'https://kube.domain.com', public: true }
- )
- end
+ context 'namespace is provided' do
+ before { subject.namespace = 'my-project' }
- it 'sets KUBE_TOKEN' do
- expect(subject.predefined_variables).to include(
- { key: 'KUBE_TOKEN', value: 'token', public: false }
- )
+ it 'sets the variables' do
+ expect(subject.predefined_variables).to include(
+ { key: 'KUBE_URL', value: 'https://kube.domain.com', public: true },
+ { key: 'KUBE_TOKEN', value: 'token', public: false },
+ { key: 'KUBE_NAMESPACE', value: 'my-project', public: true },
+ { key: 'KUBE_CA_PEM', value: 'CA PEM DATA', public: true },
+ { key: 'KUBE_CA_PEM_FILE', value: 'CA PEM DATA', public: true, file: true },
+ )
+ end
end
- it 'sets KUBE_NAMESPACE' do
- expect(subject.predefined_variables).to include(
- { key: 'KUBE_NAMESPACE', value: 'my-project', public: true }
- )
- end
+ context 'no namespace provided' do
+ it 'sets the variables' do
+ expect(subject.predefined_variables).to include(
+ { key: 'KUBE_URL', value: 'https://kube.domain.com', public: true },
+ { key: 'KUBE_TOKEN', value: 'token', public: false },
+ { key: 'KUBE_CA_PEM', value: 'CA PEM DATA', public: true },
+ { key: 'KUBE_CA_PEM_FILE', value: 'CA PEM DATA', public: true, file: true },
+ )
+ end
- it 'sets KUBE_CA_PEM' do
- expect(subject.predefined_variables).to include(
- { key: 'KUBE_CA_PEM', value: 'CA PEM DATA', public: true }
- )
- end
+ it 'sets the KUBE_NAMESPACE' do
+ kube_namespace = subject.predefined_variables.find { |h| h[:key] == 'KUBE_NAMESPACE' }
- it 'sets KUBE_CA_PEM_FILE' do
- expect(subject.predefined_variables).to include(
- { key: 'KUBE_CA_PEM_FILE', value: 'CA PEM DATA', public: true, file: true }
- )
+ expect(kube_namespace).not_to be_nil
+ expect(kube_namespace[:value]).to match(/\A#{Gitlab::Regex::PATH_REGEX_STR}-\d+\z/)
+ end
end
end
diff --git a/spec/requests/api/milestones_spec.rb b/spec/requests/api/milestones_spec.rb
index 7fb728fed6f..598968aff70 100644
--- a/spec/requests/api/milestones_spec.rb
+++ b/spec/requests/api/milestones_spec.rb
@@ -306,6 +306,8 @@ describe API::Milestones, api: true do
end
it 'returns project merge_requests for a particular milestone' do
+ # eager-load another_merge_request
+ another_merge_request
get api("/projects/#{project.id}/milestones/#{milestone.id}/merge_requests", user)
expect(response).to have_http_status(200)
diff --git a/spec/requests/api/runner_spec.rb b/spec/requests/api/runner_spec.rb
index 044b989e5ba..1cfac7353d4 100644
--- a/spec/requests/api/runner_spec.rb
+++ b/spec/requests/api/runner_spec.rb
@@ -461,6 +461,29 @@ describe API::Runner do
end
end
+ context 'when dependencies is an empty array' do
+ let!(:job) { create(:ci_build_tag, pipeline: pipeline, name: 'spinach', stage: 'test', stage_idx: 0) }
+ let!(:job2) { create(:ci_build_tag, pipeline: pipeline, name: 'rubocop', stage: 'test', stage_idx: 0) }
+ let!(:empty_dependencies_job) do
+ create(:ci_build, pipeline: pipeline, token: 'test-job-token', name: 'empty_dependencies_job',
+ stage: 'deploy', stage_idx: 1,
+ options: { dependencies: [] })
+ end
+
+ before do
+ job.success
+ job2.success
+ end
+
+ it 'returns an empty array' do
+ request_job
+
+ expect(response).to have_http_status(201)
+ expect(json_response['id']).to eq(empty_dependencies_job.id)
+ expect(json_response['dependencies'].count).to eq(0)
+ end
+ end
+
context 'when job has no tags' do
before { job.update(tags: []) }
diff --git a/spec/services/system_note_service_spec.rb b/spec/services/system_note_service_spec.rb
index 90cde705b85..5ec1ed8237b 100644
--- a/spec/services/system_note_service_spec.rb
+++ b/spec/services/system_note_service_spec.rb
@@ -221,26 +221,23 @@ describe SystemNoteService, services: true do
describe '.change_status' do
subject { described_class.change_status(noteable, project, author, status, source) }
- let(:status) { 'new_status' }
- let(:source) { nil }
+ context 'with status reopened' do
+ let(:status) { 'reopened' }
+ let(:source) { nil }
- it_behaves_like 'a system note' do
- let(:action) { 'status' }
+ it_behaves_like 'a system note' do
+ let(:action) { 'opened' }
+ end
end
context 'with a source' do
+ let(:status) { 'opened' }
let(:source) { double('commit', gfm_reference: 'commit 123456') }
it 'sets the note text' do
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
- end
- end
end
describe '.merge_when_pipeline_succeeds' do
@@ -298,9 +295,23 @@ describe SystemNoteService, services: true do
describe '.change_issue_confidentiality' do
subject { described_class.change_issue_confidentiality(noteable, project, author) }
- context 'when noteable responds to `confidential`' do
+ context 'issue has been made confidential' do
+ before do
+ noteable.update_attribute(:confidential, true)
+ end
+
+ it_behaves_like 'a system note' do
+ let(:action) { 'confidential' }
+ end
+
+ it 'sets the note text' do
+ expect(subject.note).to eq 'made the issue confidential'
+ end
+ end
+
+ context 'issue has been made visible' do
it_behaves_like 'a system note' do
- let(:action) { 'confidentiality' }
+ let(:action) { 'visible' }
end
it 'sets the note text' do
diff --git a/spec/services/users/create_service_spec.rb b/spec/services/users/create_service_spec.rb
index 66f68650f81..a111aec2f89 100644
--- a/spec/services/users/create_service_spec.rb
+++ b/spec/services/users/create_service_spec.rb
@@ -122,6 +122,32 @@ describe Users::CreateService, services: true do
end
end
+ context 'when password_automatically_set parameter is true' do
+ let(:params) do
+ {
+ name: 'John Doe',
+ username: 'jduser',
+ email: 'jd@example.com',
+ password: 'mydummypass',
+ password_automatically_set: true
+ }
+ end
+
+ it 'persists the given attributes' do
+ user = service.execute
+ user.reload
+
+ expect(user).to have_attributes(
+ name: params[:name],
+ username: params[:username],
+ email: params[:email],
+ password: params[:password],
+ created_by_id: admin_user.id,
+ password_automatically_set: params[:password_automatically_set]
+ )
+ end
+ end
+
context 'when skip_confirmation parameter is true' do
let(:params) do
{ name: 'John Doe', username: 'jduser', email: 'jd@example.com', password: 'mydummypass', skip_confirmation: true }
diff --git a/spec/support/controllers/githubish_import_controller_shared_examples.rb b/spec/support/controllers/githubish_import_controller_shared_examples.rb
index 51f1015f43c..c59b30c772d 100644
--- a/spec/support/controllers/githubish_import_controller_shared_examples.rb
+++ b/spec/support/controllers/githubish_import_controller_shared_examples.rb
@@ -180,7 +180,7 @@ shared_examples 'a GitHub-ish import controller: POST create' do
it "takes the new namespace" do
expect(Gitlab::GithubImport::ProjectCreator).
to receive(:new).with(provider_repo, provider_repo.name, an_instance_of(Group), user, access_params, type: provider).
- and_return(double(execute: true))
+ and_return(double(execute: true))
post :create, target_namespace: provider_repo.name, format: :js
end
@@ -201,7 +201,7 @@ shared_examples 'a GitHub-ish import controller: POST create' do
it "takes the current user's namespace" do
expect(Gitlab::GithubImport::ProjectCreator).
to receive(:new).with(provider_repo, provider_repo.name, user.namespace, user, access_params, type: provider).
- and_return(double(execute: true))
+ and_return(double(execute: true))
post :create, format: :js
end
@@ -229,7 +229,7 @@ shared_examples 'a GitHub-ish import controller: POST create' do
end
end
- context 'user has chosen a nested namespace and name for the project' do
+ context 'user has chosen an existing nested namespace and name for the project' do
let(:parent_namespace) { create(:namespace, name: 'foo', owner: user) }
let(:nested_namespace) { create(:namespace, name: 'bar', parent: parent_namespace, owner: user) }
let(:test_name) { 'test_name' }
@@ -242,5 +242,58 @@ shared_examples 'a GitHub-ish import controller: POST create' do
post :create, { target_namespace: nested_namespace.full_path, new_name: test_name, format: :js }
end
end
+
+ context 'user has chosen a non-existent nested namespaces and name for the project' do
+ let(:test_name) { 'test_name' }
+
+ it 'takes the selected namespace and name' do
+ expect(Gitlab::GithubImport::ProjectCreator).
+ to receive(:new).with(provider_repo, test_name, kind_of(Namespace), user, access_params, type: provider).
+ and_return(double(execute: true))
+
+ post :create, { target_namespace: 'foo/bar', new_name: test_name, format: :js }
+ end
+
+ it 'creates the namespaces' do
+ allow(Gitlab::GithubImport::ProjectCreator).
+ to receive(:new).with(provider_repo, test_name, kind_of(Namespace), user, access_params, type: provider).
+ and_return(double(execute: true))
+
+ expect { post :create, { target_namespace: 'foo/bar', new_name: test_name, format: :js } }
+ .to change { Namespace.count }.by(2)
+ end
+
+ it 'new namespace has the right parent' do
+ allow(Gitlab::GithubImport::ProjectCreator).
+ to receive(:new).with(provider_repo, test_name, kind_of(Namespace), user, access_params, type: provider).
+ and_return(double(execute: true))
+
+ post :create, { target_namespace: 'foo/bar', new_name: test_name, format: :js }
+
+ expect(Namespace.find_by_path_or_name('bar').parent.path).to eq('foo')
+ end
+ end
+
+ context 'user has chosen existent and non-existent nested namespaces and name for the project' do
+ let(:test_name) { 'test_name' }
+ let!(:parent_namespace) { create(:namespace, name: 'foo', owner: user) }
+
+ it 'takes the selected namespace and name' do
+ expect(Gitlab::GithubImport::ProjectCreator).
+ to receive(:new).with(provider_repo, test_name, kind_of(Namespace), user, access_params, type: provider).
+ and_return(double(execute: true))
+
+ post :create, { target_namespace: 'foo/foobar/bar', new_name: test_name, format: :js }
+ end
+
+ it 'creates the namespaces' do
+ allow(Gitlab::GithubImport::ProjectCreator).
+ to receive(:new).with(provider_repo, test_name, kind_of(Namespace), user, access_params, type: provider).
+ and_return(double(execute: true))
+
+ expect { post :create, { target_namespace: 'foo/foobar/bar', new_name: test_name, format: :js } }
+ .to change { Namespace.count }.by(2)
+ end
+ end
end
end
diff --git a/spec/support/drag_to_helper.rb b/spec/support/drag_to_helper.rb
index 0c0659d3ecd..ae149631ed9 100644
--- a/spec/support/drag_to_helper.rb
+++ b/spec/support/drag_to_helper.rb
@@ -3,11 +3,11 @@ module DragTo
evaluate_script("simulateDrag({scrollable: $('#{scrollable}').get(0), from: {el: $('#{selector}').eq(#{list_from_index}).get(0), index: #{from_index}}, to: {el: $('#{selector}').eq(#{list_to_index}).get(0), index: #{to_index}}});")
Timeout.timeout(Capybara.default_max_wait_time) do
- loop until drag_active?
+ loop while drag_active?
end
end
def drag_active?
- page.evaluate_script('window.SIMULATE_DRAG_ACTIVE').zero?
+ page.evaluate_script('window.SIMULATE_DRAG_ACTIVE').nonzero?
end
end
diff --git a/spec/support/issuables_list_metadata_shared_examples.rb b/spec/support/issuables_list_metadata_shared_examples.rb
index 7ea4073ef2b..3406e4c3161 100644
--- a/spec/support/issuables_list_metadata_shared_examples.rb
+++ b/spec/support/issuables_list_metadata_shared_examples.rb
@@ -33,4 +33,19 @@ shared_examples 'issuables list meta-data' do |issuable_type, action = nil|
expect(meta_data[id].upvotes).to eq(id + 2)
end
end
+
+ describe "when given empty collection" do
+ let(:project2) { create(:empty_project, :public) }
+
+ it "doesn't execute any queries with false conditions" do
+ get_action =
+ if action
+ proc { get action }
+ else
+ proc { get :index, namespace_id: project2.namespace, project_id: project2 }
+ end
+
+ expect(&get_action).not_to make_queries_matching(/WHERE (?:1=0|0=1)/)
+ end
+ end
end
diff --git a/spec/support/matchers/query_matcher.rb b/spec/support/matchers/query_matcher.rb
new file mode 100644
index 00000000000..ac8c4ab91d9
--- /dev/null
+++ b/spec/support/matchers/query_matcher.rb
@@ -0,0 +1,33 @@
+RSpec::Matchers.define :make_queries_matching do |matcher, expected_count = nil|
+ supports_block_expectations
+
+ match do |block|
+ @counter = query_count(matcher, &block)
+ if expected_count
+ @counter.count == expected_count
+ else
+ @counter.count > 0
+ end
+ end
+
+ failure_message_when_negated do |_|
+ if expected_count
+ "expected #{matcher} not to match #{expected_count} queries, got #{@counter.count} matches:\n\n#{@counter.inspect}"
+ else
+ "expected #{matcher} not to match any query, got #{@counter.count} matches:\n\n#{@counter.inspect}"
+ end
+ end
+
+ failure_message do |_|
+ if expected_count
+ "expected #{matcher} to match #{expected_count} queries, got #{@counter.count} matches:\n\n#{@counter.inspect}"
+ else
+ "expected #{matcher} to match at least one query, got #{@counter.count} matches:\n\n#{@counter.inspect}"
+ end
+ end
+
+ def query_count(regex, &block)
+ @recorder = ActiveRecord::QueryRecorder.new(&block).log
+ @recorder.select{ |q| q.match(regex) }
+ end
+end
diff --git a/spec/workers/process_commit_worker_spec.rb b/spec/workers/process_commit_worker_spec.rb
index 1c383d0514d..9afe2e610b9 100644
--- a/spec/workers/process_commit_worker_spec.rb
+++ b/spec/workers/process_commit_worker_spec.rb
@@ -99,6 +99,13 @@ describe ProcessCommitWorker do
expect(metric.first_mentioned_in_commit_at).to eq(commit.committed_date)
end
+
+ it "doesn't execute any queries with false conditions" do
+ allow(commit).to receive(:safe_message).
+ and_return("Lorem Ipsum")
+
+ expect { worker.update_issue_metrics(commit, user) }.not_to make_queries_matching(/WHERE (?:1=0|0=1)/)
+ end
end
describe '#build_commit' do