Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
		
							parent
							
								
									bc69b91579
								
							
						
					
					
						commit
						2b9f6cfb3f
					
				| 
						 | 
					@ -1058,29 +1058,6 @@ Layout/ArgumentAlignment:
 | 
				
			||||||
    - 'ee/spec/features/projects/merge_requests/user_edits_merge_request_spec.rb'
 | 
					    - 'ee/spec/features/projects/merge_requests/user_edits_merge_request_spec.rb'
 | 
				
			||||||
    - 'ee/spec/features/projects/milestones/milestone_spec.rb'
 | 
					    - 'ee/spec/features/projects/milestones/milestone_spec.rb'
 | 
				
			||||||
    - 'ee/spec/features/projects/security/user_views_security_configuration_spec.rb'
 | 
					    - 'ee/spec/features/projects/security/user_views_security_configuration_spec.rb'
 | 
				
			||||||
    - 'ee/spec/features/projects/settings/ee/repository_mirrors_settings_spec.rb'
 | 
					 | 
				
			||||||
    - 'ee/spec/features/projects/settings/merge_requests/user_manages_approval_settings_spec.rb'
 | 
					 | 
				
			||||||
    - 'ee/spec/features/projects/settings/merge_requests/user_manages_merge_pipelines_spec.rb'
 | 
					 | 
				
			||||||
    - 'ee/spec/features/projects/settings/merge_requests/user_manages_merge_requests_template_spec.rb'
 | 
					 | 
				
			||||||
    - 'ee/spec/features/projects/settings/merge_requests/user_manages_merge_trains_spec.rb'
 | 
					 | 
				
			||||||
    - 'ee/spec/features/projects/settings/user_manages_approval_settings_spec.rb'
 | 
					 | 
				
			||||||
    - 'ee/spec/features/projects/settings/user_manages_merge_requests_template_spec.rb'
 | 
					 | 
				
			||||||
    - 'ee/spec/features/registrations/email_confirmation_spec.rb'
 | 
					 | 
				
			||||||
    - 'ee/spec/features/registrations/saas/standard_flow_company_creating_project_spec.rb'
 | 
					 | 
				
			||||||
    - 'ee/spec/features/registrations/saas/standard_flow_company_joining_project_spec.rb'
 | 
					 | 
				
			||||||
    - 'ee/spec/features/registrations/saas/standard_flow_just_me_creating_project_spec.rb'
 | 
					 | 
				
			||||||
    - 'ee/spec/features/registrations/saas/standard_flow_just_me_importing_project_spec.rb'
 | 
					 | 
				
			||||||
    - 'ee/spec/features/registrations/saas/standard_flow_just_me_joining_project_spec.rb'
 | 
					 | 
				
			||||||
    - 'ee/spec/features/registrations/saas/subscription_flow_paid_plan_spec.rb'
 | 
					 | 
				
			||||||
    - 'ee/spec/features/registrations/saas/trial_flow_company_creating_project_spec.rb'
 | 
					 | 
				
			||||||
    - 'ee/spec/features/registrations/saas/trial_flow_company_importing_project_spec.rb'
 | 
					 | 
				
			||||||
    - 'ee/spec/features/registrations/saas/trial_flow_just_me_creating_project_spec.rb'
 | 
					 | 
				
			||||||
    - 'ee/spec/features/registrations/saas/trial_flow_just_me_importing_project_spec.rb'
 | 
					 | 
				
			||||||
    - 'ee/spec/features/registrations/sign_up_with_trial_from_external_site_without_confirmation_spec.rb'
 | 
					 | 
				
			||||||
    - 'ee/spec/features/search/elastic/global_search_spec.rb'
 | 
					 | 
				
			||||||
    - 'ee/spec/features/search/elastic/group_search_spec.rb'
 | 
					 | 
				
			||||||
    - 'ee/spec/features/security/project/discover_spec.rb'
 | 
					 | 
				
			||||||
    - 'ee/spec/features/users/identity_verification_spec.rb'
 | 
					 | 
				
			||||||
    - 'ee/spec/frontend/fixtures/dora/metrics.rb'
 | 
					    - 'ee/spec/frontend/fixtures/dora/metrics.rb'
 | 
				
			||||||
    - 'ee/spec/frontend/fixtures/oncall_schedule.rb'
 | 
					    - 'ee/spec/frontend/fixtures/oncall_schedule.rb'
 | 
				
			||||||
    - 'ee/spec/graphql/ee/mutations/boards/lists/create_spec.rb'
 | 
					    - 'ee/spec/graphql/ee/mutations/boards/lists/create_spec.rb'
 | 
				
			||||||
| 
						 | 
					@ -1570,22 +1547,6 @@ Layout/ArgumentAlignment:
 | 
				
			||||||
    - 'spec/components/previews/pajamas/alert_component_preview.rb'
 | 
					    - 'spec/components/previews/pajamas/alert_component_preview.rb'
 | 
				
			||||||
    - 'spec/components/previews/pajamas/banner_component_preview.rb'
 | 
					    - 'spec/components/previews/pajamas/banner_component_preview.rb'
 | 
				
			||||||
    - 'spec/components/previews/pajamas/button_component_preview.rb'
 | 
					    - 'spec/components/previews/pajamas/button_component_preview.rb'
 | 
				
			||||||
    - 'spec/features/admin/admin_mode/login_spec.rb'
 | 
					 | 
				
			||||||
    - 'spec/features/admin/integrations/user_activates_mattermost_slash_command_spec.rb'
 | 
					 | 
				
			||||||
    - 'spec/features/atom/issues_spec.rb'
 | 
					 | 
				
			||||||
    - 'spec/features/atom/merge_requests_spec.rb'
 | 
					 | 
				
			||||||
    - 'spec/features/atom/users_spec.rb'
 | 
					 | 
				
			||||||
    - 'spec/features/boards/issue_ordering_spec.rb'
 | 
					 | 
				
			||||||
    - 'spec/features/boards/multi_select_spec.rb'
 | 
					 | 
				
			||||||
    - 'spec/features/boards/sidebar_assignee_spec.rb'
 | 
					 | 
				
			||||||
    - 'spec/features/calendar_spec.rb'
 | 
					 | 
				
			||||||
    - 'spec/features/clusters/cluster_health_dashboard_spec.rb'
 | 
					 | 
				
			||||||
    - 'spec/features/commits_spec.rb'
 | 
					 | 
				
			||||||
    - 'spec/features/dashboard/activity_spec.rb'
 | 
					 | 
				
			||||||
    - 'spec/features/dashboard/datetime_on_tooltips_spec.rb'
 | 
					 | 
				
			||||||
    - 'spec/features/dashboard/merge_requests_spec.rb'
 | 
					 | 
				
			||||||
    - 'spec/features/dashboard/todos/todos_sorting_spec.rb'
 | 
					 | 
				
			||||||
    - 'spec/features/dashboard/todos/todos_spec.rb'
 | 
					 | 
				
			||||||
    - 'spec/features/error_tracking/user_filters_errors_by_status_spec.rb'
 | 
					    - 'spec/features/error_tracking/user_filters_errors_by_status_spec.rb'
 | 
				
			||||||
    - 'spec/features/error_tracking/user_searches_sentry_errors_spec.rb'
 | 
					    - 'spec/features/error_tracking/user_searches_sentry_errors_spec.rb'
 | 
				
			||||||
    - 'spec/features/error_tracking/user_sees_error_details_spec.rb'
 | 
					    - 'spec/features/error_tracking/user_sees_error_details_spec.rb'
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -69,7 +69,7 @@ export default {
 | 
				
			||||||
            this.openModal();
 | 
					            this.openModal();
 | 
				
			||||||
          },
 | 
					          },
 | 
				
			||||||
          extraAttrs: {
 | 
					          extraAttrs: {
 | 
				
			||||||
            'data-qa-selector': 'delete_merged_branches_button',
 | 
					            'data-testid': 'delete-merged-branches-button',
 | 
				
			||||||
            class: 'gl-text-red-500!',
 | 
					            class: 'gl-text-red-500!',
 | 
				
			||||||
          },
 | 
					          },
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
| 
						 | 
					@ -102,12 +102,11 @@ export default {
 | 
				
			||||||
      category="tertiary"
 | 
					      category="tertiary"
 | 
				
			||||||
      no-caret
 | 
					      no-caret
 | 
				
			||||||
      placement="right"
 | 
					      placement="right"
 | 
				
			||||||
      data-qa-selector="delete_merged_branches_dropdown_button"
 | 
					 | 
				
			||||||
      class="gl-display-none gl-md-display-block!"
 | 
					      class="gl-display-none gl-md-display-block!"
 | 
				
			||||||
      :items="dropdownItems"
 | 
					      :items="dropdownItems"
 | 
				
			||||||
    />
 | 
					    />
 | 
				
			||||||
    <gl-button
 | 
					    <gl-button
 | 
				
			||||||
      data-qa-selector="delete_merged_branches_button"
 | 
					      data-testid="delete-merged-branches-button"
 | 
				
			||||||
      category="secondary"
 | 
					      category="secondary"
 | 
				
			||||||
      variant="danger"
 | 
					      variant="danger"
 | 
				
			||||||
      class="gl-display-block gl-md-display-none!"
 | 
					      class="gl-display-block gl-md-display-none!"
 | 
				
			||||||
| 
						 | 
					@ -153,7 +152,6 @@ export default {
 | 
				
			||||||
          </gl-sprintf>
 | 
					          </gl-sprintf>
 | 
				
			||||||
          <gl-form-input
 | 
					          <gl-form-input
 | 
				
			||||||
            v-model="enteredText"
 | 
					            v-model="enteredText"
 | 
				
			||||||
            data-qa-selector="delete_merged_branches_input"
 | 
					 | 
				
			||||||
            type="text"
 | 
					            type="text"
 | 
				
			||||||
            size="sm"
 | 
					            size="sm"
 | 
				
			||||||
            class="gl-mt-2"
 | 
					            class="gl-mt-2"
 | 
				
			||||||
| 
						 | 
					@ -178,7 +176,6 @@ export default {
 | 
				
			||||||
            ref="deleteMergedBrancesButton"
 | 
					            ref="deleteMergedBrancesButton"
 | 
				
			||||||
            :disabled="isDeleteButtonDisabled"
 | 
					            :disabled="isDeleteButtonDisabled"
 | 
				
			||||||
            variant="danger"
 | 
					            variant="danger"
 | 
				
			||||||
            data-qa-selector="delete_merged_branches_confirmation_button"
 | 
					 | 
				
			||||||
            data-testid="delete-merged-branches-confirmation-button"
 | 
					            data-testid="delete-merged-branches-confirmation-button"
 | 
				
			||||||
            @click="submitForm"
 | 
					            @click="submitForm"
 | 
				
			||||||
            >{{ $options.i18n.deleteButtonText }}</gl-button
 | 
					            >{{ $options.i18n.deleteButtonText }}</gl-button
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -3,8 +3,8 @@ import {
 | 
				
			||||||
  GlAlert,
 | 
					  GlAlert,
 | 
				
			||||||
  GlLink,
 | 
					  GlLink,
 | 
				
			||||||
  GlTable,
 | 
					  GlTable,
 | 
				
			||||||
  GlDropdownItem,
 | 
					  GlDisclosureDropdown,
 | 
				
			||||||
  GlDropdown,
 | 
					  GlDisclosureDropdownItem,
 | 
				
			||||||
  GlButton,
 | 
					  GlButton,
 | 
				
			||||||
  GlFormCheckbox,
 | 
					  GlFormCheckbox,
 | 
				
			||||||
  GlLoadingIcon,
 | 
					  GlLoadingIcon,
 | 
				
			||||||
| 
						 | 
					@ -51,8 +51,8 @@ export default {
 | 
				
			||||||
    GlAlert,
 | 
					    GlAlert,
 | 
				
			||||||
    GlLink,
 | 
					    GlLink,
 | 
				
			||||||
    GlTable,
 | 
					    GlTable,
 | 
				
			||||||
    GlDropdown,
 | 
					    GlDisclosureDropdown,
 | 
				
			||||||
    GlDropdownItem,
 | 
					    GlDisclosureDropdownItem,
 | 
				
			||||||
    GlFormCheckbox,
 | 
					    GlFormCheckbox,
 | 
				
			||||||
    GlButton,
 | 
					    GlButton,
 | 
				
			||||||
    GlLoadingIcon,
 | 
					    GlLoadingIcon,
 | 
				
			||||||
| 
						 | 
					@ -426,6 +426,7 @@ export default {
 | 
				
			||||||
            v-if="hasDetails(item)"
 | 
					            v-if="hasDetails(item)"
 | 
				
			||||||
            :icon="detailsShowing ? 'chevron-up' : 'chevron-down'"
 | 
					            :icon="detailsShowing ? 'chevron-up' : 'chevron-down'"
 | 
				
			||||||
            :aria-label="detailsShowing ? __('Collapse') : __('Expand')"
 | 
					            :aria-label="detailsShowing ? __('Collapse') : __('Expand')"
 | 
				
			||||||
 | 
					            data-testid="toggle-details-button"
 | 
				
			||||||
            category="tertiary"
 | 
					            category="tertiary"
 | 
				
			||||||
            size="small"
 | 
					            size="small"
 | 
				
			||||||
            @click="
 | 
					            @click="
 | 
				
			||||||
| 
						 | 
					@ -453,18 +454,23 @@ export default {
 | 
				
			||||||
        </template>
 | 
					        </template>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        <template #cell(actions)="{ item }">
 | 
					        <template #cell(actions)="{ item }">
 | 
				
			||||||
          <gl-dropdown
 | 
					          <gl-disclosure-dropdown
 | 
				
			||||||
            category="tertiary"
 | 
					            category="tertiary"
 | 
				
			||||||
            icon="ellipsis_v"
 | 
					            icon="ellipsis_v"
 | 
				
			||||||
            :text-sr-only="true"
 | 
					            placement="right"
 | 
				
			||||||
            :text="$options.i18n.moreActionsText"
 | 
					            :toggle-text="$options.i18n.moreActionsText"
 | 
				
			||||||
 | 
					            text-sr-only
 | 
				
			||||||
            no-caret
 | 
					            no-caret
 | 
				
			||||||
            right
 | 
					 | 
				
			||||||
          >
 | 
					          >
 | 
				
			||||||
            <gl-dropdown-item data-testid="delete-file" @click="handleFileDelete([item])">
 | 
					            <gl-disclosure-dropdown-item
 | 
				
			||||||
 | 
					              data-testid="delete-file"
 | 
				
			||||||
 | 
					              @action="handleFileDelete([item])"
 | 
				
			||||||
 | 
					            >
 | 
				
			||||||
 | 
					              <template #list-item>
 | 
				
			||||||
                {{ $options.i18n.deleteFile }}
 | 
					                {{ $options.i18n.deleteFile }}
 | 
				
			||||||
            </gl-dropdown-item>
 | 
					              </template>
 | 
				
			||||||
          </gl-dropdown>
 | 
					            </gl-disclosure-dropdown-item>
 | 
				
			||||||
 | 
					          </gl-disclosure-dropdown>
 | 
				
			||||||
        </template>
 | 
					        </template>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        <template #row-details="{ item }">
 | 
					        <template #row-details="{ item }">
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -11,9 +11,9 @@
 | 
				
			||||||
        = branch.name
 | 
					        = branch.name
 | 
				
			||||||
      = clipboard_button(text: branch.name, title: _("Copy branch name"))
 | 
					      = clipboard_button(text: branch.name, title: _("Copy branch name"))
 | 
				
			||||||
      - if is_default_branch
 | 
					      - if is_default_branch
 | 
				
			||||||
        = gl_badge_tag s_('DefaultBranchLabel|default'), { variant: :neutral, size: :sm }, { class: 'gl-ml-2', data: { qa_selector: 'badge_content' } }
 | 
					        = gl_badge_tag s_('DefaultBranchLabel|default'), { variant: :neutral, size: :sm }, { class: 'gl-ml-2' }
 | 
				
			||||||
      - if protected_branch?(@project, branch)
 | 
					      - if protected_branch?(@project, branch)
 | 
				
			||||||
        = gl_badge_tag s_('Branches|protected'), { variant: :muted, size: :sm }, { class: 'gl-ml-2', data: { qa_selector: 'badge_content' } }
 | 
					        = gl_badge_tag s_('Branches|protected'), { variant: :muted, size: :sm }, { class: 'gl-ml-2' }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      = render_if_exists 'projects/branches/diverged_from_upstream', branch: branch
 | 
					      = render_if_exists 'projects/branches/diverged_from_upstream', branch: branch
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -39,7 +39,7 @@
 | 
				
			||||||
        .issuable-reference.gl-display-flex.gl-justify-content-end.gl-overflow-hidden
 | 
					        .issuable-reference.gl-display-flex.gl-justify-content-end.gl-overflow-hidden
 | 
				
			||||||
          = gl_badge_tag issuable_reference(related_merge_request),
 | 
					          = gl_badge_tag issuable_reference(related_merge_request),
 | 
				
			||||||
            { icon: mr_status[:icon], variant: mr_status[:variant], size: :md, href: merge_request_path(related_merge_request) },
 | 
					            { icon: mr_status[:icon], variant: mr_status[:variant], size: :md, href: merge_request_path(related_merge_request) },
 | 
				
			||||||
            { class: 'gl-display-block gl-text-truncate', title: mr_status[:title], data: { toggle: 'tooltip', container: 'body', qa_selector: 'badge_content' } }
 | 
					            { class: 'gl-display-block gl-text-truncate', title: mr_status[:title], data: { toggle: 'tooltip', container: 'body' } }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      - elsif mr_status.nil? && create_mr_button?(from: branch.name, source_project: @project)
 | 
					      - elsif mr_status.nil? && create_mr_button?(from: branch.name, source_project: @project)
 | 
				
			||||||
        = render Pajamas::ButtonComponent.new(icon: 'merge-request', href: create_mr_path(from: branch.name, source_project: @project), button_options: { class: 'has-tooltip', title: _('New merge request') }) do
 | 
					        = render Pajamas::ButtonComponent.new(icon: 'merge-request', href: create_mr_path(from: branch.name, source_project: @project), button_options: { class: 'has-tooltip', title: _('New merge request') }) do
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,17 @@
 | 
				
			||||||
 | 
					# frozen_string_literal: true
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class ScheduleIndexEventsOnProjectIdAndIdDescOnMergedActionForRemoval < Gitlab::Database::Migration[2.1]
 | 
				
			||||||
 | 
					  INDEX_NAME = 'index_events_on_project_id_and_id_desc_on_merged_action'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  # TODO: Index to be destroyed synchronously in https://gitlab.com/gitlab-org/gitlab/-/issues/415091
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  def up
 | 
				
			||||||
 | 
					    prepare_async_index_removal :events, [:project_id, :id], order: { id: :desc },
 | 
				
			||||||
 | 
					      where: "action = 7", name: INDEX_NAME
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  def down
 | 
				
			||||||
 | 
					    unprepare_async_index :events, [:project_id, :id], order: { id: :desc },
 | 
				
			||||||
 | 
					      where: "action = 7", name: INDEX_NAME
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,18 @@
 | 
				
			||||||
 | 
					# frozen_string_literal: true
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class PrepareIndexForVulnerabilityReadsOnCommonProjectFilters < Gitlab::Database::Migration[2.1]
 | 
				
			||||||
 | 
					  disable_ddl_transaction!
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  INDEX_NAME = 'index_project_vulnerability_reads_common_finder_query_desc'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  def up
 | 
				
			||||||
 | 
					    prepare_async_index :vulnerability_reads,
 | 
				
			||||||
 | 
					      [:project_id, :state, :report_type, :severity, :vulnerability_id],
 | 
				
			||||||
 | 
					      order: { severity: :desc, vulnerability_id: :desc },
 | 
				
			||||||
 | 
					      name: INDEX_NAME
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  def down
 | 
				
			||||||
 | 
					    unprepare_async_index_by_name :vulnerability_reads, INDEX_NAME
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1 @@
 | 
				
			||||||
 | 
					6bf4fa6d2e43f1b589204f3b58323f32d9db2344882507e14bc487913cbe6f8e
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1 @@
 | 
				
			||||||
 | 
					248e7dabf83e225c5f5ee0de87e0842e8c3ec13f6098720830ce3b817a4d36a8
 | 
				
			||||||
| 
						 | 
					@ -33596,7 +33596,7 @@ CREATE UNIQUE INDEX unique_index_for_project_pages_unique_domain ON project_sett
 | 
				
			||||||
 | 
					
 | 
				
			||||||
CREATE UNIQUE INDEX unique_index_on_system_note_metadata_id ON resource_link_events USING btree (system_note_metadata_id);
 | 
					CREATE UNIQUE INDEX unique_index_on_system_note_metadata_id ON resource_link_events USING btree (system_note_metadata_id);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
CREATE UNIQUE INDEX unique_instance_audit_event_destination_namespace_id_and_name ON audit_events_instance_external_audit_event_destinations USING btree (name);
 | 
					CREATE UNIQUE INDEX unique_instance_audit_event_destination_name ON audit_events_instance_external_audit_event_destinations USING btree (name);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
CREATE UNIQUE INDEX unique_merge_request_diff_llm_summaries_on_mr_diff_id ON merge_request_diff_llm_summaries USING btree (merge_request_diff_id);
 | 
					CREATE UNIQUE INDEX unique_merge_request_diff_llm_summaries_on_mr_diff_id ON merge_request_diff_llm_summaries USING btree (merge_request_diff_id);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -183,8 +183,8 @@ see the [Tomcat Documentation](https://tomcat.apache.org/tomcat-10.1-doc/index.h
 | 
				
			||||||
1. Install and configure Tomcat 10:
 | 
					1. Install and configure Tomcat 10:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
   ```shell
 | 
					   ```shell
 | 
				
			||||||
   cd /tmp & wget https://dlcdn.apache.org/tomcat/tomcat-10/v10.1.9/bin/apache-tomcat-10.1.9.tar.gz
 | 
					   wget https://dlcdn.apache.org/tomcat/tomcat-10/v10.1.9/bin/apache-tomcat-10.1.9.tar.gz -P /tmp
 | 
				
			||||||
   sudo tar xzvf apache-tomcat-10*tar.gz -C /opt/tomcat --strip-components=1
 | 
					   sudo tar xzvf /tmp/apache-tomcat-10*tar.gz -C /opt/tomcat --strip-components=1
 | 
				
			||||||
   sudo chown -R tomcat:tomcat /opt/tomcat/
 | 
					   sudo chown -R tomcat:tomcat /opt/tomcat/
 | 
				
			||||||
   sudo chmod -R u+x /opt/tomcat/bin
 | 
					   sudo chmod -R u+x /opt/tomcat/bin
 | 
				
			||||||
   ```
 | 
					   ```
 | 
				
			||||||
| 
						 | 
					@ -270,7 +270,8 @@ see the [Tomcat Documentation](https://tomcat.apache.org/tomcat-10.1-doc/index.h
 | 
				
			||||||
1. Install PlantUML and copy the `.war` file:
 | 
					1. Install PlantUML and copy the `.war` file:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
   ```shell
 | 
					   ```shell
 | 
				
			||||||
   cd / & git clone https://github.com/plantuml/plantuml-server.git
 | 
					   cd /
 | 
				
			||||||
 | 
					   git clone https://github.com/plantuml/plantuml-server.git
 | 
				
			||||||
   cd plantuml-server
 | 
					   cd plantuml-server
 | 
				
			||||||
   mvn package
 | 
					   mvn package
 | 
				
			||||||
   cp /plantuml-server/target/plantuml.war  /opt/tomcat/webapps/plantuml.war
 | 
					   cp /plantuml-server/target/plantuml.war  /opt/tomcat/webapps/plantuml.war
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -115,9 +115,20 @@ Example response:
 | 
				
			||||||
```
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Users on [GitLab Premium or Ultimate](https://about.gitlab.com/pricing/) may also see
 | 
					Users on [GitLab Premium or Ultimate](https://about.gitlab.com/pricing/) may also see
 | 
				
			||||||
the `group_owners_can_manage_default_branch_protection`, `file_template_project_id`, `delayed_project_deletion`,
 | 
					these parameters:
 | 
				
			||||||
`delayed_group_deletion`, `default_project_deletion_protection`, `deletion_adjourned_period`, `disable_personal_access_tokens`, `geo_node_allowed_ips`,
 | 
					
 | 
				
			||||||
or the `security_policy_global_group_approvers_enabled` parameters.
 | 
					- `group_owners_can_manage_default_branch_protection`
 | 
				
			||||||
 | 
					- `file_template_project_id`
 | 
				
			||||||
 | 
					- `geo_node_allowed_ips`
 | 
				
			||||||
 | 
					- `geo_status_timeout`
 | 
				
			||||||
 | 
					- `delayed_project_deletion`
 | 
				
			||||||
 | 
					- `delayed_group_deletion`
 | 
				
			||||||
 | 
					- `default_project_deletion_protection`
 | 
				
			||||||
 | 
					- `deletion_adjourned_period`
 | 
				
			||||||
 | 
					- `disable_personal_access_tokens`
 | 
				
			||||||
 | 
					- `security_policy_global_group_approvers_enabled`
 | 
				
			||||||
 | 
					- `delete_unconfirmed_users`
 | 
				
			||||||
 | 
					- `unconfirmed_users_delete_after_days`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
From [GitLab 15.11](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/113332), with the `always_perform_delayed_deletion` feature flag enabled,
 | 
					From [GitLab 15.11](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/113332), with the `always_perform_delayed_deletion` feature flag enabled,
 | 
				
			||||||
the `delayed_project_deletion` and `delayed_group_deletion` attributes will not be exposed. These attributes will be removed in GitLab 16.0.
 | 
					the `delayed_project_deletion` and `delayed_group_deletion` attributes will not be exposed. These attributes will be removed in GitLab 16.0.
 | 
				
			||||||
| 
						 | 
					@ -257,6 +268,8 @@ these parameters:
 | 
				
			||||||
- `deletion_adjourned_period`
 | 
					- `deletion_adjourned_period`
 | 
				
			||||||
- `disable_personal_access_tokens`
 | 
					- `disable_personal_access_tokens`
 | 
				
			||||||
- `security_policy_global_group_approvers_enabled`
 | 
					- `security_policy_global_group_approvers_enabled`
 | 
				
			||||||
 | 
					- `delete_unconfirmed_users`
 | 
				
			||||||
 | 
					- `unconfirmed_users_delete_after_days`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
From [GitLab 15.11](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/113332), with the `always_perform_delayed_deletion` feature flag enabled,
 | 
					From [GitLab 15.11](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/113332), with the `always_perform_delayed_deletion` feature flag enabled,
 | 
				
			||||||
the `delayed_project_deletion` and `delayed_group_deletion` attributes will not be exposed. These attributes will be removed in GitLab 16.0.
 | 
					the `delayed_project_deletion` and `delayed_group_deletion` attributes will not be exposed. These attributes will be removed in GitLab 16.0.
 | 
				
			||||||
| 
						 | 
					@ -328,6 +341,7 @@ listed in the descriptions of the relevant settings.
 | 
				
			||||||
| `delayed_project_deletion` **(PREMIUM SELF)** | boolean     | no                                   | Enable delayed project deletion by default in new groups. Default is `false`. [From GitLab 15.1](https://gitlab.com/gitlab-org/gitlab/-/issues/352960), can only be enabled when `delayed_group_deletion` is true. From [GitLab 15.11](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/113332), with the `always_perform_delayed_deletion` feature flag enabled, this attribute has been removed. This attribute will be completely removed in GitLab 16.0. |
 | 
					| `delayed_project_deletion` **(PREMIUM SELF)** | boolean     | no                                   | Enable delayed project deletion by default in new groups. Default is `false`. [From GitLab 15.1](https://gitlab.com/gitlab-org/gitlab/-/issues/352960), can only be enabled when `delayed_group_deletion` is true. From [GitLab 15.11](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/113332), with the `always_perform_delayed_deletion` feature flag enabled, this attribute has been removed. This attribute will be completely removed in GitLab 16.0. |
 | 
				
			||||||
| `delayed_group_deletion` **(PREMIUM SELF)**   | boolean     | no                                   | Enable delayed group deletion. Default is `true`. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/352959) in GitLab 15.0. [From GitLab 15.1](https://gitlab.com/gitlab-org/gitlab/-/issues/352960), disables and locks the group-level setting for delayed protect deletion when set to `false`. From [GitLab 15.11](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/113332), with the `always_perform_delayed_deletion` feature flag enabled, this attribute has been removed. This attribute will be completely removed in GitLab 16.0. |
 | 
					| `delayed_group_deletion` **(PREMIUM SELF)**   | boolean     | no                                   | Enable delayed group deletion. Default is `true`. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/352959) in GitLab 15.0. [From GitLab 15.1](https://gitlab.com/gitlab-org/gitlab/-/issues/352960), disables and locks the group-level setting for delayed protect deletion when set to `false`. From [GitLab 15.11](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/113332), with the `always_perform_delayed_deletion` feature flag enabled, this attribute has been removed. This attribute will be completely removed in GitLab 16.0. |
 | 
				
			||||||
| `default_project_deletion_protection` **(PREMIUM SELF)** | boolean | no                            | Enable default project deletion protection so only administrators can delete projects. Default is `false`. |
 | 
					| `default_project_deletion_protection` **(PREMIUM SELF)** | boolean | no                            | Enable default project deletion protection so only administrators can delete projects. Default is `false`. |
 | 
				
			||||||
 | 
					| `delete_unconfirmed_users` **(PREMIUM SELF)**            | boolean | no                            | Specifies whether users who have not confirmed their email should be deleted. Default is `false`. When set to `true`, unconfirmed users are deleted after `unconfirmed_users_delete_after_days` days. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/352514) in GitLab 16.1. |
 | 
				
			||||||
| `deletion_adjourned_period` **(PREMIUM SELF)** | integer    | no                                   | The number of days to wait before deleting a project or group that is marked for deletion. Value must be between `1` and `90`. Defaults to `7`. [From GitLab 15.1](https://gitlab.com/gitlab-org/gitlab/-/issues/352960), a hook on `deletion_adjourned_period` sets the period to `1` on every update, and sets both `delayed_project_deletion` and `delayed_group_deletion` to `false` if the period is `0`. |
 | 
					| `deletion_adjourned_period` **(PREMIUM SELF)** | integer    | no                                   | The number of days to wait before deleting a project or group that is marked for deletion. Value must be between `1` and `90`. Defaults to `7`. [From GitLab 15.1](https://gitlab.com/gitlab-org/gitlab/-/issues/352960), a hook on `deletion_adjourned_period` sets the period to `1` on every update, and sets both `delayed_project_deletion` and `delayed_group_deletion` to `false` if the period is `0`. |
 | 
				
			||||||
| `diagramsnet_enabled`                       | boolean          | no                                   | (If enabled, requires `diagramsnet_url`) Enable [Diagrams.net integration](../administration/integration/diagrams_net.md). Default is `true`. |
 | 
					| `diagramsnet_enabled`                       | boolean          | no                                   | (If enabled, requires `diagramsnet_url`) Enable [Diagrams.net integration](../administration/integration/diagrams_net.md). Default is `true`. |
 | 
				
			||||||
| `diagramsnet_url`                           | string           | required by: `diagramsnet_enabled`      | The Diagrams.net instance URL for integration. |
 | 
					| `diagramsnet_url`                           | string           | required by: `diagramsnet_enabled`      | The Diagrams.net instance URL for integration. |
 | 
				
			||||||
| 
						 | 
					@ -534,6 +548,7 @@ listed in the descriptions of the relevant settings.
 | 
				
			||||||
| `throttle_unauthenticated_web_requests_per_period` | integer    | required by:<br>`throttle_unauthenticated_web_enabled` | Max requests per period per IP. |
 | 
					| `throttle_unauthenticated_web_requests_per_period` | integer    | required by:<br>`throttle_unauthenticated_web_enabled` | Max requests per period per IP. |
 | 
				
			||||||
| `time_tracking_limit_to_hours`           | boolean          | no                                   | Limit display of time tracking units to hours. Default is `false`. |
 | 
					| `time_tracking_limit_to_hours`           | boolean          | no                                   | Limit display of time tracking units to hours. Default is `false`. |
 | 
				
			||||||
| `two_factor_grace_period`                | integer          | required by: `require_two_factor_authentication` | Amount of time (in hours) that users are allowed to skip forced configuration of two-factor authentication. |
 | 
					| `two_factor_grace_period`                | integer          | required by: `require_two_factor_authentication` | Amount of time (in hours) that users are allowed to skip forced configuration of two-factor authentication. |
 | 
				
			||||||
 | 
					| `unconfirmed_users_delete_after_days` **(PREMIUM SELF)** | integer           | no                  | Specifies how many days after sign-up to delete users who have not confirmed their email. Only applicable if `delete_unconfirmed_users` is set to `true`. Must be `1` or greater. Default is `7`. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/352514) in GitLab 16.1. |
 | 
				
			||||||
| `unique_ips_limit_enabled`               | boolean          | no                                   | (**If enabled, requires:** `unique_ips_limit_per_user` and `unique_ips_limit_time_window`) Limit sign in from multiple IPs. |
 | 
					| `unique_ips_limit_enabled`               | boolean          | no                                   | (**If enabled, requires:** `unique_ips_limit_per_user` and `unique_ips_limit_time_window`) Limit sign in from multiple IPs. |
 | 
				
			||||||
| `unique_ips_limit_per_user`              | integer          | required by: `unique_ips_limit_enabled` | Maximum number of IPs per user. |
 | 
					| `unique_ips_limit_per_user`              | integer          | required by: `unique_ips_limit_enabled` | Maximum number of IPs per user. |
 | 
				
			||||||
| `unique_ips_limit_time_window`           | integer          | required by: `unique_ips_limit_enabled` | How many seconds an IP is counted towards the limit. |
 | 
					| `unique_ips_limit_time_window`           | integer          | required by: `unique_ips_limit_enabled` | How many seconds an IP is counted towards the limit. |
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -872,10 +872,12 @@ If you want to reduce risk slightly, consider putting the migrations into a
 | 
				
			||||||
second merge request after the application changes are merged. This approach
 | 
					second merge request after the application changes are merged. This approach
 | 
				
			||||||
provides an opportunity to roll back.
 | 
					provides an opportunity to roll back.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Removing the foreign key on the `projects` table:
 | 
					Removing the foreign key on the `projects` table using a non-transactional migration:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
```ruby
 | 
					```ruby
 | 
				
			||||||
# first migration file
 | 
					# first migration file
 | 
				
			||||||
 | 
					class RemovingForeignKeyMigrationClass < Gitlab::Database::Migration[2.1]
 | 
				
			||||||
 | 
					  disable_ddl_transaction!
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  def up
 | 
					  def up
 | 
				
			||||||
    with_lock_retries do
 | 
					    with_lock_retries do
 | 
				
			||||||
| 
						 | 
					@ -884,8 +886,7 @@ def up
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  def down
 | 
					  def down
 | 
				
			||||||
  with_lock_retries do
 | 
					    add_concurrent_foreign_key :my_table, :projects, column: COLUMN_NAME
 | 
				
			||||||
    add_foreign_key :my_table, :projects
 | 
					 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
end
 | 
					end
 | 
				
			||||||
```
 | 
					```
 | 
				
			||||||
| 
						 | 
					@ -894,17 +895,19 @@ Dropping the table:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
```ruby
 | 
					```ruby
 | 
				
			||||||
# second migration file
 | 
					# second migration file
 | 
				
			||||||
 | 
					class DroppingTableMigrationClass < Gitlab::Database::Migration[2.1]
 | 
				
			||||||
  def up
 | 
					  def up
 | 
				
			||||||
    drop_table :my_table
 | 
					    drop_table :my_table
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  def down
 | 
					  def down
 | 
				
			||||||
  # create_table ...
 | 
					    # create_table with the same schema but without the removed foreign key ...
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
end
 | 
					end
 | 
				
			||||||
```
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
After a table has been dropped, it should be added to the database dictionary, following the steps in the [database dictionary guide](database/database_dictionary.md#dropping-tables).
 | 
					After a table has been dropped, it should be added to the database dictionary, following the
 | 
				
			||||||
 | 
					steps in the [database dictionary guide](database/database_dictionary.md#dropping-tables).
 | 
				
			||||||
 | 
					
 | 
				
			||||||
## Dropping a sequence
 | 
					## Dropping a sequence
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -139,3 +139,165 @@ end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
If classes that are defined into a namespace have a lot in common with classes in other namespaces,
 | 
					If classes that are defined into a namespace have a lot in common with classes in other namespaces,
 | 
				
			||||||
chances are that these two namespaces are part of the same bounded context.
 | 
					chances are that these two namespaces are part of the same bounded context.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Taming Omniscient classes
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					We must consider not adding new data and behavior to [omniscient classes](https://en.wikipedia.org/wiki/God_object) (also known as god objects).
 | 
				
			||||||
 | 
					We consider `Project`, `User`, `MergeRequest`, `Ci::Pipeline` and any classes above 1000 LOC to be omniscient.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Such classes are overloaded with responsibilities. New data and behavior can most of the time be added
 | 
				
			||||||
 | 
					as a separate and dedicated class.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Guidelines:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					- If you mostly need a reference to the object ID (for example `Project#id`) you could add a new model
 | 
				
			||||||
 | 
					  that uses the foreign key or a thin wrapper around the object to add special behavior.
 | 
				
			||||||
 | 
					- If you find out that by adding a method to the omniscient class you also end up adding a couple of other methods
 | 
				
			||||||
 | 
					  (private or public) it's a sign that these methods should be encapsulated in a dedicated class.
 | 
				
			||||||
 | 
					- It's temping to add a method to `Project` because that's the starting point of data and associations.
 | 
				
			||||||
 | 
					  Try to define behavior in the bounded context where it belongs, not where the data (or some of it) is.
 | 
				
			||||||
 | 
					  This helps creating facets of the omniscient object that are much more relevant in the bounded context than
 | 
				
			||||||
 | 
					  having generic and overloaded objects which bring more coupling and complexity.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Example: Define a thin domain object around a generic model
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Instead of adding multiple methods to `User` because it has an association to `abuse_trust_scores`,
 | 
				
			||||||
 | 
					try inverting the dependency.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```ruby
 | 
				
			||||||
 | 
					##
 | 
				
			||||||
 | 
					# BAD: Behavior added to User object.
 | 
				
			||||||
 | 
					class User
 | 
				
			||||||
 | 
					  def spam_score
 | 
				
			||||||
 | 
					    abuse_trust_scores.spamcheck.average(:score) || 0.0
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  def spammer?
 | 
				
			||||||
 | 
					    # Warning sign: we use a constant that belongs to a specific bounded context!
 | 
				
			||||||
 | 
					    spam_score > Abuse::TrustScore::SPAMCHECK_HAM_THRESHOLD
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  def telesign_score
 | 
				
			||||||
 | 
					    abuse_trust_scores.telesign.recent_first.first&.score || 0.0
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  def arkose_global_score
 | 
				
			||||||
 | 
					    abuse_trust_scores.arkose_global_score.recent_first.first&.score || 0.0
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  def arkose_custom_score
 | 
				
			||||||
 | 
					    abuse_trust_scores.arkose_custom_score.recent_first.first&.score || 0.0
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Usage:
 | 
				
			||||||
 | 
					user = User.find(1)
 | 
				
			||||||
 | 
					user.spam_score
 | 
				
			||||||
 | 
					user.telesign_score
 | 
				
			||||||
 | 
					user.arkose_global_score
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```ruby
 | 
				
			||||||
 | 
					##
 | 
				
			||||||
 | 
					# GOOD: Define a thin class that represents a user trust score
 | 
				
			||||||
 | 
					class Abuse::UserTrustScore
 | 
				
			||||||
 | 
					  def initialize(user)
 | 
				
			||||||
 | 
					    @user = user
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  def spam
 | 
				
			||||||
 | 
					    scores.spamcheck.average(:score) || 0.0
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  def spammer?
 | 
				
			||||||
 | 
					    spam > Abuse::TrustScore::SPAMCHECK_HAM_THRESHOLD
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  def telesign
 | 
				
			||||||
 | 
					    scores.telesign.recent_first.first&.score || 0.0
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  def arkose_global
 | 
				
			||||||
 | 
					    scores.arkose_global_score.recent_first.first&.score || 0.0
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  def arkose_custom
 | 
				
			||||||
 | 
					    scores.arkose_custom_score.recent_first.first&.score || 0.0
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  private
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  def scores
 | 
				
			||||||
 | 
					    Abuse::TrustScore.for_user(@user)
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Usage:
 | 
				
			||||||
 | 
					user = User.find(1)
 | 
				
			||||||
 | 
					user_score = Abuse::UserTrustScore.new(user)
 | 
				
			||||||
 | 
					user_score.spam
 | 
				
			||||||
 | 
					user_score.spammer?
 | 
				
			||||||
 | 
					user_score.telesign
 | 
				
			||||||
 | 
					user_score.arkose_global
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					See a real example [merge request](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/117853#note_1423070054).  
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Example: Use Dependency Inversion to extract a domain concept
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```ruby
 | 
				
			||||||
 | 
					## 
 | 
				
			||||||
 | 
					# BAD: methods related to integrations defined in Project.
 | 
				
			||||||
 | 
					class Project
 | 
				
			||||||
 | 
					  has_many :integrations
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  def find_or_initialize_integrations
 | 
				
			||||||
 | 
					    # ...
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  def find_or_initialize_integration(name)
 | 
				
			||||||
 | 
					    # ...
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  def disabled_integrations
 | 
				
			||||||
 | 
					    # ...
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  def ci_integrations
 | 
				
			||||||
 | 
					    # ...
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  # many more methods...
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```ruby
 | 
				
			||||||
 | 
					##
 | 
				
			||||||
 | 
					# GOOD: All logic related to Integrations is enclosed inside the `Integrations::`
 | 
				
			||||||
 | 
					# bounded context.
 | 
				
			||||||
 | 
					module Integrations
 | 
				
			||||||
 | 
					  class ProjectIntegrations
 | 
				
			||||||
 | 
					    def initialize(project)
 | 
				
			||||||
 | 
					      @project = project
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def all_integrations
 | 
				
			||||||
 | 
					      @project.integrations # can still leverage caching of AR associations
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def find_or_initialize(name)
 | 
				
			||||||
 | 
					      # ...
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def all_disabled
 | 
				
			||||||
 | 
					      all_integrations.disabled
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def all_ci
 | 
				
			||||||
 | 
					      all_integrations.ci_integration
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Real example of [similar refactoring](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/92985).
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -262,7 +262,7 @@ PKG_METADATA_MANIFEST_OUTPUT_FILE="/tmp/license_db_export_manifest.json"
 | 
				
			||||||
PKG_METADATA_DOWNLOADS_OUTPUT_FILE="/tmp/license_db_object_links.tsv"
 | 
					PKG_METADATA_DOWNLOADS_OUTPUT_FILE="/tmp/license_db_object_links.tsv"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Download the contents of the bucket
 | 
					# Download the contents of the bucket
 | 
				
			||||||
curl --silent --show-error --request GET "https://storage.googleapis.com/storage/v1/b/prod-export-license-bucket-1a6c642fc4de57d4/o" > "$PKG_METADATA_MANIFEST_OUTPUT_FILE"
 | 
					curl --silent --show-error --request GET "https://storage.googleapis.com/storage/v1/b/prod-export-license-bucket-1a6c642fc4de57d4/o?maxResults=7500" > "$PKG_METADATA_MANIFEST_OUTPUT_FILE"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Parse the links and names for the bucket objects and output them into a tsv file
 | 
					# Parse the links and names for the bucket objects and output them into a tsv file
 | 
				
			||||||
jq -r '.items[] | [.name, .mediaLink] | @tsv' "$PKG_METADATA_MANIFEST_OUTPUT_FILE" > "$PKG_METADATA_DOWNLOADS_OUTPUT_FILE"
 | 
					jq -r '.items[] | [.name, .mediaLink] | @tsv' "$PKG_METADATA_MANIFEST_OUTPUT_FILE" > "$PKG_METADATA_DOWNLOADS_OUTPUT_FILE"
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -186,6 +186,15 @@ If you don't want any downtime, read how to [upgrade with zero downtime](zero_do
 | 
				
			||||||
 | 
					
 | 
				
			||||||
For a dynamic view of examples of supported upgrade paths, try the [Upgrade Path tool](https://gitlab-com.gitlab.io/support/toolbox/upgrade-path/) maintained by the [GitLab Support team](https://about.gitlab.com/handbook/support/#about-the-support-team). To share feedback and help improve the tool, create an issue or MR in the [upgrade-path project](https://gitlab.com/gitlab-com/support/toolbox/upgrade-path).
 | 
					For a dynamic view of examples of supported upgrade paths, try the [Upgrade Path tool](https://gitlab-com.gitlab.io/support/toolbox/upgrade-path/) maintained by the [GitLab Support team](https://about.gitlab.com/handbook/support/#about-the-support-team). To share feedback and help improve the tool, create an issue or MR in the [upgrade-path project](https://gitlab.com/gitlab-com/support/toolbox/upgrade-path).
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Required upgrade stops are versions of GitLab that you must upgrade to before upgrading to later versions. Required upgrade stops allow required background
 | 
				
			||||||
 | 
					migrations to finish.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					During GitLab 16.x, we are scheduling two or three required upgrade stops. We will give at least two milestones of notice when we
 | 
				
			||||||
 | 
					schedule a required upgrade stop.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					The first planned required upgrade stop is scheduled for GitLab 16.3. If nothing is introduced requiring an upgrade stop, GitLab 16.3 will be treated as a
 | 
				
			||||||
 | 
					regular upgrade.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Find where your version sits in the upgrade path below, and upgrade GitLab
 | 
					Find where your version sits in the upgrade path below, and upgrade GitLab
 | 
				
			||||||
accordingly, while also consulting the
 | 
					accordingly, while also consulting the
 | 
				
			||||||
[version-specific upgrade instructions](#version-specific-upgrading-instructions):
 | 
					[version-specific upgrade instructions](#version-specific-upgrading-instructions):
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -200,6 +200,33 @@ When this feature is enabled, GitLab runs a job once a day to deactivate the dor
 | 
				
			||||||
 | 
					
 | 
				
			||||||
A maximum of 100,000 users can be deactivated per day.
 | 
					A maximum of 100,000 users can be deactivated per day.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Automatically delete unconfirmed users **(PREMIUM SELF)**
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/352514) in GitLab 16.1 [with a flag](../../administration/feature_flags.md) named `delete_unconfirmed_users_setting`. Disabled by default.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Prerequisites:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					- You must be an administrator.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					You can enable automatic deletion of users who both:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					- Never confirmed their email address.
 | 
				
			||||||
 | 
					- Signed up for GitLab more than a specified number of days in the past.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					You can configure these settings using either the [Settings API](../../api/settings.md) or in a Rails console:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```ruby
 | 
				
			||||||
 | 
					 Gitlab::CurrentSettings.update(delete_unconfirmed_users: true)
 | 
				
			||||||
 | 
					 Gitlab::CurrentSettings.update(unconfirmed_users_delete_after_days: 365)
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					When the `delete_unconfirmed_users` setting is enabled, GitLab runs a job once an hour to delete the unconfirmed users.
 | 
				
			||||||
 | 
					The job only deletes users who signed up more than `unconfirmed_users_delete_after_days` days in the past.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					This job only runs when the `email_confirmation_setting` is set to `soft` or `hard`.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					A maximum of 240,000 users can be deleted per day.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
### Activate a user
 | 
					### Activate a user
 | 
				
			||||||
 | 
					
 | 
				
			||||||
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/22257) in GitLab 12.4.
 | 
					> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/22257) in GitLab 12.4.
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -11,9 +11,7 @@ info: To determine the technical writer assigned to the Stage/Group associated w
 | 
				
			||||||
 | 
					
 | 
				
			||||||
You can create a compliance framework that is a label to identify that your project has certain compliance
 | 
					You can create a compliance framework that is a label to identify that your project has certain compliance
 | 
				
			||||||
requirements or needs additional oversight. The label can optionally enforce
 | 
					requirements or needs additional oversight. The label can optionally enforce
 | 
				
			||||||
[compliance pipeline configuration](#compliance-pipelines) to the projects on which it is
 | 
					[compliance pipeline configuration](#compliance-pipelines) to the projects on which it is applied.
 | 
				
			||||||
applied. Refer to our
 | 
					 | 
				
			||||||
applied. For more information, see [Add a compliance framework to a project](../project/settings/index.md#add-a-compliance-framework-to-a-project).
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
Compliance frameworks are created on top-level groups. Group owners can create, edit, and delete compliance frameworks:
 | 
					Compliance frameworks are created on top-level groups. Group owners can create, edit, and delete compliance frameworks:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -25,6 +23,33 @@ Compliance frameworks are created on top-level groups. Group owners can create,
 | 
				
			||||||
Subgroups and projects have access to all compliance frameworks created on their top-level group. However, compliance frameworks cannot be created, edited,
 | 
					Subgroups and projects have access to all compliance frameworks created on their top-level group. However, compliance frameworks cannot be created, edited,
 | 
				
			||||||
or deleted at the subgroup or project level. Project owners can choose a framework to apply to their projects.
 | 
					or deleted at the subgroup or project level. Project owners can choose a framework to apply to their projects.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Add a compliance framework to a project
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Prerequisite:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					- The group to which the project belongs must have a compliance framework.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					To assign a compliance framework to a project:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					1. On the left sidebar, at the top, select **Search GitLab** (**{search}**) to find your project.
 | 
				
			||||||
 | 
					1. Select **Settings** > **General**.
 | 
				
			||||||
 | 
					1. Expand **Compliance frameworks**.
 | 
				
			||||||
 | 
					1. Select a compliance framework.
 | 
				
			||||||
 | 
					1. Select **Save changes**.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					NOTE:
 | 
				
			||||||
 | 
					Frameworks cannot be added to projects in personal namespaces.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### GraphQL API
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/333249) in GitLab 14.2.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					You can use the [GraphQL API](../../api/graphql/reference/index.md#mutationprojectsetcomplianceframework) to add a
 | 
				
			||||||
 | 
					compliance framework to a project.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					If you create compliance frameworks on subgroups with GraphQL, the framework is created on the root ancestor if the user
 | 
				
			||||||
 | 
					has the correct permissions. The GitLab UI presents a read-only view to discourage this behavior.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
## Default compliance frameworks
 | 
					## Default compliance frameworks
 | 
				
			||||||
 | 
					
 | 
				
			||||||
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/375036) in GitLab 15.6.
 | 
					> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/375036) in GitLab 15.6.
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -45,22 +45,9 @@ If you're an instance administrator, you can administer all project topics from
 | 
				
			||||||
 | 
					
 | 
				
			||||||
## Add a compliance framework to a project **(PREMIUM)**
 | 
					## Add a compliance framework to a project **(PREMIUM)**
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[Compliance frameworks](../../group/compliance_frameworks.md) can be assigned to projects within group that has a
 | 
					You can
 | 
				
			||||||
compliance framework using either:
 | 
					[add compliance frameworks to projects](../../group/compliance_frameworks.md#add-a-compliance-framework-to-a-project)
 | 
				
			||||||
 | 
					in a group that has a compliance framework.
 | 
				
			||||||
- The GitLab UI:
 | 
					 | 
				
			||||||
  1. On the left sidebar, at the top, select **Search GitLab** (**{search}**) to find your project.
 | 
					 | 
				
			||||||
  1. Select **Settings** > **General**.
 | 
					 | 
				
			||||||
  1. Expand **Compliance frameworks**.
 | 
					 | 
				
			||||||
  1. Select a compliance framework.
 | 
					 | 
				
			||||||
  1. Select **Save changes**.
 | 
					 | 
				
			||||||
- In [GitLab 14.2](https://gitlab.com/gitlab-org/gitlab/-/issues/333249) and later, using the
 | 
					 | 
				
			||||||
  [GraphQL API](../../../api/graphql/reference/index.md#mutationprojectsetcomplianceframework). If you create
 | 
					 | 
				
			||||||
  compliance frameworks on subgroups with GraphQL, the framework is created on the root ancestor if the user has the
 | 
					 | 
				
			||||||
  correct permissions. The GitLab UI presents a read-only view to discourage this behavior.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
NOTE:
 | 
					 | 
				
			||||||
Frameworks can not be added to projects in personal namespaces.
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
## Configure project visibility, features, and permissions
 | 
					## Configure project visibility, features, and permissions
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -6540,6 +6540,9 @@ msgstr ""
 | 
				
			||||||
msgid "AuditStreams|An error occurred when updating external audit event stream destination. Please try it again."
 | 
					msgid "AuditStreams|An error occurred when updating external audit event stream destination. Please try it again."
 | 
				
			||||||
msgstr ""
 | 
					msgstr ""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					msgid "AuditStreams|Are you sure about deleting this destination?"
 | 
				
			||||||
 | 
					msgstr ""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
msgid "AuditStreams|Cancel editing"
 | 
					msgid "AuditStreams|Cancel editing"
 | 
				
			||||||
msgstr ""
 | 
					msgstr ""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -6549,6 +6552,12 @@ msgstr ""
 | 
				
			||||||
msgid "AuditStreams|Delete %{link}"
 | 
					msgid "AuditStreams|Delete %{link}"
 | 
				
			||||||
msgstr ""
 | 
					msgstr ""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					msgid "AuditStreams|Delete destination"
 | 
				
			||||||
 | 
					msgstr ""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					msgid "AuditStreams|Deleting the streaming destination %{destination} will stop audit events being streamed"
 | 
				
			||||||
 | 
					msgstr ""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
msgid "AuditStreams|Destination URL"
 | 
					msgid "AuditStreams|Destination URL"
 | 
				
			||||||
msgstr ""
 | 
					msgstr ""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -144,7 +144,7 @@ module QA
 | 
				
			||||||
      end
 | 
					      end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      def init_repository
 | 
					      def init_repository
 | 
				
			||||||
        run_git("git init")
 | 
					        run_git("git init --initial-branch=#{default_branch}")
 | 
				
			||||||
      end
 | 
					      end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      def pull(repository = nil, branch = nil)
 | 
					      def pull(repository = nil, branch = nil)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -14,7 +14,6 @@ module QA
 | 
				
			||||||
          end
 | 
					          end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
          view 'app/views/projects/branches/_branch.html.haml' do
 | 
					          view 'app/views/projects/branches/_branch.html.haml' do
 | 
				
			||||||
            element :badge_content
 | 
					 | 
				
			||||||
            element :branch_container
 | 
					            element :branch_container
 | 
				
			||||||
            element :branch_link
 | 
					            element :branch_link
 | 
				
			||||||
          end
 | 
					          end
 | 
				
			||||||
| 
						 | 
					@ -23,13 +22,6 @@ module QA
 | 
				
			||||||
            element :all_branches_container
 | 
					            element :all_branches_container
 | 
				
			||||||
          end
 | 
					          end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
          view 'app/assets/javascripts/branches/components/delete_merged_branches.vue' do
 | 
					 | 
				
			||||||
            element :delete_merged_branches_dropdown_button
 | 
					 | 
				
			||||||
            element :delete_merged_branches_button
 | 
					 | 
				
			||||||
            element :delete_merged_branches_input
 | 
					 | 
				
			||||||
            element :delete_merged_branches_confirmation_button
 | 
					 | 
				
			||||||
          end
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
          def delete_branch(branch_name)
 | 
					          def delete_branch(branch_name)
 | 
				
			||||||
            within_element(:branch_container, name: branch_name) do
 | 
					            within_element(:branch_container, name: branch_name) do
 | 
				
			||||||
              click_element(:delete_branch_button)
 | 
					              click_element(:delete_branch_button)
 | 
				
			||||||
| 
						 | 
					@ -47,20 +39,6 @@ module QA
 | 
				
			||||||
              end
 | 
					              end
 | 
				
			||||||
            end
 | 
					            end
 | 
				
			||||||
          end
 | 
					          end
 | 
				
			||||||
 | 
					 | 
				
			||||||
          def has_branch_with_badge?(branch_name, badge)
 | 
					 | 
				
			||||||
            within_element(:branch_container, name: branch_name) do
 | 
					 | 
				
			||||||
              has_element?(:badge_content, text: badge)
 | 
					 | 
				
			||||||
            end
 | 
					 | 
				
			||||||
          end
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
          def delete_merged_branches(branches_length)
 | 
					 | 
				
			||||||
            click_element(:delete_merged_branches_dropdown_button)
 | 
					 | 
				
			||||||
            click_element(:delete_merged_branches_button)
 | 
					 | 
				
			||||||
            fill_element(:delete_merged_branches_input, branches_length)
 | 
					 | 
				
			||||||
            click_element(:delete_merged_branches_confirmation_button)
 | 
					 | 
				
			||||||
            finished_loading?
 | 
					 | 
				
			||||||
          end
 | 
					 | 
				
			||||||
        end
 | 
					        end
 | 
				
			||||||
      end
 | 
					      end
 | 
				
			||||||
    end
 | 
					    end
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,52 @@
 | 
				
			||||||
 | 
					# frozen_string_literal: true
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					module QA
 | 
				
			||||||
 | 
					  module Resource
 | 
				
			||||||
 | 
					    module Repository
 | 
				
			||||||
 | 
					      class Branch < Base
 | 
				
			||||||
 | 
					        attr_accessor :name, :ref
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        attribute :project do
 | 
				
			||||||
 | 
					          Project.fabricate_via_api! do |resource|
 | 
				
			||||||
 | 
					            resource.name = 'branch-project'
 | 
				
			||||||
 | 
					            resource.initialize_with_readme = true
 | 
				
			||||||
 | 
					          end
 | 
				
			||||||
 | 
					        end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        def initialize
 | 
				
			||||||
 | 
					          @name = 'test'
 | 
				
			||||||
 | 
					          @ref = Runtime::Env.default_branch
 | 
				
			||||||
 | 
					        end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        def fabricate!
 | 
				
			||||||
 | 
					          raise NotImplementedError
 | 
				
			||||||
 | 
					        end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        def fabricate_via_api!
 | 
				
			||||||
 | 
					          resource_web_url(api_get)
 | 
				
			||||||
 | 
					        rescue ResourceNotFoundError
 | 
				
			||||||
 | 
					          super
 | 
				
			||||||
 | 
					        end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        def api_get_path
 | 
				
			||||||
 | 
					          "/projects/#{project.id}/repository/branches/#{name}"
 | 
				
			||||||
 | 
					        end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        def api_delete_path
 | 
				
			||||||
 | 
					          api_get_path
 | 
				
			||||||
 | 
					        end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        def api_post_path
 | 
				
			||||||
 | 
					          "/projects/#{project.id}/repository/branches"
 | 
				
			||||||
 | 
					        end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        def api_post_body
 | 
				
			||||||
 | 
					          {
 | 
				
			||||||
 | 
					            branch: name,
 | 
				
			||||||
 | 
					            ref: ref
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        end
 | 
				
			||||||
 | 
					      end
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,77 @@
 | 
				
			||||||
 | 
					# frozen_string_literal: true
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					module QA
 | 
				
			||||||
 | 
					  RSpec.describe 'Create' do
 | 
				
			||||||
 | 
					    let(:project) do
 | 
				
			||||||
 | 
					      Resource::Project.fabricate_via_api! do |project|
 | 
				
			||||||
 | 
					        project.name = 'project-qa-test'
 | 
				
			||||||
 | 
					        project.description = 'project for qa test'
 | 
				
			||||||
 | 
					      end
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    describe 'Create, Retrieve and Delete branches via API', :requires_admin, product_group: :source_code do
 | 
				
			||||||
 | 
					      created_branch = 'create-branch'
 | 
				
			||||||
 | 
					      deleted_branch = 'delete-branch'
 | 
				
			||||||
 | 
					      filename = 'file.txt'
 | 
				
			||||||
 | 
					      default_branch_commit_message = "Add #{filename}"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      before do
 | 
				
			||||||
 | 
					        Git::Repository.perform do |repository|
 | 
				
			||||||
 | 
					          repository.uri = project.repository_http_location.uri
 | 
				
			||||||
 | 
					          repository.use_default_credentials
 | 
				
			||||||
 | 
					          repository.try_add_credentials_to_netrc
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					          repository.act do
 | 
				
			||||||
 | 
					            init_repository
 | 
				
			||||||
 | 
					            configure_identity('GitLab QA', 'root@gitlab.com')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            commit_file(filename, 'Test file content', default_branch_commit_message)
 | 
				
			||||||
 | 
					            push_changes
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            checkout(deleted_branch, new_branch: true)
 | 
				
			||||||
 | 
					            push_changes(deleted_branch)
 | 
				
			||||||
 | 
					          end
 | 
				
			||||||
 | 
					        end
 | 
				
			||||||
 | 
					        project.wait_for_push default_branch_commit_message
 | 
				
			||||||
 | 
					      end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      it(
 | 
				
			||||||
 | 
					        'creates, retrieves and deletes branches',
 | 
				
			||||||
 | 
					        testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347740'
 | 
				
			||||||
 | 
					      ) do
 | 
				
			||||||
 | 
					        # Create branch
 | 
				
			||||||
 | 
					        Resource::Repository::Branch.fabricate_via_api! do |branch|
 | 
				
			||||||
 | 
					          branch.name = created_branch
 | 
				
			||||||
 | 
					          branch.project = project
 | 
				
			||||||
 | 
					        end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # Retrieve branch
 | 
				
			||||||
 | 
					        delete_branch = Resource::Repository::Branch.fabricate_via_api! do |branch|
 | 
				
			||||||
 | 
					          branch.name = deleted_branch
 | 
				
			||||||
 | 
					          branch.project = project
 | 
				
			||||||
 | 
					        end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # Delete branch
 | 
				
			||||||
 | 
					        delete_branch.remove_via_api!
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # Clone repository, verify branches and commits
 | 
				
			||||||
 | 
					        Git::Repository.perform do |repository|
 | 
				
			||||||
 | 
					          repository.uri = project.repository_http_location.uri
 | 
				
			||||||
 | 
					          repository.use_default_credentials
 | 
				
			||||||
 | 
					          repository.try_add_credentials_to_netrc
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					          repository.clone
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					          branches = repository.remote_branches
 | 
				
			||||||
 | 
					          expect(branches).to include(created_branch)
 | 
				
			||||||
 | 
					          expect(branches).not_to include(deleted_branch)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					          expect(repository.commits.first).to include(default_branch_commit_message)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					          repository.checkout(created_branch)
 | 
				
			||||||
 | 
					          expect(repository.commits.first).to include(default_branch_commit_message)
 | 
				
			||||||
 | 
					        end
 | 
				
			||||||
 | 
					      end
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
| 
						 | 
					@ -1,99 +0,0 @@
 | 
				
			||||||
# frozen_string_literal: true
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
module QA
 | 
					 | 
				
			||||||
  RSpec.describe 'Create' do
 | 
					 | 
				
			||||||
    describe 'Create, list, and delete branches via web', :requires_admin, product_group: :source_code do
 | 
					 | 
				
			||||||
      master_branch = nil
 | 
					 | 
				
			||||||
      second_branch = 'second-branch'
 | 
					 | 
				
			||||||
      third_branch = 'third-branch'
 | 
					 | 
				
			||||||
      file_1_master = 'file.txt'
 | 
					 | 
				
			||||||
      file_2_master = 'other-file.txt'
 | 
					 | 
				
			||||||
      file_second_branch = 'file-2.txt'
 | 
					 | 
				
			||||||
      file_third_branch = 'file-3.txt'
 | 
					 | 
				
			||||||
      first_commit_message_of_master_branch = "Add #{file_1_master}"
 | 
					 | 
				
			||||||
      second_commit_message_of_master_branch = "Add #{file_2_master}"
 | 
					 | 
				
			||||||
      commit_message_of_second_branch = "Add #{file_second_branch}"
 | 
					 | 
				
			||||||
      commit_message_of_third_branch = "Add #{file_third_branch}"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      before do
 | 
					 | 
				
			||||||
        Flow::Login.sign_in
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        project = Resource::Project.fabricate_via_api! do |proj|
 | 
					 | 
				
			||||||
          proj.name = 'project-qa-test'
 | 
					 | 
				
			||||||
          proj.description = 'project for qa test'
 | 
					 | 
				
			||||||
          proj.initialize_with_readme = true
 | 
					 | 
				
			||||||
        end
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        master_branch = project.default_branch
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        Git::Repository.perform do |repository|
 | 
					 | 
				
			||||||
          repository.uri = project.repository_http_location.uri
 | 
					 | 
				
			||||||
          repository.use_default_credentials
 | 
					 | 
				
			||||||
          repository.try_add_credentials_to_netrc
 | 
					 | 
				
			||||||
          repository.default_branch = master_branch
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
          repository.act do
 | 
					 | 
				
			||||||
            clone
 | 
					 | 
				
			||||||
            configure_identity('GitLab QA', 'root@gitlab.com')
 | 
					 | 
				
			||||||
            commit_file(file_1_master, 'Test file content', first_commit_message_of_master_branch)
 | 
					 | 
				
			||||||
            push_changes
 | 
					 | 
				
			||||||
            checkout(second_branch, new_branch: true)
 | 
					 | 
				
			||||||
            commit_file(file_second_branch, 'File 2 content', commit_message_of_second_branch)
 | 
					 | 
				
			||||||
            push_changes(second_branch)
 | 
					 | 
				
			||||||
            checkout(master_branch)
 | 
					 | 
				
			||||||
            # This second commit on master is needed for the master branch to be ahead
 | 
					 | 
				
			||||||
            # of the second branch, and when the second branch is merged to master it will
 | 
					 | 
				
			||||||
            # show the 'merged' badge on it.
 | 
					 | 
				
			||||||
            # Refer to the below issue note:
 | 
					 | 
				
			||||||
            # https://gitlab.com/gitlab-org/gitlab-foss/issues/55524#note_126100848
 | 
					 | 
				
			||||||
            commit_file(file_2_master, 'Other test file content', second_commit_message_of_master_branch)
 | 
					 | 
				
			||||||
            push_changes
 | 
					 | 
				
			||||||
            merge(second_branch)
 | 
					 | 
				
			||||||
            push_changes
 | 
					 | 
				
			||||||
            checkout(third_branch, new_branch: true)
 | 
					 | 
				
			||||||
            commit_file(file_third_branch, 'File 3 content', commit_message_of_third_branch)
 | 
					 | 
				
			||||||
            push_changes(third_branch)
 | 
					 | 
				
			||||||
          end
 | 
					 | 
				
			||||||
        end
 | 
					 | 
				
			||||||
        project.wait_for_push commit_message_of_third_branch
 | 
					 | 
				
			||||||
        project.visit!
 | 
					 | 
				
			||||||
      end
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      it(
 | 
					 | 
				
			||||||
        'lists branches correctly after CRUD operations',
 | 
					 | 
				
			||||||
        testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347740',
 | 
					 | 
				
			||||||
        quarantine: {
 | 
					 | 
				
			||||||
          issue: 'https://gitlab.com/gitlab-org/gitlab/-/issues/414026',
 | 
					 | 
				
			||||||
          type: :stale
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
      ) do
 | 
					 | 
				
			||||||
        Page::Project::Menu.perform(&:go_to_repository_branches)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        expect(page).to have_content(master_branch)
 | 
					 | 
				
			||||||
        expect(page).to have_content(second_branch)
 | 
					 | 
				
			||||||
        expect(page).to have_content(third_branch)
 | 
					 | 
				
			||||||
        expect(page).to have_content("Merge branch 'second-branch'")
 | 
					 | 
				
			||||||
        expect(page).to have_content(commit_message_of_second_branch)
 | 
					 | 
				
			||||||
        expect(page).to have_content(commit_message_of_third_branch)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        Page::Project::Branches::Show.perform do |branches_page|
 | 
					 | 
				
			||||||
          expect(branches_page).to have_branch_with_badge(second_branch, 'merged')
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
          branches_page.delete_branch(third_branch)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
          expect(branches_page).to have_no_branch(third_branch)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
          branches_page.delete_merged_branches('delete')
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
          expect(branches_page).to have_content(
 | 
					 | 
				
			||||||
            'Merged branches are being deleted. This can take some time depending on the number of branches. Please refresh the page to see changes.'
 | 
					 | 
				
			||||||
          )
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
          branches_page.refresh
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
          expect(branches_page).to have_no_branch(second_branch, reload: true)
 | 
					 | 
				
			||||||
        end
 | 
					 | 
				
			||||||
      end
 | 
					 | 
				
			||||||
    end
 | 
					 | 
				
			||||||
  end
 | 
					 | 
				
			||||||
end
 | 
					 | 
				
			||||||
| 
						 | 
					@ -139,8 +139,10 @@ RSpec.describe 'Admin Mode Login', feature_category: :system_access do
 | 
				
			||||||
            context 'when authn_context is worth two factors' do
 | 
					            context 'when authn_context is worth two factors' do
 | 
				
			||||||
              let(:mock_saml_response) do
 | 
					              let(:mock_saml_response) do
 | 
				
			||||||
                File.read('spec/fixtures/authentication/saml_response.xml')
 | 
					                File.read('spec/fixtures/authentication/saml_response.xml')
 | 
				
			||||||
                    .gsub('urn:oasis:names:tc:SAML:2.0:ac:classes:Password',
 | 
					                  .gsub(
 | 
				
			||||||
                          'urn:oasis:names:tc:SAML:2.0:ac:classes:SecondFactorOTPSMS')
 | 
					                    'urn:oasis:names:tc:SAML:2.0:ac:classes:Password',
 | 
				
			||||||
 | 
					                    'urn:oasis:names:tc:SAML:2.0:ac:classes:SecondFactorOTPSMS'
 | 
				
			||||||
 | 
					                  )
 | 
				
			||||||
              end
 | 
					              end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
              it 'signs user in without prompting for second factor' do
 | 
					              it 'signs user in without prompting for second factor' do
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -49,8 +49,11 @@ RSpec.describe 'Issues Feed', feature_category: :devops_reports do
 | 
				
			||||||
      before do
 | 
					      before do
 | 
				
			||||||
        personal_access_token = create(:personal_access_token, user: user)
 | 
					        personal_access_token = create(:personal_access_token, user: user)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        visit project_issues_path(project, :atom,
 | 
					        visit project_issues_path(
 | 
				
			||||||
                                  private_token: personal_access_token.token)
 | 
					          project,
 | 
				
			||||||
 | 
					          :atom,
 | 
				
			||||||
 | 
					          private_token: personal_access_token.token
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
      end
 | 
					      end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      it_behaves_like 'an authenticated issuable atom feed'
 | 
					      it_behaves_like 'an authenticated issuable atom feed'
 | 
				
			||||||
| 
						 | 
					@ -59,8 +62,11 @@ RSpec.describe 'Issues Feed', feature_category: :devops_reports do
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    context 'when authenticated via feed token' do
 | 
					    context 'when authenticated via feed token' do
 | 
				
			||||||
      before do
 | 
					      before do
 | 
				
			||||||
        visit project_issues_path(project, :atom,
 | 
					        visit project_issues_path(
 | 
				
			||||||
                                  feed_token: user.feed_token)
 | 
					          project,
 | 
				
			||||||
 | 
					          :atom,
 | 
				
			||||||
 | 
					          feed_token: user.feed_token
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
      end
 | 
					      end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      it_behaves_like 'an authenticated issuable atom feed'
 | 
					      it_behaves_like 'an authenticated issuable atom feed'
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -46,8 +46,11 @@ RSpec.describe 'Merge Requests Feed', feature_category: :devops_reports do
 | 
				
			||||||
      before do
 | 
					      before do
 | 
				
			||||||
        personal_access_token = create(:personal_access_token, user: user)
 | 
					        personal_access_token = create(:personal_access_token, user: user)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        visit project_merge_requests_path(project, :atom,
 | 
					        visit project_merge_requests_path(
 | 
				
			||||||
                                          private_token: personal_access_token.token)
 | 
					          project,
 | 
				
			||||||
 | 
					          :atom,
 | 
				
			||||||
 | 
					          private_token: personal_access_token.token
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
      end
 | 
					      end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      it_behaves_like 'an authenticated issuable atom feed'
 | 
					      it_behaves_like 'an authenticated issuable atom feed'
 | 
				
			||||||
| 
						 | 
					@ -56,8 +59,11 @@ RSpec.describe 'Merge Requests Feed', feature_category: :devops_reports do
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    context 'when authenticated via feed token' do
 | 
					    context 'when authenticated via feed token' do
 | 
				
			||||||
      before do
 | 
					      before do
 | 
				
			||||||
        visit project_merge_requests_path(project, :atom,
 | 
					        visit project_merge_requests_path(
 | 
				
			||||||
                                          feed_token: user.feed_token)
 | 
					          project,
 | 
				
			||||||
 | 
					          :atom,
 | 
				
			||||||
 | 
					          feed_token: user.feed_token
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
      end
 | 
					      end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      it_behaves_like 'an authenticated issuable atom feed'
 | 
					      it_behaves_like 'an authenticated issuable atom feed'
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -25,27 +25,33 @@ RSpec.describe "User Feed", feature_category: :devops_reports do
 | 
				
			||||||
    context 'feed content' do
 | 
					    context 'feed content' do
 | 
				
			||||||
      let(:project) { create(:project, :repository) }
 | 
					      let(:project) { create(:project, :repository) }
 | 
				
			||||||
      let(:issue) do
 | 
					      let(:issue) do
 | 
				
			||||||
        create(:issue,
 | 
					        create(
 | 
				
			||||||
 | 
					          :issue,
 | 
				
			||||||
          project: project,
 | 
					          project: project,
 | 
				
			||||||
          author: user,
 | 
					          author: user,
 | 
				
			||||||
               description: "Houston, we have a bug!\n\n***\n\nI guess.")
 | 
					          description: "Houston, we have a bug!\n\n***\n\nI guess."
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
      end
 | 
					      end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      let(:note) do
 | 
					      let(:note) do
 | 
				
			||||||
        create(:note,
 | 
					        create(
 | 
				
			||||||
 | 
					          :note,
 | 
				
			||||||
          noteable: issue,
 | 
					          noteable: issue,
 | 
				
			||||||
          author: user,
 | 
					          author: user,
 | 
				
			||||||
          note: 'Bug confirmed :+1:',
 | 
					          note: 'Bug confirmed :+1:',
 | 
				
			||||||
               project: project)
 | 
					          project: project
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
      end
 | 
					      end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      let(:merge_request) do
 | 
					      let(:merge_request) do
 | 
				
			||||||
        create(:merge_request,
 | 
					        create(
 | 
				
			||||||
 | 
					          :merge_request,
 | 
				
			||||||
          title: 'Fix bug',
 | 
					          title: 'Fix bug',
 | 
				
			||||||
          author: user,
 | 
					          author: user,
 | 
				
			||||||
          source_project: project,
 | 
					          source_project: project,
 | 
				
			||||||
          target_project: project,
 | 
					          target_project: project,
 | 
				
			||||||
               description: "Here is the fix: ")
 | 
					          description: "Here is the fix: "
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
      end
 | 
					      end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      let(:push_event) { create(:push_event, project: project, author: user) }
 | 
					      let(:push_event) { create(:push_event, project: project, author: user) }
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -220,12 +220,14 @@ RSpec.describe 'Issue Boards', :js, feature_category: :team_planning do
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  def drag(selector: '.board-list', list_from_index: 1, from_index: 0, to_index: 0, list_to_index: 1, duration: 1000)
 | 
					  def drag(selector: '.board-list', list_from_index: 1, from_index: 0, to_index: 0, list_to_index: 1, duration: 1000)
 | 
				
			||||||
    drag_to(selector: selector,
 | 
					    drag_to(
 | 
				
			||||||
 | 
					      selector: selector,
 | 
				
			||||||
      scrollable: '#board-app',
 | 
					      scrollable: '#board-app',
 | 
				
			||||||
      list_from_index: list_from_index,
 | 
					      list_from_index: list_from_index,
 | 
				
			||||||
      from_index: from_index,
 | 
					      from_index: from_index,
 | 
				
			||||||
      to_index: to_index,
 | 
					      to_index: to_index,
 | 
				
			||||||
      list_to_index: list_to_index,
 | 
					      list_to_index: list_to_index,
 | 
				
			||||||
            duration: duration)
 | 
					      duration: duration
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
end
 | 
					end
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -11,13 +11,15 @@ RSpec.describe 'Multi Select Issue', :js, feature_category: :team_planning do
 | 
				
			||||||
  let(:user)    { create(:user) }
 | 
					  let(:user)    { create(:user) }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  def drag(selector: '.board-list', list_from_index: 1, from_index: 0, to_index: 0, list_to_index: 1, duration: 1000)
 | 
					  def drag(selector: '.board-list', list_from_index: 1, from_index: 0, to_index: 0, list_to_index: 1, duration: 1000)
 | 
				
			||||||
    drag_to(selector: selector,
 | 
					    drag_to(
 | 
				
			||||||
 | 
					      selector: selector,
 | 
				
			||||||
      scrollable: '#board-app',
 | 
					      scrollable: '#board-app',
 | 
				
			||||||
      list_from_index: list_from_index,
 | 
					      list_from_index: list_from_index,
 | 
				
			||||||
      from_index: from_index,
 | 
					      from_index: from_index,
 | 
				
			||||||
      to_index: to_index,
 | 
					      to_index: to_index,
 | 
				
			||||||
      list_to_index: list_to_index,
 | 
					      list_to_index: list_to_index,
 | 
				
			||||||
            duration: duration)
 | 
					      duration: duration
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  def wait_for_board_cards(board_number, expected_cards)
 | 
					  def wait_for_board_cards(board_number, expected_cards)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -2,7 +2,8 @@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
require 'spec_helper'
 | 
					require 'spec_helper'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
RSpec.describe 'Project issue boards sidebar assignee', :js, quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/332078',
 | 
					RSpec.describe 'Project issue boards sidebar assignee', :js,
 | 
				
			||||||
 | 
					  quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/332078',
 | 
				
			||||||
  feature_category: :team_planning do
 | 
					  feature_category: :team_planning do
 | 
				
			||||||
  include BoardHelpers
 | 
					  include BoardHelpers
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -15,13 +15,15 @@ RSpec.describe 'Commits', feature_category: :source_code_management do
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    let(:creator) { create(:user, developer_projects: [project]) }
 | 
					    let(:creator) { create(:user, developer_projects: [project]) }
 | 
				
			||||||
    let!(:pipeline) do
 | 
					    let!(:pipeline) do
 | 
				
			||||||
      create(:ci_pipeline,
 | 
					      create(
 | 
				
			||||||
 | 
					        :ci_pipeline,
 | 
				
			||||||
        project: project,
 | 
					        project: project,
 | 
				
			||||||
        user: creator,
 | 
					        user: creator,
 | 
				
			||||||
        ref: project.default_branch,
 | 
					        ref: project.default_branch,
 | 
				
			||||||
        sha: project.commit.sha,
 | 
					        sha: project.commit.sha,
 | 
				
			||||||
        status: :success,
 | 
					        status: :success,
 | 
				
			||||||
             created_at: 5.months.ago)
 | 
					        created_at: 5.months.ago
 | 
				
			||||||
 | 
					      )
 | 
				
			||||||
    end
 | 
					    end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    context 'commit status is Generic Commit Status' do
 | 
					    context 'commit status is Generic Commit Status' do
 | 
				
			||||||
| 
						 | 
					@ -61,11 +63,13 @@ RSpec.describe 'Commits', feature_category: :source_code_management do
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        describe 'Project commits' do
 | 
					        describe 'Project commits' do
 | 
				
			||||||
          let!(:pipeline_from_other_branch) do
 | 
					          let!(:pipeline_from_other_branch) do
 | 
				
			||||||
            create(:ci_pipeline,
 | 
					            create(
 | 
				
			||||||
 | 
					              :ci_pipeline,
 | 
				
			||||||
              project: project,
 | 
					              project: project,
 | 
				
			||||||
              ref: 'fix',
 | 
					              ref: 'fix',
 | 
				
			||||||
              sha: project.commit.sha,
 | 
					              sha: project.commit.sha,
 | 
				
			||||||
                   status: :failed)
 | 
					              status: :failed
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
          end
 | 
					          end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
          before do
 | 
					          before do
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -59,12 +59,14 @@ RSpec.describe 'Dashboard > Activity', feature_category: :user_profile do
 | 
				
			||||||
    let!(:push_event) do
 | 
					    let!(:push_event) do
 | 
				
			||||||
      event = create(:push_event, project: project, author: user)
 | 
					      event = create(:push_event, project: project, author: user)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      create(:push_event_payload,
 | 
					      create(
 | 
				
			||||||
 | 
					        :push_event_payload,
 | 
				
			||||||
        event: event,
 | 
					        event: event,
 | 
				
			||||||
        action: :created,
 | 
					        action: :created,
 | 
				
			||||||
        commit_to: '0220c11b9a3e6c69dc8fd35321254ca9a7b98f7e',
 | 
					        commit_to: '0220c11b9a3e6c69dc8fd35321254ca9a7b98f7e',
 | 
				
			||||||
        ref: 'new_design',
 | 
					        ref: 'new_design',
 | 
				
			||||||
             commit_count: 1)
 | 
					        commit_count: 1
 | 
				
			||||||
 | 
					      )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      event
 | 
					      event
 | 
				
			||||||
    end
 | 
					    end
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -18,8 +18,13 @@ RSpec.describe 'Tooltips on .timeago dates', :js, feature_category: :user_profil
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  context 'on the activity tab' do
 | 
					  context 'on the activity tab' do
 | 
				
			||||||
    before do
 | 
					    before do
 | 
				
			||||||
      Event.create!(project: project, author_id: user.id, action: :joined,
 | 
					      Event.create!(
 | 
				
			||||||
                    updated_at: created_date, created_at: created_date)
 | 
					        project: project,
 | 
				
			||||||
 | 
					        author_id: user.id,
 | 
				
			||||||
 | 
					        action: :joined,
 | 
				
			||||||
 | 
					        updated_at: created_date,
 | 
				
			||||||
 | 
					        created_at: created_date
 | 
				
			||||||
 | 
					      )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      sign_in user
 | 
					      sign_in user
 | 
				
			||||||
      visit user_activity_path(user)
 | 
					      visit user_activity_path(user)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -79,39 +79,52 @@ RSpec.describe 'Dashboard Merge Requests', feature_category: :code_review_workfl
 | 
				
			||||||
    end
 | 
					    end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    let!(:assigned_merge_request_from_fork) do
 | 
					    let!(:assigned_merge_request_from_fork) do
 | 
				
			||||||
      create(:merge_request,
 | 
					      create(
 | 
				
			||||||
              source_branch: 'markdown', assignees: [current_user],
 | 
					        :merge_request,
 | 
				
			||||||
              target_project: public_project, source_project: forked_project,
 | 
					        source_branch: 'markdown',
 | 
				
			||||||
              author: author_user)
 | 
					        assignees: [current_user],
 | 
				
			||||||
 | 
					        target_project: public_project,
 | 
				
			||||||
 | 
					        source_project: forked_project,
 | 
				
			||||||
 | 
					        author: author_user
 | 
				
			||||||
 | 
					      )
 | 
				
			||||||
    end
 | 
					    end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    let!(:authored_merge_request) do
 | 
					    let!(:authored_merge_request) do
 | 
				
			||||||
      create(:merge_request,
 | 
					      create(
 | 
				
			||||||
 | 
					        :merge_request,
 | 
				
			||||||
        source_branch: 'markdown',
 | 
					        source_branch: 'markdown',
 | 
				
			||||||
        source_project: project,
 | 
					        source_project: project,
 | 
				
			||||||
              author: current_user)
 | 
					        author: current_user
 | 
				
			||||||
 | 
					      )
 | 
				
			||||||
    end
 | 
					    end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    let!(:authored_merge_request_from_fork) do
 | 
					    let!(:authored_merge_request_from_fork) do
 | 
				
			||||||
      create(:merge_request,
 | 
					      create(
 | 
				
			||||||
 | 
					        :merge_request,
 | 
				
			||||||
        source_branch: 'feature_conflict',
 | 
					        source_branch: 'feature_conflict',
 | 
				
			||||||
        author: current_user,
 | 
					        author: current_user,
 | 
				
			||||||
              target_project: public_project, source_project: forked_project)
 | 
					        target_project: public_project,
 | 
				
			||||||
 | 
					        source_project: forked_project
 | 
				
			||||||
 | 
					      )
 | 
				
			||||||
    end
 | 
					    end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    let!(:labeled_merge_request) do
 | 
					    let!(:labeled_merge_request) do
 | 
				
			||||||
      create(:labeled_merge_request,
 | 
					      create(
 | 
				
			||||||
 | 
					        :labeled_merge_request,
 | 
				
			||||||
        source_branch: 'labeled',
 | 
					        source_branch: 'labeled',
 | 
				
			||||||
        labels: [label],
 | 
					        labels: [label],
 | 
				
			||||||
        author: current_user,
 | 
					        author: current_user,
 | 
				
			||||||
              source_project: project)
 | 
					        source_project: project
 | 
				
			||||||
 | 
					      )
 | 
				
			||||||
    end
 | 
					    end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    let!(:other_merge_request) do
 | 
					    let!(:other_merge_request) do
 | 
				
			||||||
      create(:merge_request,
 | 
					      create(
 | 
				
			||||||
 | 
					        :merge_request,
 | 
				
			||||||
        source_branch: 'fix',
 | 
					        source_branch: 'fix',
 | 
				
			||||||
        source_project: project,
 | 
					        source_project: project,
 | 
				
			||||||
              author: author_user)
 | 
					        author: author_user
 | 
				
			||||||
 | 
					      )
 | 
				
			||||||
    end
 | 
					    end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    before do
 | 
					    before do
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -27,8 +27,9 @@ RSpec.describe 'Dashboard > User sorts todos', feature_category: :team_planning
 | 
				
			||||||
      create(:todo, user: user, project: project, target: issue_2, created_at: 4.hours.ago, updated_at: 4.hours.ago)
 | 
					      create(:todo, user: user, project: project, target: issue_2, created_at: 4.hours.ago, updated_at: 4.hours.ago)
 | 
				
			||||||
      create(:todo, user: user, project: project, target: issue_3, created_at: 3.hours.ago, updated_at: 2.minutes.ago)
 | 
					      create(:todo, user: user, project: project, target: issue_3, created_at: 3.hours.ago, updated_at: 2.minutes.ago)
 | 
				
			||||||
      create(:todo, user: user, project: project, target: issue_1, created_at: 2.hours.ago, updated_at: 2.hours.ago)
 | 
					      create(:todo, user: user, project: project, target: issue_1, created_at: 2.hours.ago, updated_at: 2.hours.ago)
 | 
				
			||||||
      create(:todo, user: user, project: project, target: merge_request_1, created_at: 1.hour.ago,
 | 
					      create(
 | 
				
			||||||
                    updated_at: 1.hour.ago)
 | 
					        :todo, user: user, project: project, target: merge_request_1, created_at: 1.hour.ago, updated_at: 1.hour.ago
 | 
				
			||||||
 | 
					      )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      merge_request_1.labels << label_1
 | 
					      merge_request_1.labels << label_1
 | 
				
			||||||
      issue_3.labels         << label_1
 | 
					      issue_3.labels         << label_1
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -443,12 +443,15 @@ RSpec.describe 'Dashboard Todos', feature_category: :team_planning do
 | 
				
			||||||
    let_it_be(:target) { create(:design, issue: issue, project: project) }
 | 
					    let_it_be(:target) { create(:design, issue: issue, project: project) }
 | 
				
			||||||
    let_it_be(:note) { create(:note, project: project, note: 'I am note, hear me roar') }
 | 
					    let_it_be(:note) { create(:note, project: project, note: 'I am note, hear me roar') }
 | 
				
			||||||
    let_it_be(:todo) do
 | 
					    let_it_be(:todo) do
 | 
				
			||||||
      create(:todo, :mentioned,
 | 
					      create(
 | 
				
			||||||
 | 
					        :todo,
 | 
				
			||||||
 | 
					        :mentioned,
 | 
				
			||||||
        user: user,
 | 
					        user: user,
 | 
				
			||||||
        project: project,
 | 
					        project: project,
 | 
				
			||||||
        target: target,
 | 
					        target: target,
 | 
				
			||||||
        author: author,
 | 
					        author: author,
 | 
				
			||||||
             note: note)
 | 
					        note: note
 | 
				
			||||||
 | 
					      )
 | 
				
			||||||
    end
 | 
					    end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    before do
 | 
					    before do
 | 
				
			||||||
| 
						 | 
					@ -467,7 +470,9 @@ RSpec.describe 'Dashboard Todos', feature_category: :team_planning do
 | 
				
			||||||
  context 'User requested access' do
 | 
					  context 'User requested access' do
 | 
				
			||||||
    shared_examples 'has todo present with access request content' do
 | 
					    shared_examples 'has todo present with access request content' do
 | 
				
			||||||
      specify do
 | 
					      specify do
 | 
				
			||||||
        create(:todo, :member_access_requested,
 | 
					        create(
 | 
				
			||||||
 | 
					          :todo,
 | 
				
			||||||
 | 
					          :member_access_requested,
 | 
				
			||||||
          user: user,
 | 
					          user: user,
 | 
				
			||||||
          target: target,
 | 
					          target: target,
 | 
				
			||||||
          author: author
 | 
					          author: author
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -2,7 +2,7 @@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
require 'spec_helper'
 | 
					require 'spec_helper'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
RSpec.describe 'File blame', :js, feature_category: :groups_and_projects do
 | 
					RSpec.describe 'File blame', :js, feature_category: :source_code_management do
 | 
				
			||||||
  include TreeHelper
 | 
					  include TreeHelper
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  let_it_be(:project) { create(:project, :public, :repository) }
 | 
					  let_it_be(:project) { create(:project, :public, :repository) }
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -2,7 +2,7 @@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
require 'spec_helper'
 | 
					require 'spec_helper'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
RSpec.describe 'Environments page', :js, feature_category: :groups_and_projects do
 | 
					RSpec.describe 'Environments page', :js, feature_category: :continuous_delivery do
 | 
				
			||||||
  include Spec::Support::Helpers::ModalHelpers
 | 
					  include Spec::Support::Helpers::ModalHelpers
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  let(:project) { create(:project) }
 | 
					  let(:project) { create(:project) }
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -2,7 +2,7 @@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
require 'spec_helper'
 | 
					require 'spec_helper'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
RSpec.describe 'Pipelines', :js, feature_category: :groups_and_projects do
 | 
					RSpec.describe 'Pipelines', :js, feature_category: :continuous_integration do
 | 
				
			||||||
  include ListboxHelpers
 | 
					  include ListboxHelpers
 | 
				
			||||||
  include ProjectForksHelper
 | 
					  include ProjectForksHelper
 | 
				
			||||||
  include Spec::Support::Helpers::ModalHelpers
 | 
					  include Spec::Support::Helpers::ModalHelpers
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -5,7 +5,6 @@ exports[`Delete merged branches component Delete merged branches confirmation mo
 | 
				
			||||||
  <gl-base-dropdown-stub
 | 
					  <gl-base-dropdown-stub
 | 
				
			||||||
    category="tertiary"
 | 
					    category="tertiary"
 | 
				
			||||||
    class="gl-disclosure-dropdown gl-display-none gl-md-display-block!"
 | 
					    class="gl-disclosure-dropdown gl-display-none gl-md-display-block!"
 | 
				
			||||||
    data-qa-selector="delete_merged_branches_dropdown_button"
 | 
					 | 
				
			||||||
    icon="ellipsis_v"
 | 
					    icon="ellipsis_v"
 | 
				
			||||||
    nocaret="true"
 | 
					    nocaret="true"
 | 
				
			||||||
    offset="[object Object]"
 | 
					    offset="[object Object]"
 | 
				
			||||||
| 
						 | 
					@ -34,7 +33,7 @@ exports[`Delete merged branches component Delete merged branches confirmation mo
 | 
				
			||||||
   
 | 
					   
 | 
				
			||||||
  <b-button-stub
 | 
					  <b-button-stub
 | 
				
			||||||
    class="gl-display-block gl-md-display-none! gl-button btn-danger-secondary"
 | 
					    class="gl-display-block gl-md-display-none! gl-button btn-danger-secondary"
 | 
				
			||||||
    data-qa-selector="delete_merged_branches_button"
 | 
					    data-testid="delete-merged-branches-button"
 | 
				
			||||||
    size="md"
 | 
					    size="md"
 | 
				
			||||||
    tag="button"
 | 
					    tag="button"
 | 
				
			||||||
    type="button"
 | 
					    type="button"
 | 
				
			||||||
| 
						 | 
					@ -100,7 +99,6 @@ exports[`Delete merged branches component Delete merged branches confirmation mo
 | 
				
			||||||
          aria-labelledby="input-label"
 | 
					          aria-labelledby="input-label"
 | 
				
			||||||
          autocomplete="off"
 | 
					          autocomplete="off"
 | 
				
			||||||
          class="gl-form-input gl-mt-2 gl-form-input-sm"
 | 
					          class="gl-form-input gl-mt-2 gl-form-input-sm"
 | 
				
			||||||
          data-qa-selector="delete_merged_branches_input"
 | 
					 | 
				
			||||||
          debounce="0"
 | 
					          debounce="0"
 | 
				
			||||||
          formatter="[Function]"
 | 
					          formatter="[Function]"
 | 
				
			||||||
          type="text"
 | 
					          type="text"
 | 
				
			||||||
| 
						 | 
					@ -146,7 +144,6 @@ exports[`Delete merged branches component Delete merged branches confirmation mo
 | 
				
			||||||
       
 | 
					       
 | 
				
			||||||
      <b-button-stub
 | 
					      <b-button-stub
 | 
				
			||||||
        class="gl-button"
 | 
					        class="gl-button"
 | 
				
			||||||
        data-qa-selector="delete_merged_branches_confirmation_button"
 | 
					 | 
				
			||||||
        data-testid="delete-merged-branches-confirmation-button"
 | 
					        data-testid="delete-merged-branches-confirmation-button"
 | 
				
			||||||
        disabled="true"
 | 
					        disabled="true"
 | 
				
			||||||
        size="md"
 | 
					        size="md"
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -37,7 +37,7 @@ const createComponent = (mountFn = shallowMountExtended, stubs = {}) => {
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const findDeleteButton = () =>
 | 
					const findDeleteButton = () =>
 | 
				
			||||||
  wrapper.findComponent('[data-qa-selector="delete_merged_branches_button"]');
 | 
					  wrapper.findComponent('[data-testid="delete-merged-branches-button"]');
 | 
				
			||||||
const findModal = () => wrapper.findComponent(GlModal);
 | 
					const findModal = () => wrapper.findComponent(GlModal);
 | 
				
			||||||
const findConfirmationButton = () =>
 | 
					const findConfirmationButton = () =>
 | 
				
			||||||
  wrapper.findByTestId('delete-merged-branches-confirmation-button');
 | 
					  wrapper.findByTestId('delete-merged-branches-confirmation-button');
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -37,11 +37,14 @@ describe('DeleteIssueModal component', () => {
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    describe('when "primary" event is emitted', () => {
 | 
					    describe('when "primary" event is emitted', () => {
 | 
				
			||||||
      let formSubmitSpy;
 | 
					      const submitMock = jest.fn();
 | 
				
			||||||
 | 
					      // Mock the form submit method
 | 
				
			||||||
 | 
					      Object.defineProperty(HTMLFormElement.prototype, 'submit', {
 | 
				
			||||||
 | 
					        value: submitMock,
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      beforeEach(() => {
 | 
					      beforeEach(() => {
 | 
				
			||||||
        wrapper = mountComponent();
 | 
					        wrapper = mountComponent();
 | 
				
			||||||
        formSubmitSpy = jest.spyOn(wrapper.vm.$refs.form, 'submit');
 | 
					 | 
				
			||||||
        findModal().vm.$emit('primary');
 | 
					        findModal().vm.$emit('primary');
 | 
				
			||||||
      });
 | 
					      });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -50,7 +53,7 @@ describe('DeleteIssueModal component', () => {
 | 
				
			||||||
      });
 | 
					      });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      it('submits the form', () => {
 | 
					      it('submits the form', () => {
 | 
				
			||||||
        expect(formSubmitSpy).toHaveBeenCalled();
 | 
					        expect(submitMock).toHaveBeenCalledTimes(1);
 | 
				
			||||||
      });
 | 
					      });
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
  });
 | 
					  });
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,7 +1,6 @@
 | 
				
			||||||
import {
 | 
					import {
 | 
				
			||||||
  GlAlert,
 | 
					  GlAlert,
 | 
				
			||||||
  GlDropdown,
 | 
					  GlDisclosureDropdown,
 | 
				
			||||||
  GlButton,
 | 
					 | 
				
			||||||
  GlFormCheckbox,
 | 
					  GlFormCheckbox,
 | 
				
			||||||
  GlLoadingIcon,
 | 
					  GlLoadingIcon,
 | 
				
			||||||
  GlModal,
 | 
					  GlModal,
 | 
				
			||||||
| 
						 | 
					@ -64,9 +63,10 @@ describe('Package Files', () => {
 | 
				
			||||||
  const findFirstRowDownloadLink = () => findFirstRow().findByTestId('download-link');
 | 
					  const findFirstRowDownloadLink = () => findFirstRow().findByTestId('download-link');
 | 
				
			||||||
  const findFirstRowFileIcon = () => findFirstRow().findComponent(FileIcon);
 | 
					  const findFirstRowFileIcon = () => findFirstRow().findComponent(FileIcon);
 | 
				
			||||||
  const findFirstRowCreatedAt = () => findFirstRow().findComponent(TimeAgoTooltip);
 | 
					  const findFirstRowCreatedAt = () => findFirstRow().findComponent(TimeAgoTooltip);
 | 
				
			||||||
  const findFirstActionMenu = () => extendedWrapper(findFirstRow().findComponent(GlDropdown));
 | 
					  const findFirstActionMenu = () =>
 | 
				
			||||||
 | 
					    extendedWrapper(findFirstRow().findComponent(GlDisclosureDropdown));
 | 
				
			||||||
  const findActionMenuDelete = () => findFirstActionMenu().findByTestId('delete-file');
 | 
					  const findActionMenuDelete = () => findFirstActionMenu().findByTestId('delete-file');
 | 
				
			||||||
  const findFirstToggleDetailsButton = () => findFirstRow().findComponent(GlButton);
 | 
					  const findFirstToggleDetailsButton = () => findFirstRow().findByTestId('toggle-details-button');
 | 
				
			||||||
  const findFirstRowShaComponent = (id) => wrapper.findByTestId(id);
 | 
					  const findFirstRowShaComponent = (id) => wrapper.findByTestId(id);
 | 
				
			||||||
  const findCheckAllCheckbox = () => wrapper.findByTestId('package-files-checkbox-all');
 | 
					  const findCheckAllCheckbox = () => wrapper.findByTestId('package-files-checkbox-all');
 | 
				
			||||||
  const findAllRowCheckboxes = () => wrapper.findAllByTestId('package-files-checkbox');
 | 
					  const findAllRowCheckboxes = () => wrapper.findAllByTestId('package-files-checkbox');
 | 
				
			||||||
| 
						 | 
					@ -262,7 +262,7 @@ describe('Package Files', () => {
 | 
				
			||||||
        expect(findFirstActionMenu().exists()).toBe(true);
 | 
					        expect(findFirstActionMenu().exists()).toBe(true);
 | 
				
			||||||
        expect(findFirstActionMenu().props('icon')).toBe('ellipsis_v');
 | 
					        expect(findFirstActionMenu().props('icon')).toBe('ellipsis_v');
 | 
				
			||||||
        expect(findFirstActionMenu().props('textSrOnly')).toBe(true);
 | 
					        expect(findFirstActionMenu().props('textSrOnly')).toBe(true);
 | 
				
			||||||
        expect(findFirstActionMenu().props('text')).toMatchInterpolatedText('More actions');
 | 
					        expect(findFirstActionMenu().props('toggleText')).toMatchInterpolatedText('More actions');
 | 
				
			||||||
      });
 | 
					      });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      describe('menu items', () => {
 | 
					      describe('menu items', () => {
 | 
				
			||||||
| 
						 | 
					@ -272,7 +272,7 @@ describe('Package Files', () => {
 | 
				
			||||||
          });
 | 
					          });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
          it('shows delete file confirmation modal', async () => {
 | 
					          it('shows delete file confirmation modal', async () => {
 | 
				
			||||||
            await findActionMenuDelete().trigger('click');
 | 
					            await findActionMenuDelete().vm.$emit('action');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            expect(showMock).toHaveBeenCalledTimes(1);
 | 
					            expect(showMock).toHaveBeenCalledTimes(1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue