diff options
| author | GitLab Bot <gitlab-bot@gitlab.com> | 2021-03-01 09:11:01 +0000 |
|---|---|---|
| committer | GitLab Bot <gitlab-bot@gitlab.com> | 2021-03-01 09:11:01 +0000 |
| commit | c7cb37255796023730d0e31324a533e55e25bc46 (patch) | |
| tree | 5140bef8ec205dd79b71066aa3ed3a3e4cb00be0 | |
| parent | 21543f57d625a70c3884d1915fa14ad340d01edc (diff) | |
| download | gitlab-ce-c7cb37255796023730d0e31324a533e55e25bc46.tar.gz | |
Add latest changes from gitlab-org/gitlab@master
43 files changed, 198 insertions, 630 deletions
diff --git a/.haml-lint_todo.yml b/.haml-lint_todo.yml index ac05540aad7..555f2645d6b 100644 --- a/.haml-lint_todo.yml +++ b/.haml-lint_todo.yml @@ -291,8 +291,6 @@ linters: - 'app/views/shared/milestones/_sidebar.html.haml' - 'app/views/shared/milestones/_top.html.haml' - 'app/views/shared/notes/_hints.html.haml' - - 'app/views/shared/notifications/_button.html.haml' - - 'app/views/shared/notifications/_new_button.html.haml' - 'app/views/shared/runners/_runner_description.html.haml' - 'app/views/shared/runners/show.html.haml' - 'app/views/shared/snippets/_header.html.haml' diff --git a/GITALY_SERVER_VERSION b/GITALY_SERVER_VERSION index 7a9a32e8ebb..66068a748a9 100644 --- a/GITALY_SERVER_VERSION +++ b/GITALY_SERVER_VERSION @@ -1 +1 @@ -59efecafe0838b6c940f67b00726c8c748d7dad5 +5d9c71d8d7188bd58f272dc62a7939ece747909d diff --git a/app/assets/javascripts/notifications/components/custom_notifications_modal.vue b/app/assets/javascripts/notifications/components/custom_notifications_modal.vue index 0f628897e17..39f5799f31c 100644 --- a/app/assets/javascripts/notifications/components/custom_notifications_modal.vue +++ b/app/assets/javascripts/notifications/components/custom_notifications_modal.vue @@ -115,7 +115,11 @@ export default { <gl-loading-icon v-if="isLoading" size="lg" class="gl-mt-3" /> <template v-else> <gl-form-group v-for="event in events" :key="event.id"> - <gl-form-checkbox v-model="event.enabled" @change="updateEvent($event, event)"> + <gl-form-checkbox + v-model="event.enabled" + :data-testid="`notification-setting-${event.id}`" + @change="updateEvent($event, event)" + > <strong>{{ event.name }}</strong ><gl-loading-icon v-if="event.loading" :inline="true" class="gl-ml-2" /> </gl-form-checkbox> diff --git a/app/assets/javascripts/notifications/components/notifications_dropdown.vue b/app/assets/javascripts/notifications/components/notifications_dropdown.vue index e4cedfdb810..365831fcdbf 100644 --- a/app/assets/javascripts/notifications/components/notifications_dropdown.vue +++ b/app/assets/javascripts/notifications/components/notifications_dropdown.vue @@ -128,7 +128,8 @@ export default { <gl-button-group v-if="isCustomNotification" v-gl-tooltip="{ title: buttonTooltip }" - data-testid="notificationButton" + data-testid="notification-button" + :class="{ disabled: disabled }" :size="buttonSize" > <gl-button @@ -165,12 +166,13 @@ export default { <gl-dropdown v-else v-gl-tooltip="{ title: buttonTooltip }" - data-testid="notificationButton" + data-testid="notification-button" :text="buttonText" :icon="buttonIcon" :loading="isLoading" :size="buttonSize" :disabled="disabled" + :class="{ disabled: disabled }" > <notifications-dropdown-item v-for="item in notificationLevels" diff --git a/app/assets/javascripts/notifications/components/notifications_dropdown_item.vue b/app/assets/javascripts/notifications/components/notifications_dropdown_item.vue index 73bb9c1b36f..2138372d8ad 100644 --- a/app/assets/javascripts/notifications/components/notifications_dropdown_item.vue +++ b/app/assets/javascripts/notifications/components/notifications_dropdown_item.vue @@ -33,7 +33,13 @@ export default { </script> <template> - <gl-dropdown-item is-check-item :is-checked="isActive" @click="$emit('item-selected', level)"> + <gl-dropdown-item + is-check-item + :is-checked="isActive" + :class="{ 'is-active': isActive }" + data-testid="notification-item" + @click="$emit('item-selected', level)" + > <div class="gl-display-flex gl-flex-direction-column"> <span class="gl-font-weight-bold">{{ title }}</span> <span class="gl-text-gray-500">{{ description }}</span> diff --git a/app/assets/javascripts/notifications/constants.js b/app/assets/javascripts/notifications/constants.js index c12f6a75f96..4f875977d78 100644 --- a/app/assets/javascripts/notifications/constants.js +++ b/app/assets/javascripts/notifications/constants.js @@ -53,6 +53,7 @@ export const i18n = { reassign_merge_request: s__('NotificationEvent|Reassign merge request'), reopen_issue: s__('NotificationEvent|Reopen issue'), reopen_merge_request: s__('NotificationEvent|Reopen merge request'), + merge_when_pipeline_succeeds: s__('NotificationEvent|Merge when pipeline succeeds'), success_pipeline: s__('NotificationEvent|Successful pipeline'), }, }; diff --git a/app/assets/javascripts/notifications_dropdown.js b/app/assets/javascripts/notifications_dropdown.js deleted file mode 100644 index d61defed14d..00000000000 --- a/app/assets/javascripts/notifications_dropdown.js +++ /dev/null @@ -1,35 +0,0 @@ -import $ from 'jquery'; -import { Rails } from '~/lib/utils/rails_ujs'; -import { __ } from '~/locale'; -import { deprecatedCreateFlash as Flash } from './flash'; - -export default function notificationsDropdown() { - $(document).on('click', '.update-notification', function updateNotificationCallback(e) { - e.preventDefault(); - - if ($(this).is('.is-active') && $(this).data('notificationLevel') === 'custom') { - return; - } - - const notificationLevel = $(this).data('notificationLevel'); - const form = $(this).parents('.notification-form').first(); - - form.find('.js-notification-loading').toggleClass('spinner'); - if (form.hasClass('no-label')) { - form.find('.js-notification-loading').toggleClass('hidden'); - form.find('.js-notifications-icon').toggleClass('hidden'); - } - form.find('#notification_setting_level').val(notificationLevel); - Rails.fire(form[0], 'submit'); - }); - - $(document).on('ajax:success', '.notification-form', (e) => { - const data = e.detail[0]; - - if (data.saved) { - $(e.currentTarget).closest('.js-notification-dropdown').replaceWith(data.html); - } else { - Flash(__('Failed to save new settings'), 'alert'); - } - }); -} diff --git a/app/assets/javascripts/notifications_form.js b/app/assets/javascripts/notifications_form.js deleted file mode 100644 index 8b90da71bef..00000000000 --- a/app/assets/javascripts/notifications_form.js +++ /dev/null @@ -1,48 +0,0 @@ -import $ from 'jquery'; -import { deprecatedCreateFlash as flash } from './flash'; -import axios from './lib/utils/axios_utils'; -import { __ } from './locale'; - -export default class NotificationsForm { - constructor() { - this.toggleCheckbox = this.toggleCheckbox.bind(this); - this.initEventListeners(); - } - - initEventListeners() { - $(document).on('change', '.js-custom-notification-event', this.toggleCheckbox); - } - - toggleCheckbox(e) { - const $checkbox = $(e.currentTarget); - const $parent = $checkbox.closest('.form-check'); - - this.saveEvent($checkbox, $parent); - } - - // eslint-disable-next-line class-methods-use-this - showCheckboxLoadingSpinner($parent) { - $parent.find('.is-loading').removeClass('gl-display-none'); - $parent.find('.is-done').addClass('gl-display-none'); - } - - saveEvent($checkbox, $parent) { - const form = $parent.parents('form').first(); - - this.showCheckboxLoadingSpinner($parent); - - axios[form.attr('method')](form.attr('action'), form.serialize()) - .then(({ data }) => { - $checkbox.enable(); - if (data.saved) { - $parent.find('.is-loading').addClass('gl-display-none'); - $parent.find('.is-done').removeClass('gl-display-none'); - - setTimeout(() => { - $parent.find('.is-done').addClass('gl-display-none'); - }, 2000); - } - }) - .catch(() => flash(__('There was an error saving your notification settings.'))); - } -} diff --git a/app/assets/javascripts/pages/groups/shared/group_details.js b/app/assets/javascripts/pages/groups/shared/group_details.js index 8c272e561db..b076ce68250 100644 --- a/app/assets/javascripts/pages/groups/shared/group_details.js +++ b/app/assets/javascripts/pages/groups/shared/group_details.js @@ -7,8 +7,6 @@ import initInviteMembersModal from '~/invite_members/init_invite_members_modal'; import initInviteMembersTrigger from '~/invite_members/init_invite_members_trigger'; import { getPagePath, getDashPath } from '~/lib/utils/common_utils'; import initNotificationsDropdown from '~/notifications'; -import notificationsDropdown from '~/notifications_dropdown'; -import NotificationsForm from '~/notifications_form'; import ProjectsList from '~/projects_list'; import GroupTabs from './group_tabs'; @@ -22,13 +20,8 @@ export default function initGroupDetails(actionName = 'show') { new GroupTabs({ parentEl: '.groups-listing', action }); new ShortcutsNavigation(); - new NotificationsForm(); - if (gon.features?.vueNotificationDropdown) { - initNotificationsDropdown(); - } else { - notificationsDropdown(); - } + initNotificationsDropdown(); new ProjectsList(); diff --git a/app/assets/javascripts/pages/profiles/index/index.js b/app/assets/javascripts/pages/profiles/index/index.js deleted file mode 100644 index 586b3be8661..00000000000 --- a/app/assets/javascripts/pages/profiles/index/index.js +++ /dev/null @@ -1,7 +0,0 @@ -import notificationsDropdown from '../../../notifications_dropdown'; -import NotificationsForm from '../../../notifications_form'; - -document.addEventListener('DOMContentLoaded', () => { - new NotificationsForm(); // eslint-disable-line no-new - notificationsDropdown(); -}); diff --git a/app/assets/javascripts/pages/profiles/notifications/show/index.js b/app/assets/javascripts/pages/profiles/notifications/show/index.js index 639f5deb72c..51ba6c7a01e 100644 --- a/app/assets/javascripts/pages/profiles/notifications/show/index.js +++ b/app/assets/javascripts/pages/profiles/notifications/show/index.js @@ -1,9 +1,5 @@ import initNotificationsDropdown from '~/notifications'; -import notificationsDropdown from '../../../../notifications_dropdown'; -import NotificationsForm from '../../../../notifications_form'; document.addEventListener('DOMContentLoaded', () => { - new NotificationsForm(); // eslint-disable-line no-new - notificationsDropdown(); initNotificationsDropdown(); }); diff --git a/app/assets/javascripts/pages/projects/show/index.js b/app/assets/javascripts/pages/projects/show/index.js index e5ec9976ac5..7bb7be45f59 100644 --- a/app/assets/javascripts/pages/projects/show/index.js +++ b/app/assets/javascripts/pages/projects/show/index.js @@ -7,16 +7,13 @@ import initInviteMembersModal from '~/invite_members/init_invite_members_modal'; import initInviteMembersTrigger from '~/invite_members/init_invite_members_trigger'; import leaveByUrl from '~/namespaces/leave_by_url'; import initVueNotificationsDropdown from '~/notifications'; -import NotificationsForm from '~/notifications_form'; import initReadMore from '~/read_more'; import UserCallout from '~/user_callout'; -import notificationsDropdown from '../../../notifications_dropdown'; import Star from '../../../star'; initReadMore(); new Star(); // eslint-disable-line no-new -new NotificationsForm(); // eslint-disable-line no-new // eslint-disable-next-line no-new new UserCallout({ setCalloutPerProject: false, @@ -43,12 +40,6 @@ if (document.querySelector('.project-show-activity')) { leaveByUrl('project'); -if (gon.features?.vueNotificationDropdown) { - initVueNotificationsDropdown(); -} else { - notificationsDropdown(); -} - initVueNotificationsDropdown(); new ShortcutsNavigation(); // eslint-disable-line no-new diff --git a/app/assets/stylesheets/framework/lists.scss b/app/assets/stylesheets/framework/lists.scss index b81e9828dec..df2ba718c72 100644 --- a/app/assets/stylesheets/framework/lists.scss +++ b/app/assets/stylesheets/framework/lists.scss @@ -192,11 +192,6 @@ ul.content-list { display: flex; align-items: center; white-space: nowrap; - - // Override style that allows the flex-row text to wrap. - &.allow-wrap { - white-space: normal; - } } .row-main-content { diff --git a/app/controllers/groups_controller.rb b/app/controllers/groups_controller.rb index 9d7aebe4505..5de207857bb 100644 --- a/app/controllers/groups_controller.rb +++ b/app/controllers/groups_controller.rb @@ -30,7 +30,6 @@ class GroupsController < Groups::ApplicationController before_action do push_frontend_feature_flag(:vue_issuables_list, @group) - push_frontend_feature_flag(:vue_notification_dropdown, @group, default_enabled: :yaml) end before_action do diff --git a/app/controllers/notification_settings_controller.rb b/app/controllers/notification_settings_controller.rb deleted file mode 100644 index 7d8c035c852..00000000000 --- a/app/controllers/notification_settings_controller.rb +++ /dev/null @@ -1,61 +0,0 @@ -# frozen_string_literal: true - -class NotificationSettingsController < ApplicationController - before_action :authenticate_user! - - feature_category :users - - def create - return render_404 unless can_read?(resource) - - @notification_setting = current_user.notification_settings_for(resource) - @saved = @notification_setting.update(notification_setting_params_for(resource)) - - render_response - end - - def update - @notification_setting = current_user.notification_settings.find(params[:id]) - @saved = @notification_setting.update(notification_setting_params_for(@notification_setting.source)) - - render_response - end - - private - - def resource - @resource ||= - if params[:project_id].present? - Project.find(params[:project_id]) - elsif params[:namespace_id].present? - Group.find(params[:namespace_id]) - end - end - - def can_read?(resource) - ability_name = resource.class.name.downcase - ability_name = "read_#{ability_name}".to_sym - - can?(current_user, ability_name, resource) - end - - def render_response - btn_class = nil - - if params[:hide_label].present? - btn_class = 'btn-xs' if params[:project_id].present? - response_template = 'shared/notifications/_new_button' - else - response_template = 'shared/notifications/_button' - end - - render json: { - html: view_to_html_string(response_template, notification_setting: @notification_setting, btn_class: btn_class), - saved: @saved - } - end - - def notification_setting_params_for(source) - params.require(:notification_setting).permit(NotificationSetting.allowed_fields(source)) - end -end diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb index 1185bcc9ea8..cd5dd7346ee 100644 --- a/app/controllers/projects_controller.rb +++ b/app/controllers/projects_controller.rb @@ -31,10 +31,6 @@ class ProjectsController < Projects::ApplicationController # Project Export Rate Limit before_action :export_rate_limit, only: [:export, :download_export, :generate_new_export] - before_action do - push_frontend_feature_flag(:vue_notification_dropdown, @project, default_enabled: :yaml) - end - before_action only: [:edit] do push_frontend_feature_flag(:allow_editing_commit_messages, @project) end diff --git a/app/graphql/types/ci/job_artifact_file_type_enum.rb b/app/graphql/types/ci/job_artifact_file_type_enum.rb index 4b484dec590..5099b0d4850 100644 --- a/app/graphql/types/ci/job_artifact_file_type_enum.rb +++ b/app/graphql/types/ci/job_artifact_file_type_enum.rb @@ -6,7 +6,8 @@ module Types graphql_name 'JobArtifactFileType' ::Ci::JobArtifact::TYPE_AND_FORMAT_PAIRS.keys.each do |file_type| - value file_type.to_s.upcase, value: file_type.to_s + description = file_type == :codequality ? "CODE QUALITY" : file_type.to_s.titleize.upcase # This is needed as doc lint will not allow codequality as one word + value file_type.to_s.upcase, value: file_type.to_s, description: "#{description} job artifact file type." end end end diff --git a/app/views/groups/_home_panel.html.haml b/app/views/groups/_home_panel.html.haml index 37c4ecc09f3..98ac4515bd8 100644 --- a/app/views/groups/_home_panel.html.haml +++ b/app/views/groups/_home_panel.html.haml @@ -23,11 +23,8 @@ .home-panel-buttons.col-md-12.col-lg-6 - if current_user .gl-display-flex.gl-flex-wrap.gl-lg-justify-content-end.gl-mx-n2{ data: { testid: 'group-buttons' } } - - if Feature.enabled?(:vue_notification_dropdown, @group, default_enabled: :yaml) - - if @notification_setting - .js-vue-notification-dropdown{ data: { disabled: emails_disabled, dropdown_items: notification_dropdown_items(@notification_setting).to_json, notification_level: @notification_setting.level, help_page_path: help_page_path('user/profile/notifications'), group_id: @group.id, container_class: 'gl-mr-3 gl-mt-3 gl-vertical-align-top' } } - - else - = render 'shared/notifications/new_button', notification_setting: @notification_setting, btn_class: 'btn gl-button gl-sm-w-auto gl-w-full', dropdown_container_class: 'gl-mr-0 gl-px-2 gl-sm-w-auto gl-w-full', emails_disabled: emails_disabled + - if @notification_setting + .js-vue-notification-dropdown{ data: { disabled: emails_disabled.to_s, dropdown_items: notification_dropdown_items(@notification_setting).to_json, notification_level: @notification_setting.level, help_page_path: help_page_path('user/profile/notifications'), group_id: @group.id, container_class: 'gl-mr-3 gl-mt-3 gl-vertical-align-top' } } - if can_create_subgroups .gl-px-2.gl-sm-w-auto.gl-w-full = link_to _("New subgroup"), new_group_path(parent_id: @group.id), class: "btn btn-success btn-md gl-button btn-success-secondary gl-mt-3 gl-sm-w-auto gl-w-full", data: { qa_selector: 'new_subgroup_button' } diff --git a/app/views/profiles/notifications/_group_settings.html.haml b/app/views/profiles/notifications/_group_settings.html.haml index abbfbd995b6..82083af9ff1 100644 --- a/app/views/profiles/notifications/_group_settings.html.haml +++ b/app/views/profiles/notifications/_group_settings.html.haml @@ -9,11 +9,8 @@ = link_to group.name, group_path(group) .table-section.section-30.text-right - - if Feature.enabled?(:vue_notification_dropdown, default_enabled: :yaml) - - if setting - .js-vue-notification-dropdown{ data: {Â disabled: emails_disabled, dropdown_items: notification_dropdown_items(setting).to_json, notification_level: setting.level, group_id: group.id, container_class: 'gl-mr-3', show_label: "true" } } - - else - = render 'shared/notifications/button', notification_setting: setting, emails_disabled: emails_disabled + - if setting + .js-vue-notification-dropdown{ data: {Â disabled: emails_disabled.to_s, dropdown_items: notification_dropdown_items(setting).to_json, notification_level: setting.level, group_id: group.id, container_class: 'gl-mr-3', show_label: "true" } } .table-section.section-30 = form_for setting, url: profile_notifications_group_path(group), method: :put, html: { class: 'update-notifications gl-display-flex' } do |f| diff --git a/app/views/profiles/notifications/_project_settings.html.haml b/app/views/profiles/notifications/_project_settings.html.haml index 8cd552caa3d..e6953d1b32e 100644 --- a/app/views/profiles/notifications/_project_settings.html.haml +++ b/app/views/profiles/notifications/_project_settings.html.haml @@ -8,8 +8,5 @@ = link_to_project(project) .float-right - - if Feature.enabled?(:vue_notification_dropdown, default_enabled: :yaml) - - if setting - .js-vue-notification-dropdown{ data: { disabled: emails_disabled, dropdown_items: notification_dropdown_items(setting).to_json, notification_level: setting.level, project_id: project.id, container_class: 'gl-mr-3', show_label: "true" } } - - else - = render 'shared/notifications/button', notification_setting: setting, emails_disabled: emails_disabled + - if setting + .js-vue-notification-dropdown{ data: { disabled: emails_disabled.to_s, dropdown_items: notification_dropdown_items(setting).to_json, notification_level: setting.level, project_id: project.id, container_class: 'gl-mr-3', show_label: "true" } } diff --git a/app/views/profiles/notifications/show.html.haml b/app/views/profiles/notifications/show.html.haml index cb0ada414ed..853188c563f 100644 --- a/app/views/profiles/notifications/show.html.haml +++ b/app/views/profiles/notifications/show.html.haml @@ -32,11 +32,8 @@ %br .clearfix .form-group.float-left.global-notification-setting - - if Feature.enabled?(:vue_notification_dropdown, default_enabled: :yaml) - - if @global_notification_setting - .js-vue-notification-dropdown{ data: { dropdown_items: notification_dropdown_items(@global_notification_setting).to_json, notification_level: @global_notification_setting.level, help_page_path: help_page_path('user/profile/notifications'), show_label: 'true' } } - - else - = render 'shared/notifications/button', notification_setting: @global_notification_setting + - if @global_notification_setting + .js-vue-notification-dropdown{ data: { dropdown_items: notification_dropdown_items(@global_notification_setting).to_json, notification_level: @global_notification_setting.level, help_page_path: help_page_path('user/profile/notifications'), show_label: 'true' } } .clearfix diff --git a/app/views/projects/_home_panel.html.haml b/app/views/projects/_home_panel.html.haml index 0d3049cd9a2..5ebe4a4b741 100644 --- a/app/views/projects/_home_panel.html.haml +++ b/app/views/projects/_home_panel.html.haml @@ -46,11 +46,8 @@ .project-repo-buttons.col-md-12.col-lg-6.d-inline-flex.flex-wrap.justify-content-lg-end - if current_user .d-inline-flex - - if Feature.enabled?(:vue_notification_dropdown, @project, default_enabled: :yaml) - - if @notification_setting - .js-vue-notification-dropdown{ data: { button_size: "small", disabled: emails_disabled, dropdown_items: notification_dropdown_items(@notification_setting).to_json, notification_level: @notification_setting.level, help_page_path: help_page_path('user/profile/notifications'), project_id: @project.id, container_class: 'gl-mr-3 gl-mt-5 gl-vertical-align-top' } } - - else - = render 'shared/notifications/new_button', notification_setting: @notification_setting, btn_class: 'btn-xs', dropdown_container_class: 'gl-mr-3', emails_disabled: emails_disabled + - if @notification_setting + .js-vue-notification-dropdown{ data: { button_size: "small", disabled: emails_disabled.to_s, dropdown_items: notification_dropdown_items(@notification_setting).to_json, notification_level: @notification_setting.level, help_page_path: help_page_path('user/profile/notifications'), project_id: @project.id, container_class: 'gl-mr-3 gl-mt-5 gl-vertical-align-top' } } .count-buttons.d-inline-flex = render 'projects/buttons/star' diff --git a/app/views/projects/tags/_tag.html.haml b/app/views/projects/tags/_tag.html.haml index 61b357831fd..1072d5bce06 100644 --- a/app/views/projects/tags/_tag.html.haml +++ b/app/views/projects/tags/_tag.html.haml @@ -2,7 +2,7 @@ - release = @releases.find { |release| release.tag == tag.name } - commit_status = @tag_pipeline_statuses[tag.name] unless @tag_pipeline_statuses.nil? -%li.flex-row.allow-wrap.js-tag-list +%li.flex-row.js-tag-list{ class: "gl-white-space-normal!" } .row-main-content = sprite_icon('tag') = link_to tag.name, project_tag_path(@project, tag.name), class: 'item-title ref-name' diff --git a/app/views/shared/notifications/_button.html.haml b/app/views/shared/notifications/_button.html.haml deleted file mode 100644 index e12531b8a8d..00000000000 --- a/app/views/shared/notifications/_button.html.haml +++ /dev/null @@ -1,37 +0,0 @@ -- btn_class = local_assigns.fetch(:btn_class, '') -- emails_disabled = local_assigns.fetch(:emails_disabled, false) - -- if notification_setting - - if emails_disabled - - button_title = notification_description(:owner_disabled) - - aria_label = button_title - - btn_class << " disabled" - - else - - button_title = _("Notification setting") - - aria_label = _("Notification setting - %{notification_title}") % { notification_title: notification_title(notification_setting.level) } - - .js-notification-dropdown.notification-dropdown.mr-md-2.home-panel-action-button.dropdown.inline - = form_for notification_setting, remote: true, html: { class: "inline notification-form" } do |f| - = hidden_setting_source_input(notification_setting) - = f.hidden_field :level, class: "notification_setting_level" - .js-notification-toggle-btns - %div{ class: ("btn-group" if notification_setting.custom?) } - - if notification_setting.custom? - %button.dropdown-new.btn.btn-default.btn-icon.gl-button.has-tooltip.notifications-btn.text-left#notifications-button{ type: "button", title: button_title, class: "#{btn_class}", "aria-label" => aria_label, data: { container: "body", toggle: "modal", target: "#" + notifications_menu_identifier("modal", notification_setting), display: 'static' } } - = sprite_icon("notifications", css_class: "js-notification-loading") - = notification_title(notification_setting.level) - %button.btn.dropdown-toggle.gl-display-flex.gl-align-items-center{ data: { toggle: "dropdown", target: notifications_menu_identifier("dropdown", notification_setting), flip: "false" } } - = sprite_icon('chevron-down') - .sr-only Toggle dropdown - - else - %button.dropdown-new.btn.btn-default.btn-icon.gl-button.has-tooltip.notifications-btn#notifications-button{ type: "button", title: button_title, class: "#{btn_class}", "aria-label" => aria_label, data: { container: "body", toggle: "dropdown", target: notifications_menu_identifier("dropdown", notification_setting), flip: "false" } } - .float-left - = sprite_icon("notifications", css_class: "js-notification-loading") - = notification_title(notification_setting.level) - .float-right - = sprite_icon("chevron-down") - - = render "shared/notifications/notification_dropdown", notification_setting: notification_setting - - = content_for :scripts_body do - = render "shared/notifications/custom_notifications", notification_setting: notification_setting diff --git a/app/views/shared/notifications/_custom_notifications.html.haml b/app/views/shared/notifications/_custom_notifications.html.haml deleted file mode 100644 index 946e3c67dcf..00000000000 --- a/app/views/shared/notifications/_custom_notifications.html.haml +++ /dev/null @@ -1,34 +0,0 @@ -- hide_label = local_assigns.fetch(:hide_label, false) - -.modal.fade{ tabindex: "-1", role: "dialog", id: notifications_menu_identifier("modal", notification_setting), "aria-labelledby": "custom-notifications-title" } - .modal-dialog - .modal-content - .modal-header - %h4#custom-notifications-title.modal-title - #{ _('Custom notification events') } - %button.close{ type: "button", "data-dismiss": "modal", "aria-label" => _('Close') } - %span{ "aria-hidden": true } × - - .modal-body - .container-fluid - = form_for notification_setting, html: { class: "custom-notifications-form" } do |f| - = hidden_setting_source_input(notification_setting) - = hidden_field_tag("hide_label", true) if hide_label - .row - .col-lg-4 - %h4.gl-mt-0= _('Notification events') - %p - - notification_link = link_to _('notification emails'), help_page_path('user/profile/notifications'), target: '_blank' - - paragraph = _('Custom notification levels are the same as participating levels. With custom notification levels you will also receive notifications for select events. To find out more, check out %{notification_link}.') % { notification_link: notification_link.html_safe } - #{ paragraph.html_safe } - .col-lg-8 - - notification_setting.email_events.each_with_index do |event, index| - - field_id = "#{notifications_menu_identifier("modal", notification_setting)}_notification_setting[#{event}]" - .form-group - .form-check{ class: ("gl-mt-0" if index == 0) } - = check_box("notification_setting", event, id: field_id, class: "js-custom-notification-event form-check-input", checked: notification_setting.public_send(event)) - %label.form-check-label{ for: field_id } - %strong - = notification_event_name(event) - %span.spinner.is-loading.gl-vertical-align-middle.gl-display-none - = sprite_icon('check', css_class: 'is-done gl-display-none gl-vertical-align-middle gl-text-green-600') diff --git a/app/views/shared/notifications/_new_button.html.haml b/app/views/shared/notifications/_new_button.html.haml deleted file mode 100644 index 4b008601783..00000000000 --- a/app/views/shared/notifications/_new_button.html.haml +++ /dev/null @@ -1,35 +0,0 @@ -- btn_class = local_assigns.fetch(:btn_class, '') -- dropdown_container_class = local_assigns.fetch(:dropdown_container_class, '') -- emails_disabled = local_assigns.fetch(:emails_disabled, false) - -- if notification_setting - - if emails_disabled - - button_title = notification_description(:owner_disabled) - - btn_class << " disabled" - - else - - button_title = _("Notification setting - %{notification_title}") % { notification_title: notification_title(notification_setting.level) } - - .js-notification-dropdown.notification-dropdown.home-panel-action-button.gl-mt-3.dropdown.inline{ class: dropdown_container_class } - = form_for notification_setting, remote: true, html: { class: "notification-form no-label" } do |f| - = hidden_setting_source_input(notification_setting) - = hidden_field_tag "hide_label", true - = f.hidden_field :level, class: "notification_setting_level" - .js-notification-toggle-btns - %div{ class: ("btn-group" if notification_setting.custom?) } - - if notification_setting.custom? - %button.dropdown-new.btn.gl-button.btn-default.has-tooltip.notifications-btn#notifications-button{ type: "button", title: button_title, class: "#{btn_class}", "aria-label" => button_title, data: { container: "body", placement: 'top', toggle: "modal", target: "#" + notifications_menu_identifier("modal", notification_setting), display: 'static' } } - = notification_setting_icon(notification_setting) - %span.js-notification-loading.fa.hidden - %button.btn.gl-button.btn-default.dropdown-toggle{ data: { toggle: "dropdown", target: notifications_menu_identifier("dropdown", notification_setting), flip: "false" }, class: "#{btn_class}" } - = sprite_icon("chevron-down", css_class: "icon mr-0") - .sr-only Toggle dropdown - - else - %button.dropdown-new.btn.gl-button.btn-default.has-tooltip.notifications-btn#notifications-button{ type: "button", title: button_title, class: "#{btn_class}", "aria-label" => button_title, data: { container: "body", placement: 'top', toggle: "dropdown", target: notifications_menu_identifier("dropdown", notification_setting), flip: "false" } } - = notification_setting_icon(notification_setting) - %span.js-notification-loading.fa.hidden - = sprite_icon("chevron-down", css_class: "icon") - - = render "shared/notifications/notification_dropdown", notification_setting: notification_setting - - = content_for :scripts_body do - = render "shared/notifications/custom_notifications", notification_setting: notification_setting, hide_label: true diff --git a/app/views/shared/notifications/_notification_dropdown.html.haml b/app/views/shared/notifications/_notification_dropdown.html.haml deleted file mode 100644 index a6ef2d51171..00000000000 --- a/app/views/shared/notifications/_notification_dropdown.html.haml +++ /dev/null @@ -1,12 +0,0 @@ -%ul.dropdown-menu.dropdown-menu-no-wrap.dropdown-menu-selectable.dropdown-menu-large{ role: "menu", class: [notifications_menu_identifier("dropdown", notification_setting)] } - - NotificationSetting.levels.each_key do |level| - - next if level == "custom" - - next if level == "global" && notification_setting.source.nil? - - = notification_list_item(level, notification_setting) - - %li.divider - %li - %a.update-notification{ href: "#", role: "button", class: ("is-active" if notification_setting.custom?), data: { toggle: "modal", target: "#" + notifications_menu_identifier("modal", notification_setting), notification_level: "custom", notification_title: "Custom" } } - %strong.dropdown-menu-inner-title= s_('NotificationSetting|Custom') - %span.dropdown-menu-inner-content= notification_description("custom") diff --git a/changelogs/unreleased/321372-feature-flag-rollout-of-vue_notification_dropdown.yml b/changelogs/unreleased/321372-feature-flag-rollout-of-vue_notification_dropdown.yml new file mode 100644 index 00000000000..02cd7ab47b6 --- /dev/null +++ b/changelogs/unreleased/321372-feature-flag-rollout-of-vue_notification_dropdown.yml @@ -0,0 +1,5 @@ +--- +title: Add Vue notifications dropdown component +merge_request: 54422 +author: +type: other diff --git a/changelogs/unreleased/323015-fix-empty-field-in-custom-notification-events-modal.yml b/changelogs/unreleased/323015-fix-empty-field-in-custom-notification-events-modal.yml new file mode 100644 index 00000000000..70b4e90f911 --- /dev/null +++ b/changelogs/unreleased/323015-fix-empty-field-in-custom-notification-events-modal.yml @@ -0,0 +1,5 @@ +--- +title: Fix empty field in custom notification events modal +merge_request: 55313 +author: Kev @KevSlashNull +type: fixed diff --git a/config/feature_flags/development/vue_notification_dropdown.yml b/config/feature_flags/development/vue_notification_dropdown.yml deleted file mode 100644 index 4c4f67fe928..00000000000 --- a/config/feature_flags/development/vue_notification_dropdown.yml +++ /dev/null @@ -1,8 +0,0 @@ ---- -name: vue_notification_dropdown -introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/52068 -rollout_issue_url: -milestone: '13.8' -type: development -group: group::optimize -default_enabled: false diff --git a/config/routes.rb b/config/routes.rb index d9cf2e9ebdd..4197aa8dbb7 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -166,9 +166,6 @@ Rails.application.routes.draw do end end - # Notification settings - resources :notification_settings, only: [:create, :update] - resources :invites, only: [:show], constraints: { id: /[A-Za-z0-9_-]+/ } do member do post :accept diff --git a/doc/api/graphql/reference/index.md b/doc/api/graphql/reference/index.md index 9315993368f..aecc7608f38 100644 --- a/doc/api/graphql/reference/index.md +++ b/doc/api/graphql/reference/index.md @@ -5417,33 +5417,33 @@ Iteration ID wildcard values. | Value | Description | | ----- | ----------- | -| `ACCESSIBILITY` | | -| `API_FUZZING` | | -| `ARCHIVE` | | -| `BROWSER_PERFORMANCE` | | -| `CLUSTER_APPLICATIONS` | | -| `COBERTURA` | | -| `CODEQUALITY` | | -| `CONTAINER_SCANNING` | | -| `COVERAGE_FUZZING` | | -| `DAST` | | -| `DEPENDENCY_SCANNING` | | -| `DOTENV` | | -| `JUNIT` | | -| `LICENSE_MANAGEMENT` | | -| `LICENSE_SCANNING` | | -| `LOAD_PERFORMANCE` | | -| `LSIF` | | -| `METADATA` | | -| `METRICS` | | -| `METRICS_REFEREE` | | -| `NETWORK_REFEREE` | | -| `PERFORMANCE` | | -| `REQUIREMENTS` | | -| `SAST` | | -| `SECRET_DETECTION` | | -| `TERRAFORM` | | -| `TRACE` | | +| `ACCESSIBILITY` | ACCESSIBILITY job artifact file type. | +| `API_FUZZING` | API FUZZING job artifact file type. | +| `ARCHIVE` | ARCHIVE job artifact file type. | +| `BROWSER_PERFORMANCE` | BROWSER PERFORMANCE job artifact file type. | +| `CLUSTER_APPLICATIONS` | CLUSTER APPLICATIONS job artifact file type. | +| `COBERTURA` | COBERTURA job artifact file type. | +| `CODEQUALITY` | CODE QUALITY job artifact file type. | +| `CONTAINER_SCANNING` | CONTAINER SCANNING job artifact file type. | +| `COVERAGE_FUZZING` | COVERAGE FUZZING job artifact file type. | +| `DAST` | DAST job artifact file type. | +| `DEPENDENCY_SCANNING` | DEPENDENCY SCANNING job artifact file type. | +| `DOTENV` | DOTENV job artifact file type. | +| `JUNIT` | JUNIT job artifact file type. | +| `LICENSE_MANAGEMENT` | LICENSE MANAGEMENT job artifact file type. | +| `LICENSE_SCANNING` | LICENSE SCANNING job artifact file type. | +| `LOAD_PERFORMANCE` | LOAD PERFORMANCE job artifact file type. | +| `LSIF` | LSIF job artifact file type. | +| `METADATA` | METADATA job artifact file type. | +| `METRICS` | METRICS job artifact file type. | +| `METRICS_REFEREE` | METRICS REFEREE job artifact file type. | +| `NETWORK_REFEREE` | NETWORK REFEREE job artifact file type. | +| `PERFORMANCE` | PERFORMANCE job artifact file type. | +| `REQUIREMENTS` | REQUIREMENTS job artifact file type. | +| `SAST` | SAST job artifact file type. | +| `SECRET_DETECTION` | SECRET DETECTION job artifact file type. | +| `TERRAFORM` | TERRAFORM job artifact file type. | +| `TRACE` | TRACE job artifact file type. | ### ListLimitMetric diff --git a/doc/development/usage_ping.md b/doc/development/usage_ping.md index fc807b4a2f8..7fa253e75f5 100644 --- a/doc/development/usage_ping.md +++ b/doc/development/usage_ping.md @@ -752,7 +752,7 @@ alt_usage_data(999) ### Adding counters to build new metrics -When adding the results of two counters, use the `add` usage data method that +When adding the results of two counters, use the `add` usage data method that handles fallback values and exceptions. It also generates a valid [SQL export](#exporting-usage-ping-sql-queries-and-definitions). Example usage: @@ -869,7 +869,7 @@ Ensure you comply with the [Changelog entries guide](changelog.md). ### 9. Ask for a Product Intelligence Review -On GitLab.com, we have DangerBot setup to monitor Product Intelligence related files and DangerBot recommends a Product Intelligence review. Mention `@gitlab-org/growth/product_intelligence/engineers` in your MR for a review. +On GitLab.com, we have DangerBot setup to monitor Product Intelligence related files and DangerBot recommends a [Product Intelligence review](usage_ping/product_intelligence_review.md). Mention `@gitlab-org/growth/product_intelligence/engineers` in your MR for a review. ### 10. Verify your metric diff --git a/doc/development/usage_ping/metrics_dictionary.md b/doc/development/usage_ping/metrics_dictionary.md index f27e258a9c2..a1f24ccd99c 100644 --- a/doc/development/usage_ping/metrics_dictionary.md +++ b/doc/development/usage_ping/metrics_dictionary.md @@ -35,14 +35,14 @@ Each metric is defined in a separate YAML file consisting of a number of fields: | `value_type` | yes | `string`; one of `string`, `number`, `boolean`. | | `status` | yes | `string`; status of the metric, may be set to `data_available`, `planned`, `in_progress`, `implemented`, `not_used`, `deprecated` | | `time_frame` | yes | `string`; may be set to a value like `7d`, `28d`, `all`, `none`. | -| `data_source` | yes | `string`: may be set to a value like `database`, `redis`, `redis_hll`, `prometheus`, `ruby`. | -| `distribution` | yes | The [distribution](https://about.gitlab.com/handbook/marketing/strategic-marketing/tiers/#definitions) where the metric applies. | -| `tier` | yes | The [tier]( https://about.gitlab.com/handbook/marketing/strategic-marketing/tiers/) where the metric applies. | +| `data_source` | yes | `string`; may be set to a value like `database`, `redis`, `redis_hll`, `prometheus`, `ruby`. | +| `distribution` | yes | `array`; may be set to one of `ce, ee` or `ee`. The [distribution](https://about.gitlab.com/handbook/marketing/strategic-marketing/tiers/#definitions) where the tracked feature is available. | +| `tier` | yes | `array`; may be set to one of `free, premium, ultimate`, `premium, ultimate` or `ultimate`. The [tier]( https://about.gitlab.com/handbook/marketing/strategic-marketing/tiers/) where the tracked feature is available. | | `milestone` | no | The milestone when the metric is introduced. | | `milestone_removed` | no | The milestone when the metric is removed. | | `introduced_by_url` | no | The URL to the Merge Request that introduced the metric. | -### Example metric definition +### Example YAML metric definition The linked [`uuid`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/config/metrics/license/uuid.yml) YAML file includes an example metric definition, where the `uuid` metric is the GitLab @@ -93,3 +93,9 @@ To create a metric definition used in EE, add the `--ee` flag. bundle exec rails generate gitlab:usage_metric_definition counts.issues --ee --dir=7d create ee/config/metrics/counts_7d/issues.yml ``` + +## Metrics added dynamic to Usage Ping payload + +The [Redis HLL metrics](../usage_ping.md#known-events-are-added-automatically-in-usage-data-payload) are added automatically to Usage Ping payload. + +A YAML metric definition is required for each metric. diff --git a/doc/development/usage_ping/product_intelligence_review.md b/doc/development/usage_ping/product_intelligence_review.md new file mode 100644 index 00000000000..852166cc7a3 --- /dev/null +++ b/doc/development/usage_ping/product_intelligence_review.md @@ -0,0 +1,80 @@ +--- +stage: Growth +group: Product Intelligence +info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments +--- + +# Product Intelligence review guidelines + +This page includes introductory material for a +[Product Intelligence](https://about.gitlab.com/handbook/engineering/development/growth/product-intelligence/) +review, and is specific to Product Intelligence reviews. For broader advice and +general best practices for code reviews, refer to our [code review guide](../code_review.md). + +## Resources for Product Intelligence reviewers + +- [Usage Ping Guide](../usage_ping.md) +- [Snowplow Guide](../snowplow.md) +- [Metrics Dictionary](metrics_dictionary.md) + +## Review process + +We recommend a Product Intelligence review when an application update touches +Product Intelligence files. + +- Changes that touch `usage_data*` files. +- Changes to the Metrics Dictionary including files in: + - [`config/metrics`](https://gitlab.com/gitlab-org/gitlab/-/tree/master/config/metrics). + - [`ee/config/metrics`](https://gitlab.com/gitlab-org/gitlab/-/tree/master/ee/config/metrics). + - [`dictionary.md`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/doc/development/usage_ping/dictionary.md). + - [`schema.json`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/config/metrics/schema.json). +- Changes to `tracking` files. +- Changes to Product Intelligence tooling. For example, + [`Gitlab::UsageMetricDefinitionGenerator`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/generators/gitlab/usage_metric_definition_generator.rb) + +### Roles and process + +The merge request **author** should: + +- Decide whether a Product Intelligence review is needed. +- If a Product Intelligence review is needed, add the labels + `~product intelligence` and `~product intelligence::review pending`. +- Assign an + [engineer](https://gitlab.com/groups/gitlab-org/growth/product-intelligence/engineers/-/group_members?with_inherited_permissions=exclude) from the Product Intelligence team for a review. +- Set the correct attributes in YAML metrics: + - `product_section`, `product_stage`, `product_group`, `product_category` + - Provide a clear description of the metric. +- Update the + [Metrics Dictionary](https://gitlab.com/gitlab-org/gitlab/-/blob/master/doc/development/usage_ping/dictionary.md) if it is needed. +- Add a changelog [according to guidelines](../changelog.md). + +The Product Intelligence **reviewer** should: + +- Perform a first-pass review on the merge request and suggest improvements to the author. +- Approve the MR, and relabel the MR with `~"product intelligence::approved"`. + +## Review workload distribution + +[Danger bot](../dangerbot.md) adds the list of Product Intelligence changed files +and pings the +[`@gitlab-org/growth/product-intelligence/engineers`](https://gitlab.com/groups/gitlab-org/growth/product-intelligence/engineers/-/group_members?with_inherited_permissions=exclude) group for merge requests +that are not drafts. + +Any of the Product Intelligence engineers can be assigned for the Product Intelligence review. + +### How to review for Product Intelligence + +- Check the [metrics location](../usage_ping.md#1-naming-and-placing-the-metrics) in + the Usage Ping JSON payload. +- Add `~database` label and ask for [database review](../database_review.md) for + metrics that are based on Database. +- For tracking using Redis HLL (HyperLogLog): + - Check the Redis slot. + - Check if a [feature flag is needed](../usage_ping.md#recommendations). +- Metrics YAML definitions: + - Check the metric `description`. + - Check the metrics `key_path`. + - Check the `product_section`, `product_stage`, `product_group`, `product_category`. + Read the [stages file](https://gitlab.com/gitlab-com/www-gitlab-com/blob/master/data/stages.yml). + - Check the file location. Consider the time frame, and if the file should be under `ee`. + - Check the tiers. diff --git a/locale/gitlab.pot b/locale/gitlab.pot index 01113ed1020..8bd7c1923a5 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -976,6 +976,9 @@ msgstr "" msgid "'%{name}' Value Stream deleted" msgstr "" +msgid "'%{name}' Value Stream saved" +msgstr "" + msgid "'%{name}' stage already exists" msgstr "" @@ -8707,7 +8710,7 @@ msgstr "" msgid "CreateValueStreamForm|'%{name}' Value Stream created" msgstr "" -msgid "CreateValueStreamForm|'%{name}' Value Stream edited" +msgid "CreateValueStreamForm|'%{name}' Value Stream saved" msgstr "" msgid "CreateValueStreamForm|Add another stage" @@ -8986,9 +8989,6 @@ msgstr "" msgid "Custom notification levels are the same as participating levels. With custom notification levels you will also receive notifications for select events. To find out more, check out %{notificationLinkStart} notification emails%{notificationLinkEnd}." msgstr "" -msgid "Custom notification levels are the same as participating levels. With custom notification levels you will also receive notifications for select events. To find out more, check out %{notification_link}." -msgstr "" - msgid "Custom project templates" msgstr "" @@ -20636,9 +20636,6 @@ msgstr "" msgid "Notification events" msgstr "" -msgid "Notification setting" -msgstr "" - msgid "Notification setting - %{notification_title}" msgstr "" @@ -20723,9 +20720,6 @@ msgstr "" msgid "NotificationLevel|Watch" msgstr "" -msgid "NotificationSetting|Custom" -msgstr "" - msgid "Notifications" msgstr "" @@ -25971,6 +25965,9 @@ msgstr "" msgid "Save Changes" msgstr "" +msgid "Save Value Stream" +msgstr "" + msgid "Save application" msgstr "" @@ -30042,9 +30039,6 @@ msgstr "" msgid "There was an error saving your changes." msgstr "" -msgid "There was an error saving your notification settings." -msgstr "" - msgid "There was an error subscribing to this label." msgstr "" @@ -35820,9 +35814,6 @@ msgstr "" msgid "not found" msgstr "" -msgid "notification emails" -msgstr "" - msgid "nounSeries|%{firstItem} and %{lastItem}" msgstr "" diff --git a/spec/controllers/notification_settings_controller_spec.rb b/spec/controllers/notification_settings_controller_spec.rb deleted file mode 100644 index c4d67df15f7..00000000000 --- a/spec/controllers/notification_settings_controller_spec.rb +++ /dev/null @@ -1,202 +0,0 @@ -# frozen_string_literal: true - -require 'spec_helper' - -RSpec.describe NotificationSettingsController do - let(:project) { create(:project) } - let(:group) { create(:group, :internal) } - let(:user) { create(:user) } - - before do - project.add_developer(user) - end - - describe '#create' do - context 'when not authorized' do - it 'redirects to sign in page' do - post :create, - params: { - project_id: project.id, - notification_setting: { level: :participating } - } - - expect(response).to redirect_to(new_user_session_path) - end - end - - context 'when authorized' do - let(:notification_setting) { user.notification_settings_for(source) } - let(:custom_events) do - events = {} - - NotificationSetting.email_events(source).each do |event| - events[event.to_s] = true - end - - events - end - - before do - sign_in(user) - end - - context 'for projects' do - let(:source) { project } - - it 'creates notification setting' do - post :create, - params: { - project_id: project.id, - notification_setting: { level: :participating } - } - - expect(response).to have_gitlab_http_status(:ok) - expect(notification_setting.level).to eq("participating") - expect(notification_setting.user_id).to eq(user.id) - expect(notification_setting.source_id).to eq(project.id) - expect(notification_setting.source_type).to eq("Project") - end - - context 'with custom settings' do - it 'creates notification setting' do - post :create, - params: { - project_id: project.id, - notification_setting: { level: :custom }.merge(custom_events) - } - - expect(response).to have_gitlab_http_status(:ok) - expect(notification_setting.level).to eq("custom") - - custom_events.each do |event, value| - expect(notification_setting.event_enabled?(event)).to eq(value) - end - end - end - end - - context 'for groups' do - let(:source) { group } - - it 'creates notification setting' do - post :create, - params: { - namespace_id: group.id, - notification_setting: { level: :watch } - } - - expect(response).to have_gitlab_http_status(:ok) - expect(notification_setting.level).to eq("watch") - expect(notification_setting.user_id).to eq(user.id) - expect(notification_setting.source_id).to eq(group.id) - expect(notification_setting.source_type).to eq("Namespace") - end - - context 'with custom settings' do - it 'creates notification setting' do - post :create, - params: { - namespace_id: group.id, - notification_setting: { level: :custom }.merge(custom_events) - } - - expect(response).to have_gitlab_http_status(:ok) - expect(notification_setting.level).to eq("custom") - - custom_events.each do |event, value| - expect(notification_setting.event_enabled?(event)).to eq(value) - end - end - end - end - end - - context 'not authorized' do - let(:private_project) { create(:project, :private) } - - before do - sign_in(user) - end - - it 'returns 404' do - post :create, - params: { - project_id: private_project.id, - notification_setting: { level: :participating } - } - - expect(response).to have_gitlab_http_status(:not_found) - end - end - end - - describe '#update' do - let(:notification_setting) { user.global_notification_setting } - - context 'when not authorized' do - it 'redirects to sign in page' do - put :update, - params: { - id: notification_setting, - notification_setting: { level: :participating } - } - - expect(response).to redirect_to(new_user_session_path) - end - end - - context 'when authorized' do - before do - sign_in(user) - end - - it 'returns success' do - put :update, - params: { - id: notification_setting, - notification_setting: { level: :participating } - } - - expect(response).to have_gitlab_http_status(:ok) - end - - context 'and setting custom notification setting' do - let(:custom_events) do - events = {} - - notification_setting.email_events.each do |event| - events[event] = "true" - end - end - - it 'returns success' do - put :update, - params: { - id: notification_setting, - notification_setting: { level: :participating, events: custom_events } - } - - expect(response).to have_gitlab_http_status(:ok) - end - end - end - - context 'not authorized' do - let(:other_user) { create(:user) } - - before do - sign_in(other_user) - end - - it 'returns 404' do - put :update, - params: { - id: notification_setting, - notification_setting: { level: :participating } - } - - expect(response).to have_gitlab_http_status(:not_found) - end - end - end -end diff --git a/spec/features/groups/show_spec.rb b/spec/features/groups/show_spec.rb index 5067f11be67..4e213f65108 100644 --- a/spec/features/groups/show_spec.rb +++ b/spec/features/groups/show_spec.rb @@ -163,7 +163,6 @@ RSpec.describe 'Group show page' do let!(:project) { create(:project, namespace: group) } before do - stub_feature_flags(vue_notification_dropdown: false) group.add_maintainer(maintainer) sign_in(maintainer) end @@ -171,14 +170,14 @@ RSpec.describe 'Group show page' do it 'is enabled by default' do visit path - expect(page).to have_selector('.notifications-btn:not(.disabled)', visible: true) + expect(page).to have_selector('[data-testid="notification-button"]:not(.disabled)') end it 'is disabled if emails are disabled' do group.update_attribute(:emails_disabled, true) visit path - expect(page).to have_selector('.notifications-btn.disabled', visible: true) + expect(page).to have_selector('[data-testid="notification-button"].disabled') end end diff --git a/spec/features/profiles/user_visits_notifications_tab_spec.rb b/spec/features/profiles/user_visits_notifications_tab_spec.rb index 289fbff0404..fb133071e7b 100644 --- a/spec/features/profiles/user_visits_notifications_tab_spec.rb +++ b/spec/features/profiles/user_visits_notifications_tab_spec.rb @@ -7,7 +7,6 @@ RSpec.describe 'User visits the notifications tab', :js do let(:user) { create(:user) } before do - stub_feature_flags(vue_notification_dropdown: false) project.add_maintainer(user) sign_in(user) visit(profile_notifications_path) @@ -16,17 +15,17 @@ RSpec.describe 'User visits the notifications tab', :js do it 'changes the project notifications setting' do expect(page).to have_content('Notifications') - first('#notifications-button').click - click_link('On mention') + first('[data-testid="notification-button"]').click + click_button('On mention') - expect(page).to have_selector('#notifications-button', text: 'On mention') + expect(page).to have_selector('[data-testid="notification-button"]', text: 'On mention') end context 'when project emails are disabled' do let(:project) { create(:project, emails_disabled: true) } it 'notification button is disabled' do - expect(page).to have_selector('.notifications-btn.disabled', visible: true) + expect(page).to have_selector('[data-testid="notification-button"].disabled') end end end diff --git a/spec/features/projects/show/user_manages_notifications_spec.rb b/spec/features/projects/show/user_manages_notifications_spec.rb index 5f7d9b0963b..9fe8dc920a9 100644 --- a/spec/features/projects/show/user_manages_notifications_spec.rb +++ b/spec/features/projects/show/user_manages_notifications_spec.rb @@ -6,38 +6,36 @@ RSpec.describe 'Projects > Show > User manages notifications', :js do let(:project) { create(:project, :public, :repository) } before do - stub_feature_flags(vue_notification_dropdown: false) sign_in(project.owner) end def click_notifications_button - first('.notifications-btn').click + first('[data-testid="notification-button"]').click end it 'changes the notification setting' do visit project_path(project) click_notifications_button - click_link 'On mention' + click_button 'On mention' - page.within('.notification-dropdown') do - expect(page).not_to have_css('.gl-spinner') - end + wait_for_requests click_notifications_button - expect(find('.update-notification.is-active')).to have_content('On mention') - expect(page).to have_css('.notifications-icon[data-testid="notifications-icon"]') + + page.within first('[data-testid="notification-button"]') do + expect(page.find('.gl-new-dropdown-item.is-active')).to have_content('On mention') + expect(page).to have_css('[data-testid="notifications-icon"]') + end end it 'changes the notification setting to disabled' do visit project_path(project) click_notifications_button - click_link 'Disabled' + click_button 'Disabled' - page.within('.notification-dropdown') do - expect(page).not_to have_css('.gl-spinner') + page.within first('[data-testid="notification-button"]') do + expect(page).to have_css('[data-testid="notifications-off-icon"]') end - - expect(page).to have_css('.notifications-icon[data-testid="notifications-off-icon"]') end context 'custom notification settings' do @@ -65,11 +63,13 @@ RSpec.describe 'Projects > Show > User manages notifications', :js do it 'shows notification settings checkbox' do visit project_path(project) click_notifications_button - page.find('a[data-notification-level="custom"]').click + click_button 'Custom' + + wait_for_requests - page.within('.custom-notifications-form') do + page.within('#custom-notifications-modal') do email_events.each do |event_name| - expect(page).to have_selector("input[name='notification_setting[#{event_name}]']") + expect(page).to have_selector("[data-testid='notification-setting-#{event_name}']") end end end @@ -80,7 +80,7 @@ RSpec.describe 'Projects > Show > User manages notifications', :js do it 'is disabled' do visit project_path(project) - expect(page).to have_selector('.notifications-btn.disabled', visible: true) + expect(page).to have_selector('[data-testid="notification-button"].disabled', visible: true) end end end diff --git a/spec/frontend/notifications/components/notifications_dropdown_spec.js b/spec/frontend/notifications/components/notifications_dropdown_spec.js index 88534a6d690..bc5c977d3e0 100644 --- a/spec/frontend/notifications/components/notifications_dropdown_spec.js +++ b/spec/frontend/notifications/components/notifications_dropdown_spec.js @@ -162,7 +162,7 @@ describe('NotificationsDropdown', () => { initialNotificationLevel: level, }); - const tooltipElement = findByTestId('notificationButton'); + const tooltipElement = findByTestId('notification-button'); const tooltip = getBinding(tooltipElement.element, 'gl-tooltip'); expect(tooltip.value.title).toBe(`${tooltipTitlePrefix} - ${title}`); diff --git a/spec/support/helpers/cycle_analytics_helpers.rb b/spec/support/helpers/cycle_analytics_helpers.rb index a90cbbf3bd3..14041ad0ac6 100644 --- a/spec/support/helpers/cycle_analytics_helpers.rb +++ b/spec/support/helpers/cycle_analytics_helpers.rb @@ -15,7 +15,7 @@ module CycleAnalyticsHelpers end def toggle_dropdown(field) - page.within("[data-testid='#{field}']") do + page.within("[data-testid*='#{field}']") do find('.dropdown-toggle').click wait_for_requests @@ -26,7 +26,7 @@ module CycleAnalyticsHelpers def select_dropdown_option_by_value(name, value, elem = '.dropdown-item') toggle_dropdown name - page.find("[data-testid='#{name}'] .dropdown-menu").find("#{elem}[value='#{value}']").click + page.find("[data-testid*='#{name}'] .dropdown-menu").find("#{elem}[value='#{value}']").click end def create_commit_referencing_issue(issue, branch_name: generate(:branch)) diff --git a/spec/views/projects/_home_panel.html.haml_spec.rb b/spec/views/projects/_home_panel.html.haml_spec.rb index cc0eb9919da..d329c57af00 100644 --- a/spec/views/projects/_home_panel.html.haml_spec.rb +++ b/spec/views/projects/_home_panel.html.haml_spec.rb @@ -9,7 +9,6 @@ RSpec.describe 'projects/_home_panel' do let(:project) { create(:project) } before do - stub_feature_flags(vue_notification_dropdown: false) assign(:project, project) allow(view).to receive(:current_user).and_return(user) @@ -25,11 +24,10 @@ RSpec.describe 'projects/_home_panel' do assign(:notification_setting, notification_settings) end - it 'makes it possible to set notification level' do + it 'renders Vue app root' do render - expect(view).to render_template('shared/notifications/_new_button') - expect(rendered).to have_selector('.notification-dropdown') + expect(rendered).to have_selector('.js-vue-notification-dropdown') end end @@ -40,10 +38,10 @@ RSpec.describe 'projects/_home_panel' do assign(:notification_setting, nil) end - it 'is not possible to set notification level' do + it 'does not render Vue app root' do render - expect(rendered).not_to have_selector('.notification_dropdown') + expect(rendered).not_to have_selector('.js-vue-notification-dropdown') end end end |
