From a071c2888d62f7a56349e99f5c070407df2e17c1 Mon Sep 17 00:00:00 2001 From: GitLab Bot Date: Wed, 23 Sep 2020 12:09:58 +0000 Subject: [PATCH] Add latest changes from gitlab-org/gitlab@master --- Gemfile.lock | 8 +- .../diffs/components/diff_row_utils.js | 99 +++++++ .../diffs/components/diff_table_cell.vue | 206 ------------- .../components/inline_diff_table_row.vue | 95 ++---- .../components/parallel_diff_table_row.vue | 182 +++--------- .../javascripts/pages/search/show/index.js | 3 + .../search/components/dropdown_filter.vue | 111 +++++++ .../search/confidential_filter/constants.js | 28 ++ .../search/confidential_filter/index.js | 39 +++ .../state_filter/components/state_filter.vue | 94 ------ .../search/state_filter/constants.js | 4 +- .../javascripts/search/state_filter/index.js | 27 +- .../stylesheets/framework/animations.scss | 3 +- .../stylesheets/framework/dropdowns.scss | 103 +++---- app/assets/stylesheets/pages/profile.scss | 4 + app/controllers/admin/users_controller.rb | 1 + app/controllers/groups/labels_controller.rb | 8 +- .../projects/settings/ci_cd_controller.rb | 6 - app/controllers/search_controller.rb | 1 - app/helpers/blob_helper.rb | 6 + app/helpers/search_helper.rb | 7 - app/models/merge_request.rb | 8 + app/models/project.rb | 4 + app/models/repository.rb | 4 +- app/models/user_preference.rb | 3 + .../merge_requests/ff_merge_service.rb | 2 +- app/services/merge_requests/merge_service.rb | 2 +- app/services/search_service.rb | 4 - app/views/admin/dashboard/index.html.haml | 8 +- app/views/admin/labels/_form.html.haml | 4 +- app/views/admin/labels/_label.html.haml | 4 +- app/views/admin/labels/index.html.haml | 2 +- app/views/admin/users/_user.html.haml | 7 +- app/views/admin/users/index.html.haml | 3 +- app/views/groups/labels/destroy.js.haml | 2 - app/views/projects/commits/_commits.html.haml | 3 +- app/views/projects/hook_logs/show.html.haml | 2 +- .../projects/settings/ci_cd/_badge.html.haml | 6 +- app/views/search/_results.html.haml | 4 +- app/views/search/results/_filters.html.haml | 7 + app/views/search/results/_issue.html.haml | 5 +- .../231180-ui-button-proj-hook_logs.yml | 5 + ...eferences-gitpod-enabled-to-usage-data.yml | 5 + .../244427-bold-search-term-issues.yml | 5 - ...-remove-redundant-cluster-agents-index.yml | 5 + .../244828-drop-iglu-registry-url-column.yml | 5 + .../249590-project-count-admin-view.yml | 5 + .../jdb-refactor-diff-rows-utils.yml | 5 + ...missing-not-if-importer-user-is-mapped.yml | 5 + .../unreleased/scoped-label-dashboard.yml | 5 + .../search_filter_by_confidential.yml | 7 + ...921093826_add_index_to_user_preferences.rb | 17 ++ ...1_remove_duplicate_cluster_agents_index.rb | 18 ++ ..._registry_url_from_application_settings.rb | 9 + db/schema_migrations/20200921093826 | 1 + db/schema_migrations/20200921203231 | 1 + db/schema_migrations/20200922054642 | 1 + db/structure.sql | 5 +- doc/user/infrastructure/index.md | 4 + lib/gitlab/checks/matching_merge_request.rb | 2 + .../partitioning/partition_creator.rb | 2 +- lib/gitlab/import_export/members_mapper.rb | 4 +- lib/gitlab/search_results.rb | 5 - lib/gitlab/usage_data.rb | 5 +- locale/gitlab.pot | 4 +- .../admin/users_controller_spec.rb | 6 + .../groups/labels_controller_spec.rb | 39 +++ spec/factories/usage_data.rb | 3 + spec/features/admin/admin_users_spec.rb | 14 + spec/features/projects/badges/list_spec.rb | 8 +- .../diffs/components/diff_row_utils_spec.js | 203 +++++++++++++ .../diffs/components/diff_table_cell_spec.js | 279 ------------------ ...filter_spec.js => dropdown_filter_spec.js} | 34 ++- spec/helpers/blob_helper_spec.rb | 10 + .../external_issue_reference_filter_spec.rb | 10 +- .../checks/matching_merge_request_spec.rb | 31 ++ spec/lib/gitlab/search_results_spec.rb | 19 -- .../lib/gitlab/snippet_search_results_spec.rb | 6 - spec/lib/gitlab/usage_data_spec.rb | 1 + spec/models/merge_request_spec.rb | 14 + .../merge_requests/ff_merge_service_spec.rb | 7 + .../merge_requests/merge_service_spec.rb | 1 + spec/support/helpers/usage_data_helpers.rb | 1 + .../relation_factory_shared_examples.rb | 2 +- spec/views/search/_results.html.haml_spec.rb | 22 ++ 85 files changed, 982 insertions(+), 977 deletions(-) create mode 100644 app/assets/javascripts/diffs/components/diff_row_utils.js delete mode 100644 app/assets/javascripts/diffs/components/diff_table_cell.vue create mode 100644 app/assets/javascripts/search/components/dropdown_filter.vue create mode 100644 app/assets/javascripts/search/confidential_filter/constants.js create mode 100644 app/assets/javascripts/search/confidential_filter/index.js delete mode 100644 app/assets/javascripts/search/state_filter/components/state_filter.vue delete mode 100644 app/views/groups/labels/destroy.js.haml create mode 100644 app/views/search/results/_filters.html.haml create mode 100644 changelogs/unreleased/231180-ui-button-proj-hook_logs.yml create mode 100644 changelogs/unreleased/242016-fj-add-user-preferences-gitpod-enabled-to-usage-data.yml delete mode 100644 changelogs/unreleased/244427-bold-search-term-issues.yml create mode 100644 changelogs/unreleased/244484-remove-redundant-cluster-agents-index.yml create mode 100644 changelogs/unreleased/244828-drop-iglu-registry-url-column.yml create mode 100644 changelogs/unreleased/249590-project-count-admin-view.yml create mode 100644 changelogs/unreleased/jdb-refactor-diff-rows-utils.yml create mode 100644 changelogs/unreleased/kassio-do-not-add-missing-not-if-importer-user-is-mapped.yml create mode 100644 changelogs/unreleased/scoped-label-dashboard.yml create mode 100644 config/feature_flags/development/search_filter_by_confidential.yml create mode 100644 db/migrate/20200921093826_add_index_to_user_preferences.rb create mode 100644 db/migrate/20200921203231_remove_duplicate_cluster_agents_index.rb create mode 100644 db/post_migrate/20200922054642_drop_snowplow_iglu_registry_url_from_application_settings.rb create mode 100644 db/schema_migrations/20200921093826 create mode 100644 db/schema_migrations/20200921203231 create mode 100644 db/schema_migrations/20200922054642 create mode 100644 spec/frontend/diffs/components/diff_row_utils_spec.js delete mode 100644 spec/frontend/diffs/components/diff_table_cell_spec.js rename spec/frontend/search/components/{state_filter_spec.js => dropdown_filter_spec.js} (73%) create mode 100644 spec/lib/gitlab/checks/matching_merge_request_spec.rb diff --git a/Gemfile.lock b/Gemfile.lock index 7d743f2029e..7af7c8f14fe 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -885,10 +885,10 @@ GEM bundler (>= 1.3.0) railties (= 6.0.3.1) sprockets-rails (>= 2.0.0) - rails-controller-testing (1.0.4) - actionpack (>= 5.0.1.x) - actionview (>= 5.0.1.x) - activesupport (>= 5.0.1.x) + rails-controller-testing (1.0.5) + actionpack (>= 5.0.1.rc1) + actionview (>= 5.0.1.rc1) + activesupport (>= 5.0.1.rc1) rails-dom-testing (2.0.3) activesupport (>= 4.2.0) nokogiri (>= 1.6) diff --git a/app/assets/javascripts/diffs/components/diff_row_utils.js b/app/assets/javascripts/diffs/components/diff_row_utils.js new file mode 100644 index 00000000000..998320c3245 --- /dev/null +++ b/app/assets/javascripts/diffs/components/diff_row_utils.js @@ -0,0 +1,99 @@ +import { getParameterByName, parseBoolean } from '~/lib/utils/common_utils'; +import { __ } from '~/locale'; +import { + MATCH_LINE_TYPE, + CONTEXT_LINE_TYPE, + LINE_HOVER_CLASS_NAME, + OLD_NO_NEW_LINE_TYPE, + NEW_NO_NEW_LINE_TYPE, + EMPTY_CELL_TYPE, +} from '../constants'; + +export const isHighlighted = (state, line, isCommented) => { + if (isCommented) return true; + + const lineCode = line?.line_code; + return lineCode ? lineCode === state.diffs.highlightedRow : false; +}; + +export const isContextLine = type => type === CONTEXT_LINE_TYPE; + +export const isMatchLine = type => type === MATCH_LINE_TYPE; + +export const isMetaLine = type => + [OLD_NO_NEW_LINE_TYPE, NEW_NO_NEW_LINE_TYPE, EMPTY_CELL_TYPE].includes(type); + +export const shouldRenderCommentButton = ( + isLoggedIn, + isCommentButtonRendered, + featureMergeRefHeadComments = false, +) => { + if (!isCommentButtonRendered) { + return false; + } + + if (isLoggedIn) { + const isDiffHead = parseBoolean(getParameterByName('diff_head')); + return !isDiffHead || featureMergeRefHeadComments; + } + + return false; +}; + +export const hasDiscussions = line => line?.discussions?.length > 0; + +export const lineHref = line => `#${line?.line_code || ''}`; + +export const lineCode = line => { + if (!line) return undefined; + return line.line_code || line.left?.line_code || line.right?.line_code; +}; + +export const classNameMapCell = (line, hll, isLoggedIn, isHover) => { + if (!line) return []; + const { type } = line; + + return [ + type, + { + hll, + [LINE_HOVER_CLASS_NAME]: isLoggedIn && isHover && !isContextLine(type) && !isMetaLine(type), + }, + ]; +}; + +export const addCommentTooltip = line => { + let tooltip; + if (!line) return tooltip; + + tooltip = __('Add a comment to this line'); + const brokenSymlinks = line.commentsDisabled; + + if (brokenSymlinks) { + if (brokenSymlinks.wasSymbolic || brokenSymlinks.isSymbolic) { + tooltip = __( + 'Commenting on symbolic links that replace or are replaced by files is currently not supported.', + ); + } else if (brokenSymlinks.wasReal || brokenSymlinks.isReal) { + tooltip = __( + 'Commenting on files that replace or are replaced by symbolic links is currently not supported.', + ); + } + } + + return tooltip; +}; + +export const parallelViewLeftLineType = (line, hll) => { + if (line?.right?.type === NEW_NO_NEW_LINE_TYPE) { + return OLD_NO_NEW_LINE_TYPE; + } + + const lineTypeClass = line?.left ? line.left.type : EMPTY_CELL_TYPE; + + return [lineTypeClass, { hll }]; +}; + +export const shouldShowCommentButton = (hover, context, meta, discussions) => { + return hover && !context && !meta && !discussions; +}; diff --git a/app/assets/javascripts/diffs/components/diff_table_cell.vue b/app/assets/javascripts/diffs/components/diff_table_cell.vue deleted file mode 100644 index 49982a81372..00000000000 --- a/app/assets/javascripts/diffs/components/diff_table_cell.vue +++ /dev/null @@ -1,206 +0,0 @@ - - - diff --git a/app/assets/javascripts/diffs/components/inline_diff_table_row.vue b/app/assets/javascripts/diffs/components/inline_diff_table_row.vue index 7fab750089e..f9d491603cb 100644 --- a/app/assets/javascripts/diffs/components/inline_diff_table_row.vue +++ b/app/assets/javascripts/diffs/components/inline_diff_table_row.vue @@ -1,22 +1,9 @@ + + diff --git a/app/assets/javascripts/search/confidential_filter/constants.js b/app/assets/javascripts/search/confidential_filter/constants.js new file mode 100644 index 00000000000..4665ce6a5d1 --- /dev/null +++ b/app/assets/javascripts/search/confidential_filter/constants.js @@ -0,0 +1,28 @@ +import { __ } from '~/locale'; + +export const FILTER_HEADER = __('Confidentiality'); + +export const FILTER_STATES = { + ANY: { + label: __('Any'), + value: null, + }, + CONFIDENTIAL: { + label: __('Confidential'), + value: 'yes', + }, + NOT_CONFIDENTIAL: { + label: __('Not confidential'), + value: 'no', + }, +}; + +export const SCOPES = { + ISSUES: 'issues', +}; + +export const FILTER_STATES_BY_SCOPE = { + [SCOPES.ISSUES]: [FILTER_STATES.ANY, FILTER_STATES.CONFIDENTIAL, FILTER_STATES.NOT_CONFIDENTIAL], +}; + +export const FILTER_PARAM = 'confidential'; diff --git a/app/assets/javascripts/search/confidential_filter/index.js b/app/assets/javascripts/search/confidential_filter/index.js new file mode 100644 index 00000000000..bec772be0dd --- /dev/null +++ b/app/assets/javascripts/search/confidential_filter/index.js @@ -0,0 +1,39 @@ +import Vue from 'vue'; +import Translate from '~/vue_shared/translate'; +import DropdownFilter from '../components/dropdown_filter.vue'; +import { + FILTER_HEADER, + FILTER_PARAM, + FILTER_STATES_BY_SCOPE, + FILTER_STATES, + SCOPES, +} from './constants'; + +Vue.use(Translate); + +export default () => { + const el = document.getElementById('js-search-filter-by-confidential'); + + if (!el) return false; + + return new Vue({ + el, + data() { + return { ...el.dataset }; + }, + + render(createElement) { + return createElement(DropdownFilter, { + props: { + initialFilter: this.filter, + filtersArray: FILTER_STATES_BY_SCOPE[this.scope], + filters: FILTER_STATES, + header: FILTER_HEADER, + param: FILTER_PARAM, + scope: this.scope, + supportedScopes: Object.values(SCOPES), + }, + }); + }, + }); +}; diff --git a/app/assets/javascripts/search/state_filter/components/state_filter.vue b/app/assets/javascripts/search/state_filter/components/state_filter.vue deleted file mode 100644 index f08adaf8c83..00000000000 --- a/app/assets/javascripts/search/state_filter/components/state_filter.vue +++ /dev/null @@ -1,94 +0,0 @@ - - - diff --git a/app/assets/javascripts/search/state_filter/constants.js b/app/assets/javascripts/search/state_filter/constants.js index 2f11cab9044..00ae1bd9750 100644 --- a/app/assets/javascripts/search/state_filter/constants.js +++ b/app/assets/javascripts/search/state_filter/constants.js @@ -2,8 +2,6 @@ import { __ } from '~/locale'; export const FILTER_HEADER = __('Status'); -export const FILTER_TEXT = __('Any Status'); - export const FILTER_STATES = { ANY: { label: __('Any'), @@ -37,3 +35,5 @@ export const FILTER_STATES_BY_SCOPE = { FILTER_STATES.CLOSED, ], }; + +export const FILTER_PARAM = 'state'; diff --git a/app/assets/javascripts/search/state_filter/index.js b/app/assets/javascripts/search/state_filter/index.js index 13708574cfb..2c12885c40b 100644 --- a/app/assets/javascripts/search/state_filter/index.js +++ b/app/assets/javascripts/search/state_filter/index.js @@ -1,6 +1,13 @@ import Vue from 'vue'; import Translate from '~/vue_shared/translate'; -import StateFilter from './components/state_filter.vue'; +import DropdownFilter from '../components/dropdown_filter.vue'; +import { + FILTER_HEADER, + FILTER_PARAM, + FILTER_STATES_BY_SCOPE, + FILTER_STATES, + SCOPES, +} from './constants'; Vue.use(Translate); @@ -11,22 +18,20 @@ export default () => { return new Vue({ el, - components: { - StateFilter, - }, data() { - const { dataset } = this.$options.el; - return { - scope: dataset.scope, - state: dataset.state, - }; + return { ...el.dataset }; }, render(createElement) { - return createElement('state-filter', { + return createElement(DropdownFilter, { props: { + initialFilter: this.filter, + filtersArray: FILTER_STATES_BY_SCOPE[this.scope], + filters: FILTER_STATES, + header: FILTER_HEADER, + param: FILTER_PARAM, scope: this.scope, - state: this.state, + supportedScopes: Object.values(SCOPES), }, }); }, diff --git a/app/assets/stylesheets/framework/animations.scss b/app/assets/stylesheets/framework/animations.scss index 136ff82e0f8..196fb3a7088 100644 --- a/app/assets/stylesheets/framework/animations.scss +++ b/app/assets/stylesheets/framework/animations.scss @@ -112,8 +112,7 @@ a { } .dropdown-menu a, -.dropdown-menu button, -.dropdown-menu-nav a { +.dropdown-menu button { transition: none; } diff --git a/app/assets/stylesheets/framework/dropdowns.scss b/app/assets/stylesheets/framework/dropdowns.scss index ad5864ef6d9..7baaa26fbec 100644 --- a/app/assets/stylesheets/framework/dropdowns.scss +++ b/app/assets/stylesheets/framework/dropdowns.scss @@ -33,8 +33,7 @@ } .show.dropdown { - .dropdown-menu, - .dropdown-menu-nav { + .dropdown-menu { @include set-visible; min-height: $dropdown-min-height; max-height: $dropdown-max-height; @@ -258,8 +257,7 @@ } } -.dropdown-menu, -.dropdown-menu-nav { +.dropdown-menu { display: none; position: absolute; width: auto; @@ -393,49 +391,56 @@ pointer-events: none; } - .dropdown-menu li { - cursor: pointer; + .dropdown-menu { + display: none; + opacity: 1; + visibility: visible; + transform: translateY(0); - &.droplab-item-active button { - @include dropdown-item-hover; - } + li { + cursor: pointer; - > a, - > button { - display: flex; - margin: 0; - text-overflow: inherit; - text-align: left; + &.droplab-item-active button { + @include dropdown-item-hover; + } - &.btn .fa:not(:last-child) { + > a, + > button { + display: flex; + margin: 0; + text-overflow: inherit; + text-align: left; + + &.btn .fa:not(:last-child) { + margin-left: 5px; + } + } + + > button.dropdown-epic-button { + flex-direction: column; + + .reference { + color: $gray-300; + margin-top: $gl-padding-4; + } + } + + &.droplab-item-selected i { + visibility: visible; + } + + .icon { + visibility: hidden; + } + + .description { + display: inline-block; + white-space: normal; margin-left: 5px; - } - } - > button.dropdown-epic-button { - flex-direction: column; - - .reference { - color: $gray-300; - margin-top: $gl-padding-4; - } - } - - &.droplab-item-selected i { - visibility: visible; - } - - .icon { - visibility: hidden; - } - - .description { - display: inline-block; - white-space: normal; - margin-left: 5px; - - p { - margin-bottom: 0; + p { + margin-bottom: 0; + } } } } @@ -447,21 +452,12 @@ } } -.droplab-dropdown .dropdown-menu, -.droplab-dropdown .dropdown-menu-nav { - display: none; - opacity: 1; - visibility: visible; - transform: translateY(0); -} - .comment-type-dropdown.show .dropdown-menu { display: block; } .filtered-search-box-input-container { - .dropdown-menu, - .dropdown-menu-nav { + .dropdown-menu { max-width: 280px; } } @@ -850,8 +846,7 @@ } header.navbar-gitlab .dropdown { - .dropdown-menu, - .dropdown-menu-nav { + .dropdown-menu { width: 100%; min-width: 100%; } diff --git a/app/assets/stylesheets/pages/profile.scss b/app/assets/stylesheets/pages/profile.scss index 4dc1f2034f3..ddffcf382c6 100644 --- a/app/assets/stylesheets/pages/profile.scss +++ b/app/assets/stylesheets/pages/profile.scss @@ -227,6 +227,10 @@ padding-left: 40px; } + .gl-label-scoped { + --label-inset-border: inset 0 0 0 1px currentColor; + } + @include media-breakpoint-up(lg) { margin-right: 5px; } diff --git a/app/controllers/admin/users_controller.rb b/app/controllers/admin/users_controller.rb index 050f83edacb..c4f72372f19 100644 --- a/app/controllers/admin/users_controller.rb +++ b/app/controllers/admin/users_controller.rb @@ -9,6 +9,7 @@ class Admin::UsersController < Admin::ApplicationController def index @users = User.filter_items(params[:filter]).order_name_asc @users = @users.search_with_secondary_emails(params[:search_query]) if params[:search_query].present? + @users = @users.includes(:authorized_projects) # rubocop: disable CodeReuse/ActiveRecord @users = @users.sort_by_attribute(@sort = params[:sort]) @users = @users.page(params[:page]) end diff --git a/app/controllers/groups/labels_controller.rb b/app/controllers/groups/labels_controller.rb index 1034ca6cd7b..df0b71d2cf0 100644 --- a/app/controllers/groups/labels_controller.rb +++ b/app/controllers/groups/labels_controller.rb @@ -60,13 +60,7 @@ class Groups::LabelsController < Groups::ApplicationController def destroy @label.destroy - - respond_to do |format| - format.html do - redirect_to group_labels_path(@group), status: :found, notice: "#{@label.name} deleted permanently" - end - format.js - end + redirect_to group_labels_path(@group), status: :found, notice: "#{@label.name} deleted permanently" end protected diff --git a/app/controllers/projects/settings/ci_cd_controller.rb b/app/controllers/projects/settings/ci_cd_controller.rb index 8746c79ed4d..d0d100fd88c 100644 --- a/app/controllers/projects/settings/ci_cd_controller.rb +++ b/app/controllers/projects/settings/ci_cd_controller.rb @@ -11,8 +11,6 @@ module Projects push_frontend_feature_flag(:ci_key_autocomplete, default_enabled: true) end - helper_method :highlight_badge - def show end @@ -52,10 +50,6 @@ module Projects private - def highlight_badge(name, content, language = nil) - Gitlab::Highlight.highlight(name, content, language: language) - end - def update_params params.require(:project).permit(*permitted_project_params) end diff --git a/app/controllers/search_controller.rb b/app/controllers/search_controller.rb index e8141bbf448..dedaf0c903a 100644 --- a/app/controllers/search_controller.rb +++ b/app/controllers/search_controller.rb @@ -38,7 +38,6 @@ class SearchController < ApplicationController @show_snippets = search_service.show_snippets? @search_results = search_service.search_results @search_objects = search_service.search_objects(preload_method) - @search_highlight = search_service.search_highlight render_commits if @scope == 'commits' eager_load_user_status if @scope == 'users' diff --git a/app/helpers/blob_helper.rb b/app/helpers/blob_helper.rb index 6d66db0e3fb..2eff87ae0ec 100644 --- a/app/helpers/blob_helper.rb +++ b/app/helpers/blob_helper.rb @@ -1,6 +1,12 @@ # frozen_string_literal: true module BlobHelper + def highlight(file_name, file_content, language: nil, plain: false) + highlighted = Gitlab::Highlight.highlight(file_name, file_content, plain: plain, language: language) + + raw %(
#{highlighted}
) + end + def no_highlight_files %w(credits changelog news copying copyright license authors) end diff --git a/app/helpers/search_helper.rb b/app/helpers/search_helper.rb index e4ab4fc7c9a..0dea6217276 100644 --- a/app/helpers/search_helper.rb +++ b/app/helpers/search_helper.rb @@ -299,13 +299,6 @@ module SearchHelper simple_search_highlight_and_truncate(issue.description, search_term, highlighter: '\1') end - def simple_search_highlight_and_truncate(text, phrase, options = {}) - truncate_length = options.delete(:length) { 200 } - text = truncate(text, length: truncate_length) - phrase = phrase.split - highlight(text, phrase, options) - end - def show_user_search_tab? return false if Feature.disabled?(:users_search, default_enabled: true) diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb index b334da1ca91..1965c5dacc7 100644 --- a/app/models/merge_request.rb +++ b/app/models/merge_request.rb @@ -1302,6 +1302,14 @@ class MergeRequest < ApplicationRecord unlock_mr end + def update_and_mark_in_progress_merge_commit_sha(commit_id) + self.update(in_progress_merge_commit_sha: commit_id) + # Since another process checks for matching merge request, we need + # to make it possible to detect whether the query should go to the + # primary. + target_project.mark_primary_write_location + end + def diverged_commits_count cache = Rails.cache.read(:"merge_request_#{id}_diverged_commits") diff --git a/app/models/project.rb b/app/models/project.rb index 4db0eaa0442..54925ae4ed8 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -2292,6 +2292,10 @@ class Project < ApplicationRecord [] end + def mark_primary_write_location + # Overriden in EE + end + def toggle_ci_cd_settings!(settings_attribute) ci_cd_settings.toggle!(settings_attribute) end diff --git a/app/models/repository.rb b/app/models/repository.rb index ef17e010ba8..6bc2ec24811 100644 --- a/app/models/repository.rb +++ b/app/models/repository.rb @@ -853,7 +853,7 @@ class Repository def merge(user, source_sha, merge_request, message) with_cache_hooks do raw_repository.merge(user, source_sha, merge_request.target_branch, message) do |commit_id| - merge_request.update(in_progress_merge_commit_sha: commit_id) + merge_request.update_and_mark_in_progress_merge_commit_sha(commit_id) nil # Return value does not matter. end end @@ -873,7 +873,7 @@ class Repository their_commit_id = commit(source)&.id raise 'Invalid merge source' if their_commit_id.nil? - merge_request&.update(in_progress_merge_commit_sha: their_commit_id) + merge_request&.update_and_mark_in_progress_merge_commit_sha(their_commit_id) with_cache_hooks { raw.ff_merge(user, their_commit_id, target_branch) } end diff --git a/app/models/user_preference.rb b/app/models/user_preference.rb index d3b3a46bf74..c05bc80415a 100644 --- a/app/models/user_preference.rb +++ b/app/models/user_preference.rb @@ -8,6 +8,9 @@ class UserPreference < ApplicationRecord belongs_to :user + scope :with_user, -> { joins(:user) } + scope :gitpod_enabled, -> { where(gitpod_enabled: true) } + validates :issue_notes_filter, :merge_request_notes_filter, inclusion: { in: NOTES_FILTERS.values }, presence: true validates :tab_width, numericality: { only_integer: true, diff --git a/app/services/merge_requests/ff_merge_service.rb b/app/services/merge_requests/ff_merge_service.rb index 79011094e88..c5640047899 100644 --- a/app/services/merge_requests/ff_merge_service.rb +++ b/app/services/merge_requests/ff_merge_service.rb @@ -27,7 +27,7 @@ module MergeRequests rescue StandardError => e raise MergeError, "Something went wrong during merge: #{e.message}" ensure - merge_request.update(in_progress_merge_commit_sha: nil) + merge_request.update_and_mark_in_progress_merge_commit_sha(nil) end end end diff --git a/app/services/merge_requests/merge_service.rb b/app/services/merge_requests/merge_service.rb index 437e87dadf7..ba22b458777 100644 --- a/app/services/merge_requests/merge_service.rb +++ b/app/services/merge_requests/merge_service.rb @@ -84,7 +84,7 @@ module MergeRequests merge_request.update!(merge_commit_sha: commit_id) ensure - merge_request.update_column(:in_progress_merge_commit_sha, nil) + merge_request.update_and_mark_in_progress_merge_commit_sha(nil) end def try_merge diff --git a/app/services/search_service.rb b/app/services/search_service.rb index 3ccd67c8d30..278cf389e07 100644 --- a/app/services/search_service.rb +++ b/app/services/search_service.rb @@ -65,10 +65,6 @@ class SearchService @search_objects ||= redact_unauthorized_results(search_results.objects(scope, page: params[:page], per_page: per_page, preload_method: preload_method)) end - def search_highlight - search_results.highlight_map(scope) - end - private def per_page diff --git a/app/views/admin/dashboard/index.html.haml b/app/views/admin/dashboard/index.html.haml index 4acfc96caf2..2f3bb19b955 100644 --- a/app/views/admin/dashboard/index.html.haml +++ b/app/views/admin/dashboard/index.html.haml @@ -19,7 +19,7 @@ %h3.text-center = s_('AdminArea|Projects: %{number_of_projects}') % { number_of_projects: approximate_count_with_delimiters(@counts, Project) } %hr - = link_to(s_('AdminArea|New project'), new_project_path, class: "btn btn-success gl-w-full") + = link_to(s_('AdminArea|New project'), new_project_path, class: "btn gl-button btn-success gl-w-full") .col-sm-4 .info-well.dark-well .well-segment.well-centered @@ -28,8 +28,8 @@ = s_('AdminArea|Users: %{number_of_users}') % { number_of_users: approximate_count_with_delimiters(@counts, User) } %hr .btn-group.d-flex{ role: 'group' } - = link_to s_('AdminArea|New user'), new_admin_user_path, class: "btn btn-success gl-w-full" - = link_to s_('AdminArea|Users statistics'), admin_dashboard_stats_path, class: 'btn btn-primary gl-w-full' + = link_to s_('AdminArea|New user'), new_admin_user_path, class: "btn gl-button btn-success gl-w-full" + = link_to s_('AdminArea|Users statistics'), admin_dashboard_stats_path, class: 'btn gl-button btn-info gl-w-full' .col-sm-4 .info-well.dark-well .well-segment.well-centered @@ -37,7 +37,7 @@ %h3.text-center = s_('AdminArea|Groups: %{number_of_groups}') % { number_of_groups: approximate_count_with_delimiters(@counts, Group) } %hr - = link_to s_('AdminArea|New group'), new_admin_group_path, class: "btn btn-success gl-w-full" + = link_to s_('AdminArea|New group'), new_admin_group_path, class: "btn gl-button btn-success gl-w-full" .row .col-md-4 #js-admin-statistics-container diff --git a/app/views/admin/labels/_form.html.haml b/app/views/admin/labels/_form.html.haml index 299d0a12e6c..664081339f3 100644 --- a/app/views/admin/labels/_form.html.haml +++ b/app/views/admin/labels/_form.html.haml @@ -27,5 +27,5 @@ = render_suggested_colors .form-actions - = f.submit _('Save'), class: 'btn btn-success js-save-button' - = link_to _("Cancel"), admin_labels_path, class: 'btn btn-cancel' + = f.submit _('Save'), class: 'btn gl-button btn-success js-save-button' + = link_to _("Cancel"), admin_labels_path, class: 'btn gl-button btn-cancel' diff --git a/app/views/admin/labels/_label.html.haml b/app/views/admin/labels/_label.html.haml index 6d934654c5d..b31b9bdab0a 100644 --- a/app/views/admin/labels/_label.html.haml +++ b/app/views/admin/labels/_label.html.haml @@ -1,7 +1,7 @@ %li.label-list-item{ id: dom_id(label) } = render "shared/label_row", label: label.present(issuable_subject: nil) .label-actions-list - = link_to edit_admin_label_path(label), class: 'btn btn-transparent label-action has-tooltip', title: _('Edit'), data: { placement: 'bottom' }, aria_label: _('Edit') do + = link_to edit_admin_label_path(label), class: 'btn gl-button btn-transparent label-action has-tooltip', title: _('Edit'), data: { placement: 'bottom' }, aria_label: _('Edit') do = sprite_icon('pencil') - = link_to admin_label_path(label), class: 'btn btn-transparent remove-row label-action has-tooltip', title: _('Delete'), data: { placement: 'bottom', confirm: "Delete this label? Are you sure?" }, aria_label: _('Delete'), method: :delete, remote: true do + = link_to admin_label_path(label), class: 'btn gl-button btn-transparent remove-row label-action has-tooltip', title: _('Delete'), data: { placement: 'bottom', confirm: "Delete this label? Are you sure?" }, aria_label: _('Delete'), method: :delete, remote: true do = sprite_icon('remove') diff --git a/app/views/admin/labels/index.html.haml b/app/views/admin/labels/index.html.haml index 38137f360fd..76d37626fff 100644 --- a/app/views/admin/labels/index.html.haml +++ b/app/views/admin/labels/index.html.haml @@ -1,7 +1,7 @@ - page_title _("Labels") %div - = link_to new_admin_label_path, class: "float-right btn btn-nr btn-success" do + = link_to new_admin_label_path, class: "float-right btn gl-button btn-nr btn-success" do = _('New label') %h3.page-title = _('Labels') diff --git a/app/views/admin/users/_user.html.haml b/app/views/admin/users/_user.html.haml index 160303890f5..284307d1d54 100644 --- a/app/views/admin/users/_user.html.haml +++ b/app/views/admin/users/_user.html.haml @@ -4,7 +4,12 @@ = _('Name') .table-mobile-content = render 'user_detail', user: user - .table-section.section-25 + .table-section.section-10 + .table-mobile-header{ role: 'rowheader' } + = _('Projects') + .table-mobile-content.gl-str-truncated{ data: { testid: "user-project-count-#{user.id}" } } + = user.authorized_projects.length + .table-section.section-15 .table-mobile-header{ role: 'rowheader' } = _('Created on') .table-mobile-content diff --git a/app/views/admin/users/index.html.haml b/app/views/admin/users/index.html.haml index 118bdf7bb17..78c3e41278d 100644 --- a/app/views/admin/users/index.html.haml +++ b/app/views/admin/users/index.html.haml @@ -72,7 +72,8 @@ .table-holder .thead-white.text-nowrap.gl-responsive-table-row.table-row-header{ role: 'row' } .table-section.section-40{ role: 'rowheader' }= _('Name') - .table-section.section-25{ role: 'rowheader' }= _('Created on') + .table-section.section-10{ role: 'rowheader' }= _('Projects') + .table-section.section-15{ role: 'rowheader' }= _('Created on') .table-section.section-15{ role: 'rowheader' }= _('Last activity') = render partial: 'admin/users/user', collection: @users diff --git a/app/views/groups/labels/destroy.js.haml b/app/views/groups/labels/destroy.js.haml deleted file mode 100644 index 3dfbfc77c0d..00000000000 --- a/app/views/groups/labels/destroy.js.haml +++ /dev/null @@ -1,2 +0,0 @@ -- if @group.labels.empty? - $('.labels').load(document.URL + ' .nothing-here-block').hide().fadeIn(1000) diff --git a/app/views/projects/commits/_commits.html.haml b/app/views/projects/commits/_commits.html.haml index 293500a6c31..63cc96c2c05 100644 --- a/app/views/projects/commits/_commits.html.haml +++ b/app/views/projects/commits/_commits.html.haml @@ -28,7 +28,8 @@ = render partial: 'projects/commits/commit', collection: context_commits, locals: { project: project, ref: ref, merge_request: merge_request } - if hidden > 0 - %li.alert.alert-warning + %li.gl-alert.gl-alert-warning + = sprite_icon('warning', size: 16, css_class: 'gl-icon gl-alert-icon gl-alert-icon-no-title') = n_('%s additional commit has been omitted to prevent performance issues.', '%s additional commits have been omitted to prevent performance issues.', hidden) % number_with_delimiter(hidden) - if project.context_commits_enabled? && can_update_merge_request && context_commits&.empty? diff --git a/app/views/projects/hook_logs/show.html.haml b/app/views/projects/hook_logs/show.html.haml index 8a8c396a9e4..ebe179c3454 100644 --- a/app/views/projects/hook_logs/show.html.haml +++ b/app/views/projects/hook_logs/show.html.haml @@ -7,6 +7,6 @@ %h4.gl-mt-0 Request details .col-lg-9 - = link_to 'Resend Request', @hook_log.present.retry_path, method: :post, class: "btn btn-default float-right gl-ml-3" + = link_to 'Resend Request', @hook_log.present.retry_path, method: :post, class: "btn gl-button btn-default float-right gl-ml-3" = render partial: 'shared/hook_logs/content', locals: { hook_log: @hook_log } diff --git a/app/views/projects/settings/ci_cd/_badge.html.haml b/app/views/projects/settings/ci_cd/_badge.html.haml index 2c3e6387972..82c8ec088e5 100644 --- a/app/views/projects/settings/ci_cd/_badge.html.haml +++ b/app/views/projects/settings/ci_cd/_badge.html.haml @@ -15,18 +15,18 @@ .col-md-2.text-center Markdown .col-md-10.code.js-syntax-highlight - = highlight_badge('.md', badge.to_markdown, language: 'markdown') + = highlight('.md', badge.to_markdown, language: 'markdown') .row %hr .row .col-md-2.text-center HTML .col-md-10.code.js-syntax-highlight - = highlight_badge('.html', badge.to_html, language: 'html') + = highlight('.html', badge.to_html, language: 'html') .row %hr .row .col-md-2.text-center AsciiDoc .col-md-10.code.js-syntax-highlight - = highlight_badge('.adoc', badge.to_asciidoc) + = highlight('.adoc', badge.to_asciidoc) diff --git a/app/views/search/_results.html.haml b/app/views/search/_results.html.haml index e0dbb5135e9..b02a147af71 100644 --- a/app/views/search/_results.html.haml +++ b/app/views/search/_results.html.haml @@ -1,4 +1,5 @@ - if @search_objects.to_a.empty? + = render partial: "search/results/filters" = render partial: "search/results/empty" = render_if_exists 'shared/promotions/promote_advanced_search' = render_if_exists 'search/form_revert_to_basic' @@ -21,8 +22,7 @@ - link_to_group = link_to(@group.name, @group, class: 'ml-md-1') = _("in group %{link_to_group}").html_safe % { link_to_group: link_to_group } = render_if_exists 'shared/promotions/promote_advanced_search' - - #js-search-filter-by-state{ 'v-cloak': true, data: { scope: @scope, state: params[:state] } } + = render partial: "search/results/filters" .results.gl-mt-3 - if @scope == 'commits' diff --git a/app/views/search/results/_filters.html.haml b/app/views/search/results/_filters.html.haml new file mode 100644 index 00000000000..8c402ddb3d1 --- /dev/null +++ b/app/views/search/results/_filters.html.haml @@ -0,0 +1,7 @@ +.d-lg-flex.align-items-end + #js-search-filter-by-state{ 'v-cloak': true, data: { scope: @scope, filter: params[:state]} } + - if Feature.enabled?(:search_filter_by_confidential, @group) + #js-search-filter-by-confidential{ 'v-cloak': true, data: { scope: @scope, filter: params[:confidential] } } + + - if %w(issues merge_requests).include?(@scope) + %hr.gl-mt-4.gl-mb-4 diff --git a/app/views/search/results/_issue.html.haml b/app/views/search/results/_issue.html.haml index a101e60f297..e0336d98f04 100644 --- a/app/views/search/results/_issue.html.haml +++ b/app/views/search/results/_issue.html.haml @@ -9,5 +9,6 @@ %span.term.str-truncated.gl-font-weight-bold.gl-ml-2= issue.title .gl-text-gray-500.gl-my-3 = sprintf(s_(' %{project_name}#%{issue_iid} · opened %{issue_created} by %{author}'), { project_name: issue.project.full_name, issue_iid: issue.iid, issue_created: time_ago_with_tooltip(issue.created_at, placement: 'bottom'), author: link_to_member(@project, issue.author, avatar: false) }).html_safe - .description.term.col-sm-10.gl-px-0 - = highlight_and_truncate_issue(issue, @search_term, @search_highlight) + - if issue.description.present? + .description.term.col-sm-10.gl-px-0 + = truncate(issue.description, length: 200) diff --git a/changelogs/unreleased/231180-ui-button-proj-hook_logs.yml b/changelogs/unreleased/231180-ui-button-proj-hook_logs.yml new file mode 100644 index 00000000000..5e26bbc3f97 --- /dev/null +++ b/changelogs/unreleased/231180-ui-button-proj-hook_logs.yml @@ -0,0 +1,5 @@ +--- +title: Set hook_log css to gl-button +merge_request: 42730 +author: Mike Terhar @mterhar +type: other diff --git a/changelogs/unreleased/242016-fj-add-user-preferences-gitpod-enabled-to-usage-data.yml b/changelogs/unreleased/242016-fj-add-user-preferences-gitpod-enabled-to-usage-data.yml new file mode 100644 index 00000000000..780c98d46f6 --- /dev/null +++ b/changelogs/unreleased/242016-fj-add-user-preferences-gitpod-enabled-to-usage-data.yml @@ -0,0 +1,5 @@ +--- +title: Add Gitpod enabled user setting to Usage Data +merge_request: 42570 +author: +type: changed diff --git a/changelogs/unreleased/244427-bold-search-term-issues.yml b/changelogs/unreleased/244427-bold-search-term-issues.yml deleted file mode 100644 index cd7f30a79ad..00000000000 --- a/changelogs/unreleased/244427-bold-search-term-issues.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Global Search - Bold Issue's Search Term -merge_request: 41411 -author: -type: changed diff --git a/changelogs/unreleased/244484-remove-redundant-cluster-agents-index.yml b/changelogs/unreleased/244484-remove-redundant-cluster-agents-index.yml new file mode 100644 index 00000000000..5286361142c --- /dev/null +++ b/changelogs/unreleased/244484-remove-redundant-cluster-agents-index.yml @@ -0,0 +1,5 @@ +--- +title: Remove duplicate index on cluster_agents +merge_request: 42902 +author: +type: other diff --git a/changelogs/unreleased/244828-drop-iglu-registry-url-column.yml b/changelogs/unreleased/244828-drop-iglu-registry-url-column.yml new file mode 100644 index 00000000000..ee87d496276 --- /dev/null +++ b/changelogs/unreleased/244828-drop-iglu-registry-url-column.yml @@ -0,0 +1,5 @@ +--- +title: Drop Iglu registry URL column +merge_request: 42939 +author: +type: removed diff --git a/changelogs/unreleased/249590-project-count-admin-view.yml b/changelogs/unreleased/249590-project-count-admin-view.yml new file mode 100644 index 00000000000..5e9d9473f43 --- /dev/null +++ b/changelogs/unreleased/249590-project-count-admin-view.yml @@ -0,0 +1,5 @@ +--- +title: Display user project count on Admin Dashboard +merge_request: 42871 +author: +type: added diff --git a/changelogs/unreleased/jdb-refactor-diff-rows-utils.yml b/changelogs/unreleased/jdb-refactor-diff-rows-utils.yml new file mode 100644 index 00000000000..eadc3ef0e94 --- /dev/null +++ b/changelogs/unreleased/jdb-refactor-diff-rows-utils.yml @@ -0,0 +1,5 @@ +--- +title: Move shared logic into utils +merge_request: 42407 +author: +type: other diff --git a/changelogs/unreleased/kassio-do-not-add-missing-not-if-importer-user-is-mapped.yml b/changelogs/unreleased/kassio-do-not-add-missing-not-if-importer-user-is-mapped.yml new file mode 100644 index 00000000000..b878cc1311b --- /dev/null +++ b/changelogs/unreleased/kassio-do-not-add-missing-not-if-importer-user-is-mapped.yml @@ -0,0 +1,5 @@ +--- +title: Allow member mapping to map importer user on Group/Project Import +merge_request: 42882 +author: +type: changed diff --git a/changelogs/unreleased/scoped-label-dashboard.yml b/changelogs/unreleased/scoped-label-dashboard.yml new file mode 100644 index 00000000000..b564fd4cd8e --- /dev/null +++ b/changelogs/unreleased/scoped-label-dashboard.yml @@ -0,0 +1,5 @@ +--- +title: Fix profile scoped label CSS +merge_request: 43005 +author: +type: changed diff --git a/config/feature_flags/development/search_filter_by_confidential.yml b/config/feature_flags/development/search_filter_by_confidential.yml new file mode 100644 index 00000000000..0a952a4d25e --- /dev/null +++ b/config/feature_flags/development/search_filter_by_confidential.yml @@ -0,0 +1,7 @@ +--- +name: search_filter_by_confidential +introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/40793 +rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/244923 +group: group::global search +type: development +default_enabled: false \ No newline at end of file diff --git a/db/migrate/20200921093826_add_index_to_user_preferences.rb b/db/migrate/20200921093826_add_index_to_user_preferences.rb new file mode 100644 index 00000000000..78b04eb7a83 --- /dev/null +++ b/db/migrate/20200921093826_add_index_to_user_preferences.rb @@ -0,0 +1,17 @@ +# frozen_string_literal: true + +class AddIndexToUserPreferences < ActiveRecord::Migration[6.0] + include Gitlab::Database::MigrationHelpers + + DOWNTIME = false + + disable_ddl_transaction! + + def up + add_concurrent_index :user_preferences, :gitpod_enabled, name: :index_user_preferences_on_gitpod_enabled + end + + def down + remove_concurrent_index :user_preferences, :gitpod_enabled, name: :index_user_preferences_on_gitpod_enabled + end +end diff --git a/db/migrate/20200921203231_remove_duplicate_cluster_agents_index.rb b/db/migrate/20200921203231_remove_duplicate_cluster_agents_index.rb new file mode 100644 index 00000000000..3f073e32d84 --- /dev/null +++ b/db/migrate/20200921203231_remove_duplicate_cluster_agents_index.rb @@ -0,0 +1,18 @@ +# frozen_string_literal: true + +class RemoveDuplicateClusterAgentsIndex < ActiveRecord::Migration[6.0] + include Gitlab::Database::MigrationHelpers + + DOWNTIME = false + INDEX = 'index_cluster_agents_on_project_id' + + disable_ddl_transaction! + + def up + remove_concurrent_index_by_name :cluster_agents, INDEX + end + + def down + add_concurrent_index :cluster_agents, :project_id, name: INDEX + end +end diff --git a/db/post_migrate/20200922054642_drop_snowplow_iglu_registry_url_from_application_settings.rb b/db/post_migrate/20200922054642_drop_snowplow_iglu_registry_url_from_application_settings.rb new file mode 100644 index 00000000000..d3e64f1f4c2 --- /dev/null +++ b/db/post_migrate/20200922054642_drop_snowplow_iglu_registry_url_from_application_settings.rb @@ -0,0 +1,9 @@ +# frozen_string_literal: true + +class DropSnowplowIgluRegistryUrlFromApplicationSettings < ActiveRecord::Migration[6.0] + DOWNTIME = false + + def change + remove_column :application_settings, :snowplow_iglu_registry_url, :string, limit: 255 + end +end diff --git a/db/schema_migrations/20200921093826 b/db/schema_migrations/20200921093826 new file mode 100644 index 00000000000..4a405de5215 --- /dev/null +++ b/db/schema_migrations/20200921093826 @@ -0,0 +1 @@ +8d14013bcb4d8302c91e331f619fb6f621ab79907aebc421d99c9484ecd7a5d8 \ No newline at end of file diff --git a/db/schema_migrations/20200921203231 b/db/schema_migrations/20200921203231 new file mode 100644 index 00000000000..544b7146ebf --- /dev/null +++ b/db/schema_migrations/20200921203231 @@ -0,0 +1 @@ +7f62ce5117a16213bad6537dfeae2af4016262c533f8fa6b7a19572077bcf8d7 \ No newline at end of file diff --git a/db/schema_migrations/20200922054642 b/db/schema_migrations/20200922054642 new file mode 100644 index 00000000000..443c13c256e --- /dev/null +++ b/db/schema_migrations/20200922054642 @@ -0,0 +1 @@ +ad63096e49440f7f2a15ea2747689ca39f52fdcebc1949a1feed82a22f432e9e \ No newline at end of file diff --git a/db/structure.sql b/db/structure.sql index dabcdff725c..6975123a879 100644 --- a/db/structure.sql +++ b/db/structure.sql @@ -9196,7 +9196,6 @@ CREATE TABLE application_settings ( throttle_incident_management_notification_enabled boolean DEFAULT false NOT NULL, throttle_incident_management_notification_period_in_seconds integer DEFAULT 3600, throttle_incident_management_notification_per_period integer DEFAULT 3600, - snowplow_iglu_registry_url character varying(255), push_event_hooks_limit integer DEFAULT 3 NOT NULL, push_event_activities_limit integer DEFAULT 3 NOT NULL, custom_http_clone_url_root character varying(511), @@ -19789,8 +19788,6 @@ CREATE INDEX index_cluster_agent_tokens_on_agent_id ON cluster_agent_tokens USIN CREATE UNIQUE INDEX index_cluster_agent_tokens_on_token_encrypted ON cluster_agent_tokens USING btree (token_encrypted); -CREATE INDEX index_cluster_agents_on_project_id ON cluster_agents USING btree (project_id); - CREATE UNIQUE INDEX index_cluster_agents_on_project_id_and_name ON cluster_agents USING btree (project_id, name); CREATE UNIQUE INDEX index_cluster_groups_on_cluster_id_and_group_id ON cluster_groups USING btree (cluster_id, group_id); @@ -21335,6 +21332,8 @@ CREATE UNIQUE INDEX index_user_interacted_projects_on_project_id_and_user_id ON CREATE INDEX index_user_interacted_projects_on_user_id ON user_interacted_projects USING btree (user_id); +CREATE INDEX index_user_preferences_on_gitpod_enabled ON user_preferences USING btree (gitpod_enabled); + CREATE UNIQUE INDEX index_user_preferences_on_user_id ON user_preferences USING btree (user_id); CREATE INDEX index_user_statuses_on_user_id ON user_statuses USING btree (user_id); diff --git a/doc/user/infrastructure/index.md b/doc/user/infrastructure/index.md index 227a67f8c8a..216cf9ed607 100644 --- a/doc/user/infrastructure/index.md +++ b/doc/user/infrastructure/index.md @@ -82,6 +82,10 @@ local machine, this is a simple way to get started: -backend-config="retry_wait_min=5" ``` + NOTE: **Note:** + The name of your state can contain only uppercase and lowercase letters, + decimal digits, hyphens and underscores. + You can now run `terraform plan` and `terraform apply` as you normally would. ## Get started using GitLab CI diff --git a/lib/gitlab/checks/matching_merge_request.rb b/lib/gitlab/checks/matching_merge_request.rb index 71361b12d07..db7af0088d0 100644 --- a/lib/gitlab/checks/matching_merge_request.rb +++ b/lib/gitlab/checks/matching_merge_request.rb @@ -20,3 +20,5 @@ module Gitlab end end end + +Gitlab::Checks::MatchingMergeRequest.prepend_if_ee('EE::Gitlab::Checks::MatchingMergeRequest') diff --git a/lib/gitlab/database/partitioning/partition_creator.rb b/lib/gitlab/database/partitioning/partition_creator.rb index 4c1b13fe3b5..fefa3d8e182 100644 --- a/lib/gitlab/database/partitioning/partition_creator.rb +++ b/lib/gitlab/database/partitioning/partition_creator.rb @@ -72,7 +72,7 @@ module Gitlab end def with_lock_retries(&block) - Gitlab::Database::WithLockRetries.new({ + Gitlab::Database::WithLockRetries.new(**{ klass: self.class, logger: Gitlab::AppLogger }).run(&block) diff --git a/lib/gitlab/import_export/members_mapper.rb b/lib/gitlab/import_export/members_mapper.rb index bc3a56b3706..6b37683ea68 100644 --- a/lib/gitlab/import_export/members_mapper.rb +++ b/lib/gitlab/import_export/members_mapper.rb @@ -35,7 +35,7 @@ module Gitlab end def include?(old_user_id) - map.has_key?(old_user_id) && map[old_user_id] != default_user_id + map.has_key?(old_user_id) end private @@ -63,6 +63,8 @@ module Gitlab end def add_team_member(member, existing_user = nil) + return true if existing_user && @importable.members.exists?(user_id: existing_user.id) + member['user'] = existing_user member_hash = member_hash(member) diff --git a/lib/gitlab/search_results.rb b/lib/gitlab/search_results.rb index 87e2a7dd2fa..06d8dca2f70 100644 --- a/lib/gitlab/search_results.rb +++ b/lib/gitlab/search_results.rb @@ -116,11 +116,6 @@ module Gitlab UsersFinder.new(current_user, search: query).execute end - # highlighting is only performed by Elasticsearch backed results - def highlight_map(scope) - {} - end - private def projects diff --git a/lib/gitlab/usage_data.rb b/lib/gitlab/usage_data.rb index 0370800397c..816386574f2 100644 --- a/lib/gitlab/usage_data.rb +++ b/lib/gitlab/usage_data.rb @@ -445,8 +445,11 @@ module Gitlab # rubocop: enable UsageData/LargeTable # rubocop: enable CodeReuse/ActiveRecord + # augmented in EE def user_preferences_usage - {} # augmented in EE + { + user_preferences_user_gitpod_enabled: count(UserPreference.with_user.gitpod_enabled.merge(User.active)) + } end def merge_requests_users(time_period) diff --git a/locale/gitlab.pot b/locale/gitlab.pot index f63183a52b7..4984f88a45b 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -3037,10 +3037,10 @@ msgstr "" msgid "Any" msgstr "" -msgid "Any Author" +msgid "Any %{header}" msgstr "" -msgid "Any Status" +msgid "Any Author" msgstr "" msgid "Any branch" diff --git a/spec/controllers/admin/users_controller_spec.rb b/spec/controllers/admin/users_controller_spec.rb index e4cdcda756b..e346c329720 100644 --- a/spec/controllers/admin/users_controller_spec.rb +++ b/spec/controllers/admin/users_controller_spec.rb @@ -23,6 +23,12 @@ RSpec.describe Admin::UsersController do expect(assigns(:users)).to eq([admin]) end + + it 'eager loads authorized projects association' do + get :index + + expect(assigns(:users).first.association(:authorized_projects)).to be_loaded + end end describe 'GET :id' do diff --git a/spec/controllers/groups/labels_controller_spec.rb b/spec/controllers/groups/labels_controller_spec.rb index 20ee19b01d1..8a08b5b8849 100644 --- a/spec/controllers/groups/labels_controller_spec.rb +++ b/spec/controllers/groups/labels_controller_spec.rb @@ -56,4 +56,43 @@ RSpec.describe Groups::LabelsController do expect(response).to have_gitlab_http_status(:ok) end end + + describe 'DELETE #destroy' do + context 'when current user has ability to destroy the label' do + before do + sign_in(user) + end + + it 'removes the label' do + label = create(:group_label, group: group) + delete :destroy, params: { group_id: group.to_param, id: label.to_param } + + expect { label.reload }.to raise_error(ActiveRecord::RecordNotFound) + end + + context 'when label is succesfuly destroyed' do + it 'redirects to the group labels page' do + label = create(:group_label, group: group) + delete :destroy, params: { group_id: group.to_param, id: label.to_param } + + expect(response).to redirect_to(group_labels_path) + end + end + end + + context 'when current_user does not have ability to destroy the label' do + let(:another_user) { create(:user) } + + before do + sign_in(another_user) + end + + it 'responds with status 404' do + label = create(:group_label, group: group) + delete :destroy, params: { group_id: group.to_param, id: label.to_param } + + expect(response).to have_gitlab_http_status(:not_found) + end + end + end end diff --git a/spec/factories/usage_data.rb b/spec/factories/usage_data.rb index 5b20205a235..6fb2268ff7d 100644 --- a/spec/factories/usage_data.rb +++ b/spec/factories/usage_data.rb @@ -102,6 +102,9 @@ FactoryBot.define do create(:package, project: projects[1]) create(:package, created_at: 2.months.ago, project: projects[1]) + # User Preferences + create(:user_preference, gitpod_enabled: true) + ProjectFeature.first.update_attribute('repository_access_level', 0) # Create fresh & a month (28-days SMAU) old data diff --git a/spec/features/admin/admin_users_spec.rb b/spec/features/admin/admin_users_spec.rb index a37210d2acc..c9da9ef5735 100644 --- a/spec/features/admin/admin_users_spec.rb +++ b/spec/features/admin/admin_users_spec.rb @@ -31,6 +31,7 @@ RSpec.describe "Admin::Users" do expect(page).to have_content(current_user.last_activity_on.strftime("%e %b, %Y")) expect(page).to have_content(user.email) expect(page).to have_content(user.name) + expect(page).to have_content('Projects') expect(page).to have_button('Block') expect(page).to have_button('Deactivate') expect(page).to have_button('Delete user') @@ -48,6 +49,19 @@ RSpec.describe "Admin::Users" do end end + context 'user project count' do + before do + project = create(:project) + project.add_maintainer(current_user) + end + + it 'displays count of users projects' do + visit admin_users_path + + expect(page.find("[data-testid='user-project-count-#{current_user.id}']").text).to eq("1") + end + end + describe 'search and sort' do before do create(:user, name: 'Foo Bar', last_activity_on: 3.days.ago) diff --git a/spec/features/projects/badges/list_spec.rb b/spec/features/projects/badges/list_spec.rb index d1e635f11c0..3382bdcd65f 100644 --- a/spec/features/projects/badges/list_spec.rb +++ b/spec/features/projects/badges/list_spec.rb @@ -17,10 +17,10 @@ RSpec.describe 'list of badges' do expect(page).to have_content 'Markdown' expect(page).to have_content 'HTML' expect(page).to have_content 'AsciiDoc' - expect(page).to have_css('.js-syntax-highlight', count: 3) + expect(page).to have_css('.highlight', count: 3) expect(page).to have_xpath("//img[@alt='pipeline status']") - page.within('.js-syntax-highlight', match: :first) do + page.within('.highlight', match: :first) do expect(page).to have_content 'badges/master/pipeline.svg' end end @@ -32,10 +32,10 @@ RSpec.describe 'list of badges' do expect(page).to have_content 'Markdown' expect(page).to have_content 'HTML' expect(page).to have_content 'AsciiDoc' - expect(page).to have_css('.js-syntax-highlight', count: 3) + expect(page).to have_css('.highlight', count: 3) expect(page).to have_xpath("//img[@alt='coverage report']") - page.within('.js-syntax-highlight', match: :first) do + page.within('.highlight', match: :first) do expect(page).to have_content 'badges/master/coverage.svg' end end diff --git a/spec/frontend/diffs/components/diff_row_utils_spec.js b/spec/frontend/diffs/components/diff_row_utils_spec.js new file mode 100644 index 00000000000..394b6cb1914 --- /dev/null +++ b/spec/frontend/diffs/components/diff_row_utils_spec.js @@ -0,0 +1,203 @@ +import * as utils from '~/diffs/components/diff_row_utils'; +import { + MATCH_LINE_TYPE, + CONTEXT_LINE_TYPE, + OLD_NO_NEW_LINE_TYPE, + NEW_NO_NEW_LINE_TYPE, + EMPTY_CELL_TYPE, +} from '~/diffs/constants'; + +const LINE_CODE = 'abc123'; + +describe('isHighlighted', () => { + it('should return true if line is highlighted', () => { + const state = { diffs: { highlightedRow: LINE_CODE } }; + const line = { line_code: LINE_CODE }; + const isCommented = false; + expect(utils.isHighlighted(state, line, isCommented)).toBe(true); + }); + + it('should return false if line is not highlighted', () => { + const state = { diffs: { highlightedRow: 'xxx' } }; + const line = { line_code: LINE_CODE }; + const isCommented = false; + expect(utils.isHighlighted(state, line, isCommented)).toBe(false); + }); + + it('should return true if isCommented is true', () => { + const state = { diffs: { highlightedRow: 'xxx' } }; + const line = { line_code: LINE_CODE }; + const isCommented = true; + expect(utils.isHighlighted(state, line, isCommented)).toBe(true); + }); +}); + +describe('isContextLine', () => { + it('return true if line type is context', () => { + expect(utils.isContextLine(CONTEXT_LINE_TYPE)).toBe(true); + }); + + it('return false if line type is not context', () => { + expect(utils.isContextLine('xxx')).toBe(false); + }); +}); + +describe('isMatchLine', () => { + it('return true if line type is match', () => { + expect(utils.isMatchLine(MATCH_LINE_TYPE)).toBe(true); + }); + + it('return false if line type is not match', () => { + expect(utils.isMatchLine('xxx')).toBe(false); + }); +}); + +describe('isMetaLine', () => { + it.each` + type | expectation + ${OLD_NO_NEW_LINE_TYPE} | ${true} + ${NEW_NO_NEW_LINE_TYPE} | ${true} + ${EMPTY_CELL_TYPE} | ${true} + ${'xxx'} | ${false} + `('should return $expectation if type is $type', ({ type, expectation }) => { + expect(utils.isMetaLine(type)).toBe(expectation); + }); +}); + +describe('shouldRenderCommentButton', () => { + it('should return false if comment button is not rendered', () => { + expect(utils.shouldRenderCommentButton(true, false)).toBe(false); + }); + + it('should return false if not logged in', () => { + expect(utils.shouldRenderCommentButton(false, true)).toBe(false); + }); + + it('should return true logged in and rendered', () => { + expect(utils.shouldRenderCommentButton(true, true)).toBe(true); + }); +}); + +describe('hasDiscussions', () => { + it('should return false if line is undefined', () => { + expect(utils.hasDiscussions()).toBe(false); + }); + + it('should return false if discussions is undefined', () => { + expect(utils.hasDiscussions({})).toBe(false); + }); + + it('should return false if discussions has legnth of 0', () => { + expect(utils.hasDiscussions({ discussions: [] })).toBe(false); + }); + + it('should return true if discussions has legnth > 0', () => { + expect(utils.hasDiscussions({ discussions: [1] })).toBe(true); + }); +}); + +describe('lineHref', () => { + it(`should return #${LINE_CODE}`, () => { + expect(utils.lineHref({ line_code: LINE_CODE })).toEqual(`#${LINE_CODE}`); + }); + + it(`should return '#' if line is undefined`, () => { + expect(utils.lineHref()).toEqual('#'); + }); + + it(`should return '#' if line_code is undefined`, () => { + expect(utils.lineHref({})).toEqual('#'); + }); +}); + +describe('lineCode', () => { + it(`should return undefined if line_code is undefined`, () => { + expect(utils.lineCode()).toEqual(undefined); + expect(utils.lineCode({ left: {} })).toEqual(undefined); + expect(utils.lineCode({ right: {} })).toEqual(undefined); + }); + + it(`should return ${LINE_CODE}`, () => { + expect(utils.lineCode({ line_code: LINE_CODE })).toEqual(LINE_CODE); + expect(utils.lineCode({ left: { line_code: LINE_CODE } })).toEqual(LINE_CODE); + expect(utils.lineCode({ right: { line_code: LINE_CODE } })).toEqual(LINE_CODE); + }); +}); + +describe('classNameMapCell', () => { + it.each` + line | hll | loggedIn | hovered | expectation + ${undefined} | ${true} | ${true} | ${true} | ${[]} + ${{ type: 'new' }} | ${false} | ${false} | ${false} | ${['new', { hll: false, 'is-over': false }]} + ${{ type: 'new' }} | ${true} | ${true} | ${false} | ${['new', { hll: true, 'is-over': false }]} + ${{ type: 'new' }} | ${true} | ${false} | ${true} | ${['new', { hll: true, 'is-over': false }]} + ${{ type: 'new' }} | ${true} | ${true} | ${true} | ${['new', { hll: true, 'is-over': true }]} + `('should return $expectation', ({ line, hll, loggedIn, hovered, expectation }) => { + const classes = utils.classNameMapCell(line, hll, loggedIn, hovered); + expect(classes).toEqual(expectation); + }); +}); + +describe('addCommentTooltip', () => { + const brokenSymLinkTooltip = + 'Commenting on symbolic links that replace or are replaced by files is currently not supported.'; + const brokenRealTooltip = + 'Commenting on files that replace or are replaced by symbolic links is currently not supported.'; + it('should return default tooltip', () => { + expect(utils.addCommentTooltip()).toBeUndefined(); + }); + + it('should return broken symlink tooltip', () => { + expect(utils.addCommentTooltip({ commentsDisabled: { wasSymbolic: true } })).toEqual( + brokenSymLinkTooltip, + ); + expect(utils.addCommentTooltip({ commentsDisabled: { isSymbolic: true } })).toEqual( + brokenSymLinkTooltip, + ); + }); + + it('should return broken real tooltip', () => { + expect(utils.addCommentTooltip({ commentsDisabled: { wasReal: true } })).toEqual( + brokenRealTooltip, + ); + expect(utils.addCommentTooltip({ commentsDisabled: { isReal: true } })).toEqual( + brokenRealTooltip, + ); + }); +}); + +describe('parallelViewLeftLineType', () => { + it(`should return ${OLD_NO_NEW_LINE_TYPE}`, () => { + expect(utils.parallelViewLeftLineType({ right: { type: NEW_NO_NEW_LINE_TYPE } })).toEqual( + OLD_NO_NEW_LINE_TYPE, + ); + }); + + it(`should return 'new'`, () => { + expect(utils.parallelViewLeftLineType({ left: { type: 'new' } })).toContain('new'); + }); + + it(`should return ${EMPTY_CELL_TYPE}`, () => { + expect(utils.parallelViewLeftLineType({})).toContain(EMPTY_CELL_TYPE); + }); + + it(`should return hll:true`, () => { + expect(utils.parallelViewLeftLineType({}, true)[1]).toEqual({ hll: true }); + }); +}); + +describe('shouldShowCommentButton', () => { + it.each` + hover | context | meta | discussions | expectation + ${true} | ${false} | ${false} | ${false} | ${true} + ${false} | ${false} | ${false} | ${false} | ${false} + ${true} | ${true} | ${false} | ${false} | ${false} + ${true} | ${true} | ${true} | ${false} | ${false} + ${true} | ${true} | ${true} | ${true} | ${false} + `( + 'should return $expectation when hover is $hover', + ({ hover, context, meta, discussions, expectation }) => { + expect(utils.shouldShowCommentButton(hover, context, meta, discussions)).toBe(expectation); + }, + ); +}); diff --git a/spec/frontend/diffs/components/diff_table_cell_spec.js b/spec/frontend/diffs/components/diff_table_cell_spec.js deleted file mode 100644 index 02f5c27eecb..00000000000 --- a/spec/frontend/diffs/components/diff_table_cell_spec.js +++ /dev/null @@ -1,279 +0,0 @@ -import { createLocalVue, shallowMount } from '@vue/test-utils'; -import Vuex from 'vuex'; -import { TEST_HOST } from 'helpers/test_constants'; -import DiffTableCell from '~/diffs/components/diff_table_cell.vue'; -import DiffGutterAvatars from '~/diffs/components/diff_gutter_avatars.vue'; -import { LINE_POSITION_RIGHT } from '~/diffs/constants'; -import { createStore } from '~/mr_notes/stores'; -import discussionsMockData from '../mock_data/diff_discussions'; -import diffFileMockData from '../mock_data/diff_file'; - -const localVue = createLocalVue(); -localVue.use(Vuex); - -const TEST_USER_ID = 'abc123'; -const TEST_USER = { id: TEST_USER_ID }; -const TEST_LINE_NUMBER = 1; -const TEST_LINE_CODE = 'LC_42'; -const TEST_FILE_HASH = diffFileMockData.file_hash; - -describe('DiffTableCell', () => { - const symlinkishFileTooltip = - 'Commenting on symbolic links that replace or are replaced by files is currently not supported.'; - const realishFileTooltip = - 'Commenting on files that replace or are replaced by symbolic links is currently not supported.'; - const otherFileTooltip = 'Add a comment to this line'; - - let wrapper; - let line; - let store; - - beforeEach(() => { - store = createStore(); - store.state.notes.userData = TEST_USER; - - line = { - line_code: TEST_LINE_CODE, - type: 'new', - old_line: null, - new_line: 1, - discussions: [{ ...discussionsMockData }], - discussionsExpanded: true, - text: '+ - Bad dates\n', - rich_text: '+ - Bad dates\n', - meta_data: null, - }; - }); - - afterEach(() => { - wrapper.destroy(); - }); - - const setWindowLocation = value => { - Object.defineProperty(window, 'location', { - writable: true, - value, - }); - }; - - const createComponent = (props = {}) => { - wrapper = shallowMount(DiffTableCell, { - localVue, - store, - propsData: { - line, - fileHash: TEST_FILE_HASH, - contextLinesPath: '/context/lines/path', - isHighlighted: false, - ...props, - }, - }); - }; - - const findTd = () => wrapper.find({ ref: 'td' }); - const findNoteButton = () => wrapper.find({ ref: 'addDiffNoteButton' }); - const findLineNumber = () => wrapper.find({ ref: 'lineNumberRef' }); - const findTooltip = () => wrapper.find({ ref: 'addNoteTooltip' }); - const findAvatars = () => wrapper.find(DiffGutterAvatars); - - describe('td', () => { - it('highlights when isHighlighted true', () => { - createComponent({ isHighlighted: true }); - - expect(findTd().classes()).toContain('hll'); - }); - - it('does not highlight when isHighlighted false', () => { - createComponent({ isHighlighted: false }); - - expect(findTd().classes()).not.toContain('hll'); - }); - }); - - describe('comment button', () => { - it.each` - showCommentButton | userData | query | mergeRefHeadComments | expectation - ${true} | ${TEST_USER} | ${'diff_head=false'} | ${false} | ${true} - ${true} | ${TEST_USER} | ${'diff_head=true'} | ${true} | ${true} - ${true} | ${TEST_USER} | ${'diff_head=true'} | ${false} | ${false} - ${false} | ${TEST_USER} | ${'diff_head=true'} | ${true} | ${false} - ${false} | ${TEST_USER} | ${'bogus'} | ${true} | ${false} - ${true} | ${null} | ${''} | ${true} | ${false} - `( - 'exists is $expectation - with showCommentButton ($showCommentButton) userData ($userData) query ($query)', - ({ showCommentButton, userData, query, mergeRefHeadComments, expectation }) => { - store.state.notes.userData = userData; - gon.features = { mergeRefHeadComments }; - setWindowLocation({ href: `${TEST_HOST}?${query}` }); - createComponent({ showCommentButton }); - - wrapper.setData({ isCommentButtonRendered: showCommentButton }); - - return wrapper.vm.$nextTick().then(() => { - expect(findNoteButton().exists()).toBe(expectation); - }); - }, - ); - - it.each` - isHover | otherProps | discussions | expectation - ${true} | ${{}} | ${[]} | ${true} - ${false} | ${{}} | ${[]} | ${false} - ${true} | ${{ line: { ...line, type: 'context' } }} | ${[]} | ${false} - ${true} | ${{ line: { ...line, type: 'old-nonewline' } }} | ${[]} | ${false} - ${true} | ${{}} | ${[{}]} | ${false} - `( - 'visible is $expectation - with isHover ($isHover), discussions ($discussions), otherProps ($otherProps)', - ({ isHover, otherProps, discussions, expectation }) => { - line.discussions = discussions; - createComponent({ - showCommentButton: true, - isHover, - ...otherProps, - }); - - wrapper.setData({ - isCommentButtonRendered: true, - }); - - return wrapper.vm.$nextTick().then(() => { - expect(findNoteButton().isVisible()).toBe(expectation); - }); - }, - ); - - it.each` - disabled | commentsDisabled - ${'disabled'} | ${true} - ${undefined} | ${false} - `( - 'has attribute disabled=$disabled when the outer component has prop commentsDisabled=$commentsDisabled', - ({ disabled, commentsDisabled }) => { - line.commentsDisabled = commentsDisabled; - - createComponent({ - showCommentButton: true, - isHover: true, - }); - - wrapper.setData({ isCommentButtonRendered: true }); - - return wrapper.vm.$nextTick().then(() => { - expect(findNoteButton().attributes('disabled')).toBe(disabled); - }); - }, - ); - - it.each` - tooltip | commentsDisabled - ${symlinkishFileTooltip} | ${{ wasSymbolic: true }} - ${symlinkishFileTooltip} | ${{ isSymbolic: true }} - ${realishFileTooltip} | ${{ wasReal: true }} - ${realishFileTooltip} | ${{ isReal: true }} - ${otherFileTooltip} | ${false} - `( - 'has the correct tooltip when commentsDisabled=$commentsDisabled', - ({ tooltip, commentsDisabled }) => { - line.commentsDisabled = commentsDisabled; - - createComponent({ - showCommentButton: true, - isHover: true, - }); - - wrapper.setData({ isCommentButtonRendered: true }); - - return wrapper.vm.$nextTick().then(() => { - expect(findTooltip().attributes('title')).toBe(tooltip); - }); - }, - ); - }); - - describe('line number', () => { - describe('without lineNumber prop', () => { - it('does not render', () => { - createComponent({ lineType: 'old' }); - - expect(findLineNumber().exists()).toBe(false); - }); - }); - - describe('with lineNumber prop', () => { - describe.each` - lineProps | expectedHref | expectedClickArg - ${{ line_code: TEST_LINE_CODE }} | ${`#${TEST_LINE_CODE}`} | ${TEST_LINE_CODE} - ${{ line_code: undefined }} | ${'#'} | ${undefined} - ${{ line_code: undefined, left: { line_code: TEST_LINE_CODE } }} | ${'#'} | ${TEST_LINE_CODE} - ${{ line_code: undefined, right: { line_code: TEST_LINE_CODE } }} | ${'#'} | ${TEST_LINE_CODE} - `('with line ($lineProps)', ({ lineProps, expectedHref, expectedClickArg }) => { - beforeEach(() => { - jest.spyOn(store, 'dispatch').mockImplementation(); - Object.assign(line, lineProps); - createComponent({ lineNumber: TEST_LINE_NUMBER }); - }); - - it('renders', () => { - expect(findLineNumber().exists()).toBe(true); - expect(findLineNumber().attributes()).toEqual({ - href: expectedHref, - 'data-linenumber': TEST_LINE_NUMBER.toString(), - }); - }); - - it('on click, dispatches setHighlightedRow', () => { - expect(store.dispatch).not.toHaveBeenCalled(); - - findLineNumber().trigger('click'); - - expect(store.dispatch).toHaveBeenCalledWith('diffs/setHighlightedRow', expectedClickArg); - }); - }); - }); - }); - - describe('diff-gutter-avatars', () => { - describe('with showCommentButton', () => { - beforeEach(() => { - jest.spyOn(store, 'dispatch').mockImplementation(); - - createComponent({ showCommentButton: true }); - }); - - it('renders', () => { - expect(findAvatars().props()).toEqual({ - discussions: line.discussions, - discussionsExpanded: line.discussionsExpanded, - }); - }); - - it('toggles line discussion', () => { - expect(store.dispatch).not.toHaveBeenCalled(); - - findAvatars().vm.$emit('toggleLineDiscussions'); - - expect(store.dispatch).toHaveBeenCalledWith('diffs/toggleLineDiscussions', { - lineCode: TEST_LINE_CODE, - fileHash: TEST_FILE_HASH, - expanded: !line.discussionsExpanded, - }); - }); - }); - - it.each` - props | lineProps | expectation - ${{ showCommentButton: true }} | ${{}} | ${true} - ${{ showCommentButton: false }} | ${{}} | ${false} - ${{ showCommentButton: true, linePosition: LINE_POSITION_RIGHT }} | ${{ type: null }} | ${false} - ${{ showCommentButton: true }} | ${{ discussions: [] }} | ${false} - `( - 'exists is $expectation - with props ($props), line ($lineProps)', - ({ props, lineProps, expectation }) => { - Object.assign(line, lineProps); - createComponent(props); - - expect(findAvatars().exists()).toBe(expectation); - }, - ); - }); -}); diff --git a/spec/frontend/search/components/state_filter_spec.js b/spec/frontend/search/components/dropdown_filter_spec.js similarity index 73% rename from spec/frontend/search/components/state_filter_spec.js rename to spec/frontend/search/components/dropdown_filter_spec.js index 26344f2b592..ffac038e1c5 100644 --- a/spec/frontend/search/components/state_filter_spec.js +++ b/spec/frontend/search/components/dropdown_filter_spec.js @@ -1,11 +1,11 @@ import { shallowMount } from '@vue/test-utils'; import { GlDropdown, GlDropdownItem } from '@gitlab/ui'; -import StateFilter from '~/search/state_filter/components/state_filter.vue'; +import DropdownFilter from '~/search/components/dropdown_filter.vue'; import { FILTER_STATES, - SCOPES, FILTER_STATES_BY_SCOPE, - FILTER_TEXT, + FILTER_HEADER, + SCOPES, } from '~/search/state_filter/constants'; import * as urlUtils from '~/lib/utils/url_utility'; @@ -15,14 +15,19 @@ jest.mock('~/lib/utils/url_utility', () => ({ })); function createComponent(props = { scope: 'issues' }) { - return shallowMount(StateFilter, { + return shallowMount(DropdownFilter, { propsData: { + filtersArray: FILTER_STATES_BY_SCOPE.issues, + filters: FILTER_STATES, + header: FILTER_HEADER, + param: 'state', + supportedScopes: Object.values(SCOPES), ...props, }, }); } -describe('StateFilter', () => { +describe('DropdownFilter', () => { let wrapper; beforeEach(() => { @@ -41,7 +46,7 @@ describe('StateFilter', () => { describe('template', () => { describe.each` - scope | showStateDropdown + scope | showDropdown ${'issues'} | ${true} ${'merge_requests'} | ${true} ${'projects'} | ${false} @@ -50,26 +55,25 @@ describe('StateFilter', () => { ${'notes'} | ${false} ${'wiki_blobs'} | ${false} ${'blobs'} | ${false} - `(`state dropdown`, ({ scope, showStateDropdown }) => { + `(`dropdown`, ({ scope, showDropdown }) => { beforeEach(() => { wrapper = createComponent({ scope }); }); - it(`does${showStateDropdown ? '' : ' not'} render when scope is ${scope}`, () => { - expect(findGlDropdown().exists()).toBe(showStateDropdown); + it(`does${showDropdown ? '' : ' not'} render when scope is ${scope}`, () => { + expect(findGlDropdown().exists()).toBe(showDropdown); }); }); describe.each` - state | label - ${FILTER_STATES.ANY.value} | ${FILTER_TEXT} + initialFilter | label + ${FILTER_STATES.ANY.value} | ${`Any ${FILTER_HEADER}`} ${FILTER_STATES.OPEN.value} | ${FILTER_STATES.OPEN.label} ${FILTER_STATES.CLOSED.value} | ${FILTER_STATES.CLOSED.label} - ${FILTER_STATES.MERGED.value} | ${FILTER_STATES.MERGED.label} - `(`filter text`, ({ state, label }) => { - describe(`when state is ${state}`, () => { + `(`filter text`, ({ initialFilter, label }) => { + describe(`when initialFilter is ${initialFilter}`, () => { beforeEach(() => { - wrapper = createComponent({ scope: 'issues', state }); + wrapper = createComponent({ scope: 'issues', initialFilter }); }); it(`sets dropdown label to ${label}`, () => { diff --git a/spec/helpers/blob_helper_spec.rb b/spec/helpers/blob_helper_spec.rb index 98ee7c7b97c..06f86e7716a 100644 --- a/spec/helpers/blob_helper_spec.rb +++ b/spec/helpers/blob_helper_spec.rb @@ -5,6 +5,16 @@ require 'spec_helper' RSpec.describe BlobHelper do include TreeHelper + describe '#highlight' do + it 'wraps highlighted content' do + expect(helper.highlight('test.rb', '52')).to eq(%q[
52
]) + end + + it 'handles plain version' do + expect(helper.highlight('test.rb', '52', plain: true)).to eq(%q[
52
]) + end + end + describe "#sanitize_svg_data" do let(:input_svg_path) { File.join(Rails.root, 'spec', 'fixtures', 'unsanitized.svg') } let(:data) { File.read(input_svg_path) } diff --git a/spec/lib/banzai/filter/external_issue_reference_filter_spec.rb b/spec/lib/banzai/filter/external_issue_reference_filter_spec.rb index e7b6c910b8a..35ef2abfa63 100644 --- a/spec/lib/banzai/filter/external_issue_reference_filter_spec.rb +++ b/spec/lib/banzai/filter/external_issue_reference_filter_spec.rb @@ -5,6 +5,8 @@ require 'spec_helper' RSpec.describe Banzai::Filter::ExternalIssueReferenceFilter do include FilterSpecHelper + let_it_be_with_refind(:project) { create(:project) } + shared_examples_for "external issue tracker" do it_behaves_like 'a reference containing an element node' @@ -116,7 +118,7 @@ RSpec.describe Banzai::Filter::ExternalIssueReferenceFilter do end context "redmine project" do - let(:project) { create(:redmine_project) } + let_it_be(:service) { create(:redmine_service, project: project) } before do project.update!(issues_enabled: false) @@ -138,7 +140,7 @@ RSpec.describe Banzai::Filter::ExternalIssueReferenceFilter do end context "youtrack project" do - let(:project) { create(:youtrack_project) } + let_it_be(:service) { create(:youtrack_service, project: project) } before do project.update!(issues_enabled: false) @@ -181,7 +183,7 @@ RSpec.describe Banzai::Filter::ExternalIssueReferenceFilter do end context "jira project" do - let(:project) { create(:jira_project) } + let_it_be(:service) { create(:jira_service, project: project) } let(:reference) { issue.to_reference } context "with right markdown" do @@ -210,7 +212,7 @@ RSpec.describe Banzai::Filter::ExternalIssueReferenceFilter do end context "ewm project" do - let_it_be(:project) { create(:ewm_project) } + let_it_be(:service) { create(:ewm_service, project: project) } before do project.update!(issues_enabled: false) diff --git a/spec/lib/gitlab/checks/matching_merge_request_spec.rb b/spec/lib/gitlab/checks/matching_merge_request_spec.rb new file mode 100644 index 00000000000..ca7ee784ee3 --- /dev/null +++ b/spec/lib/gitlab/checks/matching_merge_request_spec.rb @@ -0,0 +1,31 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Gitlab::Checks::MatchingMergeRequest do + describe '#match?' do + let_it_be(:newrev) { '012345678' } + let_it_be(:target_branch) { 'feature' } + let_it_be(:project) { create(:project, :repository) } + let_it_be(:locked_merge_request) do + create(:merge_request, + :locked, + source_project: project, + target_project: project, + target_branch: target_branch, + in_progress_merge_commit_sha: newrev) + end + + subject { described_class.new(newrev, target_branch, project) } + + it 'matches a merge request' do + expect(subject.match?).to be true + end + + it 'does not match any merge request' do + matcher = described_class.new(newrev, 'test', project) + + expect(matcher.match?).to be false + end + end +end diff --git a/spec/lib/gitlab/search_results_spec.rb b/spec/lib/gitlab/search_results_spec.rb index c9473a74ba1..b4cf6a568b4 100644 --- a/spec/lib/gitlab/search_results_spec.rb +++ b/spec/lib/gitlab/search_results_spec.rb @@ -58,25 +58,6 @@ RSpec.describe Gitlab::SearchResults do end end - describe '#highlight_map' do - using RSpec::Parameterized::TableSyntax - - where(:scope, :expected) do - 'projects' | {} - 'issues' | {} - 'merge_requests' | {} - 'milestones' | {} - 'users' | {} - 'unknown' | {} - end - - with_them do - it 'returns the expected highlight_map' do - expect(results.highlight_map(scope)).to eq(expected) - end - end - end - describe '#formatted_limited_count' do using RSpec::Parameterized::TableSyntax diff --git a/spec/lib/gitlab/snippet_search_results_spec.rb b/spec/lib/gitlab/snippet_search_results_spec.rb index 2177b2be6d6..e1ae26a4d9e 100644 --- a/spec/lib/gitlab/snippet_search_results_spec.rb +++ b/spec/lib/gitlab/snippet_search_results_spec.rb @@ -21,12 +21,6 @@ RSpec.describe Gitlab::SnippetSearchResults do end end - describe '#highlight_map' do - it 'returns the expected highlight map' do - expect(results.highlight_map('snippet_titles')).to eq({}) - end - end - describe '#objects' do it 'uses page and per_page to paginate results' do snippet2 = create(:snippet, :public, content: 'foo', file_name: 'foo') diff --git a/spec/lib/gitlab/usage_data_spec.rb b/spec/lib/gitlab/usage_data_spec.rb index 5a54b29edc3..e54082b4521 100644 --- a/spec/lib/gitlab/usage_data_spec.rb +++ b/spec/lib/gitlab/usage_data_spec.rb @@ -499,6 +499,7 @@ RSpec.describe Gitlab::UsageData, :aggregate_failures do expect(count_data[:projects_with_packages]).to eq(2) expect(count_data[:packages]).to eq(4) + expect(count_data[:user_preferences_user_gitpod_enabled]).to eq(1) end it 'gathers object store usage correctly' do diff --git a/spec/models/merge_request_spec.rb b/spec/models/merge_request_spec.rb index c54064a182a..5fde9b8661d 100644 --- a/spec/models/merge_request_spec.rb +++ b/spec/models/merge_request_spec.rb @@ -4299,4 +4299,18 @@ RSpec.describe MergeRequest, factory_default: :keep do expect(merge_request.allows_reviewers?).to be(true) end end + + describe '#update_and_mark_in_progress_merge_commit_sha' do + let(:ref) { subject.target_project.repository.commit.id } + + before do + expect(subject.target_project).to receive(:mark_primary_write_location) + end + + it 'updates commit ID' do + expect { subject.update_and_mark_in_progress_merge_commit_sha(ref) } + .to change { subject.in_progress_merge_commit_sha } + .from(nil).to(ref) + end + end end diff --git a/spec/services/merge_requests/ff_merge_service_spec.rb b/spec/services/merge_requests/ff_merge_service_spec.rb index 3038e48a184..64c473d947f 100644 --- a/spec/services/merge_requests/ff_merge_service_spec.rb +++ b/spec/services/merge_requests/ff_merge_service_spec.rb @@ -72,15 +72,22 @@ RSpec.describe MergeRequests::FfMergeService do end it 'does not update squash_commit_sha if it is not a squash' do + expect(merge_request).to receive(:update_and_mark_in_progress_merge_commit_sha).twice.and_call_original + expect { execute_ff_merge }.not_to change { merge_request.squash_commit_sha } + expect(merge_request.in_progress_merge_commit_sha).to be_nil end it 'updates squash_commit_sha if it is a squash' do + expect(merge_request).to receive(:update_and_mark_in_progress_merge_commit_sha).twice.and_call_original + merge_request.update!(squash: true) expect { execute_ff_merge } .to change { merge_request.squash_commit_sha } .from(nil) + + expect(merge_request.in_progress_merge_commit_sha).to be_nil end end diff --git a/spec/services/merge_requests/merge_service_spec.rb b/spec/services/merge_requests/merge_service_spec.rb index 184d54fe09b..d0e3102f157 100644 --- a/spec/services/merge_requests/merge_service_spec.rb +++ b/spec/services/merge_requests/merge_service_spec.rb @@ -22,6 +22,7 @@ RSpec.describe MergeRequests::MergeService do context 'valid params' do before do allow(service).to receive(:execute_hooks) + expect(merge_request).to receive(:update_and_mark_in_progress_merge_commit_sha).twice.and_call_original perform_enqueued_jobs do service.execute(merge_request) diff --git a/spec/support/helpers/usage_data_helpers.rb b/spec/support/helpers/usage_data_helpers.rb index d92fcdc2d4a..90f205eecdb 100644 --- a/spec/support/helpers/usage_data_helpers.rb +++ b/spec/support/helpers/usage_data_helpers.rb @@ -133,6 +133,7 @@ module UsageDataHelpers todos uploads web_hooks + user_preferences_user_gitpod_enabled ).push(*SMAU_KEYS) USAGE_DATA_KEYS = %i( diff --git a/spec/support/shared_examples/lib/gitlab/import_export/relation_factory_shared_examples.rb b/spec/support/shared_examples/lib/gitlab/import_export/relation_factory_shared_examples.rb index 4c14d97446e..33061f17bde 100644 --- a/spec/support/shared_examples/lib/gitlab/import_export/relation_factory_shared_examples.rb +++ b/spec/support/shared_examples/lib/gitlab/import_export/relation_factory_shared_examples.rb @@ -65,7 +65,7 @@ RSpec.shared_examples 'Notes user references' do include_examples 'sets the note author to the mapped user' - include_examples 'adds original autor note' + include_examples 'does not add original autor note' end context 'and the note author exists in the target instance' do diff --git a/spec/views/search/_results.html.haml_spec.rb b/spec/views/search/_results.html.haml_spec.rb index 9e95dc40ff8..033b2304e33 100644 --- a/spec/views/search/_results.html.haml_spec.rb +++ b/spec/views/search/_results.html.haml_spec.rb @@ -60,6 +60,28 @@ RSpec.describe 'search/_results' do expect(rendered).to have_selector('#js-search-filter-by-state') end + + context 'Feature search_filter_by_confidential' do + context 'when disabled' do + before do + stub_feature_flags(search_filter_by_confidential: false) + end + + it 'does not render the confidential drop down' do + render + + expect(rendered).not_to have_selector('#js-search-filter-by-confidential') + end + end + + context 'when enabled' do + it 'renders the confidential drop down' do + render + + expect(rendered).to have_selector('#js-search-filter-by-confidential') + end + end + end end end end