Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2023-08-24 00:08:46 +00:00
parent e79ee0307d
commit a85d15fdb3
75 changed files with 441 additions and 1346 deletions

View File

@ -1879,26 +1879,6 @@ Layout/ArgumentAlignment:
- 'spec/rubocop/cop/rspec/env_mocking_spec.rb' - 'spec/rubocop/cop/rspec/env_mocking_spec.rb'
- 'spec/rubocop/cop/style/regexp_literal_mixed_preserve_spec.rb' - 'spec/rubocop/cop/style/regexp_literal_mixed_preserve_spec.rb'
- 'spec/rubocop/formatter/graceful_formatter_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/sidekiq/cron/job_gem_dependency_spec.rb'
- 'spec/support/shared_examples/initializers/uses_gitlab_url_blocker_shared_examples.rb' - 'spec/support/shared_examples/initializers/uses_gitlab_url_blocker_shared_examples.rb'
- 'spec/support/shared_examples/integrations/integration_settings_form.rb' - 'spec/support/shared_examples/integrations/integration_settings_form.rb'

View File

@ -10,16 +10,18 @@ import {
TYPE_ISSUE, TYPE_ISSUE,
WORKSPACE_PROJECT, WORKSPACE_PROJECT,
} from '~/issues/constants'; } 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 Poll from '~/lib/utils/poll';
import { containsSensitiveToken, confirmSensitiveAction, i18n } from '~/lib/utils/secret_detection';
import { visitUrl } from '~/lib/utils/url_utility'; import { visitUrl } from '~/lib/utils/url_utility';
import { __, sprintf } from '~/locale'; import { __, sprintf } from '~/locale';
import ConfidentialityBadge from '~/vue_shared/components/confidentiality_badge.vue'; 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 { ISSUE_TYPE_PATH, INCIDENT_TYPE_PATH, POLLING_DELAY } from '../constants';
import eventHub from '../event_hub'; import eventHub from '../event_hub';
import getIssueStateQuery from '../queries/get_issue_state.query.graphql'; import getIssueStateQuery from '../queries/get_issue_state.query.graphql';
import Service from '../services/index'; import Service from '../services/index';
import Store from '../stores';
import DescriptionComponent from './description.vue'; import DescriptionComponent from './description.vue';
import EditedComponent from './edited.vue'; import EditedComponent from './edited.vue';
import FormComponent from './form.vue'; import FormComponent from './form.vue';
@ -234,7 +236,16 @@ export default {
}, },
}, },
data() { data() {
const store = new Store({ return {
formState: {
title: '',
description: '',
lockedWarningVisible: false,
updateLoading: false,
lock_version: 0,
issuableTemplates: {},
},
state: {
titleHtml: this.initialTitleHtml, titleHtml: this.initialTitleHtml,
titleText: this.initialTitleText, titleText: this.initialTitleText,
descriptionHtml: this.initialDescriptionHtml, descriptionHtml: this.initialDescriptionHtml,
@ -244,11 +255,7 @@ export default {
updatedByPath: this.updatedByPath, updatedByPath: this.updatedByPath,
taskCompletionStatus: this.initialTaskCompletionStatus, taskCompletionStatus: this.initialTaskCompletionStatus,
lock_version: this.lockVersion, lock_version: this.lockVersion,
}); },
return {
store,
state: store.state,
showForm: false, showForm: false,
templatesRequested: false, templatesRequested: false,
isStickyHeaderShowing: false, isStickyHeaderShowing: false,
@ -264,17 +271,9 @@ export default {
headerClasses() { headerClasses() {
return this.issuableType === TYPE_INCIDENT ? 'gl-mb-3' : 'gl-mb-6'; return this.issuableType === TYPE_INCIDENT ? 'gl-mb-3' : 'gl-mb-6';
}, },
issuableTemplates() {
return this.store.formState.issuableTemplates;
},
formState() {
return this.store.formState;
},
issueChanged() { issueChanged() {
const { const {
store: {
formState: { description, title }, formState: { description, title },
},
initialDescriptionText, initialDescriptionText,
initialTitleText, initialTitleText,
} = this; } = this;
@ -322,7 +321,7 @@ export default {
this.poll = new Poll({ this.poll = new Poll({
resource: this.service, resource: this.service,
method: 'getData', method: 'getData',
successCallback: (res) => this.store.updateState(res.data), successCallback: (res) => this.updateState(res.data),
errorCallback(err) { errorCallback(err) {
throw new Error(err); throw new Error(err);
}, },
@ -360,23 +359,37 @@ export default {
} }
return undefined; 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 return this.service
.getData() .getData()
.then((res) => res.data) .then((res) => res.data)
.then((data) => { .then(this.updateState)
this.store.updateState(data); .catch(() => createAlert({ message: this.defaultErrorMessage }));
})
.catch(() => {
createAlert({
message: this.defaultErrorMessage,
});
});
}, },
setFormState(state) { setFormState(state) {
this.store.setFormState(state); this.formState = { ...this.formState, ...state };
}, },
updateFormState(templates = {}) { updateFormState(templates = {}) {
@ -416,7 +429,7 @@ export default {
this.templatesRequested = true; this.templatesRequested = true;
this.requestTemplatesAndShowForm(); this.requestTemplatesAndShowForm();
} else { } else {
this.updateAndShowForm(this.issuableTemplates); this.updateAndShowForm(this.formState.issuableTemplates);
} }
}, },
@ -427,10 +440,7 @@ export default {
async updateIssuable() { async updateIssuable() {
this.setFormState({ updateLoading: true }); this.setFormState({ updateLoading: true });
const { const { formState, issueState } = this;
store: { formState },
issueState,
} = this;
const issuablePayload = issueState.isDirty const issuablePayload = issueState.isDirty
? { ...formState, issue_type: issueState.issueType } ? { ...formState, issue_type: issueState.issueType }
: formState; : formState;
@ -464,7 +474,7 @@ export default {
visitUrl(URI); visitUrl(URI);
} }
}) })
.then(this.updateStoreState) .then(this.refetchData)
.then(() => { .then(() => {
eventHub.$emit('close.form'); eventHub.$emit('close.form');
}) })
@ -518,7 +528,7 @@ export default {
this.poll.enable(); this.poll.enable();
this.poll.makeDelayedRequest(POLLING_DELAY); this.poll.makeDelayedRequest(POLLING_DELAY);
this.updateStoreState(); this.refetchData();
}, },
}, },
}; };
@ -531,7 +541,7 @@ export default {
:endpoint="endpoint" :endpoint="endpoint"
:form-state="formState" :form-state="formState"
:initial-description-text="initialDescriptionText" :initial-description-text="initialDescriptionText"
:issuable-templates="issuableTemplates" :issuable-templates="formState.issuableTemplates"
:markdown-docs-path="markdownDocsPath" :markdown-docs-path="markdownDocsPath"
:markdown-preview-path="markdownPreviewPath" :markdown-preview-path="markdownPreviewPath"
:project-path="projectPath" :project-path="projectPath"

View File

@ -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);
}
}

View File

@ -154,6 +154,15 @@ module Integrations
supported_events.map { |event| event_channel_name(event) } supported_events.map { |event| event_channel_name(event) }
end end
override :api_field_names
def api_field_names
if mask_configurable_channels?
super - event_channel_names
else
super
end
end
def form_fields def form_fields
super.reject { |field| field[:name].end_with?('channel') } super.reject { |field| field[:name].end_with?('channel') }
end end

View File

@ -180,11 +180,7 @@ class Packages::Package < ApplicationRecord
scope :preload_conan_metadatum, -> { preload(:conan_metadatum) } scope :preload_conan_metadatum, -> { preload(:conan_metadatum) }
scope :with_npm_scope, ->(scope) do 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)}") 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
end end
scope :without_nuget_temporary_name, -> { where.not(name: Packages::Nuget::TEMPORARY_PACKAGE_NAME) } scope :without_nuget_temporary_name, -> { where.not(name: Packages::Nuget::TEMPORARY_PACKAGE_NAME) }

View File

@ -47,10 +47,6 @@ module Groups
private private
def valid_path_change? 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 unless group.packages_feature_enabled?
return true if params[:path].blank? return true if params[:path].blank?
return true if group.has_parent? return true if group.has_parent?
@ -68,21 +64,6 @@ module Groups
false false
end 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) def before_assignment_hook(group, params)
@full_path_before = group.full_path @full_path_before = group.full_path
@path_before = group.path @path_before = group.path

View File

@ -9,15 +9,15 @@ module Environments
feature_category :continuous_delivery feature_category :continuous_delivery
def perform(job_id, _params = {}) def perform(job_id, _params = {})
Ci::Build.find_by_id(job_id).try do |build| Ci::Processable.find_by_id(job_id).try do |job|
stop_environment(build) if build.stops_environment? && build.stop_action_successful? stop_environment(job) if job.stops_environment? && job.stop_action_successful?
end end
end end
private private
def stop_environment(build) def stop_environment(job)
build.persisted_environment.fire_state_event(:stop_complete) job.persisted_environment.fire_state_event(:stop_complete)
end end
end end
end end

View File

@ -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

View File

@ -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 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. 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 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 processes may switch to using that instead of being terminated. This situation could lead to notably compromised
performance. performance.

View File

@ -24,6 +24,8 @@ Users on self-managed GitLab can disable this rate limit.
## Configure GitLab Shell operation 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. `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**. 1. On the left sidebar, select **Your work > Admin Area**.

View File

@ -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 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 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 [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.
cumulative results.
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. 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.

View File

@ -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. 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, 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: `**"`, `'`, ```, `(`, `[`, `{`, `<`, `*`, `_**`.
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.
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. On the left sidebar, select your avatar.
1. Select **Preferences**. 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 the **Surround text selection when typing quotes or brackets** checkbox.
1. Select **Save changes**. 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 ### 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: To add a new list item when you press the <kbd>Enter</kbd> key:

View File

@ -510,3 +510,11 @@ To disable the feature flag, run this command:
# Disable # Disable
Feature.disable(:github_importer_lower_per_page_limit, group) 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.

View File

@ -5,7 +5,7 @@ module Quality
module Seeders module Seeders
class Issues class Issues
DEFAULT_BACKFILL_WEEKS = 52 DEFAULT_BACKFILL_WEEKS = 52
DEFAULT_AVERAGE_ISSUES_PER_WEEK = 10 DEFAULT_AVERAGE_ISSUES_PER_WEEK = 20
attr_reader :project, :user attr_reader :project, :user
@ -14,23 +14,27 @@ module Quality
end end
def seed(backfill_weeks: DEFAULT_BACKFILL_WEEKS, average_issues_per_week: DEFAULT_AVERAGE_ISSUES_PER_WEEK) 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 created_at = backfill_weeks.to_i.weeks.ago
team = project.team.users team = project.team.users
created_issues_count = 0 created_issues_count = 0
loop do loop do
rand(average_issues_per_week * 2).times do rand(1..average_issues_per_week).times do
params = { params = {
title: FFaker::Lorem.sentence(6), title: FFaker::Lorem.sentence(6),
description: FFaker::Lorem.sentence, description: FFaker::Lorem.sentence,
created_at: created_at + rand(6).days, created_at: created_at + rand(6).days,
state: %w[opened closed].sample, state: %w[opened closed].sample,
milestone: project.milestones.sample, milestone_id: project.milestones.sample&.id,
assignee_ids: Array(team.pluck(:id).sample(3)), assignee_ids: Array(team.pluck(:id).sample(rand(3))),
due_date: rand(10).days.from_now,
labels: labels.join(',') labels: labels.join(',')
} }.merge(additional_params)
params[:closed_at] = params[:created_at] + rand(35).days if params[:state] == 'closed'
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 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? if create_result.success?
@ -49,6 +53,46 @@ module Quality
private 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 def labels
@labels_pool ||= project.labels.limit(rand(3)).pluck(:title).tap do |labels_array| @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 labels_array.concat(project.group.labels.limit(rand(3)).pluck(:title)) if project.group

View File

@ -22,7 +22,7 @@ module QA
return return
end 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::Index.perform(&:click_new_merge_request)
Page::MergeRequest::New.perform do |merge_request| Page::MergeRequest::New.perform do |merge_request|
merge_request.select_source_branch(source_branch) merge_request.select_source_branch(source_branch)

View File

@ -5,174 +5,13 @@ module QA
module Group module Group
class Menu < Page::Base class Menu < Page::Base
include QA::Page::SubMenus::Common include QA::Page::SubMenus::Common
include Page::SubMenus::SuperSidebar::Manage
if Runtime::Env.super_sidebar_enabled? include Page::SubMenus::SuperSidebar::Plan
prepend Page::SubMenus::SuperSidebar::Manage include Page::SubMenus::SuperSidebar::Settings
prepend Page::SubMenus::SuperSidebar::Plan include SubMenus::SuperSidebar::Main
prepend Page::SubMenus::SuperSidebar::Settings include SubMenus::SuperSidebar::Build
prepend SubMenus::SuperSidebar::Main include SubMenus::SuperSidebar::Operate
prepend SubMenus::SuperSidebar::Build include SubMenus::SuperSidebar::Deploy
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
end end
end end
end end

View File

@ -8,7 +8,7 @@ module QA
module Deploy module Deploy
extend QA::Page::PageConcern extend QA::Page::PageConcern
def self.prepended(base) def self.included(base)
super super
base.class_eval do base.class_eval do

View File

@ -8,7 +8,7 @@ module QA
module Main module Main
extend QA::Page::PageConcern extend QA::Page::PageConcern
def self.prepended(base) def self.included(base)
super super
base.class_eval do base.class_eval do

View File

@ -8,7 +8,7 @@ module QA
module Operate module Operate
extend QA::Page::PageConcern extend QA::Page::PageConcern
def self.prepended(base) def self.included(base)
super super
base.class_eval do base.class_eval do

View File

@ -4,64 +4,27 @@ module QA
module Page module Page
module Profile module Profile
class Menu < Page::Base class Menu < Page::Base
prepend QA::Mobile::Page::SubMenus::Common if QA::Runtime::Env.mobile_layout? include SubMenus::CreateNewMenu
# TODO: integrate back once super sidebar becomes default include SubMenus::SuperSidebar::ContextSwitcher
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
def click_ssh_keys def click_ssh_keys
within_sidebar do click_element(:nav_item_link, submenu_item: 'SSH Keys')
click_element(:ssh_keys_link)
end
end end
def click_account def click_account
within_sidebar do click_element(:nav_item_link, submenu_item: 'Account')
click_element(:profile_account_link)
end
end end
def click_emails def click_emails
within_sidebar do click_element(:nav_item_link, submenu_item: 'Emails')
click_element(:profile_emails_link)
end
end end
def click_password def click_password
within_sidebar do click_element(:nav_item_link, submenu_item: 'Password')
click_element(:profile_password_link)
end
end end
private def click_access_tokens
click_element(:nav_item_link, submenu_item: 'Access Tokens')
def within_sidebar(&block)
page.within('.sidebar-top-level-items', &block)
end end
end end
end end

View File

@ -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

View File

@ -5,20 +5,7 @@ module QA
module Project module Project
class Menu < Page::Base class Menu < Page::Base
include SubMenus::Common 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 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::Plan
include SubMenus::SuperSidebar::Settings include SubMenus::SuperSidebar::Settings
include SubMenus::SuperSidebar::Code include SubMenus::SuperSidebar::Code
@ -26,62 +13,8 @@ module QA
include SubMenus::SuperSidebar::Operate include SubMenus::SuperSidebar::Operate
include SubMenus::SuperSidebar::Monitor include SubMenus::SuperSidebar::Monitor
include SubMenus::SuperSidebar::Main include SubMenus::SuperSidebar::Main
end include Page::SubMenus::SuperSidebar::Manage
include Page::SubMenus::SuperSidebar::Deploy
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
end end
end end
end end

View File

@ -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)

View File

@ -13,14 +13,6 @@ module QA
base.class_eval do base.class_eval do
include QA::Page::SubMenus::Common 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 view 'app/views/layouts/nav/_top_bar.html.haml' do
element :toggle_mobile_nav_button element :toggle_mobile_nav_button
end end

View File

@ -11,21 +11,12 @@ module QA
super super
base.class_eval do base.class_eval do
# TODO: remove this when the super sidebar is enabled by default include QA::Page::SubMenus::CreateNewMenu
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
end end
end end
def go_to_new_issue def go_to_new_issue
within_new_item_menu do 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') click_element(:create_menu_item, create_menu_item: 'new_issue')
end end
end end

View File

@ -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

View File

@ -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

View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -4,12 +4,12 @@ module QA
module Page module Page
module SubMenus module SubMenus
module Common module Common
prepend Mobile::Page::SubMenus::Common if QA::Runtime::Env.mobile_layout?
def self.included(base) def self.included(base)
super super
base.class_eval do 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 view 'app/assets/javascripts/super_sidebar/components/super_sidebar.vue' do
element :navbar element :navbar
end end
@ -33,7 +33,7 @@ module QA
yield yield
end end
# Implementation for super-sidebar, will replace within_submenu # Open sidebar navigation submenu
# #
# @param [String] parent_menu_name # @param [String] parent_menu_name
# @param [String] parent_section_id # @param [String] parent_section_id

View File

@ -7,7 +7,7 @@ module QA
module ContextSwitcher module ContextSwitcher
extend QA::Page::PageConcern extend QA::Page::PageConcern
def self.prepended(base) def self.included(base)
super super
base.class_eval do base.class_eval do

View File

@ -4,10 +4,6 @@ module QA
module Page module Page
module User module User
class Show < Page::Base 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 view 'app/views/users/_follow_user.html.haml' do
element :follow_user_link element :follow_user_link
end end
@ -25,9 +21,7 @@ module QA
end end
def click_following_tab def click_following_tab
return click_element(:nav_item_link, submenu_item: 'Following') if Runtime::Env.super_sidebar_enabled? click_element(:nav_item_link, submenu_item: 'Following')
click_element(:following_tab)
end end
def click_user_link(username) def click_user_link(username)

View File

@ -12,7 +12,7 @@ module QA
def fabricate! def fabricate!
project.visit! 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| Page::Project::Snippet::New.perform do |new_snippet|
new_snippet.click_create_first_snippet new_snippet.click_create_first_snippet

View File

@ -667,10 +667,6 @@ module QA
ENV['DEFAULT_CHROME_DOWNLOAD_PATH'] || Dir.tmpdir ENV['DEFAULT_CHROME_DOWNLOAD_PATH'] || Dir.tmpdir
end end
def super_sidebar_enabled?
enabled?(ENV['QA_SUPER_SIDEBAR_ENABLED'], default: true)
end
def require_slack_env! def require_slack_env!
missing_env = %i[slack_workspace slack_email slack_password].select do |method| missing_env = %i[slack_workspace slack_email slack_password].select do |method|
::QA::Runtime::Env.public_send(method).nil? ::QA::Runtime::Env.public_send(method).nil?

View File

@ -17,25 +17,31 @@ module QA
end end
let(:group) do 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 end
let(:developer_user) { create(:user, username: "developer_user_#{SecureRandom.hex(4)}", api_client: admin_api_client) } 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 before do
group.add_member(developer_user, Resource::Members::AccessLevel::DEVELOPER) group.add_member(developer_user, Resource::Members::AccessLevel::DEVELOPER)
end 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( it(
'allows enforcing 2FA via UI and logging in with 2FA', 'allows enforcing 2FA via UI and logging in with 2FA',
testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347931', 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'
}
) do ) do
enforce_two_factor_authentication_on_group(group) enforce_two_factor_authentication_on_group(group)
@ -58,21 +64,13 @@ module QA
expect(Page::Main::Menu.perform(&:signed_in?)).to be_truthy expect(Page::Main::Menu.perform(&:signed_in?)).to be_truthy
end 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. # 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. # Any future tests should use the API for this purpose.
def enforce_two_factor_authentication_on_group(group) def enforce_two_factor_authentication_on_group(group)
Flow::Login.while_signed_in(as: owner_user) do Flow::Login.while_signed_in(as: owner_user) do
group.visit! 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) Page::Group::Settings::General.perform(&:set_require_2fa_enabled)
QA::Support::Retrier.retry_on_exception(reload_page: page) do QA::Support::Retrier.retry_on_exception(reload_page: page) do

View File

@ -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 it 'is received by a user for project invitation', testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347961' do
project.visit! project.visit!
Page::Project::Menu.perform(&:click_members) Page::Project::Menu.perform(&:go_to_members)
Page::Project::Members.perform do |member_settings| Page::Project::Members.perform do |member_settings|
member_settings.add_member(user.username) member_settings.add_member(user.username)
end end

View File

@ -22,7 +22,7 @@ module QA
user user
Flow::Login.sign_in_as_admin Flow::Login.sign_in_as_admin
project.visit! project.visit!
Page::Project::Menu.perform(&:click_members) Page::Project::Menu.perform(&:go_to_members)
Page::Project::Members.perform do |members| Page::Project::Members.perform do |members|
members.add_member(user.username) members.add_member(user.username)
end end

View File

@ -18,7 +18,7 @@ module QA
testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347809' do testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347809' do
project.visit! 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::Show.perform(&:click_create_your_first_page)
Page::Project::Wiki::Edit.perform do |edit| Page::Project::Wiki::Edit.perform do |edit|

View File

@ -142,7 +142,7 @@ module QA
project.group.visit! 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| Page::Group::DependencyProxy.perform do |index|
expect(index).to have_blob_count("Contains 1 blobs of images") expect(index).to have_blob_count("Contains 1 blobs of images")

View File

@ -163,7 +163,7 @@ module QA
project.group.visit! 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| Page::Project::Packages::Index.perform do |index|
expect(index).to have_package(package.name) expect(index).to have_package(package.name)

View File

@ -18,7 +18,7 @@ module QA
it 'transfers a subgroup to another group', it 'transfers a subgroup to another group',
testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347692' do 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| Page::Group::Settings::General.perform do |general|
general.transfer_group(sub_group_for_transfer, target_group) general.transfer_group(sub_group_for_transfer, target_group)

View File

@ -11,7 +11,7 @@ module QA
project = create(:project, name: 'add-member-project') project = create(:project, name: 'add-member-project')
project.visit! project.visit!
Page::Project::Menu.perform(&:click_members) Page::Project::Menu.perform(&:go_to_members)
Page::Project::Members.perform do |members| Page::Project::Members.perform do |members|
members.add_member(user.username) members.add_member(user.username)
members.search_member(user.username) members.search_member(user.username)

View File

@ -5,7 +5,7 @@ module QA
describe 'Invite group', :reliable, product_group: :tenant_scale do describe 'Invite group', :reliable, product_group: :tenant_scale do
shared_examples 'invites group to project' do shared_examples 'invites group to project' do
it 'grants group and members correct access level' 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| Page::Project::Members.perform do |project_members|
project_members.invite_group(group.path, 'Developer') project_members.invite_group(group.path, 'Developer')

View File

@ -15,7 +15,7 @@ module QA
end.project end.project
project.visit! project.visit!
Page::Project::Menu.perform(&:click_activity) Page::Project::Menu.perform(&:go_to_activity)
Page::Project::Activity.perform do |activity| Page::Project::Activity.perform do |activity|
activity.click_push_events activity.click_push_events

View File

@ -24,7 +24,7 @@ module QA
Flow::Login.while_signed_in_as_admin do Flow::Login.while_signed_in_as_admin do
group.visit! group.visit!
Page::Group::Menu.perform(&:click_subgroup_members_item) Page::Group::Menu.perform(&:go_to_members)
Page::Group::Members.perform do |members_page| Page::Group::Members.perform do |members_page|
members_page.search_member(user.username) members_page.search_member(user.username)
members_page.remove_member(user.username) members_page.remove_member(user.username)

View File

@ -116,12 +116,12 @@ module QA
# @return [String] 'gitlab' or 'jihulab' # @return [String] 'gitlab' or 'jihulab'
def production_domain(tld) def production_domain(tld)
return 'gitlab' unless GitlabEdition.jh? return 'gitlab' unless GitlabEdition.jh?
return 'gitlab' if tld == 'hk' return 'gitlab' if tld == 'hk' || tld == 'cn'
return 'jihulab' if tld == 'com' return 'jihulab' if tld == 'com'
end end
def opts_tld def opts_tld
GitlabEdition.jh? ? '(.com|.hk)' : '(.com|.net)' GitlabEdition.jh? ? '(.com|.hk|.cn)' : '(.com|.net)'
end end
def get_tld(host) def get_tld(host)

View File

@ -30,11 +30,19 @@ RSpec.describe QA::Specs::Helpers::ContextSelector do
end end
it 'returns true when url has .net' do 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") QA::Runtime::Scenario.define(:gitlab_address, "https://release.gitlab.net")
expect(described_class.context_matches?).to be_truthy expect(described_class.context_matches?).to be_truthy
end 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 it 'returns false when url does not have .com' do
QA::Runtime::Scenario.define(:gitlab_address, "https://gitlab.test") 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/") QA::Runtime::Scenario.define(:gitlab_address, "https://gitlab.hk/")
expect(described_class.context_matches?(:production)).to be_truthy 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 end
it 'matches domain' do it 'matches domain' do

View File

@ -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);
});
});
});

View File

@ -364,4 +364,17 @@ RSpec.describe Integrations::BaseChatNotification, feature_category: :integratio
expect { subject.event_channel_value(:foo) }.to raise_error(NoMethodError) expect { subject.event_channel_value(:foo) }.to raise_error(NoMethodError)
end end
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 end

View File

@ -875,14 +875,6 @@ RSpec.describe Packages::Package, type: :model, feature_category: :package_regis
subject { described_class.with_npm_scope('test') } subject { described_class.with_npm_scope('test') }
it { is_expected.to contain_exactly(package1) } 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 end
describe '.without_nuget_temporary_name' do describe '.without_nuget_temporary_name' do

View File

@ -43,9 +43,7 @@ RSpec.describe DesignManagement::SaveDesignsService, feature_category: :design_m
design_files = files_to_upload || files design_files = files_to_upload || files
design_files.each(&:rewind) design_files.each(&:rewind)
service = described_class.new(project, user, service = described_class.new(project, user, issue: issue, files: design_files)
issue: issue,
files: design_files)
service.execute service.execute
end end
@ -390,9 +388,12 @@ RSpec.describe DesignManagement::SaveDesignsService, feature_category: :design_m
before do before do
path = File.join(build(:design, issue: issue, filename: filename).full_path) path = File.join(build(:design, issue: issue, filename: filename).full_path)
design_repository.create_if_not_exists design_repository.create_if_not_exists
design_repository.create_file(user, path, 'something fake', design_repository.create_file(
user,
path, 'something fake',
branch_name: project.default_branch_or_main, branch_name: project.default_branch_or_main,
message: 'Somehow created without being tracked in db') message: 'Somehow created without being tracked in db'
)
end end
it 'creates the design and a new version for it' do it 'creates the design and a new version for it' do

View File

@ -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 it 'raises an argument error if discussions do not belong to the same noteable' do
other_merge_request = create(:merge_request) other_merge_request = create(:merge_request)
other_discussion = create(:diff_note_on_merge_request, other_discussion = create(
:diff_note_on_merge_request,
noteable: other_merge_request, noteable: other_merge_request,
project: other_merge_request.source_project).to_discussion project: other_merge_request.source_project
).to_discussion
expect do expect do
described_class.new(project, user, one_or_more_discussions: [discussion, other_discussion]) described_class.new(project, user, one_or_more_discussions: [discussion, other_discussion])
end.to raise_error( end.to raise_error(

View File

@ -292,9 +292,12 @@ RSpec.describe DraftNotes::PublishService, feature_category: :code_review_workfl
other_user = create(:user) other_user = create(:user)
project.add_developer(other_user) project.add_developer(other_user)
create(:draft_note, merge_request: merge_request, create(
:draft_note,
merge_request: merge_request,
author: user, author: user,
note: "thanks\n/assign #{other_user.to_reference}") note: "thanks\n/assign #{other_user.to_reference}"
)
expect { publish }.to change { DraftNote.count }.by(-1).and change { Note.count }.by(2) expect { publish }.to change { DraftNote.count }.by(-1).and change { Note.count }.by(2)
expect(merge_request.reload.assignees).to match_array([other_user]) expect(merge_request.reload.assignees).to match_array([other_user])

View File

@ -135,8 +135,7 @@ RSpec.describe Environments::StopService, feature_category: :continuous_delivery
context 'when branch for stop action is protected' do context 'when branch for stop action is protected' do
before do before do
project.add_developer(user) project.add_developer(user)
create(:protected_branch, :no_one_can_push, create(:protected_branch, :no_one_can_push, name: 'master', project: project)
name: 'master', project: project)
end end
it 'does not stop environment' do it 'does not stop environment' do

View File

@ -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 'not allowing a path update'
it_behaves_like 'allowing an update', on: :name 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
context 'updating the subgroup' do 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: :path
it_behaves_like 'allowing an update', on: :name 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 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: :path
it_behaves_like 'allowing an update', on: :name 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
context 'updating the subgroup' do 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: :path
it_behaves_like 'allowing an update', on: :name 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 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: :path
it_behaves_like 'allowing an update', on: :name 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
context 'updating the subgroup' do 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: :path
it_behaves_like 'allowing an update', on: :name 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 end
end end

View File

@ -88,7 +88,8 @@ RSpec.describe LooseForeignKeys::BatchCleanerService, feature_category: :databas
expect(loose_fk_child_table_1.count).to eq(4) expect(loose_fk_child_table_1.count).to eq(4)
expect(loose_fk_child_table_2.count).to eq(4) expect(loose_fk_child_table_2.count).to eq(4)
described_class.new(parent_table: '_test_loose_fk_parent_table', described_class.new(
parent_table: '_test_loose_fk_parent_table',
loose_foreign_key_definitions: loose_foreign_key_definitions, loose_foreign_key_definitions: loose_foreign_key_definitions,
deleted_parent_records: LooseForeignKeys::DeletedRecord.load_batch_for_table('public._test_loose_fk_parent_table', 100) deleted_parent_records: LooseForeignKeys::DeletedRecord.load_batch_for_table('public._test_loose_fk_parent_table', 100)
).execute ).execute
@ -125,7 +126,8 @@ RSpec.describe LooseForeignKeys::BatchCleanerService, feature_category: :databas
let(:deleted_records_incremented_counter) { Gitlab::Metrics.registry.get(:loose_foreign_key_incremented_deleted_records) } let(:deleted_records_incremented_counter) { Gitlab::Metrics.registry.get(:loose_foreign_key_incremented_deleted_records) }
let(:cleaner) do let(:cleaner) do
described_class.new(parent_table: '_test_loose_fk_parent_table', described_class.new(
parent_table: '_test_loose_fk_parent_table',
loose_foreign_key_definitions: loose_foreign_key_definitions, loose_foreign_key_definitions: loose_foreign_key_definitions,
deleted_parent_records: LooseForeignKeys::DeletedRecord.load_batch_for_table('public._test_loose_fk_parent_table', 100), deleted_parent_records: LooseForeignKeys::DeletedRecord.load_batch_for_table('public._test_loose_fk_parent_table', 100),
modification_tracker: modification_tracker modification_tracker: modification_tracker

View File

@ -24,8 +24,13 @@ RSpec.describe NoteSummary, feature_category: :code_review_workflow do
describe '#note' do describe '#note' do
it 'returns note hash' do it 'returns note hash' do
freeze_time do freeze_time do
expect(create_note_summary.note).to eq(noteable: noteable, project: project, author: user, note: 'note', expect(create_note_summary.note).to eq(
created_at: Time.current) noteable: noteable,
project: project,
author: user,
note: 'note',
created_at: Time.current
)
end end
end end

View File

@ -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(:member_and_not_mentioned) { create(:user, developer_projects: [project]) }
let_it_be(:non_member_and_mentioned) { create(:user) } let_it_be(:non_member_and_mentioned) { create(:user) }
let_it_be(:note) do let_it_be(:note) do
create(:diff_note_on_design, create(
:diff_note_on_design,
noteable: design, noteable: design,
note: "Hello #{member_and_mentioned.to_reference}, G'day #{non_member_and_mentioned.to_reference}") note: "Hello #{member_and_mentioned.to_reference}, G'day #{non_member_and_mentioned.to_reference}"
)
end end
let_it_be(:note_2) do let_it_be(:note_2) do
@ -3506,12 +3508,14 @@ RSpec.describe NotificationService, :mailer, feature_category: :team_planning do
let(:commit) { project.commit } let(:commit) { project.commit }
def create_pipeline(user, status) def create_pipeline(user, status)
create(:ci_pipeline, status, create(
:ci_pipeline, status,
project: project, project: project,
user: user, user: user,
ref: 'refs/heads/master', ref: 'refs/heads/master',
sha: commit.id, sha: commit.id,
before_sha: '00000000') before_sha: '00000000'
)
end end
before_all do before_all do
@ -4020,12 +4024,14 @@ RSpec.describe NotificationService, :mailer, feature_category: :team_planning do
project.add_maintainer(reviewer) project.add_maintainer(reviewer)
merge_request.assignees.each { |assignee| project.add_maintainer(assignee) } merge_request.assignees.each { |assignee| project.add_maintainer(assignee) }
create(:diff_note_on_merge_request, create(
:diff_note_on_merge_request,
project: project, project: project,
noteable: merge_request, noteable: merge_request,
author: reviewer, author: reviewer,
review: review, review: review,
note: "cc @mention") note: "cc @mention"
)
end end
it 'sends emails' do 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) } subject { notification.inactive_project_deletion_warning(project, deletion_date) }
it "sends email to project owners and maintainers" do it "sends email to project owners and maintainers" do
expect { subject }.to have_enqueued_email(project, maintainer, deletion_date, expect { subject }.to have_enqueued_email(
mail: "inactive_project_deletion_warning_email") project,
expect { subject }.not_to have_enqueued_email(project, developer, deletion_date, maintainer,
mail: "inactive_project_deletion_warning_email") 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
end end

View File

@ -21,9 +21,9 @@ RSpec.describe Pages::MigrateLegacyStorageToDeploymentService, feature_category:
project.mark_pages_as_deployed project.mark_pages_as_deployed
expect(project.pages_metadatum.reload.deployed).to eq(true) expect(project.pages_metadatum.reload.deployed).to eq(true)
expect(service.execute).to( expect(service.execute).to eq(
eq(status: :success, status: :success,
message: "Archive not created. Missing public directory in #{project.pages_path}? Marked project as not deployed") 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) expect(project.pages_metadatum.reload.deployed).to eq(false)
@ -35,9 +35,9 @@ RSpec.describe Pages::MigrateLegacyStorageToDeploymentService, feature_category:
project.mark_pages_as_deployed project.mark_pages_as_deployed
expect(project.pages_metadatum.reload.deployed).to eq(true) expect(project.pages_metadatum.reload.deployed).to eq(true)
expect(service.execute).to( expect(service.execute).to eq(
eq(status: :success, status: :success,
message: "Archive not created. Missing public directory in #{project.pages_path}? Marked project as not deployed") 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) 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(project.pages_metadatum.reload.deployed).to eq(true)
expect(service.execute).to( expect(service.execute).to eq(
eq(status: :error, status: :error,
message: "Archive not created. Missing public directory in #{project.pages_path}") message: "Archive not created. Missing public directory in #{project.pages_path}"
) )
expect(project.pages_metadatum.reload.deployed).to eq(true) 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 it 'removes pages archive when can not save deployment' do
archive = fixture_file_upload("spec/fixtures/pages.zip") archive = fixture_file_upload("spec/fixtures/pages.zip")
expect_next_instance_of(::Pages::ZipDirectoryService) do |zip_service| expect_next_instance_of(::Pages::ZipDirectoryService) do |zip_service|
expect(zip_service).to receive(:execute).and_return(status: :success, expect(zip_service).to receive(:execute).and_return(
archive_path: archive.path, status: :success, archive_path: archive.path, entries_count: 3
entries_count: 3) )
end end
expect_next_instance_of(PagesDeployment) do |deployment| expect_next_instance_of(PagesDeployment) do |deployment|

View File

@ -132,8 +132,7 @@ RSpec.describe PagesDomains::ObtainLetsEncryptCertificateService, feature_catego
ef.create_extension("basicConstraints", "CA:TRUE", true), ef.create_extension("basicConstraints", "CA:TRUE", true),
ef.create_extension("subjectKeyIdentifier", "hash") ef.create_extension("subjectKeyIdentifier", "hash")
] ]
cert.add_extension ef.create_extension("authorityKeyIdentifier", cert.add_extension ef.create_extension("authorityKeyIdentifier", "keyid:always,issuer:always")
"keyid:always,issuer:always")
cert.sign key, OpenSSL::Digest.new('SHA256') cert.sign key, OpenSSL::Digest.new('SHA256')

View File

@ -28,9 +28,11 @@ RSpec.describe PreviewMarkdownService, feature_category: :team_planning do
let(:text) { "```suggestion\nfoo\n```" } let(:text) { "```suggestion\nfoo\n```" }
let(:params) do let(:params) do
suggestion_params.merge(text: text, suggestion_params.merge(
text: text,
target_type: 'MergeRequest', target_type: 'MergeRequest',
target_id: merge_request.iid) target_id: merge_request.iid
)
end end
let(:service) { described_class.new(project, user, params) } let(:service) { described_class.new(project, user, params) }
@ -52,15 +54,16 @@ RSpec.describe PreviewMarkdownService, feature_category: :team_planning do
end end
it 'returns suggestions referenced in text' do it 'returns suggestions referenced in text' do
position = Gitlab::Diff::Position.new(new_path: path, position = Gitlab::Diff::Position.new(new_path: path, new_line: line, diff_refs: diff_refs)
new_line: line,
diff_refs: diff_refs)
expect(Gitlab::Diff::SuggestionsParser) expect(Gitlab::Diff::SuggestionsParser)
.to receive(:parse) .to receive(:parse)
.with(text, position: position, .with(
text,
position: position,
project: merge_request.project, project: merge_request.project,
supports_suggestion: true) supports_suggestion: true
)
.and_call_original .and_call_original
result = service.execute result = service.execute

View File

@ -6,7 +6,9 @@ RSpec.describe ProtectedBranches::ApiService, feature_category: :compliance_mana
shared_examples 'execute with entity' do shared_examples 'execute with entity' do
it 'creates a protected branch with prefilled defaults' do it 'creates a protected branch with prefilled defaults' do
expect(::ProtectedBranches::CreateService).to receive(:new).with( expect(::ProtectedBranches::CreateService).to receive(:new).with(
entity, user, hash_including( entity,
user,
hash_including(
push_access_levels_attributes: [{ access_level: Gitlab::Access::MAINTAINER }], push_access_levels_attributes: [{ access_level: Gitlab::Access::MAINTAINER }],
merge_access_levels_attributes: [{ access_level: Gitlab::Access::MAINTAINER }] merge_access_levels_attributes: [{ access_level: Gitlab::Access::MAINTAINER }]
) )
@ -17,7 +19,9 @@ RSpec.describe ProtectedBranches::ApiService, feature_category: :compliance_mana
it 'updates a protected branch without prefilled defaults' do it 'updates a protected branch without prefilled defaults' do
expect(::ProtectedBranches::UpdateService).to receive(:new).with( expect(::ProtectedBranches::UpdateService).to receive(:new).with(
entity, user, hash_including( entity,
user,
hash_including(
push_access_levels_attributes: [], push_access_levels_attributes: [],
merge_access_levels_attributes: [] merge_access_levels_attributes: []
) )

View File

@ -190,9 +190,7 @@ RSpec.describe PushEventPayloadService, feature_category: :source_code_managemen
end end
it 'returns :removed when removing an existing ref' do it 'returns :removed when removing an existing ref' do
service = described_class.new(event, service = described_class.new(event, before: '123', after: Gitlab::Git::BLANK_SHA)
before: '123',
after: Gitlab::Git::BLANK_SHA)
expect(service.action).to eq(:removed) expect(service.action).to eq(:removed)
end end

View File

@ -29,9 +29,11 @@ RSpec.describe QuickActions::InterpretService, feature_category: :team_planning
end end
before do before do
stub_licensed_features(multiple_issue_assignees: false, stub_licensed_features(
multiple_issue_assignees: false,
multiple_merge_request_reviewers: false, multiple_merge_request_reviewers: false,
multiple_merge_request_assignees: false) multiple_merge_request_assignees: false
)
end end
describe '#execute' do describe '#execute' do

View File

@ -42,9 +42,7 @@ RSpec.describe Releases::DestroyService, feature_category: :release_orchestratio
let!(:release) {} let!(:release) {}
it 'returns an error' do it 'returns an error' do
is_expected.to include(status: :error, is_expected.to include(status: :error, message: 'Release does not exist', http_status: 404)
message: 'Release does not exist',
http_status: 404)
end end
end end
@ -52,9 +50,7 @@ RSpec.describe Releases::DestroyService, feature_category: :release_orchestratio
let(:user) { repoter } let(:user) { repoter }
it 'returns an error' do it 'returns an error' do
is_expected.to include(status: :error, is_expected.to include(status: :error, message: 'Access Denied', http_status: 403)
message: 'Access Denied',
http_status: 403)
end end
end end

View File

@ -33,8 +33,7 @@ RSpec.describe ResourceAccessTokens::RevokeService, feature_category: :system_ac
subject subject
expect( expect(
Users::GhostUserMigration.where(user: resource_bot, Users::GhostUserMigration.where(user: resource_bot, initiator_user: user)
initiator_user: user)
).to be_exists ).to be_exists
end end

View File

@ -61,8 +61,7 @@ RSpec.describe ResourceEvents::MergeIntoNotesService, feature_category: :team_pl
create_event(created_at: 4.days.ago) create_event(created_at: 4.days.ago)
event = create_event(created_at: 1.day.ago) event = create_event(created_at: 1.day.ago)
notes = described_class.new(resource, user, notes = described_class.new(resource, user, last_fetched_at: 2.days.ago).execute
last_fetched_at: 2.days.ago).execute
expect(notes.count).to eq 1 expect(notes.count).to eq 1
expect(notes.first.discussion_id).to eq event.reload.discussion_id expect(notes.first.discussion_id).to eq event.reload.discussion_id

View File

@ -16,7 +16,8 @@ RSpec.describe Security::MergeReportsService, '#execute', feature_category: :cod
let(:identifier_wasc) { build(:ci_reports_security_identifier, external_id: '13', external_type: 'wasc') } let(:identifier_wasc) { build(:ci_reports_security_identifier, external_id: '13', external_type: 'wasc') }
let(:finding_id_1) do let(:finding_id_1) do
build(:ci_reports_security_finding, build(
:ci_reports_security_finding,
identifiers: [identifier_1_primary, identifier_1_cve], identifiers: [identifier_1_primary, identifier_1_cve],
scanner: scanner_1, scanner: scanner_1,
severity: :low severity: :low
@ -24,7 +25,8 @@ RSpec.describe Security::MergeReportsService, '#execute', feature_category: :cod
end end
let(:finding_id_1_extra) do let(:finding_id_1_extra) do
build(:ci_reports_security_finding, build(
:ci_reports_security_finding,
identifiers: [identifier_1_primary, identifier_1_cve], identifiers: [identifier_1_primary, identifier_1_cve],
scanner: scanner_1, scanner: scanner_1,
severity: :low severity: :low
@ -32,7 +34,8 @@ RSpec.describe Security::MergeReportsService, '#execute', feature_category: :cod
end end
let(:finding_id_2_loc_1) do let(:finding_id_2_loc_1) do
build(:ci_reports_security_finding, build(
:ci_reports_security_finding,
identifiers: [identifier_2_primary, identifier_2_cve], identifiers: [identifier_2_primary, identifier_2_cve],
location: build(:ci_reports_security_locations_sast, start_line: 32, end_line: 34), location: build(:ci_reports_security_locations_sast, start_line: 32, end_line: 34),
scanner: scanner_2, scanner: scanner_2,
@ -41,7 +44,8 @@ RSpec.describe Security::MergeReportsService, '#execute', feature_category: :cod
end end
let(:finding_id_2_loc_1_extra) do let(:finding_id_2_loc_1_extra) do
build(:ci_reports_security_finding, build(
:ci_reports_security_finding,
identifiers: [identifier_2_primary, identifier_2_cve], identifiers: [identifier_2_primary, identifier_2_cve],
location: build(:ci_reports_security_locations_sast, start_line: 32, end_line: 34), location: build(:ci_reports_security_locations_sast, start_line: 32, end_line: 34),
scanner: scanner_2, scanner: scanner_2,
@ -50,7 +54,8 @@ RSpec.describe Security::MergeReportsService, '#execute', feature_category: :cod
end end
let(:finding_id_2_loc_2) do let(:finding_id_2_loc_2) do
build(:ci_reports_security_finding, build(
:ci_reports_security_finding,
identifiers: [identifier_2_primary, identifier_2_cve], identifiers: [identifier_2_primary, identifier_2_cve],
location: build(:ci_reports_security_locations_sast, start_line: 42, end_line: 44), location: build(:ci_reports_security_locations_sast, start_line: 42, end_line: 44),
scanner: scanner_2, scanner: scanner_2,
@ -59,7 +64,8 @@ RSpec.describe Security::MergeReportsService, '#execute', feature_category: :cod
end end
let(:finding_cwe_1) do let(:finding_cwe_1) do
build(:ci_reports_security_finding, build(
:ci_reports_security_finding,
identifiers: [identifier_cwe], identifiers: [identifier_cwe],
scanner: scanner_3, scanner: scanner_3,
severity: :high severity: :high
@ -67,7 +73,8 @@ RSpec.describe Security::MergeReportsService, '#execute', feature_category: :cod
end end
let(:finding_cwe_2) do let(:finding_cwe_2) do
build(:ci_reports_security_finding, build(
:ci_reports_security_finding,
identifiers: [identifier_cwe], identifiers: [identifier_cwe],
scanner: scanner_1, scanner: scanner_1,
severity: :critical severity: :critical
@ -75,7 +82,8 @@ RSpec.describe Security::MergeReportsService, '#execute', feature_category: :cod
end end
let(:finding_wasc_1) do let(:finding_wasc_1) do
build(:ci_reports_security_finding, build(
:ci_reports_security_finding,
identifiers: [identifier_wasc], identifiers: [identifier_wasc],
scanner: scanner_1, scanner: scanner_1,
severity: :medium severity: :medium
@ -83,7 +91,8 @@ RSpec.describe Security::MergeReportsService, '#execute', feature_category: :cod
end end
let(:finding_wasc_2) do let(:finding_wasc_2) do
build(:ci_reports_security_finding, build(
:ci_reports_security_finding,
identifiers: [identifier_wasc], identifiers: [identifier_wasc],
scanner: scanner_2, scanner: scanner_2,
severity: :critical severity: :critical

View File

@ -4,13 +4,11 @@ require 'spec_helper'
RSpec.describe Environments::StopJobSuccessWorker, feature_category: :continuous_delivery do RSpec.describe Environments::StopJobSuccessWorker, feature_category: :continuous_delivery do
describe '#perform' 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 subject { described_class.new.perform(job.id) }
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) }
shared_examples_for 'stopping an associated environment' do
it 'stops the environment' do it 'stops the environment' do
expect(environment).to be_available expect(environment).to be_available
@ -19,9 +17,9 @@ RSpec.describe Environments::StopJobSuccessWorker, feature_category: :continuous
expect(environment.reload).to be_stopped expect(environment.reload).to be_stopped
end end
context 'when the build fails' do context 'when the job fails' do
before do before do
build.update!(status: :failed) job.update!(status: :failed)
environment.update!(state: :available) environment.update!(state: :available)
end end
@ -34,9 +32,26 @@ RSpec.describe Environments::StopJobSuccessWorker, feature_category: :continuous
end end
end end
end end
context 'with build job' do
let!(:job) do
create(:ci_build, :stop_review_app, environment: environment.name, project: environment.project,
status: :success)
end end
context 'when build does not exist' do 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 it 'does not raise exception' do
expect { described_class.new.perform(123) } expect { described_class.new.perform(123) }
.not_to raise_error .not_to raise_error