diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2020-09-25 15:10:00 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2020-09-25 15:10:00 +0300 |
commit | f6d19ed8eb581689706f192e64c349b33fb7a916 (patch) | |
tree | 59173b6588639d077866bc9dc110a038ac171a68 | |
parent | e6bcd6e73801e31e15ef0c9c1e6ad790626cdfaf (diff) |
Add latest changes from gitlab-org/gitlab@master
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 |