Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2020-09-25 15:10:00 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2020-09-25 15:10:00 +0300
commitf6d19ed8eb581689706f192e64c349b33fb7a916 (patch)
tree59173b6588639d077866bc9dc110a038ac171a68
parente6bcd6e73801e31e15ef0c9c1e6ad790626cdfaf (diff)
Add latest changes from gitlab-org/gitlab@master
-rw-r--r--app/assets/javascripts/custom_metrics/components/delete_custom_metric_modal.vue8
-rw-r--r--app/assets/javascripts/diffs/components/parallel_diff_comment_row.vue8
-rw-r--r--app/assets/javascripts/issue_show/components/incidents/incident_tabs.vue9
-rw-r--r--app/assets/javascripts/sidebar/mount_sidebar.js4
-rw-r--r--app/assets/javascripts/task_list.js9
-rw-r--r--app/assets/javascripts/vue_shared/components/alert_details_table.vue23
-rw-r--r--app/assets/stylesheets/pages/pipelines.scss8
-rw-r--r--app/views/projects/issues/show.html.haml3
-rw-r--r--app/views/projects/merge_requests/_description.html.haml3
-rw-r--r--app/views/shared/notes/_edit.html.haml2
-rw-r--r--app/views/shared/snippets/_header.html.haml2
-rw-r--r--changelogs/unreleased/208260-fix-mr-task-lists-starting-with-new-line.yml5
-rw-r--r--changelogs/unreleased/220503_bug_add_branches_to_be_notified_to_hangouts_service_api.yml5
-rw-r--r--changelogs/unreleased/tr-filter-internal-strings.yml5
-rw-r--r--config/feature_categories.yml3
-rw-r--r--doc/administration/geo/replication/faq.md4
-rw-r--r--doc/api/services.md2
-rw-r--r--doc/development/geo/framework.md34
-rw-r--r--lib/api/helpers/services_helpers.rb6
-rw-r--r--lib/gitlab/database/batch_count.rb25
-rw-r--r--spec/features/issuables/merge_request_discussion_lock_spec.rb79
-rw-r--r--spec/features/merge_request/user_views_open_merge_request_spec.rb2
-rw-r--r--spec/features/projects/commit/user_comments_on_commit_spec.rb48
-rw-r--r--spec/features/task_lists_spec.rb115
-rw-r--r--spec/frontend/issue_show/components/incidents/incident_tabs_spec.js2
-rw-r--r--spec/frontend/vue_shared/components/alert_detail_table_spec.js21
-rw-r--r--spec/lib/gitlab/database/batch_count_spec.rb36
-rw-r--r--spec/requests/api/services_spec.rb30
28 files changed, 396 insertions, 105 deletions
diff --git a/app/assets/javascripts/custom_metrics/components/delete_custom_metric_modal.vue b/app/assets/javascripts/custom_metrics/components/delete_custom_metric_modal.vue
index 34e4aeb290f..7c4117d7e8b 100644
--- a/app/assets/javascripts/custom_metrics/components/delete_custom_metric_modal.vue
+++ b/app/assets/javascripts/custom_metrics/components/delete_custom_metric_modal.vue
@@ -1,11 +1,11 @@
<script>
-import { GlModal, GlModalDirective, GlDeprecatedButton } from '@gitlab/ui';
+import { GlModal, GlModalDirective, GlButton } from '@gitlab/ui';
import { s__ } from '~/locale';
export default {
components: {
GlModal,
- GlDeprecatedButton,
+ GlButton,
},
directives: {
'gl-modal': GlModalDirective,
@@ -33,9 +33,9 @@ export default {
</script>
<template>
<div class="d-inline-block float-right mr-3">
- <gl-deprecated-button v-gl-modal="$options.modalId" variant="danger">
+ <gl-button v-gl-modal="$options.modalId" variant="danger" category="primary">
{{ __('Delete') }}
- </gl-deprecated-button>
+ </gl-button>
<gl-modal
:title="s__('Metrics|Delete metric?')"
:ok-title="s__('Metrics|Delete metric')"
diff --git a/app/assets/javascripts/diffs/components/parallel_diff_comment_row.vue b/app/assets/javascripts/diffs/components/parallel_diff_comment_row.vue
index b525490f7cc..127e3f214cf 100644
--- a/app/assets/javascripts/diffs/components/parallel_diff_comment_row.vue
+++ b/app/assets/javascripts/diffs/components/parallel_diff_comment_row.vue
@@ -113,8 +113,8 @@ export default {
},
methods: {
...mapActions('diffs', ['showCommentForm']),
- showNewDiscussionForm() {
- this.showCommentForm({ lineCode: this.line.line_code, fileHash: this.diffFileHash });
+ showNewDiscussionForm(lineCode) {
+ this.showCommentForm({ lineCode, fileHash: this.diffFileHash });
},
},
};
@@ -134,7 +134,7 @@ export default {
v-if="!hasDraftLeft"
:has-form="showLeftSideCommentForm"
:render-reply-placeholder="shouldRenderReplyPlaceholderOnLeft"
- @showNewDiscussionForm="showNewDiscussionForm"
+ @showNewDiscussionForm="showNewDiscussionForm(line.left.line_code)"
>
<template #form>
<diff-line-note-form
@@ -159,7 +159,7 @@ export default {
v-if="!hasDraftRight"
:has-form="showRightSideCommentForm"
:render-reply-placeholder="shouldRenderReplyPlaceholderOnRight"
- @showNewDiscussionForm="showNewDiscussionForm"
+ @showNewDiscussionForm="showNewDiscussionForm(line.right.line_code)"
>
<template #form>
<diff-line-note-form
diff --git a/app/assets/javascripts/issue_show/components/incidents/incident_tabs.vue b/app/assets/javascripts/issue_show/components/incidents/incident_tabs.vue
index 4104ddbf06f..5925c013e89 100644
--- a/app/assets/javascripts/issue_show/components/incidents/incident_tabs.vue
+++ b/app/assets/javascripts/issue_show/components/incidents/incident_tabs.vue
@@ -45,13 +45,6 @@ export default {
loading() {
return this.$apollo.queries.alert.loading;
},
- alertTableFields() {
- if (this.alert) {
- const { detailsUrl, __typename, ...restDetails } = this.alert;
- return restDetails;
- }
- return null;
- },
},
};
</script>
@@ -64,7 +57,7 @@ export default {
<description-component v-bind="$attrs" />
</gl-tab>
<gl-tab v-if="alert" class="alert-management-details" :title="s__('Incident|Alert details')">
- <alert-details-table :alert="alertTableFields" :loading="loading" />
+ <alert-details-table :alert="alert" :loading="loading" />
</gl-tab>
</gl-tabs>
</div>
diff --git a/app/assets/javascripts/sidebar/mount_sidebar.js b/app/assets/javascripts/sidebar/mount_sidebar.js
index 9bcc222f96c..047d9a4d0e8 100644
--- a/app/assets/javascripts/sidebar/mount_sidebar.js
+++ b/app/assets/javascripts/sidebar/mount_sidebar.js
@@ -132,7 +132,9 @@ function mountLockComponent() {
({ store }) => store,
);
} else {
- importStore = import(/* webpackChunkName: 'mrNotesStore' */ '~/mr_notes/stores');
+ importStore = import(/* webpackChunkName: 'mrNotesStore' */ '~/mr_notes/stores').then(
+ store => store.default,
+ );
}
importStore
diff --git a/app/assets/javascripts/task_list.js b/app/assets/javascripts/task_list.js
index f2b05946a08..b51951674d5 100644
--- a/app/assets/javascripts/task_list.js
+++ b/app/assets/javascripts/task_list.js
@@ -31,6 +31,15 @@ export default class TaskList {
init() {
this.disable(); // Prevent duplicate event bindings
+ const taskListFields = document.querySelectorAll(
+ `${this.taskListContainerSelector} .js-task-list-field[data-value]`,
+ );
+
+ taskListFields.forEach(taskListField => {
+ // eslint-disable-next-line no-param-reassign
+ taskListField.value = taskListField.dataset.value;
+ });
+
$(this.taskListContainerSelector).taskList('enable');
$(document).on('tasklist:changed', this.taskListContainerSelector, this.updateHandler);
}
diff --git a/app/assets/javascripts/vue_shared/components/alert_details_table.vue b/app/assets/javascripts/vue_shared/components/alert_details_table.vue
index c94e784c01e..3b05f76d663 100644
--- a/app/assets/javascripts/vue_shared/components/alert_details_table.vue
+++ b/app/assets/javascripts/vue_shared/components/alert_details_table.vue
@@ -9,6 +9,22 @@ import {
const thClass = 'gl-bg-transparent! gl-border-1! gl-border-b-solid! gl-border-gray-200!';
const tdClass = 'gl-border-gray-100! gl-p-5!';
+const allowedFields = [
+ 'iid',
+ 'title',
+ 'severity',
+ 'status',
+ 'startedAt',
+ 'eventCount',
+ 'monitoringTool',
+ 'service',
+ 'description',
+ 'endedAt',
+ 'details',
+];
+
+const filterAllowedFields = ([fieldName]) => allowedFields.includes(fieldName);
+const arrayToObject = ([fieldName, value]) => ({ fieldName, value });
export default {
components: {
@@ -46,10 +62,9 @@ export default {
if (!this.alert) {
return [];
}
- return Object.entries(this.alert).map(([fieldName, value]) => ({
- fieldName,
- value,
- }));
+ return Object.entries(this.alert)
+ .filter(filterAllowedFields)
+ .map(arrayToObject);
},
},
};
diff --git a/app/assets/stylesheets/pages/pipelines.scss b/app/assets/stylesheets/pages/pipelines.scss
index 8b104ce9017..40530acf1d2 100644
--- a/app/assets/stylesheets/pages/pipelines.scss
+++ b/app/assets/stylesheets/pages/pipelines.scss
@@ -1050,14 +1050,6 @@ button.mini-pipeline-graph-dropdown-toggle {
line-height: 1.5;
}
-.legend-all {
- color: $gl-text-color-secondary;
-}
-
-.legend-success {
- color: $green-500;
-}
-
.test-reports-table {
.build-trace {
@include build-trace();
diff --git a/app/views/projects/issues/show.html.haml b/app/views/projects/issues/show.html.haml
index 4e6b0064057..564f9793780 100644
--- a/app/views/projects/issues/show.html.haml
+++ b/app/views/projects/issues/show.html.haml
@@ -66,9 +66,8 @@
.title-container
%h2.title= markdown_field(@issue, :title)
- if @issue.description.present?
- .description{ class: can?(current_user, :update_issue, @issue) ? 'js-task-list-container' : '' }
+ .description
.md= markdown_field(@issue, :description)
- %textarea.hidden.js-task-list-field= @issue.description
= edited_time_ago_with_tooltip(@issue, placement: 'bottom', html_class: 'issue-edited-ago js-issue-edited-ago')
diff --git a/app/views/projects/merge_requests/_description.html.haml b/app/views/projects/merge_requests/_description.html.haml
index 354a384b647..c20479662dd 100644
--- a/app/views/projects/merge_requests/_description.html.haml
+++ b/app/views/projects/merge_requests/_description.html.haml
@@ -3,7 +3,6 @@
.description.qa-description{ class: can?(current_user, :update_merge_request, @merge_request) ? 'js-task-list-container' : '' }
.md
= markdown_field(@merge_request, :description)
- %textarea.hidden.js-task-list-field
- = @merge_request.description
+ %textarea.hidden.js-task-list-field{ data: { value: @merge_request.description } }
= edited_time_ago_with_tooltip(@merge_request, placement: 'bottom')
diff --git a/app/views/shared/notes/_edit.html.haml b/app/views/shared/notes/_edit.html.haml
index 84a3ef9d8fe..9cfb3f3b576 100644
--- a/app/views/shared/notes/_edit.html.haml
+++ b/app/views/shared/notes/_edit.html.haml
@@ -1 +1 @@
-%textarea.hidden.js-task-list-field.original-task-list{ data: { update_url: note_url(note) } }= note.note
+%textarea.hidden.js-task-list-field.original-task-list{ data: { update_url: note_url(note), value: note.note } }
diff --git a/app/views/shared/snippets/_header.html.haml b/app/views/shared/snippets/_header.html.haml
index 1cf20f805dc..e2680fac019 100644
--- a/app/views/shared/snippets/_header.html.haml
+++ b/app/views/shared/snippets/_header.html.haml
@@ -21,8 +21,6 @@
.description
.md
= markdown_field(@snippet, :description)
- %textarea.hidden.js-task-list-field
- = @snippet.description
- if @snippet.updated_at != @snippet.created_at
= edited_time_ago_with_tooltip(@snippet, placement: 'bottom', exclude_author: true)
diff --git a/changelogs/unreleased/208260-fix-mr-task-lists-starting-with-new-line.yml b/changelogs/unreleased/208260-fix-mr-task-lists-starting-with-new-line.yml
new file mode 100644
index 00000000000..3f9efc17abc
--- /dev/null
+++ b/changelogs/unreleased/208260-fix-mr-task-lists-starting-with-new-line.yml
@@ -0,0 +1,5 @@
+---
+title: Fix checking of task lists when MR description starts with a blank line
+merge_request: 43125
+author:
+type: fixed
diff --git a/changelogs/unreleased/220503_bug_add_branches_to_be_notified_to_hangouts_service_api.yml b/changelogs/unreleased/220503_bug_add_branches_to_be_notified_to_hangouts_service_api.yml
new file mode 100644
index 00000000000..22f81687cef
--- /dev/null
+++ b/changelogs/unreleased/220503_bug_add_branches_to_be_notified_to_hangouts_service_api.yml
@@ -0,0 +1,5 @@
+---
+title: Fix branches_to_be_notified API param for hangouts chat service
+merge_request: 35599
+author:
+type: fixed
diff --git a/changelogs/unreleased/tr-filter-internal-strings.yml b/changelogs/unreleased/tr-filter-internal-strings.yml
new file mode 100644
index 00000000000..e761c5d54cd
--- /dev/null
+++ b/changelogs/unreleased/tr-filter-internal-strings.yml
@@ -0,0 +1,5 @@
+---
+title: Remove internal fields from alert details table
+merge_request: 43076
+author:
+type: changed
diff --git a/config/feature_categories.yml b/config/feature_categories.yml
index 7b85f910d85..b6091802f97 100644
--- a/config/feature_categories.yml
+++ b/config/feature_categories.yml
@@ -19,7 +19,6 @@
- auto_devops
- backup_restore
- behavior_analytics
-- billing
- chatops
- cloud_native_installation
- cluster_cost_optimization
@@ -87,6 +86,8 @@
- planning_analytics
- product_analytics
- projects
+- provision
+- purchase
- quality_management
- release_evidence
- release_orchestration
diff --git a/doc/administration/geo/replication/faq.md b/doc/administration/geo/replication/faq.md
index 3892d73b465..f7f391b360e 100644
--- a/doc/administration/geo/replication/faq.md
+++ b/doc/administration/geo/replication/faq.md
@@ -67,3 +67,7 @@ That's totally fine. We use HTTP(s) to fetch repository changes from the **prima
## Is this possible to set up a Docker Registry for a **secondary** node that mirrors the one on the **primary** node?
Yes. See [Docker Registry for a **secondary** node](docker_registry.md).
+
+## Can I login to a secondary node?
+
+Yes, but secondary nodes receive all authentication data (like user accounts and logins) from the primary instance. This means you will be re-directed to the primary for authentication and routed back afterwards.
diff --git a/doc/api/services.md b/doc/api/services.md
index 405047a433d..61b75d3dcff 100644
--- a/doc/api/services.md
+++ b/doc/api/services.md
@@ -659,7 +659,7 @@ Parameters:
| `webhook` | string | true | The Hangouts Chat webhook. For example, `https://chat.googleapis.com/v1/spaces...`. |
| `notify_only_broken_pipelines` | boolean | false | Send notifications for broken pipelines |
| `notify_only_default_branch` | boolean | false | DEPRECATED: This parameter has been replaced with `branches_to_be_notified` |
-| `branches_to_be_notified` | string | all | Branches to send notifications for. Valid options are "all", "default", "protected", and "default_and_protected" |
+| `branches_to_be_notified` | string | false | Branches to send notifications for. Valid options are "all", "default", "protected", and "default_and_protected" |
| `push_events` | boolean | false | Enable notifications for push events |
| `issues_events` | boolean | false | Enable notifications for issue events |
| `confidential_issues_events` | boolean | false | Enable notifications for confidential issue events |
diff --git a/doc/development/geo/framework.md b/doc/development/geo/framework.md
index b720a6ca47e..a03ce3e3913 100644
--- a/doc/development/geo/framework.md
+++ b/doc/development/geo/framework.md
@@ -91,8 +91,6 @@ module Geo
::Packages::PackageFile
end
- # Change this to `true` to release replication of this model. Then remove
- # this override in the next release.
# The feature flag follows the format `geo_#{replicable_name}_replication`,
# so here it would be `geo_package_file_replication`
def self.replication_enabled_by_default?
@@ -220,8 +218,6 @@ For example, to add support for files referenced by a `Widget` model with a
model_record.file
end
- # Change this to `true` to release replication of this model. Then remove
- # this override in the next release.
# The feature flag follows the format `geo_#{replicable_name}_replication`,
# so here it would be `geo_widget_replication`
def self.replication_enabled_by_default?
@@ -693,3 +689,33 @@ To do: This should be done as part of
Widget sync and verification data (aggregate and individual) should now be
available in the Admin UI!
+
+#### Releasing the feature
+
+1. In `ee/app/replicators/geo/widget_replicator.rb`, delete the `self.replication_enabled_by_default?` method:
+
+ ```ruby
+ module Geo
+ class WidgetReplicator < Gitlab::Geo::Replicator
+ ...
+
+ # REMOVE THIS METHOD
+ def self.replication_enabled_by_default?
+ false
+ end
+ # REMOVE THIS METHOD
+
+ ...
+ end
+ end
+ ```
+
+1. In `ee/app/graphql/types/geo/geo_node_type.rb`, remove the `feature_flag` option for the released type:
+
+ ```ruby
+ field :widget_registries, ::Types::Geo::WidgetRegistryType.connection_type,
+ null: true,
+ resolver: ::Resolvers::Geo::WidgetRegistriesResolver,
+ description: 'Find widget registries on this Geo node',
+ feature_flag: :geo_widget_replication # REMOVE THIS LINE
+ ```
diff --git a/lib/api/helpers/services_helpers.rb b/lib/api/helpers/services_helpers.rb
index 4bceda51900..4adb27a7414 100644
--- a/lib/api/helpers/services_helpers.rb
+++ b/lib/api/helpers/services_helpers.rb
@@ -381,6 +381,12 @@ module API
type: String,
desc: 'The Hangouts Chat webhook. e.g. https://chat.googleapis.com/v1/spaces…'
},
+ {
+ required: false,
+ name: :branches_to_be_notified,
+ type: String,
+ desc: 'Branches for which notifications are to be sent'
+ },
chat_notification_events
].flatten,
'hipchat' => [
diff --git a/lib/gitlab/database/batch_count.rb b/lib/gitlab/database/batch_count.rb
index 1762b81b7d8..558ce115b4a 100644
--- a/lib/gitlab/database/batch_count.rb
+++ b/lib/gitlab/database/batch_count.rb
@@ -8,15 +8,20 @@
# In order to not use a possible complex time consuming query when calculating min and max for batch_distinct_count
# the start and finish can be sent specifically
#
+# Grouped relations can be used as well. However, the preferred batch count should be around 10K because group by count is more expensive.
+#
# See https://gitlab.com/gitlab-org/gitlab/-/merge_requests/22705
#
# Examples:
# extend ::Gitlab::Database::BatchCount
# batch_count(User.active)
# batch_count(::Clusters::Cluster.aws_installed.enabled, :cluster_id)
+# batch_count(Namespace.group(:type))
# batch_distinct_count(::Project, :creator_id)
# batch_distinct_count(::Project.with_active_services.service_desk_enabled.where(time_period), start: ::User.minimum(:id), finish: ::User.maximum(:id))
+# batch_distinct_count(Project.group(:visibility_level), :creator_id)
# batch_sum(User, :sign_in_count)
+# batch_sum(Issue.group(:state_id), :weight))
module Gitlab
module Database
module BatchCount
@@ -77,12 +82,12 @@ module Gitlab
raise "Batch counting expects positive values only for #{@column}" if start < 0 || finish < 0
return FALLBACK if unwanted_configuration?(finish, batch_size, start)
- counter = 0
+ results = nil
batch_start = start
while batch_start <= finish
begin
- counter += batch_fetch(batch_start, batch_start + batch_size, mode)
+ results = merge_results(results, batch_fetch(batch_start, batch_start + batch_size, mode))
batch_start += batch_size
rescue ActiveRecord::QueryCanceled
# retry with a safe batch size & warmer cache
@@ -95,7 +100,17 @@ module Gitlab
sleep(SLEEP_TIME_IN_SECONDS)
end
- counter
+ results
+ end
+
+ def merge_results(results, object)
+ return object unless results
+
+ if object.is_a?(Hash)
+ results.merge!(object) { |_, a, b| a + b }
+ else
+ results + object
+ end
end
def batch_fetch(start, finish, mode)
@@ -118,11 +133,11 @@ module Gitlab
end
def actual_start(start)
- start || @relation.minimum(@column) || 0
+ start || @relation.unscope(:group).minimum(@column) || 0
end
def actual_finish(finish)
- finish || @relation.maximum(@column) || 0
+ finish || @relation.unscope(:group).maximum(@column) || 0
end
def check_mode!(mode)
diff --git a/spec/features/issuables/merge_request_discussion_lock_spec.rb b/spec/features/issuables/merge_request_discussion_lock_spec.rb
new file mode 100644
index 00000000000..4e0265839f6
--- /dev/null
+++ b/spec/features/issuables/merge_request_discussion_lock_spec.rb
@@ -0,0 +1,79 @@
+# frozen_string_literal: true
+# TODO use shared examples to merge this spec with discussion_lock_spec.rb
+# https://gitlab.com/gitlab-org/gitlab/-/issues/255910
+
+require 'spec_helper'
+
+RSpec.describe 'Merge Request Discussion Lock', :js do
+ let(:user) { create(:user) }
+ let(:project) { create(:project, :public, :repository) }
+ let(:merge_request) { create(:merge_request, source_project: project, author: user) }
+
+ before do
+ sign_in(user)
+ end
+
+ context 'when a user is a team member' do
+ before do
+ project.add_developer(user)
+ end
+
+ context 'when the discussion is unlocked' do
+ it 'the user can lock the merge_request' do
+ visit project_merge_request_path(merge_request.project, merge_request)
+
+ expect(find('.issuable-sidebar')).to have_content('Unlocked')
+
+ page.within('.issuable-sidebar') do
+ find('.lock-edit').click
+ click_button('Lock')
+ end
+
+ expect(find('[data-testid="lock-status"]')).to have_content('Locked')
+ end
+ end
+
+ context 'when the discussion is locked' do
+ before do
+ merge_request.update_attribute(:discussion_locked, true)
+ visit project_merge_request_path(merge_request.project, merge_request)
+ end
+
+ it 'the user can unlock the merge_request' do
+ expect(find('.issuable-sidebar')).to have_content('Locked')
+
+ page.within('.issuable-sidebar') do
+ find('.lock-edit').click
+ click_button('Unlock')
+ end
+
+ expect(find('[data-testid="lock-status"]')).to have_content('Unlocked')
+ end
+ end
+ end
+
+ context 'when a user is not a team member' do
+ context 'when the discussion is unlocked' do
+ before do
+ visit project_merge_request_path(merge_request.project, merge_request)
+ end
+
+ it 'the user can not lock the merge_request' do
+ expect(find('.issuable-sidebar')).to have_content('Unlocked')
+ expect(find('.issuable-sidebar')).not_to have_selector('.lock-edit')
+ end
+ end
+
+ context 'when the discussion is locked' do
+ before do
+ merge_request.update_attribute(:discussion_locked, true)
+ visit project_merge_request_path(merge_request.project, merge_request)
+ end
+
+ it 'the user can not unlock the merge_request' do
+ expect(find('.issuable-sidebar')).to have_content('Locked')
+ expect(find('.issuable-sidebar')).not_to have_selector('.lock-edit')
+ end
+ end
+ end
+end
diff --git a/spec/features/merge_request/user_views_open_merge_request_spec.rb b/spec/features/merge_request/user_views_open_merge_request_spec.rb
index 448844ae57d..44e6baa2931 100644
--- a/spec/features/merge_request/user_views_open_merge_request_spec.rb
+++ b/spec/features/merge_request/user_views_open_merge_request_spec.rb
@@ -22,7 +22,7 @@ RSpec.describe 'User views an open merge request' do
# returns the whole document, not the node's actual parent element
expect(find(:xpath, "#{node.path}/..").text).to eq(merge_request.description[2..-1])
- expect(page).to have_content(merge_request.title).and have_content(merge_request.description)
+ expect(page).to have_content(merge_request.title)
end
end
diff --git a/spec/features/projects/commit/user_comments_on_commit_spec.rb b/spec/features/projects/commit/user_comments_on_commit_spec.rb
index 87a022d74a3..0fa4975bb25 100644
--- a/spec/features/projects/commit/user_comments_on_commit_spec.rb
+++ b/spec/features/projects/commit/user_comments_on_commit_spec.rb
@@ -6,19 +6,22 @@ RSpec.describe "User comments on commit", :js do
include Spec::Support::Helpers::Features::NotesHelpers
include RepoHelpers
- let(:project) { create(:project, :repository) }
- let(:user) { create(:user) }
+ let_it_be(:project) { create(:project, :repository) }
+ let_it_be(:user) { create(:user) }
let(:comment_text) { "XML attached" }
- before do
- sign_in(user)
+ before_all do
project.add_developer(user)
+ end
- visit(project_commit_path(project, sample_commit.id))
+ before do
+ sign_in(user)
end
context "when adding new comment" do
it "adds comment" do
+ visit(project_commit_path(project, sample_commit.id))
+
emoji_code = ":+1:"
page.within(".js-main-target-form") do
@@ -57,6 +60,8 @@ RSpec.describe "User comments on commit", :js do
context "when editing comment" do
before do
+ visit(project_commit_path(project, sample_commit.id))
+
add_note(comment_text)
end
@@ -87,6 +92,8 @@ RSpec.describe "User comments on commit", :js do
context "when deleting comment" do
before do
+ visit(project_commit_path(project, sample_commit.id))
+
add_note(comment_text)
end
@@ -108,4 +115,35 @@ RSpec.describe "User comments on commit", :js do
expect(page).not_to have_css(".note")
end
end
+
+ context 'when checking task lists' do
+ let(:note_with_task) do
+ <<-EOT.strip_heredoc
+
+ - [ ] Task 1
+ EOT
+ end
+
+ before do
+ create(:note_on_commit, project: project, commit_id: sample_commit.id, note: note_with_task, author: user)
+ create(:note_on_commit, project: project, commit_id: sample_commit.id, note: note_with_task, author: user)
+
+ visit(project_commit_path(project, sample_commit.id))
+ end
+
+ it 'allows the tasks to be checked' do
+ expect(page).to have_selector('li.task-list-item', count: 2)
+ expect(page).to have_selector('li.task-list-item input[checked]', count: 0)
+
+ all('.task-list-item-checkbox').each do |checkbox|
+ checkbox.click
+ end
+ wait_for_requests
+
+ visit(project_commit_path(project, sample_commit.id))
+
+ expect(page).to have_selector('li.task-list-item', count: 2)
+ expect(page).to have_selector('li.task-list-item input[checked]', count: 2)
+ end
+ end
end
diff --git a/spec/features/task_lists_spec.rb b/spec/features/task_lists_spec.rb
index a9cfe794177..0f8daaf8e15 100644
--- a/spec/features/task_lists_spec.rb
+++ b/spec/features/task_lists_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Task Lists' do
+RSpec.describe 'Task Lists', :js do
include Warden::Test::Helpers
let_it_be(:project) { create(:project, :public, :repository) }
@@ -38,41 +38,7 @@ RSpec.describe 'Task Lists' do
MARKDOWN
end
- let(:nested_tasks_markdown) do
- <<-EOT.strip_heredoc
- - [ ] Task a
- - [x] Task a.1
- - [ ] Task a.2
- - [ ] Task b
-
- 1. [ ] Task 1
- 1. [ ] Task 1.1
- 1. [x] Task 1.2
- EOT
- end
-
- let(:commented_tasks_markdown) do
- <<-EOT.strip_heredoc
- <!--
- - [ ] a
- -->
-
- - [ ] b
- EOT
- end
-
- let(:summary_no_blank_line_markdown) do
- <<-EOT.strip_heredoc
- <details>
- <summary>No blank line after summary element breaks task list</summary>
- 1. [ ] People Ops: do such and such
- </details>
-
- * [ ] Task 1
- EOT
- end
-
- before(:all) do
+ before_all do
project.add_maintainer(user)
project.add_guest(user2)
end
@@ -86,7 +52,7 @@ RSpec.describe 'Task Lists' do
end
describe 'for Issues' do
- describe 'multiple tasks', :js do
+ describe 'multiple tasks' do
let!(:issue) { create(:issue, description: markdown, author: user, project: project) }
it 'renders' do
@@ -127,7 +93,7 @@ RSpec.describe 'Task Lists' do
end
end
- describe 'single incomplete task', :js do
+ describe 'single incomplete task' do
let!(:issue) { create(:issue, description: singleIncompleteMarkdown, author: user, project: project) }
it 'renders' do
@@ -146,7 +112,7 @@ RSpec.describe 'Task Lists' do
end
end
- describe 'single complete task', :js do
+ describe 'single complete task' do
let!(:issue) { create(:issue, description: singleCompleteMarkdown, author: user, project: project) }
it 'renders' do
@@ -175,7 +141,7 @@ RSpec.describe 'Task Lists' do
project: project, author: user)
end
- it 'renders for note body', :js do
+ it 'renders for note body' do
visit_issue(project, issue)
expect(page).to have_selector('.note ul.task-list', count: 1)
@@ -183,14 +149,14 @@ RSpec.describe 'Task Lists' do
expect(page).to have_selector('.note ul input[checked]', count: 2)
end
- it 'contains the required selectors', :js do
+ it 'contains the required selectors' do
visit_issue(project, issue)
expect(page).to have_selector('.note .js-task-list-container')
expect(page).to have_selector('.note .js-task-list-container .task-list .task-list-item .task-list-item-checkbox')
end
- it 'is only editable by author', :js do
+ it 'is only editable by author' do
visit_issue(project, issue)
expect(page).to have_selector('.js-task-list-container')
@@ -209,7 +175,7 @@ RSpec.describe 'Task Lists' do
project: project, author: user)
end
- it 'renders for note body', :js do
+ it 'renders for note body' do
visit_issue(project, issue)
expect(page).to have_selector('.note ul.task-list', count: 1)
@@ -224,7 +190,7 @@ RSpec.describe 'Task Lists' do
project: project, author: user)
end
- it 'renders for note body', :js do
+ it 'renders for note body' do
visit_issue(project, issue)
expect(page).to have_selector('.note ul.task-list', count: 1)
@@ -240,7 +206,7 @@ RSpec.describe 'Task Lists' do
end
shared_examples 'multiple tasks' do
- it 'renders for description', :js do
+ it 'renders for description' do
visit_merge_request(project, merge)
wait_for_requests
@@ -249,7 +215,7 @@ RSpec.describe 'Task Lists' do
expect(page).to have_selector('ul input[checked]', count: 2)
end
- it 'contains the required selectors', :js do
+ it 'contains the required selectors' do
visit_merge_request(project, merge)
wait_for_requests
@@ -261,7 +227,7 @@ RSpec.describe 'Task Lists' do
expect(page).to have_selector('form.js-issuable-update')
end
- it 'is only editable by author', :js do
+ it 'is only editable by author' do
visit_merge_request(project, merge)
wait_for_requests
@@ -300,7 +266,7 @@ RSpec.describe 'Task Lists' do
describe 'single incomplete task' do
let!(:merge) { create(:merge_request, :simple, description: singleIncompleteMarkdown, author: user, source_project: project) }
- it 'renders for description', :js do
+ it 'renders for description' do
visit_merge_request(project, merge)
wait_for_requests
@@ -319,7 +285,7 @@ RSpec.describe 'Task Lists' do
describe 'single complete task' do
let!(:merge) { create(:merge_request, :simple, description: singleCompleteMarkdown, author: user, source_project: project) }
- it 'renders for description', :js do
+ it 'renders for description' do
visit_merge_request(project, merge)
wait_for_requests
@@ -337,7 +303,17 @@ RSpec.describe 'Task Lists' do
end
describe 'markdown task edge cases' do
- describe 'commented tasks', :js do
+ describe 'commented tasks' do
+ let(:commented_tasks_markdown) do
+ <<-EOT.strip_heredoc
+ <!--
+ - [ ] a
+ -->
+
+ - [ ] b
+ EOT
+ end
+
let!(:issue) { create(:issue, description: commented_tasks_markdown, author: user, project: project) }
it 'renders' do
@@ -360,7 +336,18 @@ RSpec.describe 'Task Lists' do
end
end
- describe 'summary with no blank line', :js do
+ describe 'summary with no blank line' do
+ let(:summary_no_blank_line_markdown) do
+ <<-EOT.strip_heredoc
+ <details>
+ <summary>No blank line after summary element breaks task list</summary>
+ 1. [ ] People Ops: do such and such
+ </details>
+
+ * [ ] Task 1
+ EOT
+ end
+
let!(:issue) { create(:issue, description: summary_no_blank_line_markdown, author: user, project: project) }
it 'renders' do
@@ -382,5 +369,31 @@ RSpec.describe 'Task Lists' do
expect(page).to have_selector('ul input[checked]', count: 1)
end
end
+
+ describe 'markdown starting with new line character' do
+ let(:markdown_starting_with_new_line) do
+ <<-EOT.strip_heredoc
+
+ - [ ] Task 1
+ EOT
+ end
+
+ let(:merge_request) { create(:merge_request, description: markdown_starting_with_new_line, author: user, source_project: project) }
+
+ it 'allows the task to be checked' do
+ visit project_merge_request_path(project, merge_request)
+ wait_for_requests
+
+ expect(page).to have_selector('ul input[checked]', count: 0)
+
+ find('.task-list-item-checkbox').click
+ wait_for_requests
+
+ visit project_merge_request_path(project, merge_request)
+ wait_for_requests
+
+ expect(page).to have_selector('ul input[checked]', count: 1)
+ end
+ end
end
end
diff --git a/spec/frontend/issue_show/components/incidents/incident_tabs_spec.js b/spec/frontend/issue_show/components/incidents/incident_tabs_spec.js
index a51b497cd79..6babba37b57 100644
--- a/spec/frontend/issue_show/components/incidents/incident_tabs_spec.js
+++ b/spec/frontend/issue_show/components/incidents/incident_tabs_spec.js
@@ -79,7 +79,7 @@ describe('Incident Tabs component', () => {
it('renders the alert details table with the correct props', () => {
const alert = { iid: mockAlert.iid };
- expect(findAlertDetailsComponent().props('alert')).toEqual(alert);
+ expect(findAlertDetailsComponent().props('alert')).toMatchObject(alert);
expect(findAlertDetailsComponent().props('loading')).toBe(true);
});
diff --git a/spec/frontend/vue_shared/components/alert_detail_table_spec.js b/spec/frontend/vue_shared/components/alert_detail_table_spec.js
index 9c38ccad8a7..608a93d11fd 100644
--- a/spec/frontend/vue_shared/components/alert_detail_table_spec.js
+++ b/spec/frontend/vue_shared/components/alert_detail_table_spec.js
@@ -14,6 +14,7 @@ const mockAlert = {
assignees: { nodes: [] },
notes: { nodes: [] },
todos: { nodes: [] },
+ __typename: 'AlertManagementAlert',
};
describe('AlertDetails', () => {
@@ -35,6 +36,8 @@ describe('AlertDetails', () => {
});
const findTableComponent = () => wrapper.find(GlTable);
+ const findTableKeys = () => findTableComponent().findAll('tbody td:first-child');
+ const findTableField = (fields, fieldName) => fields.filter(row => row.text() === fieldName);
describe('Alert details', () => {
describe('empty state', () => {
@@ -69,6 +72,24 @@ describe('AlertDetails', () => {
it('renders a cell based on alert data', () => {
expect(findTableComponent().text()).toContain('SyntaxError: Invalid or unexpected token');
});
+
+ it('should show allowed alert fields', () => {
+ const fields = findTableKeys();
+
+ expect(findTableField(fields, 'Iid').exists()).toBe(true);
+ expect(findTableField(fields, 'Title').exists()).toBe(true);
+ expect(findTableField(fields, 'Severity').exists()).toBe(true);
+ expect(findTableField(fields, 'Status').exists()).toBe(true);
+ });
+
+ it('should not show disallowed alert fields', () => {
+ const fields = findTableKeys();
+
+ expect(findTableField(fields, 'Typename').exists()).toBe(false);
+ expect(findTableField(fields, 'Todos').exists()).toBe(false);
+ expect(findTableField(fields, 'Notes').exists()).toBe(false);
+ expect(findTableField(fields, 'Assignees').exists()).toBe(false);
+ });
});
});
});
diff --git a/spec/lib/gitlab/database/batch_count_spec.rb b/spec/lib/gitlab/database/batch_count_spec.rb
index 71d3666602f..93c499eb56d 100644
--- a/spec/lib/gitlab/database/batch_count_spec.rb
+++ b/spec/lib/gitlab/database/batch_count_spec.rb
@@ -108,6 +108,24 @@ RSpec.describe Gitlab::Database::BatchCount do
expect { described_class.batch_count(model.distinct(column)) }.to raise_error 'Use distinct count for optimized distinct counting'
end
end
+
+ context 'when a relation is grouped' do
+ let!(:one_more_issue) { create(:issue, author: user, project: model.first.project) }
+
+ before do
+ stub_const('Gitlab::Database::BatchCounter::MIN_REQUIRED_BATCH_SIZE', 1)
+ end
+
+ context 'count by default column' do
+ let(:count) do
+ described_class.batch_count(model.group(column), batch_size: 2)
+ end
+
+ it 'counts grouped records' do
+ expect(count).to eq({ user.id => 4, another_user.id => 2 })
+ end
+ end
+ end
end
describe '#batch_distinct_count' do
@@ -175,6 +193,24 @@ RSpec.describe Gitlab::Database::BatchCount do
end.to raise_error 'Use distinct count only with non id fields'
end
end
+
+ context 'when a relation is grouped' do
+ let!(:one_more_issue) { create(:issue, author: user, project: model.first.project) }
+
+ before do
+ stub_const('Gitlab::Database::BatchCounter::MIN_REQUIRED_BATCH_SIZE', 1)
+ end
+
+ context 'distinct count by non-unique column' do
+ let(:count) do
+ described_class.batch_distinct_count(model.group(column), :project_id, batch_size: 2)
+ end
+
+ it 'counts grouped records' do
+ expect(count).to eq({ user.id => 3, another_user.id => 2 })
+ end
+ end
+ end
end
describe '#batch_sum' do
diff --git a/spec/requests/api/services_spec.rb b/spec/requests/api/services_spec.rb
index 5528a0c094f..63ed57c5045 100644
--- a/spec/requests/api/services_spec.rb
+++ b/spec/requests/api/services_spec.rb
@@ -264,4 +264,34 @@ RSpec.describe API::Services do
expect(json_response['properties']['notify_only_broken_pipelines']).to eq(true)
end
end
+
+ describe 'Hangouts Chat service' do
+ let(:service_name) { 'hangouts-chat' }
+ let(:params) do
+ {
+ webhook: 'https://hook.example.com',
+ branches_to_be_notified: 'default'
+ }
+ end
+
+ before do
+ project.create_hangouts_chat_service(
+ active: true,
+ properties: params
+ )
+ end
+
+ it 'accepts branches_to_be_notified for update', :aggregate_failures do
+ put api("/projects/#{project.id}/services/#{service_name}", user), params: params.merge(branches_to_be_notified: 'all')
+
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(json_response['properties']['branches_to_be_notified']).to eq('all')
+ end
+
+ it 'only requires the webhook param' do
+ put api("/projects/#{project.id}/services/#{service_name}", user), params: { webhook: 'https://hook.example.com' }
+
+ expect(response).to have_gitlab_http_status(:ok)
+ end
+ end
end