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/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'

View File

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

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

View File

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

View File

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

View File

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

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

View File

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

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 [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.

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.
### 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:

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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

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

View File

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

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

View File

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

View File

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

View File

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

View File

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

View File

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

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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

View File

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

View File

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

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

View File

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

View File

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

View File

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

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

View File

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

View File

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

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(: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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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