Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
		
							parent
							
								
									e79ee0307d
								
							
						
					
					
						commit
						a85d15fdb3
					
				|  | @ -1879,26 +1879,6 @@ Layout/ArgumentAlignment: | |||
|     - 'spec/rubocop/cop/rspec/env_mocking_spec.rb' | ||||
|     - 'spec/rubocop/cop/style/regexp_literal_mixed_preserve_spec.rb' | ||||
|     - 'spec/rubocop/formatter/graceful_formatter_spec.rb' | ||||
|     - 'spec/services/design_management/save_designs_service_spec.rb' | ||||
|     - 'spec/services/discussions/resolve_service_spec.rb' | ||||
|     - 'spec/services/draft_notes/publish_service_spec.rb' | ||||
|     - 'spec/services/environments/stop_service_spec.rb' | ||||
|     - 'spec/services/environments/stop_stale_service_spec.rb' | ||||
|     - 'spec/services/loose_foreign_keys/batch_cleaner_service_spec.rb' | ||||
|     - 'spec/services/metrics/dashboard/clone_dashboard_service_spec.rb' | ||||
|     - 'spec/services/note_summary_spec.rb' | ||||
|     - 'spec/services/notification_service_spec.rb' | ||||
|     - 'spec/services/pages/migrate_legacy_storage_to_deployment_service_spec.rb' | ||||
|     - 'spec/services/pages_domains/obtain_lets_encrypt_certificate_service_spec.rb' | ||||
|     - 'spec/services/preview_markdown_service_spec.rb' | ||||
|     - 'spec/services/protected_branches/api_service_spec.rb' | ||||
|     - 'spec/services/push_event_payload_service_spec.rb' | ||||
|     - 'spec/services/quick_actions/interpret_service_spec.rb' | ||||
|     - 'spec/services/releases/destroy_service_spec.rb' | ||||
|     - 'spec/services/resource_access_tokens/revoke_service_spec.rb' | ||||
|     - 'spec/services/resource_events/merge_into_notes_service_spec.rb' | ||||
|     - 'spec/services/security/ci_configuration/dependency_scanning_create_service_spec.rb' | ||||
|     - 'spec/services/security/merge_reports_service_spec.rb' | ||||
|     - 'spec/sidekiq/cron/job_gem_dependency_spec.rb' | ||||
|     - 'spec/support/shared_examples/initializers/uses_gitlab_url_blocker_shared_examples.rb' | ||||
|     - 'spec/support/shared_examples/integrations/integration_settings_form.rb' | ||||
|  |  | |||
|  | @ -10,16 +10,18 @@ import { | |||
|   TYPE_ISSUE, | ||||
|   WORKSPACE_PROJECT, | ||||
| } from '~/issues/constants'; | ||||
| import updateDescription from '~/issues/show/utils/update_description'; | ||||
| import { sanitize } from '~/lib/dompurify'; | ||||
| import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils'; | ||||
| import Poll from '~/lib/utils/poll'; | ||||
| import { containsSensitiveToken, confirmSensitiveAction, i18n } from '~/lib/utils/secret_detection'; | ||||
| import { visitUrl } from '~/lib/utils/url_utility'; | ||||
| import { __, sprintf } from '~/locale'; | ||||
| import ConfidentialityBadge from '~/vue_shared/components/confidentiality_badge.vue'; | ||||
| import { containsSensitiveToken, confirmSensitiveAction, i18n } from '~/lib/utils/secret_detection'; | ||||
| import { ISSUE_TYPE_PATH, INCIDENT_TYPE_PATH, POLLING_DELAY } from '../constants'; | ||||
| import eventHub from '../event_hub'; | ||||
| import getIssueStateQuery from '../queries/get_issue_state.query.graphql'; | ||||
| import Service from '../services/index'; | ||||
| import Store from '../stores'; | ||||
| import DescriptionComponent from './description.vue'; | ||||
| import EditedComponent from './edited.vue'; | ||||
| import FormComponent from './form.vue'; | ||||
|  | @ -234,21 +236,26 @@ export default { | |||
|     }, | ||||
|   }, | ||||
|   data() { | ||||
|     const store = new Store({ | ||||
|       titleHtml: this.initialTitleHtml, | ||||
|       titleText: this.initialTitleText, | ||||
|       descriptionHtml: this.initialDescriptionHtml, | ||||
|       descriptionText: this.initialDescriptionText, | ||||
|       updatedAt: this.updatedAt, | ||||
|       updatedByName: this.updatedByName, | ||||
|       updatedByPath: this.updatedByPath, | ||||
|       taskCompletionStatus: this.initialTaskCompletionStatus, | ||||
|       lock_version: this.lockVersion, | ||||
|     }); | ||||
| 
 | ||||
|     return { | ||||
|       store, | ||||
|       state: store.state, | ||||
|       formState: { | ||||
|         title: '', | ||||
|         description: '', | ||||
|         lockedWarningVisible: false, | ||||
|         updateLoading: false, | ||||
|         lock_version: 0, | ||||
|         issuableTemplates: {}, | ||||
|       }, | ||||
|       state: { | ||||
|         titleHtml: this.initialTitleHtml, | ||||
|         titleText: this.initialTitleText, | ||||
|         descriptionHtml: this.initialDescriptionHtml, | ||||
|         descriptionText: this.initialDescriptionText, | ||||
|         updatedAt: this.updatedAt, | ||||
|         updatedByName: this.updatedByName, | ||||
|         updatedByPath: this.updatedByPath, | ||||
|         taskCompletionStatus: this.initialTaskCompletionStatus, | ||||
|         lock_version: this.lockVersion, | ||||
|       }, | ||||
|       showForm: false, | ||||
|       templatesRequested: false, | ||||
|       isStickyHeaderShowing: false, | ||||
|  | @ -264,17 +271,9 @@ export default { | |||
|     headerClasses() { | ||||
|       return this.issuableType === TYPE_INCIDENT ? 'gl-mb-3' : 'gl-mb-6'; | ||||
|     }, | ||||
|     issuableTemplates() { | ||||
|       return this.store.formState.issuableTemplates; | ||||
|     }, | ||||
|     formState() { | ||||
|       return this.store.formState; | ||||
|     }, | ||||
|     issueChanged() { | ||||
|       const { | ||||
|         store: { | ||||
|           formState: { description, title }, | ||||
|         }, | ||||
|         formState: { description, title }, | ||||
|         initialDescriptionText, | ||||
|         initialTitleText, | ||||
|       } = this; | ||||
|  | @ -322,7 +321,7 @@ export default { | |||
|     this.poll = new Poll({ | ||||
|       resource: this.service, | ||||
|       method: 'getData', | ||||
|       successCallback: (res) => this.store.updateState(res.data), | ||||
|       successCallback: (res) => this.updateState(res.data), | ||||
|       errorCallback(err) { | ||||
|         throw new Error(err); | ||||
|       }, | ||||
|  | @ -360,23 +359,37 @@ export default { | |||
|       } | ||||
|       return undefined; | ||||
|     }, | ||||
|     updateState(data) { | ||||
|       const stateShouldUpdate = | ||||
|         this.state.titleText !== data.title_text || | ||||
|         this.state.descriptionText !== data.description_text; | ||||
| 
 | ||||
|     updateStoreState() { | ||||
|       if (stateShouldUpdate) { | ||||
|         this.formState.lockedWarningVisible = true; | ||||
|       } | ||||
| 
 | ||||
|       Object.assign(this.state, convertObjectPropsToCamelCase(data)); | ||||
|       // find if there is an open details node inside of the issue description. | ||||
|       const descriptionSection = document.body.querySelector( | ||||
|         '.detail-page-description.content-block', | ||||
|       ); | ||||
|       const details = | ||||
|         descriptionSection != null && descriptionSection.getElementsByTagName('details'); | ||||
| 
 | ||||
|       this.state.descriptionHtml = updateDescription(sanitize(data.description), details); | ||||
|       this.state.titleHtml = sanitize(data.title); | ||||
|       this.state.lock_version = data.lock_version; | ||||
|     }, | ||||
|     refetchData() { | ||||
|       return this.service | ||||
|         .getData() | ||||
|         .then((res) => res.data) | ||||
|         .then((data) => { | ||||
|           this.store.updateState(data); | ||||
|         }) | ||||
|         .catch(() => { | ||||
|           createAlert({ | ||||
|             message: this.defaultErrorMessage, | ||||
|           }); | ||||
|         }); | ||||
|         .then(this.updateState) | ||||
|         .catch(() => createAlert({ message: this.defaultErrorMessage })); | ||||
|     }, | ||||
| 
 | ||||
|     setFormState(state) { | ||||
|       this.store.setFormState(state); | ||||
|       this.formState = { ...this.formState, ...state }; | ||||
|     }, | ||||
| 
 | ||||
|     updateFormState(templates = {}) { | ||||
|  | @ -416,7 +429,7 @@ export default { | |||
|         this.templatesRequested = true; | ||||
|         this.requestTemplatesAndShowForm(); | ||||
|       } else { | ||||
|         this.updateAndShowForm(this.issuableTemplates); | ||||
|         this.updateAndShowForm(this.formState.issuableTemplates); | ||||
|       } | ||||
|     }, | ||||
| 
 | ||||
|  | @ -427,10 +440,7 @@ export default { | |||
|     async updateIssuable() { | ||||
|       this.setFormState({ updateLoading: true }); | ||||
| 
 | ||||
|       const { | ||||
|         store: { formState }, | ||||
|         issueState, | ||||
|       } = this; | ||||
|       const { formState, issueState } = this; | ||||
|       const issuablePayload = issueState.isDirty | ||||
|         ? { ...formState, issue_type: issueState.issueType } | ||||
|         : formState; | ||||
|  | @ -464,7 +474,7 @@ export default { | |||
|             visitUrl(URI); | ||||
|           } | ||||
|         }) | ||||
|         .then(this.updateStoreState) | ||||
|         .then(this.refetchData) | ||||
|         .then(() => { | ||||
|           eventHub.$emit('close.form'); | ||||
|         }) | ||||
|  | @ -518,7 +528,7 @@ export default { | |||
|       this.poll.enable(); | ||||
|       this.poll.makeDelayedRequest(POLLING_DELAY); | ||||
| 
 | ||||
|       this.updateStoreState(); | ||||
|       this.refetchData(); | ||||
|     }, | ||||
|   }, | ||||
| }; | ||||
|  | @ -531,7 +541,7 @@ export default { | |||
|         :endpoint="endpoint" | ||||
|         :form-state="formState" | ||||
|         :initial-description-text="initialDescriptionText" | ||||
|         :issuable-templates="issuableTemplates" | ||||
|         :issuable-templates="formState.issuableTemplates" | ||||
|         :markdown-docs-path="markdownDocsPath" | ||||
|         :markdown-preview-path="markdownPreviewPath" | ||||
|         :project-path="projectPath" | ||||
|  |  | |||
|  | @ -1,46 +0,0 @@ | |||
| import { sanitize } from '~/lib/dompurify'; | ||||
| import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils'; | ||||
| import updateDescription from '../utils/update_description'; | ||||
| 
 | ||||
| export default class Store { | ||||
|   constructor(initialState) { | ||||
|     this.state = initialState; | ||||
|     this.formState = { | ||||
|       title: '', | ||||
|       description: '', | ||||
|       lockedWarningVisible: false, | ||||
|       updateLoading: false, | ||||
|       lock_version: 0, | ||||
|       issuableTemplates: {}, | ||||
|     }; | ||||
|   } | ||||
| 
 | ||||
|   updateState(data) { | ||||
|     if (this.stateShouldUpdate(data)) { | ||||
|       this.formState.lockedWarningVisible = true; | ||||
|     } | ||||
| 
 | ||||
|     Object.assign(this.state, convertObjectPropsToCamelCase(data)); | ||||
|     // find if there is an open details node inside of the issue description.
 | ||||
|     const descriptionSection = document.body.querySelector( | ||||
|       '.detail-page-description.content-block', | ||||
|     ); | ||||
|     const details = | ||||
|       descriptionSection != null && descriptionSection.getElementsByTagName('details'); | ||||
| 
 | ||||
|     this.state.descriptionHtml = updateDescription(sanitize(data.description), details); | ||||
|     this.state.titleHtml = sanitize(data.title); | ||||
|     this.state.lock_version = data.lock_version; | ||||
|   } | ||||
| 
 | ||||
|   stateShouldUpdate(data) { | ||||
|     return ( | ||||
|       this.state.titleText !== data.title_text || | ||||
|       this.state.descriptionText !== data.description_text | ||||
|     ); | ||||
|   } | ||||
| 
 | ||||
|   setFormState(state) { | ||||
|     this.formState = Object.assign(this.formState, state); | ||||
|   } | ||||
| } | ||||
|  | @ -154,6 +154,15 @@ module Integrations | |||
|       supported_events.map { |event| event_channel_name(event) } | ||||
|     end | ||||
| 
 | ||||
|     override :api_field_names | ||||
|     def api_field_names | ||||
|       if mask_configurable_channels? | ||||
|         super - event_channel_names | ||||
|       else | ||||
|         super | ||||
|       end | ||||
|     end | ||||
| 
 | ||||
|     def form_fields | ||||
|       super.reject { |field| field[:name].end_with?('channel') } | ||||
|     end | ||||
|  |  | |||
|  | @ -180,11 +180,7 @@ class Packages::Package < ApplicationRecord | |||
|   scope :preload_conan_metadatum, -> { preload(:conan_metadatum) } | ||||
| 
 | ||||
|   scope :with_npm_scope, ->(scope) do | ||||
|     if Feature.enabled?(:npm_package_registry_fix_group_path_validation) | ||||
|       npm.where("position('/' in packages_packages.name) > 0 AND split_part(packages_packages.name, '/', 1) = :package_scope", package_scope: "@#{sanitize_sql_like(scope)}") | ||||
|     else | ||||
|       npm.where("name ILIKE :package_name", package_name: "@#{sanitize_sql_like(scope)}/%") | ||||
|     end | ||||
|     npm.where("position('/' in packages_packages.name) > 0 AND split_part(packages_packages.name, '/', 1) = :package_scope", package_scope: "@#{sanitize_sql_like(scope)}") | ||||
|   end | ||||
| 
 | ||||
|   scope :without_nuget_temporary_name, -> { where.not(name: Packages::Nuget::TEMPORARY_PACKAGE_NAME) } | ||||
|  |  | |||
|  | @ -47,10 +47,6 @@ module Groups | |||
|     private | ||||
| 
 | ||||
|     def valid_path_change? | ||||
|       unless Feature.enabled?(:npm_package_registry_fix_group_path_validation) | ||||
|         return valid_path_change_with_npm_packages? | ||||
|       end | ||||
| 
 | ||||
|       return true unless group.packages_feature_enabled? | ||||
|       return true if params[:path].blank? | ||||
|       return true if group.has_parent? | ||||
|  | @ -68,21 +64,6 @@ module Groups | |||
|       false | ||||
|     end | ||||
| 
 | ||||
|     # TODO: delete this function along with npm_package_registry_fix_group_path_validation | ||||
|     def valid_path_change_with_npm_packages? | ||||
|       return true unless group.packages_feature_enabled? | ||||
|       return true if params[:path].blank? | ||||
|       return true if !group.has_parent? && group.path == params[:path] | ||||
| 
 | ||||
|       npm_packages = ::Packages::GroupPackagesFinder.new(current_user, group, package_type: :npm).execute | ||||
|       if npm_packages.exists? | ||||
|         group.errors.add(:path, s_('GroupSettings|cannot change when group contains projects with NPM packages')) | ||||
|         return | ||||
|       end | ||||
| 
 | ||||
|       true | ||||
|     end | ||||
| 
 | ||||
|     def before_assignment_hook(group, params) | ||||
|       @full_path_before = group.full_path | ||||
|       @path_before = group.path | ||||
|  |  | |||
|  | @ -9,15 +9,15 @@ module Environments | |||
|     feature_category :continuous_delivery | ||||
| 
 | ||||
|     def perform(job_id, _params = {}) | ||||
|       Ci::Build.find_by_id(job_id).try do |build| | ||||
|         stop_environment(build) if build.stops_environment? && build.stop_action_successful? | ||||
|       Ci::Processable.find_by_id(job_id).try do |job| | ||||
|         stop_environment(job) if job.stops_environment? && job.stop_action_successful? | ||||
|       end | ||||
|     end | ||||
| 
 | ||||
|     private | ||||
| 
 | ||||
|     def stop_environment(build) | ||||
|       build.persisted_environment.fire_state_event(:stop_complete) | ||||
|     def stop_environment(job) | ||||
|       job.persisted_environment.fire_state_event(:stop_complete) | ||||
|     end | ||||
|   end | ||||
| end | ||||
|  |  | |||
|  | @ -1,8 +0,0 @@ | |||
| --- | ||||
| name: npm_package_registry_fix_group_path_validation | ||||
| introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/127164 | ||||
| rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/420160 | ||||
| milestone: '16.3' | ||||
| type: development | ||||
| group: group::package registry | ||||
| default_enabled: false | ||||
|  | @ -872,10 +872,6 @@ When reached, limits _do_ result in disconnects that negatively impact users. | |||
| For consistent and stable performance, you should first explore other options such as | ||||
| adjusting node specifications, and [reviewing large repositories](../../user/project/repository/managing_large_repositories.md) or workloads. | ||||
| 
 | ||||
| FLAG: | ||||
| On self-managed GitLab, by default repository cgroups are not available. To make it available, an administrator can | ||||
| [enable the feature flag](../feature_flags.md) named `gitaly_run_cmds_in_cgroup`. | ||||
| 
 | ||||
| When enabling cgroups for memory, you should ensure that no swap is configured on the Gitaly nodes as | ||||
| processes may switch to using that instead of being terminated. This situation could lead to notably compromised | ||||
| performance. | ||||
|  |  | |||
|  | @ -24,6 +24,8 @@ Users on self-managed GitLab can disable this rate limit. | |||
| 
 | ||||
| ## Configure GitLab Shell operation limit | ||||
| 
 | ||||
| > [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/123761) in GitLab 16.2. | ||||
| 
 | ||||
| `Git operations using SSH` is enabled by default. Defaults to 600 per user per minute. | ||||
| 
 | ||||
| 1. On the left sidebar, select **Your work > Admin Area**. | ||||
|  |  | |||
|  | @ -26,8 +26,10 @@ For example, if a pipeline contains DAST and SAST jobs, but the DAST job fails b | |||
| 
 | ||||
| The pipeline vulnerability report only shows results contained in the security report artifacts. This report differs from | ||||
| the [Vulnerability Report](index.md), which contains cumulative results of all successful jobs, and from the merge request | ||||
| [security widget](../index.md#view-security-scan-information-in-merge-requests), which combines the branch results with | ||||
| cumulative results. | ||||
| [security widget](../index.md#view-security-scan-information-in-merge-requests), which contains new vulnerability findings that don't already exist in the default branch.  | ||||
| 
 | ||||
| NOTE: | ||||
| If a new advisory is added to our advisory database and the last pipeline for the default branch is stale, the resulting vulnerability may appear in the MR widget as "New" when it is already in the default branch. This will be resolved by [Continuous Vulnerability Scans](https://gitlab.com/groups/gitlab-org/-/epics/7886). | ||||
| 
 | ||||
| The pipeline vulnerability report only displays after the pipeline is complete. If the pipeline has a [blocking manual job](../../../ci/jobs/job_control.md#types-of-manual-jobs), the pipeline waits for the manual job and the vulnerabilities cannot be displayed if the blocking manual job did not run. | ||||
| 
 | ||||
|  |  | |||
|  | @ -178,12 +178,11 @@ To show one file per page on the **Changes** tab: | |||
| 
 | ||||
| Then, to move between files on the **Changes** tab, below each file, select the **Previous** and **Next** buttons. | ||||
| 
 | ||||
| ### Autocomplete characters | ||||
| ### Auto-enclose characters | ||||
| 
 | ||||
| When you type an opening character, like a bracket or quote mark, in a description or comment box, | ||||
| GitLab can automatically insert the closing character as you type. For example, if you begin your text with an open bracket, GitLab can insert the closing bracket. | ||||
| Automatically add the corresponding closing character to text when you type the opening character. For example, you can automatically insert a closing bracket when you type an opening bracket. This setting works only in description and comment boxes and for the following characters: `**"`, `'`, ```, `(`, `[`, `{`, `<`, `*`, `_**`. | ||||
| 
 | ||||
| To autocomplete characters in description and comment boxes: | ||||
| To auto-enclose characters in description and comment boxes: | ||||
| 
 | ||||
| 1. On the left sidebar, select your avatar. | ||||
| 1. Select **Preferences**. | ||||
|  | @ -191,9 +190,12 @@ To autocomplete characters in description and comment boxes: | |||
| 1. Select the **Surround text selection when typing quotes or brackets** checkbox. | ||||
| 1. Select **Save changes**. | ||||
| 
 | ||||
| In a description or comment box, you can now type a word, highlight it, then type an | ||||
| opening character. Instead of replacing the text, the closing character is added to the end. | ||||
| 
 | ||||
| ### Automate new list items | ||||
| 
 | ||||
| Create a new list item when you press <kbd>Enter</kbd> within a list in description and comment boxes. | ||||
| Create a new list item when you press <kbd>Enter</kbd> in a list in description and comment boxes. | ||||
| 
 | ||||
| To add a new list item when you press the <kbd>Enter</kbd> key: | ||||
| 
 | ||||
|  |  | |||
|  | @ -510,3 +510,11 @@ To disable the feature flag, run this command: | |||
| # Disable | ||||
| Feature.disable(:github_importer_lower_per_page_limit, group) | ||||
| ``` | ||||
| 
 | ||||
| ## Known limitations | ||||
| 
 | ||||
| When importing a GitHub pull request with assigned reviewers that do not exist in the GitLab instance, the reviewers will not be imported. | ||||
| 
 | ||||
| In this case, the import will create comment events showing the non-existent users were added as reviewers and approvers. However, the actual reviewer status and approval are not applied to the merge request in GitLab. | ||||
| 
 | ||||
| There is currently no workaround to map the reviewers if they do not exist in the GitLab instance. The importer cannot apply approvals or reviewers from users that cannot be mapped. | ||||
|  |  | |||
|  | @ -5,7 +5,7 @@ module Quality | |||
|   module Seeders | ||||
|     class Issues | ||||
|       DEFAULT_BACKFILL_WEEKS = 52 | ||||
|       DEFAULT_AVERAGE_ISSUES_PER_WEEK = 10 | ||||
|       DEFAULT_AVERAGE_ISSUES_PER_WEEK = 20 | ||||
| 
 | ||||
|       attr_reader :project, :user | ||||
| 
 | ||||
|  | @ -14,23 +14,27 @@ module Quality | |||
|       end | ||||
| 
 | ||||
|       def seed(backfill_weeks: DEFAULT_BACKFILL_WEEKS, average_issues_per_week: DEFAULT_AVERAGE_ISSUES_PER_WEEK) | ||||
|         create_milestones! | ||||
|         create_team_members! | ||||
| 
 | ||||
|         created_at = backfill_weeks.to_i.weeks.ago | ||||
|         team = project.team.users | ||||
|         created_issues_count = 0 | ||||
| 
 | ||||
|         loop do | ||||
|           rand(average_issues_per_week * 2).times do | ||||
|           rand(1..average_issues_per_week).times do | ||||
|             params = { | ||||
|               title: FFaker::Lorem.sentence(6), | ||||
|               description: FFaker::Lorem.sentence, | ||||
|               created_at: created_at + rand(6).days, | ||||
|               state: %w[opened closed].sample, | ||||
|               milestone: project.milestones.sample, | ||||
|               assignee_ids: Array(team.pluck(:id).sample(3)), | ||||
|               milestone_id: project.milestones.sample&.id, | ||||
|               assignee_ids: Array(team.pluck(:id).sample(rand(3))), | ||||
|               due_date: rand(10).days.from_now, | ||||
|               labels: labels.join(',') | ||||
|             } | ||||
|             params[:closed_at] = params[:created_at] + rand(35).days if params[:state] == 'closed' | ||||
|             }.merge(additional_params) | ||||
| 
 | ||||
|             params[:closed_at] = params[:created_at] + rand(35).days if params[:state] == 'closed' | ||||
|             create_result = ::Issues::CreateService.new(container: project, current_user: team.sample, params: params, perform_spam_check: false).execute_without_rate_limiting | ||||
| 
 | ||||
|             if create_result.success? | ||||
|  | @ -49,6 +53,46 @@ module Quality | |||
| 
 | ||||
|       private | ||||
| 
 | ||||
|       # Overriden on Quality::Seeders::Insights::Issues | ||||
|       def additional_params | ||||
|         {} | ||||
|       end | ||||
| 
 | ||||
|       def create_team_members! | ||||
|         3.times do |i| | ||||
|           user = FactoryBot.create( | ||||
|             :user, | ||||
|             name: "I User#{i}", | ||||
|             username: "i-user-#{i}-#{suffix}", | ||||
|             email: "i-user-#{i}@#{suffix}.com" | ||||
|           ) | ||||
| 
 | ||||
|           # need owner access to allow changing Issue#created_at | ||||
|           project.add_owner(user) | ||||
|         end | ||||
| 
 | ||||
|         AuthorizedProjectUpdate::ProjectRecalculateService.new(project).execute | ||||
|         # Refind object toreload ProjectTeam association which is memoized at Project model | ||||
|         @project = Project.find(project.id) | ||||
|       end | ||||
| 
 | ||||
|       def create_milestones! | ||||
|         3.times do |i| | ||||
|           params = { | ||||
|             project: project, | ||||
|             title: "Sprint #{i}", | ||||
|             description: FFaker::Lorem.sentence, | ||||
|             state: [:active, :closed].sample | ||||
|           } | ||||
| 
 | ||||
|           FactoryBot.create(:milestone, **params) | ||||
|         end | ||||
|       end | ||||
| 
 | ||||
|       def suffix | ||||
|         @suffix ||= Time.now.to_i | ||||
|       end | ||||
| 
 | ||||
|       def labels | ||||
|         @labels_pool ||= project.labels.limit(rand(3)).pluck(:title).tap do |labels_array| | ||||
|           labels_array.concat(project.group.labels.limit(rand(3)).pluck(:title)) if project.group | ||||
|  |  | |||
|  | @ -22,7 +22,7 @@ module QA | |||
|           return | ||||
|         end | ||||
| 
 | ||||
|         Page::Project::Menu.perform(&:click_merge_requests) | ||||
|         Page::Project::Menu.perform(&:go_to_merge_requests) | ||||
|         Page::MergeRequest::Index.perform(&:click_new_merge_request) | ||||
|         Page::MergeRequest::New.perform do |merge_request| | ||||
|           merge_request.select_source_branch(source_branch) | ||||
|  |  | |||
|  | @ -5,174 +5,13 @@ module QA | |||
|     module Group | ||||
|       class Menu < Page::Base | ||||
|         include QA::Page::SubMenus::Common | ||||
| 
 | ||||
|         if Runtime::Env.super_sidebar_enabled? | ||||
|           prepend Page::SubMenus::SuperSidebar::Manage | ||||
|           prepend Page::SubMenus::SuperSidebar::Plan | ||||
|           prepend Page::SubMenus::SuperSidebar::Settings | ||||
|           prepend SubMenus::SuperSidebar::Main | ||||
|           prepend SubMenus::SuperSidebar::Build | ||||
|           prepend SubMenus::SuperSidebar::Operate | ||||
|           prepend SubMenus::SuperSidebar::Deploy | ||||
|         end | ||||
| 
 | ||||
|         def click_group_members_item | ||||
|           return go_to_members if Runtime::Env.super_sidebar_enabled? | ||||
| 
 | ||||
|           hover_group_information do | ||||
|             within_submenu do | ||||
|               click_element(:sidebar_menu_item_link, menu_item: 'Members') | ||||
|             end | ||||
|           end | ||||
|         end | ||||
| 
 | ||||
|         def click_subgroup_members_item | ||||
|           return go_to_members if Runtime::Env.super_sidebar_enabled? | ||||
| 
 | ||||
|           hover_subgroup_information do | ||||
|             within_submenu do | ||||
|               click_element(:sidebar_menu_item_link, menu_item: 'Members') | ||||
|             end | ||||
|           end | ||||
|         end | ||||
| 
 | ||||
|         def click_settings | ||||
|           within_sidebar do | ||||
|             click_element(:sidebar_menu_link, menu_item: 'Settings') | ||||
|           end | ||||
|         end | ||||
| 
 | ||||
|         def click_group_general_settings_item | ||||
|           return go_to_general_settings if Runtime::Env.super_sidebar_enabled? | ||||
| 
 | ||||
|           hover_group_settings do | ||||
|             within_submenu do | ||||
|               click_element(:sidebar_menu_item_link, menu_item: 'General') | ||||
|             end | ||||
|           end | ||||
|         end | ||||
| 
 | ||||
|         def go_to_milestones | ||||
|           hover_issues do | ||||
|             within_submenu do | ||||
|               click_element(:sidebar_menu_item_link, menu_item: 'Milestones') | ||||
|             end | ||||
|           end | ||||
|         end | ||||
| 
 | ||||
|         def go_to_runners | ||||
|           hover_group_ci_cd do | ||||
|             within_submenu do | ||||
|               click_element(:sidebar_menu_item_link, menu_item: 'Runners') | ||||
|             end | ||||
|           end | ||||
|         end | ||||
| 
 | ||||
|         def go_to_package_settings | ||||
|           hover_group_settings do | ||||
|             within_submenu do | ||||
|               click_element(:sidebar_menu_item_link, menu_item: 'Packages and registries') | ||||
|             end | ||||
|           end | ||||
|         end | ||||
| 
 | ||||
|         def go_to_group_packages | ||||
|           return go_to_package_registry if Runtime::Env.super_sidebar_enabled? | ||||
| 
 | ||||
|           hover_group_packages do | ||||
|             within_submenu do | ||||
|               click_element(:sidebar_menu_item_link, menu_item: 'Package Registry') | ||||
|             end | ||||
|           end | ||||
|         end | ||||
| 
 | ||||
|         def go_to_group_dependency_proxy | ||||
|           return go_to_dependency_proxy if Runtime::Env.super_sidebar_enabled? | ||||
| 
 | ||||
|           hover_group_packages do | ||||
|             within_submenu do | ||||
|               click_element(:sidebar_menu_item_link, menu_item: 'Dependency Proxy') | ||||
|             end | ||||
|           end | ||||
|         end | ||||
| 
 | ||||
|         def go_to_repository_settings | ||||
|           hover_group_settings do | ||||
|             within_submenu do | ||||
|               click_element(:sidebar_menu_item_link, menu_item: 'Repository') | ||||
|             end | ||||
|           end | ||||
|         end | ||||
| 
 | ||||
|         def go_to_access_token_settings | ||||
|           hover_group_settings do | ||||
|             within_submenu do | ||||
|               click_element(:sidebar_menu_item_link, menu_item: 'Access Tokens') | ||||
|             end | ||||
|           end | ||||
|         end | ||||
| 
 | ||||
|         private | ||||
| 
 | ||||
|         def hover_settings | ||||
|           within_sidebar do | ||||
|             scroll_to_element(:sidebar_menu_link, menu_item: 'Settings') | ||||
|             find_element(:sidebar_menu_link, menu_item: 'Settings').hover | ||||
| 
 | ||||
|             yield | ||||
|           end | ||||
|         end | ||||
| 
 | ||||
|         def hover_issues | ||||
|           within_sidebar do | ||||
|             scroll_to_element(:sidebar_menu_link, menu_item: 'Issues') | ||||
|             find_element(:sidebar_menu_link, menu_item: 'Issues').hover | ||||
| 
 | ||||
|             yield | ||||
|           end | ||||
|         end | ||||
| 
 | ||||
|         def hover_group_information | ||||
|           within_sidebar do | ||||
|             find_element(:sidebar_menu_link, menu_item: 'Group information').hover | ||||
| 
 | ||||
|             yield | ||||
|           end | ||||
|         end | ||||
| 
 | ||||
|         def hover_subgroup_information | ||||
|           within_sidebar do | ||||
|             find_element(:sidebar_menu_link, menu_item: 'Subgroup information').hover | ||||
| 
 | ||||
|             yield | ||||
|           end | ||||
|         end | ||||
| 
 | ||||
|         def hover_group_ci_cd | ||||
|           within_sidebar do | ||||
|             find_element(:sidebar_menu_link, menu_item: 'CI/CD').hover | ||||
| 
 | ||||
|             yield | ||||
|           end | ||||
|         end | ||||
| 
 | ||||
|         def hover_group_packages | ||||
|           within_sidebar do | ||||
|             scroll_to_element(:sidebar_menu_link, menu_item: 'Packages and registries') | ||||
|             find_element(:sidebar_menu_link, menu_item: 'Packages and registries').hover | ||||
| 
 | ||||
|             yield | ||||
|           end | ||||
|         end | ||||
| 
 | ||||
|         def hover_group_settings | ||||
|           within_sidebar do | ||||
|             scroll_to_element(:sidebar_menu_link, menu_item: 'Settings') | ||||
|             find_element(:sidebar_menu_link, menu_item: 'Settings').hover | ||||
| 
 | ||||
|             yield | ||||
|           end | ||||
|         end | ||||
|         include Page::SubMenus::SuperSidebar::Manage | ||||
|         include Page::SubMenus::SuperSidebar::Plan | ||||
|         include Page::SubMenus::SuperSidebar::Settings | ||||
|         include SubMenus::SuperSidebar::Main | ||||
|         include SubMenus::SuperSidebar::Build | ||||
|         include SubMenus::SuperSidebar::Operate | ||||
|         include SubMenus::SuperSidebar::Deploy | ||||
|       end | ||||
|     end | ||||
|   end | ||||
|  |  | |||
|  | @ -8,7 +8,7 @@ module QA | |||
|           module Deploy | ||||
|             extend QA::Page::PageConcern | ||||
| 
 | ||||
|             def self.prepended(base) | ||||
|             def self.included(base) | ||||
|               super | ||||
| 
 | ||||
|               base.class_eval do | ||||
|  |  | |||
|  | @ -8,7 +8,7 @@ module QA | |||
|           module Main | ||||
|             extend QA::Page::PageConcern | ||||
| 
 | ||||
|             def self.prepended(base) | ||||
|             def self.included(base) | ||||
|               super | ||||
| 
 | ||||
|               base.class_eval do | ||||
|  |  | |||
|  | @ -8,7 +8,7 @@ module QA | |||
|           module Operate | ||||
|             extend QA::Page::PageConcern | ||||
| 
 | ||||
|             def self.prepended(base) | ||||
|             def self.included(base) | ||||
|               super | ||||
| 
 | ||||
|               base.class_eval do | ||||
|  |  | |||
|  | @ -4,64 +4,27 @@ module QA | |||
|   module Page | ||||
|     module Profile | ||||
|       class Menu < Page::Base | ||||
|         prepend QA::Mobile::Page::SubMenus::Common if QA::Runtime::Env.mobile_layout? | ||||
|         # TODO: integrate back once super sidebar becomes default | ||||
|         prepend QA::Page::Profile::SuperSidebar::Menu if QA::Runtime::Env.super_sidebar_enabled? | ||||
| 
 | ||||
|         view 'lib/sidebars/user_settings/menus/access_tokens_menu.rb' do | ||||
|           element :access_token_link | ||||
|         end | ||||
| 
 | ||||
|         view 'lib/sidebars/user_settings/menus/ssh_keys_menu.rb' do | ||||
|           element :ssh_keys_link | ||||
|         end | ||||
| 
 | ||||
|         view 'lib/sidebars/user_settings/menus/emails_menu.rb' do | ||||
|           element :profile_emails_link | ||||
|         end | ||||
| 
 | ||||
|         view 'lib/sidebars/user_settings/menus/password_menu.rb' do | ||||
|           element :profile_password_link | ||||
|         end | ||||
| 
 | ||||
|         view 'lib/sidebars/user_settings/menus/account_menu.rb' do | ||||
|           element :profile_account_link | ||||
|         end | ||||
| 
 | ||||
|         def click_access_tokens | ||||
|           within_sidebar do | ||||
|             click_element(:access_token_link) | ||||
|           end | ||||
|         end | ||||
|         include SubMenus::CreateNewMenu | ||||
|         include SubMenus::SuperSidebar::ContextSwitcher | ||||
| 
 | ||||
|         def click_ssh_keys | ||||
|           within_sidebar do | ||||
|             click_element(:ssh_keys_link) | ||||
|           end | ||||
|           click_element(:nav_item_link, submenu_item: 'SSH Keys') | ||||
|         end | ||||
| 
 | ||||
|         def click_account | ||||
|           within_sidebar do | ||||
|             click_element(:profile_account_link) | ||||
|           end | ||||
|           click_element(:nav_item_link, submenu_item: 'Account') | ||||
|         end | ||||
| 
 | ||||
|         def click_emails | ||||
|           within_sidebar do | ||||
|             click_element(:profile_emails_link) | ||||
|           end | ||||
|           click_element(:nav_item_link, submenu_item: 'Emails') | ||||
|         end | ||||
| 
 | ||||
|         def click_password | ||||
|           within_sidebar do | ||||
|             click_element(:profile_password_link) | ||||
|           end | ||||
|           click_element(:nav_item_link, submenu_item: 'Password') | ||||
|         end | ||||
| 
 | ||||
|         private | ||||
| 
 | ||||
|         def within_sidebar(&block) | ||||
|           page.within('.sidebar-top-level-items', &block) | ||||
|         def click_access_tokens | ||||
|           click_element(:nav_item_link, submenu_item: 'Access Tokens') | ||||
|         end | ||||
|       end | ||||
|     end | ||||
|  |  | |||
|  | @ -1,31 +0,0 @@ | |||
| # frozen_string_literal: true | ||||
| 
 | ||||
| module QA | ||||
|   module Page | ||||
|     module Profile | ||||
|       module SuperSidebar | ||||
|         module Menu | ||||
|           def click_ssh_keys | ||||
|             click_element(:nav_item_link, submenu_item: 'SSH Keys') | ||||
|           end | ||||
| 
 | ||||
|           def click_account | ||||
|             click_element(:nav_item_link, submenu_item: 'Account') | ||||
|           end | ||||
| 
 | ||||
|           def click_emails | ||||
|             click_element(:nav_item_link, submenu_item: 'Emails') | ||||
|           end | ||||
| 
 | ||||
|           def click_password | ||||
|             click_element(:nav_item_link, submenu_item: 'Password') | ||||
|           end | ||||
| 
 | ||||
|           def click_access_tokens | ||||
|             click_element(:nav_item_link, submenu_item: 'Access Tokens') | ||||
|           end | ||||
|         end | ||||
|       end | ||||
|     end | ||||
|   end | ||||
| end | ||||
|  | @ -5,83 +5,16 @@ module QA | |||
|     module Project | ||||
|       class Menu < Page::Base | ||||
|         include SubMenus::Common | ||||
|         include SubMenus::Project | ||||
|         include SubMenus::CiCd | ||||
|         include SubMenus::Issues | ||||
|         include SubMenus::Deployments | ||||
|         include SubMenus::Monitor | ||||
|         include SubMenus::Infrastructure | ||||
|         include SubMenus::Repository | ||||
|         include SubMenus::Settings | ||||
|         include SubMenus::Packages | ||||
|         include SubMenus::CreateNewMenu | ||||
| 
 | ||||
|         if Runtime::Env.super_sidebar_enabled? | ||||
|           include Page::SubMenus::SuperSidebar::Manage | ||||
|           include Page::SubMenus::SuperSidebar::Deploy | ||||
|           include SubMenus::SuperSidebar::Plan | ||||
|           include SubMenus::SuperSidebar::Settings | ||||
|           include SubMenus::SuperSidebar::Code | ||||
|           include SubMenus::SuperSidebar::Build | ||||
|           include SubMenus::SuperSidebar::Operate | ||||
|           include SubMenus::SuperSidebar::Monitor | ||||
|           include SubMenus::SuperSidebar::Main | ||||
|         end | ||||
| 
 | ||||
|         def click_merge_requests | ||||
|           return go_to_merge_requests if Runtime::Env.super_sidebar_enabled? | ||||
| 
 | ||||
|           within_sidebar do | ||||
|             click_element(:sidebar_menu_link, menu_item: 'Merge requests') | ||||
|           end | ||||
|         end | ||||
| 
 | ||||
|         def click_wiki | ||||
|           return go_to_wiki if Runtime::Env.super_sidebar_enabled? | ||||
| 
 | ||||
|           within_sidebar do | ||||
|             click_element(:sidebar_menu_link, menu_item: 'Wiki') | ||||
|           end | ||||
|         end | ||||
| 
 | ||||
|         def click_activity | ||||
|           return go_to_activity if Runtime::Env.super_sidebar_enabled? | ||||
| 
 | ||||
|           hover_project_information do | ||||
|             within_submenu do | ||||
|               click_element(:sidebar_menu_item_link, menu_item: 'Activity') | ||||
|             end | ||||
|           end | ||||
|         end | ||||
| 
 | ||||
|         def click_snippets | ||||
|           return go_to_snippets if Runtime::Env.super_sidebar_enabled? | ||||
| 
 | ||||
|           within_sidebar do | ||||
|             click_element(:sidebar_menu_link, menu_item: 'Snippets') | ||||
|           end | ||||
|         end | ||||
| 
 | ||||
|         def click_members | ||||
|           return go_to_members if Runtime::Env.super_sidebar_enabled? | ||||
| 
 | ||||
|           hover_project_information do | ||||
|             within_submenu do | ||||
|               click_element(:sidebar_menu_item_link, menu_item: 'Members') | ||||
|             end | ||||
|           end | ||||
|         end | ||||
| 
 | ||||
|         private | ||||
| 
 | ||||
|         def hover_project_information | ||||
|           within_sidebar do | ||||
|             scroll_to_element(:sidebar_menu_link, menu_item: 'Project information') | ||||
|             find_element(:sidebar_menu_link, menu_item: 'Project information').hover | ||||
| 
 | ||||
|             yield | ||||
|           end | ||||
|         end | ||||
|         include SubMenus::SuperSidebar::Plan | ||||
|         include SubMenus::SuperSidebar::Settings | ||||
|         include SubMenus::SuperSidebar::Code | ||||
|         include SubMenus::SuperSidebar::Build | ||||
|         include SubMenus::SuperSidebar::Operate | ||||
|         include SubMenus::SuperSidebar::Monitor | ||||
|         include SubMenus::SuperSidebar::Main | ||||
|         include Page::SubMenus::SuperSidebar::Manage | ||||
|         include Page::SubMenus::SuperSidebar::Deploy | ||||
|       end | ||||
|     end | ||||
|   end | ||||
|  |  | |||
|  | @ -1,47 +0,0 @@ | |||
| # frozen_string_literal: true | ||||
| 
 | ||||
| module QA | ||||
|   module Page | ||||
|     module Project | ||||
|       module SubMenus | ||||
|         module CiCd | ||||
|           extend QA::Page::PageConcern | ||||
| 
 | ||||
|           def self.included(base) | ||||
|             super | ||||
| 
 | ||||
|             base.class_eval do | ||||
|               include QA::Page::Project::SubMenus::Common | ||||
|             end | ||||
|           end | ||||
| 
 | ||||
|           def go_to_pipelines | ||||
|             within_sidebar do | ||||
|               click_element(:sidebar_menu_link, menu_item: 'CI/CD') | ||||
|             end | ||||
|           end | ||||
| 
 | ||||
|           def go_to_pipeline_editor | ||||
|             hover_ci_cd_pipelines do | ||||
|               within_submenu do | ||||
|                 click_element(:sidebar_menu_item_link, menu_item: 'Editor') | ||||
|               end | ||||
|             end | ||||
|           end | ||||
| 
 | ||||
|           private | ||||
| 
 | ||||
|           def hover_ci_cd_pipelines | ||||
|             within_sidebar do | ||||
|               find_element(:sidebar_menu_link, menu_item: 'CI/CD').hover | ||||
| 
 | ||||
|               yield | ||||
|             end | ||||
|           end | ||||
|         end | ||||
|       end | ||||
|     end | ||||
|   end | ||||
| end | ||||
| 
 | ||||
| QA::Page::Project::SubMenus::CiCd.prepend_mod_with('Page::Project::SubMenus::CiCd', namespace: QA) | ||||
|  | @ -13,14 +13,6 @@ module QA | |||
|             base.class_eval do | ||||
|               include QA::Page::SubMenus::Common | ||||
| 
 | ||||
|               view 'app/views/shared/nav/_sidebar_menu_item.html.haml' do | ||||
|                 element :sidebar_menu_item_link | ||||
|               end | ||||
| 
 | ||||
|               view 'app/views/shared/nav/_sidebar_menu.html.haml' do | ||||
|                 element :sidebar_menu_link | ||||
|               end | ||||
| 
 | ||||
|               view 'app/views/layouts/nav/_top_bar.html.haml' do | ||||
|                 element :toggle_mobile_nav_button | ||||
|               end | ||||
|  |  | |||
|  | @ -11,21 +11,12 @@ module QA | |||
|             super | ||||
| 
 | ||||
|             base.class_eval do | ||||
|               # TODO: remove this when the super sidebar is enabled by default | ||||
|               view 'app/helpers/nav/new_dropdown_helper.rb' do | ||||
|                 element :new_issue_link | ||||
|               end | ||||
| 
 | ||||
|               view 'app/helpers/sidebars_helper.rb' do | ||||
|                 element :create_menu_item | ||||
|               end | ||||
|               include QA::Page::SubMenus::CreateNewMenu | ||||
|             end | ||||
|           end | ||||
| 
 | ||||
|           def go_to_new_issue | ||||
|             within_new_item_menu do | ||||
|               next click_element(:new_issue_link) unless QA::Runtime::Env.super_sidebar_enabled? | ||||
| 
 | ||||
|               click_element(:create_menu_item, create_menu_item: 'new_issue') | ||||
|             end | ||||
|           end | ||||
|  |  | |||
|  | @ -1,40 +0,0 @@ | |||
| # frozen_string_literal: true | ||||
| 
 | ||||
| module QA | ||||
|   module Page | ||||
|     module Project | ||||
|       module SubMenus | ||||
|         module Deployments | ||||
|           extend QA::Page::PageConcern | ||||
| 
 | ||||
|           def self.included(base) | ||||
|             super | ||||
| 
 | ||||
|             base.class_eval do | ||||
|               include QA::Page::Project::SubMenus::Common | ||||
|             end | ||||
|           end | ||||
| 
 | ||||
|           def go_to_deployments_environments | ||||
|             hover_deployments do | ||||
|               within_submenu do | ||||
|                 click_element(:sidebar_menu_item_link, menu_item: 'Environments') | ||||
|               end | ||||
|             end | ||||
|           end | ||||
| 
 | ||||
|           private | ||||
| 
 | ||||
|           def hover_deployments | ||||
|             within_sidebar do | ||||
|               scroll_to_element(:sidebar_menu_link, menu_item: 'Deployments') | ||||
|               find_element(:sidebar_menu_link, menu_item: 'Deployments').hover | ||||
| 
 | ||||
|               yield | ||||
|             end | ||||
|           end | ||||
|         end | ||||
|       end | ||||
|     end | ||||
|   end | ||||
| end | ||||
|  | @ -1,40 +0,0 @@ | |||
| # frozen_string_literal: true | ||||
| 
 | ||||
| module QA | ||||
|   module Page | ||||
|     module Project | ||||
|       module SubMenus | ||||
|         module Infrastructure | ||||
|           extend QA::Page::PageConcern | ||||
| 
 | ||||
|           def self.included(base) | ||||
|             super | ||||
| 
 | ||||
|             base.class_eval do | ||||
|               include QA::Page::Project::SubMenus::Common | ||||
|             end | ||||
|           end | ||||
| 
 | ||||
|           def go_to_infrastructure_kubernetes | ||||
|             hover_infrastructure do | ||||
|               within_submenu do | ||||
|                 click_link('Kubernetes clusters') | ||||
|               end | ||||
|             end | ||||
|           end | ||||
| 
 | ||||
|           private | ||||
| 
 | ||||
|           def hover_infrastructure | ||||
|             within_sidebar do | ||||
|               scroll_to_element(:sidebar_menu_link, menu_item: 'Infrastructure') | ||||
|               find_element(:sidebar_menu_link, menu_item: 'Infrastructure').hover | ||||
| 
 | ||||
|               yield | ||||
|             end | ||||
|           end | ||||
|         end | ||||
|       end | ||||
|     end | ||||
|   end | ||||
| end | ||||
|  | @ -1,78 +0,0 @@ | |||
| # frozen_string_literal: true | ||||
| 
 | ||||
| module QA | ||||
|   module Page | ||||
|     module Project | ||||
|       module SubMenus | ||||
|         module Issues | ||||
|           extend QA::Page::PageConcern | ||||
| 
 | ||||
|           def self.included(base) | ||||
|             super | ||||
| 
 | ||||
|             base.class_eval do | ||||
|               include QA::Page::Project::SubMenus::Common | ||||
|             end | ||||
|           end | ||||
| 
 | ||||
|           def go_to_issues | ||||
|             within_sidebar do | ||||
|               click_element(:sidebar_menu_link, menu_item: 'Issues') | ||||
|             end | ||||
|           end | ||||
| 
 | ||||
|           def click_milestones | ||||
|             within_sidebar do | ||||
|               click_element(:sidebar_menu_item_link, menu_item: 'Milestones') | ||||
|             end | ||||
|           end | ||||
| 
 | ||||
|           def go_to_issue_boards | ||||
|             hover_issues do | ||||
|               within_submenu do | ||||
|                 click_element(:sidebar_menu_item_link, menu_item: 'Boards') | ||||
|               end | ||||
|             end | ||||
|           end | ||||
| 
 | ||||
|           def go_to_labels | ||||
|             hover_issues do | ||||
|               within_submenu do | ||||
|                 click_element(:sidebar_menu_item_link, menu_item: 'Labels') | ||||
|               end | ||||
|             end | ||||
|           end | ||||
| 
 | ||||
|           def go_to_milestones | ||||
|             hover_issues do | ||||
|               within_submenu do | ||||
|                 click_element(:sidebar_menu_item_link, menu_item: 'Milestones') | ||||
|               end | ||||
|             end | ||||
|           end | ||||
| 
 | ||||
|           def go_to_jira_issues | ||||
|             hover_issues do | ||||
|               within_submenu do | ||||
|                 click_element(:sidebar_menu_item_link, menu_item: 'Jira issues') | ||||
|               end | ||||
|             end | ||||
|           end | ||||
| 
 | ||||
|           private | ||||
| 
 | ||||
|           def hover_issues | ||||
|             within_sidebar do | ||||
|               scroll_to_element(:sidebar_menu_link, menu_item: 'Issues') | ||||
|               find_element(:sidebar_menu_link, menu_item: 'Issues').hover | ||||
| 
 | ||||
|               yield | ||||
|             end | ||||
|           end | ||||
|         end | ||||
|       end | ||||
|     end | ||||
|   end | ||||
| end | ||||
| 
 | ||||
| QA::Page::Project::SubMenus::Issues.prepend_mod_with('Page::Project::SubMenus::Issues', namespace: QA) | ||||
|  | @ -1,64 +0,0 @@ | |||
| # frozen_string_literal: true | ||||
| 
 | ||||
| module QA | ||||
|   module Page | ||||
|     module Project | ||||
|       module SubMenus | ||||
|         module Monitor | ||||
|           extend QA::Page::PageConcern | ||||
| 
 | ||||
|           def self.included(base) | ||||
|             super | ||||
| 
 | ||||
|             base.class_eval do | ||||
|               include QA::Page::Project::SubMenus::Common | ||||
|             end | ||||
|           end | ||||
| 
 | ||||
|           def go_to_monitor_incidents | ||||
|             hover_monitor do | ||||
|               within_submenu do | ||||
|                 click_element(:sidebar_menu_item_link, menu_item: 'Incidents') | ||||
|               end | ||||
|             end | ||||
|           end | ||||
| 
 | ||||
|           def go_to_monitor_alerts | ||||
|             hover_monitor do | ||||
|               within_submenu do | ||||
|                 click_element(:sidebar_menu_item_link, menu_item: 'Alerts') | ||||
|               end | ||||
|             end | ||||
|           end | ||||
| 
 | ||||
|           def go_to_monitor_on_call_schedules | ||||
|             hover_monitor do | ||||
|               within_submenu do | ||||
|                 click_element(:sidebar_menu_item_link, menu_item: 'On-call Schedules') | ||||
|               end | ||||
|             end | ||||
|           end | ||||
| 
 | ||||
|           def go_to_monitor_escalation_policies | ||||
|             hover_monitor do | ||||
|               within_submenu do | ||||
|                 click_element(:sidebar_menu_item_link, menu_item: 'Escalation Policies') | ||||
|               end | ||||
|             end | ||||
|           end | ||||
| 
 | ||||
|           private | ||||
| 
 | ||||
|           def hover_monitor | ||||
|             within_sidebar do | ||||
|               scroll_to_element(:sidebar_menu_link, menu_item: 'Monitor') | ||||
|               find_element(:sidebar_menu_link, menu_item: 'Monitor').hover | ||||
| 
 | ||||
|               yield | ||||
|             end | ||||
|           end | ||||
|         end | ||||
|       end | ||||
|     end | ||||
|   end | ||||
| end | ||||
|  | @ -1,48 +0,0 @@ | |||
| # frozen_string_literal: true | ||||
| 
 | ||||
| module QA | ||||
|   module Page | ||||
|     module Project | ||||
|       module SubMenus | ||||
|         module Packages | ||||
|           extend QA::Page::PageConcern | ||||
| 
 | ||||
|           def go_to_package_registry | ||||
|             hover_registry do | ||||
|               within_submenu do | ||||
|                 click_element(:sidebar_menu_item_link, menu_item: 'Package Registry') | ||||
|               end | ||||
|             end | ||||
|           end | ||||
| 
 | ||||
|           def go_to_container_registry | ||||
|             hover_registry do | ||||
|               within_submenu do | ||||
|                 click_link('Container Registry') | ||||
|               end | ||||
|             end | ||||
|           end | ||||
| 
 | ||||
|           def go_to_infrastructure_registry | ||||
|             hover_registry do | ||||
|               within_submenu do | ||||
|                 click_link('Terraform modules') | ||||
|               end | ||||
|             end | ||||
|           end | ||||
| 
 | ||||
|           private | ||||
| 
 | ||||
|           def hover_registry | ||||
|             within_sidebar do | ||||
|               scroll_to_element(:sidebar_menu_link, menu_item: 'Packages and registries') | ||||
|               find_element(:sidebar_menu_link, menu_item: 'Packages and registries').hover | ||||
| 
 | ||||
|               yield | ||||
|             end | ||||
|           end | ||||
|         end | ||||
|       end | ||||
|     end | ||||
|   end | ||||
| end | ||||
|  | @ -1,29 +0,0 @@ | |||
| # frozen_string_literal: true | ||||
| 
 | ||||
| module QA | ||||
|   module Page | ||||
|     module Project | ||||
|       module SubMenus | ||||
|         module Project | ||||
|           extend QA::Page::PageConcern | ||||
| 
 | ||||
|           def self.included(base) | ||||
|             super | ||||
| 
 | ||||
|             base.class_eval do | ||||
|               include QA::Page::Project::SubMenus::Common | ||||
|             end | ||||
|           end | ||||
| 
 | ||||
|           def click_project | ||||
|             retry_on_exception do | ||||
|               within_sidebar do | ||||
|                 click_element(:sidebar_menu_link, menu_item: 'Project scope') | ||||
|               end | ||||
|             end | ||||
|           end | ||||
|         end | ||||
|       end | ||||
|     end | ||||
|   end | ||||
| end | ||||
|  | @ -1,63 +0,0 @@ | |||
| # frozen_string_literal: true | ||||
| 
 | ||||
| module QA | ||||
|   module Page | ||||
|     module Project | ||||
|       module SubMenus | ||||
|         module Repository | ||||
|           extend QA::Page::PageConcern | ||||
| 
 | ||||
|           def self.included(base) | ||||
|             super | ||||
| 
 | ||||
|             base.class_eval do | ||||
|               include QA::Page::Project::SubMenus::Common | ||||
|             end | ||||
|           end | ||||
| 
 | ||||
|           def click_repository | ||||
|             within_sidebar do | ||||
|               click_element(:sidebar_menu_link, menu_item: 'Repository') | ||||
|             end | ||||
|           end | ||||
| 
 | ||||
|           def go_to_repository_branches | ||||
|             hover_repository do | ||||
|               within_submenu do | ||||
|                 click_element(:sidebar_menu_item_link, menu_item: 'Branches') | ||||
|               end | ||||
|             end | ||||
|           end | ||||
| 
 | ||||
|           def go_to_repository_tags | ||||
|             hover_repository do | ||||
|               within_submenu do | ||||
|                 click_element(:sidebar_menu_item_link, menu_item: 'Tags') | ||||
|               end | ||||
|             end | ||||
|           end | ||||
| 
 | ||||
|           def go_to_repository_contributors | ||||
|             return go_to_contributor_statistics if Runtime::Env.super_sidebar_enabled? | ||||
| 
 | ||||
|             hover_repository do | ||||
|               within_submenu do | ||||
|                 click_element(:sidebar_menu_item_link, menu_item: 'Contributor statistics') | ||||
|               end | ||||
|             end | ||||
|           end | ||||
| 
 | ||||
|           private | ||||
| 
 | ||||
|           def hover_repository | ||||
|             within_sidebar do | ||||
|               find_element(:sidebar_menu_link, menu_item: 'Repository').hover | ||||
| 
 | ||||
|               yield | ||||
|             end | ||||
|           end | ||||
|         end | ||||
|       end | ||||
|     end | ||||
|   end | ||||
| end | ||||
|  | @ -1,102 +0,0 @@ | |||
| # frozen_string_literal: true | ||||
| 
 | ||||
| module QA | ||||
|   module Page | ||||
|     module Project | ||||
|       module SubMenus | ||||
|         module Settings | ||||
|           extend QA::Page::PageConcern | ||||
| 
 | ||||
|           def self.included(base) | ||||
|             super | ||||
| 
 | ||||
|             base.class_eval do | ||||
|               include QA::Page::Project::SubMenus::Common | ||||
|             end | ||||
|           end | ||||
| 
 | ||||
|           def go_to_ci_cd_settings | ||||
|             hover_settings do | ||||
|               within_submenu do | ||||
|                 click_element(:sidebar_menu_item_link, menu_item: 'CI/CD') | ||||
|               end | ||||
|             end | ||||
|           end | ||||
| 
 | ||||
|           def go_to_repository_settings | ||||
|             hover_settings do | ||||
|               within_submenu do | ||||
|                 click_element(:sidebar_menu_item_link, menu_item: 'Repository') | ||||
|               end | ||||
|             end | ||||
|           end | ||||
| 
 | ||||
|           def go_to_general_settings | ||||
|             hover_settings do | ||||
|               within_submenu do | ||||
|                 click_element(:sidebar_menu_item_link, menu_item: 'General') | ||||
|               end | ||||
|             end | ||||
|           end | ||||
| 
 | ||||
|           def click_settings | ||||
|             within_sidebar do | ||||
|               click_element(:sidebar_menu_link, menu_item: 'Settings') | ||||
|             end | ||||
|           end | ||||
| 
 | ||||
|           def go_to_integrations_settings | ||||
|             hover_settings do | ||||
|               within_submenu do | ||||
|                 click_element(:sidebar_menu_item_link, menu_item: 'Integrations') | ||||
|               end | ||||
|             end | ||||
|           end | ||||
| 
 | ||||
|           def go_to_monitor_settings | ||||
|             hover_settings do | ||||
|               within_submenu do | ||||
|                 click_element(:sidebar_menu_item_link, menu_item: 'Monitor') | ||||
|               end | ||||
|             end | ||||
|           end | ||||
| 
 | ||||
|           def go_to_access_token_settings | ||||
|             hover_settings do | ||||
|               within_submenu do | ||||
|                 click_element(:sidebar_menu_item_link, menu_item: 'Access Tokens') | ||||
|               end | ||||
|             end | ||||
|           end | ||||
| 
 | ||||
|           def go_to_pages_settings | ||||
|             hover_settings do | ||||
|               within_submenu do | ||||
|                 click_element(:sidebar_menu_item_link, menu_item: 'Pages') | ||||
|               end | ||||
|             end | ||||
|           end | ||||
| 
 | ||||
|           def go_to_merge_request_settings | ||||
|             hover_settings do | ||||
|               within_submenu do | ||||
|                 click_element(:sidebar_menu_item_link, menu_item: 'Merge requests') | ||||
|               end | ||||
|             end | ||||
|           end | ||||
| 
 | ||||
|           private | ||||
| 
 | ||||
|           def hover_settings | ||||
|             within_sidebar do | ||||
|               scroll_to_element(:sidebar_menu_link, menu_item: 'Settings') | ||||
|               find_element(:sidebar_menu_link, menu_item: 'Settings').hover | ||||
| 
 | ||||
|               yield | ||||
|             end | ||||
|           end | ||||
|         end | ||||
|       end | ||||
|     end | ||||
|   end | ||||
| end | ||||
|  | @ -4,12 +4,12 @@ module QA | |||
|   module Page | ||||
|     module SubMenus | ||||
|       module Common | ||||
|         prepend Mobile::Page::SubMenus::Common if QA::Runtime::Env.mobile_layout? | ||||
| 
 | ||||
|         def self.included(base) | ||||
|           super | ||||
| 
 | ||||
|           base.class_eval do | ||||
|             prepend Mobile::Page::SubMenus::Common if QA::Runtime::Env.mobile_layout? | ||||
| 
 | ||||
|             view 'app/assets/javascripts/super_sidebar/components/super_sidebar.vue' do | ||||
|               element :navbar | ||||
|             end | ||||
|  | @ -33,7 +33,7 @@ module QA | |||
|           yield | ||||
|         end | ||||
| 
 | ||||
|         # Implementation for super-sidebar, will replace within_submenu | ||||
|         # Open sidebar navigation submenu | ||||
|         # | ||||
|         # @param [String] parent_menu_name | ||||
|         # @param [String] parent_section_id | ||||
|  |  | |||
|  | @ -7,7 +7,7 @@ module QA | |||
|         module ContextSwitcher | ||||
|           extend QA::Page::PageConcern | ||||
| 
 | ||||
|           def self.prepended(base) | ||||
|           def self.included(base) | ||||
|             super | ||||
| 
 | ||||
|             base.class_eval do | ||||
|  |  | |||
|  | @ -4,10 +4,6 @@ module QA | |||
|   module Page | ||||
|     module User | ||||
|       class Show < Page::Base | ||||
|         view 'app/views/users/show.html.haml' do | ||||
|           element :following_tab | ||||
|         end | ||||
| 
 | ||||
|         view 'app/views/users/_follow_user.html.haml' do | ||||
|           element :follow_user_link | ||||
|         end | ||||
|  | @ -25,9 +21,7 @@ module QA | |||
|         end | ||||
| 
 | ||||
|         def click_following_tab | ||||
|           return click_element(:nav_item_link, submenu_item: 'Following') if Runtime::Env.super_sidebar_enabled? | ||||
| 
 | ||||
|           click_element(:following_tab) | ||||
|           click_element(:nav_item_link, submenu_item: 'Following') | ||||
|         end | ||||
| 
 | ||||
|         def click_user_link(username) | ||||
|  |  | |||
|  | @ -12,7 +12,7 @@ module QA | |||
|       def fabricate! | ||||
|         project.visit! | ||||
| 
 | ||||
|         Page::Project::Menu.perform { |sidebar| sidebar.click_snippets } | ||||
|         Page::Project::Menu.perform(&:go_to_snippets) | ||||
| 
 | ||||
|         Page::Project::Snippet::New.perform do |new_snippet| | ||||
|           new_snippet.click_create_first_snippet | ||||
|  |  | |||
|  | @ -667,10 +667,6 @@ module QA | |||
|         ENV['DEFAULT_CHROME_DOWNLOAD_PATH'] || Dir.tmpdir | ||||
|       end | ||||
| 
 | ||||
|       def super_sidebar_enabled? | ||||
|         enabled?(ENV['QA_SUPER_SIDEBAR_ENABLED'], default: true) | ||||
|       end | ||||
| 
 | ||||
|       def require_slack_env! | ||||
|         missing_env = %i[slack_workspace slack_email slack_password].select do |method| | ||||
|           ::QA::Runtime::Env.public_send(method).nil? | ||||
|  |  | |||
|  | @ -17,25 +17,31 @@ module QA | |||
|       end | ||||
| 
 | ||||
|       let(:group) do | ||||
|         create(:group, sandbox: sandbox_group, api_client: owner_api_client, path: "group-with-2fa-#{SecureRandom.hex(8)}") | ||||
|         create(:group, sandbox: sandbox_group, api_client: owner_api_client, | ||||
|           path: "group-with-2fa-#{SecureRandom.hex(8)}") | ||||
|       end | ||||
| 
 | ||||
|       let(:developer_user) { create(:user, username: "developer_user_#{SecureRandom.hex(4)}", api_client: admin_api_client) } | ||||
| 
 | ||||
|       let(:two_fa_expected_text) { /The group settings for.*require you to enable Two-Factor Authentication for your account.*You need to do this before/ } | ||||
|       let(:two_fa_expected_text) do | ||||
|         /The group settings for.*require you to enable Two-Factor Authentication for your account.*You need to do this before/ | ||||
|       end | ||||
| 
 | ||||
|       before do | ||||
|         group.add_member(developer_user, Resource::Members::AccessLevel::DEVELOPER) | ||||
|       end | ||||
| 
 | ||||
|       after do | ||||
|         group.set_require_two_factor_authentication(value: 'false') | ||||
|         group.remove_via_api! do |resource| | ||||
|           resource.api_client = admin_api_client | ||||
|         end | ||||
|         developer_user.remove_via_api! | ||||
|       end | ||||
| 
 | ||||
|       it( | ||||
|         'allows enforcing 2FA via UI and logging in with 2FA', | ||||
|         testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347931', | ||||
|         quarantine: { | ||||
|           type: :bug, | ||||
|           only: { condition: -> { QA::Runtime::Env.super_sidebar_enabled? } }, | ||||
|           issue: 'https://gitlab.com/gitlab-org/gitlab/-/issues/409336' | ||||
|         } | ||||
|         testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347931' | ||||
|       ) do | ||||
|         enforce_two_factor_authentication_on_group(group) | ||||
| 
 | ||||
|  | @ -58,21 +64,13 @@ module QA | |||
|         expect(Page::Main::Menu.perform(&:signed_in?)).to be_truthy | ||||
|       end | ||||
| 
 | ||||
|       after do | ||||
|         group.set_require_two_factor_authentication(value: 'false') | ||||
|         group.remove_via_api! do |resource| | ||||
|           resource.api_client = admin_api_client | ||||
|         end | ||||
|         developer_user.remove_via_api! | ||||
|       end | ||||
| 
 | ||||
|       # We are intentionally using the UI to enforce 2FA to exercise the flow with UI. | ||||
|       # Any future tests should use the API for this purpose. | ||||
|       def enforce_two_factor_authentication_on_group(group) | ||||
|         Flow::Login.while_signed_in(as: owner_user) do | ||||
|           group.visit! | ||||
| 
 | ||||
|           Page::Group::Menu.perform(&:click_group_general_settings_item) | ||||
|           Page::Group::Menu.perform(&:go_to_general_settings) | ||||
|           Page::Group::Settings::General.perform(&:set_require_2fa_enabled) | ||||
| 
 | ||||
|           QA::Support::Retrier.retry_on_exception(reload_page: page) do | ||||
|  |  | |||
|  | @ -18,7 +18,7 @@ module QA | |||
|       it 'is received by a user for project invitation', testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347961' do | ||||
|         project.visit! | ||||
| 
 | ||||
|         Page::Project::Menu.perform(&:click_members) | ||||
|         Page::Project::Menu.perform(&:go_to_members) | ||||
|         Page::Project::Members.perform do |member_settings| | ||||
|           member_settings.add_member(user.username) | ||||
|         end | ||||
|  |  | |||
|  | @ -22,7 +22,7 @@ module QA | |||
|           user | ||||
|           Flow::Login.sign_in_as_admin | ||||
|           project.visit! | ||||
|           Page::Project::Menu.perform(&:click_members) | ||||
|           Page::Project::Menu.perform(&:go_to_members) | ||||
|           Page::Project::Members.perform do |members| | ||||
|             members.add_member(user.username) | ||||
|           end | ||||
|  |  | |||
|  | @ -18,7 +18,7 @@ module QA | |||
|         testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347809' do | ||||
|         project.visit! | ||||
| 
 | ||||
|         Page::Project::Menu.perform(&:click_wiki) | ||||
|         Page::Project::Menu.perform(&:go_to_wiki) | ||||
|         Page::Project::Wiki::Show.perform(&:click_create_your_first_page) | ||||
| 
 | ||||
|         Page::Project::Wiki::Edit.perform do |edit| | ||||
|  |  | |||
|  | @ -142,7 +142,7 @@ module QA | |||
| 
 | ||||
|           project.group.visit! | ||||
| 
 | ||||
|           Page::Group::Menu.perform(&:go_to_group_dependency_proxy) | ||||
|           Page::Group::Menu.perform(&:go_to_dependency_proxy) | ||||
| 
 | ||||
|           Page::Group::DependencyProxy.perform do |index| | ||||
|             expect(index).to have_blob_count("Contains 1 blobs of images") | ||||
|  |  | |||
|  | @ -163,7 +163,7 @@ module QA | |||
| 
 | ||||
|           project.group.visit! | ||||
| 
 | ||||
|           Page::Group::Menu.perform(&:go_to_group_packages) | ||||
|           Page::Group::Menu.perform(&:go_to_package_registry) | ||||
| 
 | ||||
|           Page::Project::Packages::Index.perform do |index| | ||||
|             expect(index).to have_package(package.name) | ||||
|  |  | |||
|  | @ -18,7 +18,7 @@ module QA | |||
| 
 | ||||
|       it 'transfers a subgroup to another group', | ||||
|         testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347692' do | ||||
|         Page::Group::Menu.perform(&:click_group_general_settings_item) | ||||
|         Page::Group::Menu.perform(&:go_to_general_settings) | ||||
|         Page::Group::Settings::General.perform do |general| | ||||
|           general.transfer_group(sub_group_for_transfer, target_group) | ||||
| 
 | ||||
|  |  | |||
|  | @ -11,7 +11,7 @@ module QA | |||
|         project = create(:project, name: 'add-member-project') | ||||
|         project.visit! | ||||
| 
 | ||||
|         Page::Project::Menu.perform(&:click_members) | ||||
|         Page::Project::Menu.perform(&:go_to_members) | ||||
|         Page::Project::Members.perform do |members| | ||||
|           members.add_member(user.username) | ||||
|           members.search_member(user.username) | ||||
|  |  | |||
|  | @ -5,7 +5,7 @@ module QA | |||
|     describe 'Invite group', :reliable, product_group: :tenant_scale do | ||||
|       shared_examples 'invites group to project' do | ||||
|         it 'grants group and members correct access level' do | ||||
|           Page::Project::Menu.perform(&:click_members) | ||||
|           Page::Project::Menu.perform(&:go_to_members) | ||||
|           Page::Project::Members.perform do |project_members| | ||||
|             project_members.invite_group(group.path, 'Developer') | ||||
| 
 | ||||
|  |  | |||
|  | @ -15,7 +15,7 @@ module QA | |||
|           end.project | ||||
| 
 | ||||
|           project.visit! | ||||
|           Page::Project::Menu.perform(&:click_activity) | ||||
|           Page::Project::Menu.perform(&:go_to_activity) | ||||
|           Page::Project::Activity.perform do |activity| | ||||
|             activity.click_push_events | ||||
| 
 | ||||
|  |  | |||
|  | @ -24,7 +24,7 @@ module QA | |||
|           Flow::Login.while_signed_in_as_admin do | ||||
|             group.visit! | ||||
| 
 | ||||
|             Page::Group::Menu.perform(&:click_subgroup_members_item) | ||||
|             Page::Group::Menu.perform(&:go_to_members) | ||||
|             Page::Group::Members.perform do |members_page| | ||||
|               members_page.search_member(user.username) | ||||
|               members_page.remove_member(user.username) | ||||
|  |  | |||
|  | @ -116,12 +116,12 @@ module QA | |||
|         # @return [String] 'gitlab' or 'jihulab' | ||||
|         def production_domain(tld) | ||||
|           return 'gitlab' unless GitlabEdition.jh? | ||||
|           return 'gitlab' if tld == 'hk' | ||||
|           return 'gitlab' if tld == 'hk' || tld == 'cn' | ||||
|           return 'jihulab' if tld == 'com' | ||||
|         end | ||||
| 
 | ||||
|         def opts_tld | ||||
|           GitlabEdition.jh? ? '(.com|.hk)' : '(.com|.net)' | ||||
|           GitlabEdition.jh? ? '(.com|.hk|.cn)' : '(.com|.net)' | ||||
|         end | ||||
| 
 | ||||
|         def get_tld(host) | ||||
|  |  | |||
|  | @ -30,11 +30,19 @@ RSpec.describe QA::Specs::Helpers::ContextSelector do | |||
|     end | ||||
| 
 | ||||
|     it 'returns true when url has .net' do | ||||
|       allow(GitlabEdition).to receive(:jh?).and_return(false) | ||||
|       QA::Runtime::Scenario.define(:gitlab_address, "https://release.gitlab.net") | ||||
| 
 | ||||
|       expect(described_class.context_matches?).to be_truthy | ||||
|     end | ||||
| 
 | ||||
|     it 'returns true when url has .cn on jh side' do | ||||
|       allow(GitlabEdition).to receive(:jh?).and_return(true) | ||||
|       QA::Runtime::Scenario.define(:gitlab_address, "https://release.gitlab.cn") | ||||
| 
 | ||||
|       expect(described_class.context_matches?).to be_truthy | ||||
|     end | ||||
| 
 | ||||
|     it 'returns false when url does not have .com' do | ||||
|       QA::Runtime::Scenario.define(:gitlab_address, "https://gitlab.test") | ||||
| 
 | ||||
|  | @ -83,6 +91,9 @@ RSpec.describe QA::Specs::Helpers::ContextSelector do | |||
| 
 | ||||
|         QA::Runtime::Scenario.define(:gitlab_address, "https://gitlab.hk/") | ||||
|         expect(described_class.context_matches?(:production)).to be_truthy | ||||
| 
 | ||||
|         QA::Runtime::Scenario.define(:gitlab_address, "https://gitlab.cn/") | ||||
|         expect(described_class.context_matches?(:production)).to be_truthy | ||||
|       end | ||||
| 
 | ||||
|       it 'matches domain' do | ||||
|  |  | |||
|  | @ -1,39 +0,0 @@ | |||
| import Store from '~/issues/show/stores'; | ||||
| import updateDescription from '~/issues/show/utils/update_description'; | ||||
| 
 | ||||
| jest.mock('~/issues/show/utils/update_description'); | ||||
| 
 | ||||
| describe('Store', () => { | ||||
|   let store; | ||||
| 
 | ||||
|   beforeEach(() => { | ||||
|     store = new Store({ | ||||
|       descriptionHtml: '<p>This is a description</p>', | ||||
|     }); | ||||
|   }); | ||||
| 
 | ||||
|   describe('updateState', () => { | ||||
|     beforeEach(() => { | ||||
|       document.body.innerHTML = ` | ||||
|             <div class="detail-page-description content-block"> | ||||
|               <details open> | ||||
|                 <summary>One</summary> | ||||
|               </details> | ||||
|               <details> | ||||
|                 <summary>Two</summary> | ||||
|               </details> | ||||
|             </div> | ||||
|           `;
 | ||||
|     }); | ||||
| 
 | ||||
|     afterEach(() => { | ||||
|       document.getElementsByTagName('html')[0].innerHTML = ''; | ||||
|     }); | ||||
| 
 | ||||
|     it('calls updateDetailsState', () => { | ||||
|       store.updateState({ description: '' }); | ||||
| 
 | ||||
|       expect(updateDescription).toHaveBeenCalledTimes(1); | ||||
|     }); | ||||
|   }); | ||||
| }); | ||||
|  | @ -364,4 +364,17 @@ RSpec.describe Integrations::BaseChatNotification, feature_category: :integratio | |||
|       expect { subject.event_channel_value(:foo) }.to raise_error(NoMethodError) | ||||
|     end | ||||
|   end | ||||
| 
 | ||||
|   describe '#api_field_names' do | ||||
|     context 'when channels are masked' do | ||||
|       let(:project) { build(:project) } | ||||
|       let(:integration) { build(:discord_integration, project: project, webhook: 'https://discord.com/api/') } | ||||
| 
 | ||||
|       it 'does not include channel properties', :aggregate_failures do | ||||
|         integration.event_channel_names.each do |field| | ||||
|           expect(integration.api_field_names).not_to include(field) | ||||
|         end | ||||
|       end | ||||
|     end | ||||
|   end | ||||
| end | ||||
|  |  | |||
|  | @ -875,14 +875,6 @@ RSpec.describe Packages::Package, type: :model, feature_category: :package_regis | |||
|     subject { described_class.with_npm_scope('test') } | ||||
| 
 | ||||
|     it { is_expected.to contain_exactly(package1) } | ||||
| 
 | ||||
|     context 'when npm_package_registry_fix_group_path_validation is disabled' do | ||||
|       before do | ||||
|         stub_feature_flags(npm_package_registry_fix_group_path_validation: false) | ||||
|       end | ||||
| 
 | ||||
|       it { is_expected.to contain_exactly(package1) } | ||||
|     end | ||||
|   end | ||||
| 
 | ||||
|   describe '.without_nuget_temporary_name' do | ||||
|  |  | |||
|  | @ -43,9 +43,7 @@ RSpec.describe DesignManagement::SaveDesignsService, feature_category: :design_m | |||
|     design_files = files_to_upload || files | ||||
|     design_files.each(&:rewind) | ||||
| 
 | ||||
|     service = described_class.new(project, user, | ||||
|                                   issue: issue, | ||||
|                                   files: design_files) | ||||
|     service = described_class.new(project, user, issue: issue, files: design_files) | ||||
|     service.execute | ||||
|   end | ||||
| 
 | ||||
|  | @ -390,9 +388,12 @@ RSpec.describe DesignManagement::SaveDesignsService, feature_category: :design_m | |||
|         before do | ||||
|           path = File.join(build(:design, issue: issue, filename: filename).full_path) | ||||
|           design_repository.create_if_not_exists | ||||
|           design_repository.create_file(user, path, 'something fake', | ||||
|                                         branch_name: project.default_branch_or_main, | ||||
|                                         message: 'Somehow created without being tracked in db') | ||||
|           design_repository.create_file( | ||||
|             user, | ||||
|             path, 'something fake', | ||||
|             branch_name: project.default_branch_or_main, | ||||
|             message: 'Somehow created without being tracked in db' | ||||
|           ) | ||||
|         end | ||||
| 
 | ||||
|         it 'creates the design and a new version for it' do | ||||
|  |  | |||
|  | @ -94,9 +94,11 @@ RSpec.describe Discussions::ResolveService, feature_category: :code_review_workf | |||
| 
 | ||||
|     it 'raises an argument error if discussions do not belong to the same noteable' do | ||||
|       other_merge_request = create(:merge_request) | ||||
|       other_discussion = create(:diff_note_on_merge_request, | ||||
|                                 noteable: other_merge_request, | ||||
|                                 project: other_merge_request.source_project).to_discussion | ||||
|       other_discussion = create( | ||||
|         :diff_note_on_merge_request, | ||||
|         noteable: other_merge_request, | ||||
|         project: other_merge_request.source_project | ||||
|       ).to_discussion | ||||
|       expect do | ||||
|         described_class.new(project, user, one_or_more_discussions: [discussion, other_discussion]) | ||||
|       end.to raise_error( | ||||
|  |  | |||
|  | @ -292,9 +292,12 @@ RSpec.describe DraftNotes::PublishService, feature_category: :code_review_workfl | |||
|       other_user = create(:user) | ||||
|       project.add_developer(other_user) | ||||
| 
 | ||||
|       create(:draft_note, merge_request: merge_request, | ||||
|                           author: user, | ||||
|                           note: "thanks\n/assign #{other_user.to_reference}") | ||||
|       create( | ||||
|         :draft_note, | ||||
|         merge_request: merge_request, | ||||
|         author: user, | ||||
|         note: "thanks\n/assign #{other_user.to_reference}" | ||||
|       ) | ||||
| 
 | ||||
|       expect { publish }.to change { DraftNote.count }.by(-1).and change { Note.count }.by(2) | ||||
|       expect(merge_request.reload.assignees).to match_array([other_user]) | ||||
|  |  | |||
|  | @ -135,8 +135,7 @@ RSpec.describe Environments::StopService, feature_category: :continuous_delivery | |||
|       context 'when branch for stop action is protected' do | ||||
|         before do | ||||
|           project.add_developer(user) | ||||
|           create(:protected_branch, :no_one_can_push, | ||||
|                  name: 'master', project: project) | ||||
|           create(:protected_branch, :no_one_can_push, name: 'master', project: project) | ||||
|         end | ||||
| 
 | ||||
|         it 'does not stop environment' do | ||||
|  |  | |||
|  | @ -3,9 +3,9 @@ | |||
| require 'spec_helper' | ||||
| 
 | ||||
| RSpec.describe Environments::StopStaleService, | ||||
|                :clean_gitlab_redis_shared_state, | ||||
|                :sidekiq_inline, | ||||
|                feature_category: :continuous_delivery do | ||||
|   :clean_gitlab_redis_shared_state, | ||||
|   :sidekiq_inline, | ||||
|   feature_category: :continuous_delivery do | ||||
|   let_it_be(:project) { create(:project, :repository) } | ||||
|   let_it_be(:user) { create(:user) } | ||||
| 
 | ||||
|  |  | |||
|  | @ -264,18 +264,6 @@ RSpec.describe Groups::UpdateService, feature_category: :groups_and_projects do | |||
| 
 | ||||
|         it_behaves_like 'not allowing a path update' | ||||
|         it_behaves_like 'allowing an update', on: :name | ||||
| 
 | ||||
|         context 'when npm_package_registry_fix_group_path_validation is disabled' do | ||||
|           before do | ||||
|             stub_feature_flags(npm_package_registry_fix_group_path_validation: false) | ||||
|             expect_next_instance_of(::Groups::UpdateService) do |service| | ||||
|               expect(service).to receive(:valid_path_change_with_npm_packages?).and_call_original | ||||
|             end | ||||
|           end | ||||
| 
 | ||||
|           it_behaves_like 'not allowing a path update' | ||||
|           it_behaves_like 'allowing an update', on: :name | ||||
|         end | ||||
|       end | ||||
| 
 | ||||
|       context 'updating the subgroup' do | ||||
|  | @ -283,18 +271,6 @@ RSpec.describe Groups::UpdateService, feature_category: :groups_and_projects do | |||
| 
 | ||||
|         it_behaves_like 'allowing an update', on: :path | ||||
|         it_behaves_like 'allowing an update', on: :name | ||||
| 
 | ||||
|         context 'when npm_package_registry_fix_group_path_validation is disabled' do | ||||
|           before do | ||||
|             stub_feature_flags(npm_package_registry_fix_group_path_validation: false) | ||||
|             expect_next_instance_of(::Groups::UpdateService) do |service| | ||||
|               expect(service).to receive(:valid_path_change_with_npm_packages?).and_call_original | ||||
|             end | ||||
|           end | ||||
| 
 | ||||
|           it_behaves_like 'not allowing a path update' | ||||
|           it_behaves_like 'allowing an update', on: :name | ||||
|         end | ||||
|       end | ||||
|     end | ||||
| 
 | ||||
|  | @ -306,18 +282,6 @@ RSpec.describe Groups::UpdateService, feature_category: :groups_and_projects do | |||
| 
 | ||||
|         it_behaves_like 'allowing an update', on: :path | ||||
|         it_behaves_like 'allowing an update', on: :name | ||||
| 
 | ||||
|         context 'when npm_package_registry_fix_group_path_validation is disabled' do | ||||
|           before do | ||||
|             stub_feature_flags(npm_package_registry_fix_group_path_validation: false) | ||||
|             expect_next_instance_of(::Groups::UpdateService) do |service| | ||||
|               expect(service).to receive(:valid_path_change_with_npm_packages?).and_call_original | ||||
|             end | ||||
|           end | ||||
| 
 | ||||
|           it_behaves_like 'not allowing a path update' | ||||
|           it_behaves_like 'allowing an update', on: :name | ||||
|         end | ||||
|       end | ||||
| 
 | ||||
|       context 'updating the subgroup' do | ||||
|  | @ -325,18 +289,6 @@ RSpec.describe Groups::UpdateService, feature_category: :groups_and_projects do | |||
| 
 | ||||
|         it_behaves_like 'allowing an update', on: :path | ||||
|         it_behaves_like 'allowing an update', on: :name | ||||
| 
 | ||||
|         context 'when npm_package_registry_fix_group_path_validation is disabled' do | ||||
|           before do | ||||
|             stub_feature_flags(npm_package_registry_fix_group_path_validation: false) | ||||
|             expect_next_instance_of(::Groups::UpdateService) do |service| | ||||
|               expect(service).to receive(:valid_path_change_with_npm_packages?).and_call_original | ||||
|             end | ||||
|           end | ||||
| 
 | ||||
|           it_behaves_like 'not allowing a path update' | ||||
|           it_behaves_like 'allowing an update', on: :name | ||||
|         end | ||||
|       end | ||||
|     end | ||||
| 
 | ||||
|  | @ -348,18 +300,6 @@ RSpec.describe Groups::UpdateService, feature_category: :groups_and_projects do | |||
| 
 | ||||
|         it_behaves_like 'allowing an update', on: :path | ||||
|         it_behaves_like 'allowing an update', on: :name | ||||
| 
 | ||||
|         context 'when npm_package_registry_fix_group_path_validation is disabled' do | ||||
|           before do | ||||
|             stub_feature_flags(npm_package_registry_fix_group_path_validation: false) | ||||
|             expect_next_instance_of(::Groups::UpdateService) do |service| | ||||
|               expect(service).to receive(:valid_path_change_with_npm_packages?).and_call_original | ||||
|             end | ||||
|           end | ||||
| 
 | ||||
|           it_behaves_like 'not allowing a path update' | ||||
|           it_behaves_like 'allowing an update', on: :name | ||||
|         end | ||||
|       end | ||||
| 
 | ||||
|       context 'updating the subgroup' do | ||||
|  | @ -367,18 +307,6 @@ RSpec.describe Groups::UpdateService, feature_category: :groups_and_projects do | |||
| 
 | ||||
|         it_behaves_like 'allowing an update', on: :path | ||||
|         it_behaves_like 'allowing an update', on: :name | ||||
| 
 | ||||
|         context 'when npm_package_registry_fix_group_path_validation is disabled' do | ||||
|           before do | ||||
|             stub_feature_flags(npm_package_registry_fix_group_path_validation: false) | ||||
|             expect_next_instance_of(::Groups::UpdateService) do |service| | ||||
|               expect(service).to receive(:valid_path_change_with_npm_packages?).and_call_original | ||||
|             end | ||||
|           end | ||||
| 
 | ||||
|           it_behaves_like 'not allowing a path update' | ||||
|           it_behaves_like 'allowing an update', on: :name | ||||
|         end | ||||
|       end | ||||
|     end | ||||
|   end | ||||
|  |  | |||
|  | @ -88,10 +88,11 @@ RSpec.describe LooseForeignKeys::BatchCleanerService, feature_category: :databas | |||
|       expect(loose_fk_child_table_1.count).to eq(4) | ||||
|       expect(loose_fk_child_table_2.count).to eq(4) | ||||
| 
 | ||||
|       described_class.new(parent_table: '_test_loose_fk_parent_table', | ||||
|                           loose_foreign_key_definitions: loose_foreign_key_definitions, | ||||
|                           deleted_parent_records: LooseForeignKeys::DeletedRecord.load_batch_for_table('public._test_loose_fk_parent_table', 100) | ||||
|                          ).execute | ||||
|       described_class.new( | ||||
|         parent_table: '_test_loose_fk_parent_table', | ||||
|         loose_foreign_key_definitions: loose_foreign_key_definitions, | ||||
|         deleted_parent_records: LooseForeignKeys::DeletedRecord.load_batch_for_table('public._test_loose_fk_parent_table', 100) | ||||
|       ).execute | ||||
|     end | ||||
| 
 | ||||
|     it 'cleans up the child records' do | ||||
|  | @ -125,11 +126,12 @@ RSpec.describe LooseForeignKeys::BatchCleanerService, feature_category: :databas | |||
|       let(:deleted_records_incremented_counter) { Gitlab::Metrics.registry.get(:loose_foreign_key_incremented_deleted_records) } | ||||
| 
 | ||||
|       let(:cleaner) do | ||||
|         described_class.new(parent_table: '_test_loose_fk_parent_table', | ||||
|                             loose_foreign_key_definitions: loose_foreign_key_definitions, | ||||
|                             deleted_parent_records: LooseForeignKeys::DeletedRecord.load_batch_for_table('public._test_loose_fk_parent_table', 100), | ||||
|                             modification_tracker: modification_tracker | ||||
|                            ) | ||||
|         described_class.new( | ||||
|           parent_table: '_test_loose_fk_parent_table', | ||||
|           loose_foreign_key_definitions: loose_foreign_key_definitions, | ||||
|           deleted_parent_records: LooseForeignKeys::DeletedRecord.load_batch_for_table('public._test_loose_fk_parent_table', 100), | ||||
|           modification_tracker: modification_tracker | ||||
|         ) | ||||
|       end | ||||
| 
 | ||||
|       before do | ||||
|  |  | |||
|  | @ -24,8 +24,13 @@ RSpec.describe NoteSummary, feature_category: :code_review_workflow do | |||
|   describe '#note' do | ||||
|     it 'returns note hash' do | ||||
|       freeze_time do | ||||
|         expect(create_note_summary.note).to eq(noteable: noteable, project: project, author: user, note: 'note', | ||||
|                                                created_at: Time.current) | ||||
|         expect(create_note_summary.note).to eq( | ||||
|           noteable: noteable, | ||||
|           project: project, | ||||
|           author: user, | ||||
|           note: 'note', | ||||
|           created_at: Time.current | ||||
|         ) | ||||
|       end | ||||
|     end | ||||
| 
 | ||||
|  |  | |||
|  | @ -1219,9 +1219,11 @@ RSpec.describe NotificationService, :mailer, feature_category: :team_planning do | |||
|       let_it_be(:member_and_not_mentioned) { create(:user, developer_projects: [project]) } | ||||
|       let_it_be(:non_member_and_mentioned) { create(:user) } | ||||
|       let_it_be(:note) do | ||||
|         create(:diff_note_on_design, | ||||
|            noteable: design, | ||||
|            note: "Hello #{member_and_mentioned.to_reference}, G'day #{non_member_and_mentioned.to_reference}") | ||||
|         create( | ||||
|           :diff_note_on_design, | ||||
|           noteable: design, | ||||
|           note: "Hello #{member_and_mentioned.to_reference}, G'day #{non_member_and_mentioned.to_reference}" | ||||
|         ) | ||||
|       end | ||||
| 
 | ||||
|       let_it_be(:note_2) do | ||||
|  | @ -3506,12 +3508,14 @@ RSpec.describe NotificationService, :mailer, feature_category: :team_planning do | |||
|       let(:commit) { project.commit } | ||||
| 
 | ||||
|       def create_pipeline(user, status) | ||||
|         create(:ci_pipeline, status, | ||||
|                project: project, | ||||
|                user: user, | ||||
|                ref: 'refs/heads/master', | ||||
|                sha: commit.id, | ||||
|                before_sha: '00000000') | ||||
|         create( | ||||
|           :ci_pipeline, status, | ||||
|           project: project, | ||||
|           user: user, | ||||
|           ref: 'refs/heads/master', | ||||
|           sha: commit.id, | ||||
|           before_sha: '00000000' | ||||
|         ) | ||||
|       end | ||||
| 
 | ||||
|       before_all do | ||||
|  | @ -4020,12 +4024,14 @@ RSpec.describe NotificationService, :mailer, feature_category: :team_planning do | |||
|       project.add_maintainer(reviewer) | ||||
|       merge_request.assignees.each { |assignee| project.add_maintainer(assignee) } | ||||
| 
 | ||||
|       create(:diff_note_on_merge_request, | ||||
|              project: project, | ||||
|              noteable: merge_request, | ||||
|              author: reviewer, | ||||
|              review: review, | ||||
|              note: "cc @mention") | ||||
|       create( | ||||
|         :diff_note_on_merge_request, | ||||
|         project: project, | ||||
|         noteable: merge_request, | ||||
|         author: reviewer, | ||||
|         review: review, | ||||
|         note: "cc @mention" | ||||
|       ) | ||||
|     end | ||||
| 
 | ||||
|     it 'sends emails' do | ||||
|  | @ -4067,10 +4073,18 @@ RSpec.describe NotificationService, :mailer, feature_category: :team_planning do | |||
|     subject { notification.inactive_project_deletion_warning(project, deletion_date) } | ||||
| 
 | ||||
|     it "sends email to project owners and maintainers" do | ||||
|       expect { subject }.to have_enqueued_email(project, maintainer, deletion_date, | ||||
|                                                 mail: "inactive_project_deletion_warning_email") | ||||
|       expect { subject }.not_to have_enqueued_email(project, developer, deletion_date, | ||||
|                                                     mail: "inactive_project_deletion_warning_email") | ||||
|       expect { subject }.to have_enqueued_email( | ||||
|         project, | ||||
|         maintainer, | ||||
|         deletion_date, | ||||
|         mail: "inactive_project_deletion_warning_email" | ||||
|       ) | ||||
|       expect { subject }.not_to have_enqueued_email( | ||||
|         project, | ||||
|         developer, | ||||
|         deletion_date, | ||||
|         mail: "inactive_project_deletion_warning_email" | ||||
|       ) | ||||
|     end | ||||
|   end | ||||
| 
 | ||||
|  |  | |||
|  | @ -21,9 +21,9 @@ RSpec.describe Pages::MigrateLegacyStorageToDeploymentService, feature_category: | |||
|       project.mark_pages_as_deployed | ||||
|       expect(project.pages_metadatum.reload.deployed).to eq(true) | ||||
| 
 | ||||
|       expect(service.execute).to( | ||||
|         eq(status: :success, | ||||
|            message: "Archive not created. Missing public directory in #{project.pages_path}? Marked project as not deployed") | ||||
|       expect(service.execute).to eq( | ||||
|         status: :success, | ||||
|         message: "Archive not created. Missing public directory in #{project.pages_path}? Marked project as not deployed" | ||||
|       ) | ||||
| 
 | ||||
|       expect(project.pages_metadatum.reload.deployed).to eq(false) | ||||
|  | @ -35,9 +35,9 @@ RSpec.describe Pages::MigrateLegacyStorageToDeploymentService, feature_category: | |||
|       project.mark_pages_as_deployed | ||||
|       expect(project.pages_metadatum.reload.deployed).to eq(true) | ||||
| 
 | ||||
|       expect(service.execute).to( | ||||
|         eq(status: :success, | ||||
|            message: "Archive not created. Missing public directory in #{project.pages_path}? Marked project as not deployed") | ||||
|       expect(service.execute).to eq( | ||||
|         status: :success, | ||||
|         message: "Archive not created. Missing public directory in #{project.pages_path}? Marked project as not deployed" | ||||
|       ) | ||||
| 
 | ||||
|       expect(project.pages_metadatum.reload.deployed).to eq(true) | ||||
|  | @ -49,9 +49,9 @@ RSpec.describe Pages::MigrateLegacyStorageToDeploymentService, feature_category: | |||
| 
 | ||||
|     expect(project.pages_metadatum.reload.deployed).to eq(true) | ||||
| 
 | ||||
|     expect(service.execute).to( | ||||
|       eq(status: :error, | ||||
|          message: "Archive not created. Missing public directory in #{project.pages_path}") | ||||
|     expect(service.execute).to eq( | ||||
|       status: :error, | ||||
|       message: "Archive not created. Missing public directory in #{project.pages_path}" | ||||
|     ) | ||||
| 
 | ||||
|     expect(project.pages_metadatum.reload.deployed).to eq(true) | ||||
|  | @ -60,9 +60,9 @@ RSpec.describe Pages::MigrateLegacyStorageToDeploymentService, feature_category: | |||
|   it 'removes pages archive when can not save deployment' do | ||||
|     archive = fixture_file_upload("spec/fixtures/pages.zip") | ||||
|     expect_next_instance_of(::Pages::ZipDirectoryService) do |zip_service| | ||||
|       expect(zip_service).to receive(:execute).and_return(status: :success, | ||||
|                                                           archive_path: archive.path, | ||||
|                                                           entries_count: 3) | ||||
|       expect(zip_service).to receive(:execute).and_return( | ||||
|         status: :success, archive_path: archive.path, entries_count: 3 | ||||
|       ) | ||||
|     end | ||||
| 
 | ||||
|     expect_next_instance_of(PagesDeployment) do |deployment| | ||||
|  |  | |||
|  | @ -132,8 +132,7 @@ RSpec.describe PagesDomains::ObtainLetsEncryptCertificateService, feature_catego | |||
|         ef.create_extension("basicConstraints", "CA:TRUE", true), | ||||
|         ef.create_extension("subjectKeyIdentifier", "hash") | ||||
|       ] | ||||
|       cert.add_extension ef.create_extension("authorityKeyIdentifier", | ||||
|                                              "keyid:always,issuer:always") | ||||
|       cert.add_extension ef.create_extension("authorityKeyIdentifier", "keyid:always,issuer:always") | ||||
| 
 | ||||
|       cert.sign key, OpenSSL::Digest.new('SHA256') | ||||
| 
 | ||||
|  |  | |||
|  | @ -28,9 +28,11 @@ RSpec.describe PreviewMarkdownService, feature_category: :team_planning do | |||
| 
 | ||||
|     let(:text) { "```suggestion\nfoo\n```" } | ||||
|     let(:params) do | ||||
|       suggestion_params.merge(text: text, | ||||
|                               target_type: 'MergeRequest', | ||||
|                               target_id: merge_request.iid) | ||||
|       suggestion_params.merge( | ||||
|         text: text, | ||||
|         target_type: 'MergeRequest', | ||||
|         target_id: merge_request.iid | ||||
|       ) | ||||
|     end | ||||
| 
 | ||||
|     let(:service) { described_class.new(project, user, params) } | ||||
|  | @ -52,15 +54,16 @@ RSpec.describe PreviewMarkdownService, feature_category: :team_planning do | |||
|       end | ||||
| 
 | ||||
|       it 'returns suggestions referenced in text' do | ||||
|         position = Gitlab::Diff::Position.new(new_path: path, | ||||
|                                               new_line: line, | ||||
|                                               diff_refs: diff_refs) | ||||
|         position = Gitlab::Diff::Position.new(new_path: path, new_line: line, diff_refs: diff_refs) | ||||
| 
 | ||||
|         expect(Gitlab::Diff::SuggestionsParser) | ||||
|           .to receive(:parse) | ||||
|           .with(text, position: position, | ||||
|                       project: merge_request.project, | ||||
|                       supports_suggestion: true) | ||||
|           .with( | ||||
|             text, | ||||
|             position: position, | ||||
|             project: merge_request.project, | ||||
|             supports_suggestion: true | ||||
|           ) | ||||
|           .and_call_original | ||||
| 
 | ||||
|         result = service.execute | ||||
|  |  | |||
|  | @ -6,10 +6,12 @@ RSpec.describe ProtectedBranches::ApiService, feature_category: :compliance_mana | |||
|   shared_examples 'execute with entity' do | ||||
|     it 'creates a protected branch with prefilled defaults' do | ||||
|       expect(::ProtectedBranches::CreateService).to receive(:new).with( | ||||
|         entity, user, hash_including( | ||||
|                         push_access_levels_attributes: [{ access_level: Gitlab::Access::MAINTAINER }], | ||||
|                         merge_access_levels_attributes: [{ access_level: Gitlab::Access::MAINTAINER }] | ||||
|                       ) | ||||
|         entity, | ||||
|         user, | ||||
|         hash_including( | ||||
|           push_access_levels_attributes: [{ access_level: Gitlab::Access::MAINTAINER }], | ||||
|           merge_access_levels_attributes: [{ access_level: Gitlab::Access::MAINTAINER }] | ||||
|         ) | ||||
|       ).and_call_original | ||||
| 
 | ||||
|       expect(described_class.new(entity, user, { name: 'new name' }).create).to be_valid | ||||
|  | @ -17,10 +19,12 @@ RSpec.describe ProtectedBranches::ApiService, feature_category: :compliance_mana | |||
| 
 | ||||
|     it 'updates a protected branch without prefilled defaults' do | ||||
|       expect(::ProtectedBranches::UpdateService).to receive(:new).with( | ||||
|         entity, user, hash_including( | ||||
|                         push_access_levels_attributes: [], | ||||
|                         merge_access_levels_attributes: [] | ||||
|                       ) | ||||
|         entity, | ||||
|         user, | ||||
|         hash_including( | ||||
|           push_access_levels_attributes: [], | ||||
|           merge_access_levels_attributes: [] | ||||
|         ) | ||||
|       ).and_call_original | ||||
| 
 | ||||
|       expect do | ||||
|  |  | |||
|  | @ -190,9 +190,7 @@ RSpec.describe PushEventPayloadService, feature_category: :source_code_managemen | |||
|     end | ||||
| 
 | ||||
|     it 'returns :removed when removing an existing ref' do | ||||
|       service = described_class.new(event, | ||||
|                                     before: '123', | ||||
|                                     after: Gitlab::Git::BLANK_SHA) | ||||
|       service = described_class.new(event, before: '123', after: Gitlab::Git::BLANK_SHA) | ||||
| 
 | ||||
|       expect(service.action).to eq(:removed) | ||||
|     end | ||||
|  |  | |||
|  | @ -29,9 +29,11 @@ RSpec.describe QuickActions::InterpretService, feature_category: :team_planning | |||
|   end | ||||
| 
 | ||||
|   before do | ||||
|     stub_licensed_features(multiple_issue_assignees: false, | ||||
|                            multiple_merge_request_reviewers: false, | ||||
|                            multiple_merge_request_assignees: false) | ||||
|     stub_licensed_features( | ||||
|       multiple_issue_assignees: false, | ||||
|       multiple_merge_request_reviewers: false, | ||||
|       multiple_merge_request_assignees: false | ||||
|     ) | ||||
|   end | ||||
| 
 | ||||
|   describe '#execute' do | ||||
|  |  | |||
|  | @ -42,9 +42,7 @@ RSpec.describe Releases::DestroyService, feature_category: :release_orchestratio | |||
|       let!(:release) {} | ||||
| 
 | ||||
|       it 'returns an error' do | ||||
|         is_expected.to include(status: :error, | ||||
|                                message: 'Release does not exist', | ||||
|                                http_status: 404) | ||||
|         is_expected.to include(status: :error, message: 'Release does not exist', http_status: 404) | ||||
|       end | ||||
|     end | ||||
| 
 | ||||
|  | @ -52,9 +50,7 @@ RSpec.describe Releases::DestroyService, feature_category: :release_orchestratio | |||
|       let(:user) { repoter } | ||||
| 
 | ||||
|       it 'returns an error' do | ||||
|         is_expected.to include(status: :error, | ||||
|                                message: 'Access Denied', | ||||
|                                http_status: 403) | ||||
|         is_expected.to include(status: :error, message: 'Access Denied', http_status: 403) | ||||
|       end | ||||
|     end | ||||
| 
 | ||||
|  |  | |||
|  | @ -33,8 +33,7 @@ RSpec.describe ResourceAccessTokens::RevokeService, feature_category: :system_ac | |||
|         subject | ||||
| 
 | ||||
|         expect( | ||||
|           Users::GhostUserMigration.where(user: resource_bot, | ||||
|                                           initiator_user: user) | ||||
|           Users::GhostUserMigration.where(user: resource_bot, initiator_user: user) | ||||
|         ).to be_exists | ||||
|       end | ||||
| 
 | ||||
|  |  | |||
|  | @ -61,8 +61,7 @@ RSpec.describe ResourceEvents::MergeIntoNotesService, feature_category: :team_pl | |||
|       create_event(created_at: 4.days.ago) | ||||
|       event = create_event(created_at: 1.day.ago) | ||||
| 
 | ||||
|       notes = described_class.new(resource, user, | ||||
|                                   last_fetched_at: 2.days.ago).execute | ||||
|       notes = described_class.new(resource, user, last_fetched_at: 2.days.ago).execute | ||||
| 
 | ||||
|       expect(notes.count).to eq 1 | ||||
|       expect(notes.first.discussion_id).to eq event.reload.discussion_id | ||||
|  |  | |||
|  | @ -3,7 +3,7 @@ | |||
| require 'spec_helper' | ||||
| 
 | ||||
| RSpec.describe Security::CiConfiguration::DependencyScanningCreateService, :snowplow, | ||||
|                feature_category: :software_composition_analysis do | ||||
|   feature_category: :software_composition_analysis do | ||||
|   subject(:result) { described_class.new(project, user).execute } | ||||
| 
 | ||||
|   let(:branch_name) { 'set-dependency-scanning-config-1' } | ||||
|  |  | |||
|  | @ -16,78 +16,87 @@ RSpec.describe Security::MergeReportsService, '#execute', feature_category: :cod | |||
|   let(:identifier_wasc) { build(:ci_reports_security_identifier, external_id: '13', external_type: 'wasc') } | ||||
| 
 | ||||
|   let(:finding_id_1) do | ||||
|     build(:ci_reports_security_finding, | ||||
|           identifiers: [identifier_1_primary, identifier_1_cve], | ||||
|           scanner: scanner_1, | ||||
|           severity: :low | ||||
|          ) | ||||
|     build( | ||||
|       :ci_reports_security_finding, | ||||
|       identifiers: [identifier_1_primary, identifier_1_cve], | ||||
|       scanner: scanner_1, | ||||
|       severity: :low | ||||
|     ) | ||||
|   end | ||||
| 
 | ||||
|   let(:finding_id_1_extra) do | ||||
|     build(:ci_reports_security_finding, | ||||
|           identifiers: [identifier_1_primary, identifier_1_cve], | ||||
|           scanner: scanner_1, | ||||
|           severity: :low | ||||
|          ) | ||||
|     build( | ||||
|       :ci_reports_security_finding, | ||||
|       identifiers: [identifier_1_primary, identifier_1_cve], | ||||
|       scanner: scanner_1, | ||||
|       severity: :low | ||||
|     ) | ||||
|   end | ||||
| 
 | ||||
|   let(:finding_id_2_loc_1) do | ||||
|     build(:ci_reports_security_finding, | ||||
|           identifiers: [identifier_2_primary, identifier_2_cve], | ||||
|           location: build(:ci_reports_security_locations_sast, start_line: 32, end_line: 34), | ||||
|           scanner: scanner_2, | ||||
|           severity: :medium | ||||
|          ) | ||||
|     build( | ||||
|       :ci_reports_security_finding, | ||||
|       identifiers: [identifier_2_primary, identifier_2_cve], | ||||
|       location: build(:ci_reports_security_locations_sast, start_line: 32, end_line: 34), | ||||
|       scanner: scanner_2, | ||||
|       severity: :medium | ||||
|     ) | ||||
|   end | ||||
| 
 | ||||
|   let(:finding_id_2_loc_1_extra) do | ||||
|     build(:ci_reports_security_finding, | ||||
|           identifiers: [identifier_2_primary, identifier_2_cve], | ||||
|           location: build(:ci_reports_security_locations_sast, start_line: 32, end_line: 34), | ||||
|           scanner: scanner_2, | ||||
|           severity: :medium | ||||
|          ) | ||||
|     build( | ||||
|       :ci_reports_security_finding, | ||||
|       identifiers: [identifier_2_primary, identifier_2_cve], | ||||
|       location: build(:ci_reports_security_locations_sast, start_line: 32, end_line: 34), | ||||
|       scanner: scanner_2, | ||||
|       severity: :medium | ||||
|     ) | ||||
|   end | ||||
| 
 | ||||
|   let(:finding_id_2_loc_2) do | ||||
|     build(:ci_reports_security_finding, | ||||
|           identifiers: [identifier_2_primary, identifier_2_cve], | ||||
|           location: build(:ci_reports_security_locations_sast, start_line: 42, end_line: 44), | ||||
|           scanner: scanner_2, | ||||
|           severity: :medium | ||||
|          ) | ||||
|     build( | ||||
|       :ci_reports_security_finding, | ||||
|       identifiers: [identifier_2_primary, identifier_2_cve], | ||||
|       location: build(:ci_reports_security_locations_sast, start_line: 42, end_line: 44), | ||||
|       scanner: scanner_2, | ||||
|       severity: :medium | ||||
|     ) | ||||
|   end | ||||
| 
 | ||||
|   let(:finding_cwe_1) do | ||||
|     build(:ci_reports_security_finding, | ||||
|           identifiers: [identifier_cwe], | ||||
|           scanner: scanner_3, | ||||
|           severity: :high | ||||
|          ) | ||||
|     build( | ||||
|       :ci_reports_security_finding, | ||||
|       identifiers: [identifier_cwe], | ||||
|       scanner: scanner_3, | ||||
|       severity: :high | ||||
|     ) | ||||
|   end | ||||
| 
 | ||||
|   let(:finding_cwe_2) do | ||||
|     build(:ci_reports_security_finding, | ||||
|           identifiers: [identifier_cwe], | ||||
|           scanner: scanner_1, | ||||
|           severity: :critical | ||||
|          ) | ||||
|     build( | ||||
|       :ci_reports_security_finding, | ||||
|       identifiers: [identifier_cwe], | ||||
|       scanner: scanner_1, | ||||
|       severity: :critical | ||||
|     ) | ||||
|   end | ||||
| 
 | ||||
|   let(:finding_wasc_1) do | ||||
|     build(:ci_reports_security_finding, | ||||
|           identifiers: [identifier_wasc], | ||||
|           scanner: scanner_1, | ||||
|           severity: :medium | ||||
|          ) | ||||
|     build( | ||||
|       :ci_reports_security_finding, | ||||
|       identifiers: [identifier_wasc], | ||||
|       scanner: scanner_1, | ||||
|       severity: :medium | ||||
|     ) | ||||
|   end | ||||
| 
 | ||||
|   let(:finding_wasc_2) do | ||||
|     build(:ci_reports_security_finding, | ||||
|           identifiers: [identifier_wasc], | ||||
|           scanner: scanner_2, | ||||
|           severity: :critical | ||||
|          ) | ||||
|     build( | ||||
|       :ci_reports_security_finding, | ||||
|       identifiers: [identifier_wasc], | ||||
|       scanner: scanner_2, | ||||
|       severity: :critical | ||||
|     ) | ||||
|   end | ||||
| 
 | ||||
|   let(:report_1_findings) { [finding_id_1, finding_id_2_loc_1, finding_id_2_loc_1_extra, finding_cwe_2, finding_wasc_1] } | ||||
|  |  | |||
|  | @ -4,39 +4,54 @@ require 'spec_helper' | |||
| 
 | ||||
| RSpec.describe Environments::StopJobSuccessWorker, feature_category: :continuous_delivery do | ||||
|   describe '#perform' do | ||||
|     subject { described_class.new.perform(build.id) } | ||||
|     let_it_be_with_refind(:environment) { create(:environment, state: :available) } | ||||
| 
 | ||||
|     context 'when build exists' do | ||||
|       context 'when the build will stop an environment' do | ||||
|         let!(:build) { create(:ci_build, :stop_review_app, environment: environment.name, project: environment.project, status: :success) } # rubocop:disable Layout/LineLength | ||||
|         let(:environment) { create(:environment, state: :available) } | ||||
|     subject { described_class.new.perform(job.id) } | ||||
| 
 | ||||
|         it 'stops the environment' do | ||||
|     shared_examples_for 'stopping an associated environment' do | ||||
|       it 'stops the environment' do | ||||
|         expect(environment).to be_available | ||||
| 
 | ||||
|         subject | ||||
| 
 | ||||
|         expect(environment.reload).to be_stopped | ||||
|       end | ||||
| 
 | ||||
|       context 'when the job fails' do | ||||
|         before do | ||||
|           job.update!(status: :failed) | ||||
|           environment.update!(state: :available) | ||||
|         end | ||||
| 
 | ||||
|         it 'does not stop the environment' do | ||||
|           expect(environment).to be_available | ||||
| 
 | ||||
|           subject | ||||
| 
 | ||||
|           expect(environment.reload).to be_stopped | ||||
|         end | ||||
| 
 | ||||
|         context 'when the build fails' do | ||||
|           before do | ||||
|             build.update!(status: :failed) | ||||
|             environment.update!(state: :available) | ||||
|           end | ||||
| 
 | ||||
|           it 'does not stop the environment' do | ||||
|             expect(environment).to be_available | ||||
| 
 | ||||
|             subject | ||||
| 
 | ||||
|             expect(environment.reload).not_to be_stopped | ||||
|           end | ||||
|           expect(environment.reload).not_to be_stopped | ||||
|         end | ||||
|       end | ||||
|     end | ||||
| 
 | ||||
|     context 'when build does not exist' do | ||||
|     context 'with build job' do | ||||
|       let!(:job) do | ||||
|         create(:ci_build, :stop_review_app, environment: environment.name, project: environment.project, | ||||
|           status: :success) | ||||
|       end | ||||
| 
 | ||||
|       it_behaves_like 'stopping an associated environment' | ||||
|     end | ||||
| 
 | ||||
|     context 'with bridge job' do | ||||
|       let!(:job) do | ||||
|         create(:ci_bridge, :stop_review_app, environment: environment.name, project: environment.project, | ||||
|           status: :success) | ||||
|       end | ||||
| 
 | ||||
|       it_behaves_like 'stopping an associated environment' | ||||
|     end | ||||
| 
 | ||||
|     context 'when job does not exist' do | ||||
|       it 'does not raise exception' do | ||||
|         expect { described_class.new.perform(123) } | ||||
|           .not_to raise_error | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue