Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
34f4e3a054
commit
c593b347c9
|
@ -23,7 +23,7 @@ review-cleanup:
|
|||
- ruby -rrubygems scripts/review_apps/automated_cleanup.rb
|
||||
- gcp_cleanup
|
||||
|
||||
.review-app-pipeline-generate:
|
||||
review-app-pipeline-generate:
|
||||
image: ${GITLAB_DEPENDENCY_PROXY}ruby:${RUBY_VERSION}
|
||||
stage: prepare
|
||||
extends:
|
||||
|
@ -52,8 +52,7 @@ review-cleanup:
|
|||
exit $exit_code
|
||||
fi
|
||||
|
||||
# Temporarily disable review-apps due to https://gitlab.com/gitlab-org/gitlab/-/issues/371809
|
||||
.start-review-app-pipeline:
|
||||
start-review-app-pipeline:
|
||||
extends:
|
||||
- .review:rules:start-review-app-pipeline
|
||||
resource_group: review/${CI_COMMIT_REF_SLUG}${SCHEDULE_TYPE} # CI_ENVIRONMENT_SLUG is not available here and we want this to be the same as the environment
|
||||
|
|
|
@ -0,0 +1,79 @@
|
|||
<script>
|
||||
import { GlFormGroup, GlFormInput } from '@gitlab/ui';
|
||||
|
||||
import {
|
||||
DUPLICATES_SETTING_EXCEPTION_TITLE,
|
||||
DUPLICATES_SETTINGS_EXCEPTION_LEGEND,
|
||||
} from '~/packages_and_registries/settings/group/constants';
|
||||
|
||||
export default {
|
||||
name: 'ExceptionsInput',
|
||||
i18n: {
|
||||
DUPLICATES_SETTING_EXCEPTION_TITLE,
|
||||
DUPLICATES_SETTINGS_EXCEPTION_LEGEND,
|
||||
},
|
||||
components: {
|
||||
GlFormGroup,
|
||||
GlFormInput,
|
||||
},
|
||||
props: {
|
||||
loading: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false,
|
||||
},
|
||||
duplicatesAllowed: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
required: false,
|
||||
},
|
||||
duplicateExceptionRegex: {
|
||||
type: String,
|
||||
default: '',
|
||||
required: false,
|
||||
},
|
||||
duplicateExceptionRegexError: {
|
||||
type: String,
|
||||
default: '',
|
||||
required: false,
|
||||
},
|
||||
id: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
name: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
isExceptionRegexValid() {
|
||||
return !this.duplicateExceptionRegexError;
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
update(type, value) {
|
||||
this.$emit('update', { [type]: value });
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<gl-form-group
|
||||
class="gl-mb-0"
|
||||
:label="$options.i18n.DUPLICATES_SETTING_EXCEPTION_TITLE"
|
||||
label-sr-only
|
||||
:invalid-feedback="duplicateExceptionRegexError"
|
||||
:label-for="id"
|
||||
>
|
||||
<gl-form-input
|
||||
:id="id"
|
||||
:disabled="duplicatesAllowed || loading"
|
||||
size="lg"
|
||||
:value="duplicateExceptionRegex"
|
||||
:state="isExceptionRegexValid"
|
||||
@change="update(name, $event)"
|
||||
/>
|
||||
</gl-form-group>
|
||||
</template>
|
|
@ -1,27 +1,50 @@
|
|||
<script>
|
||||
import DuplicatesSettings from '~/packages_and_registries/settings/group/components/duplicates_settings.vue';
|
||||
import GenericSettings from '~/packages_and_registries/settings/group/components/generic_settings.vue';
|
||||
import MavenSettings from '~/packages_and_registries/settings/group/components/maven_settings.vue';
|
||||
import { GlTableLite, GlToggle } from '@gitlab/ui';
|
||||
import {
|
||||
GENERIC_PACKAGE_FORMAT,
|
||||
MAVEN_PACKAGE_FORMAT,
|
||||
PACKAGE_FORMATS_TABLE_HEADER,
|
||||
PACKAGE_SETTINGS_HEADER,
|
||||
PACKAGE_SETTINGS_DESCRIPTION,
|
||||
DUPLICATES_SETTING_EXCEPTION_TITLE,
|
||||
DUPLICATES_TOGGLE_LABEL,
|
||||
} from '~/packages_and_registries/settings/group/constants';
|
||||
import updateNamespacePackageSettings from '~/packages_and_registries/settings/group/graphql/mutations/update_group_packages_settings.mutation.graphql';
|
||||
import { updateGroupPackageSettings } from '~/packages_and_registries/settings/group/graphql/utils/cache_update';
|
||||
import { updateGroupPackagesSettingsOptimisticResponse } from '~/packages_and_registries/settings/group/graphql/utils/optimistic_responses';
|
||||
import SettingsBlock from '~/packages_and_registries/shared/components/settings_block.vue';
|
||||
import ExceptionsInput from '~/packages_and_registries/settings/group/components/exceptions_input.vue';
|
||||
|
||||
export default {
|
||||
name: 'PackageSettings',
|
||||
i18n: {
|
||||
PACKAGE_SETTINGS_HEADER,
|
||||
PACKAGE_SETTINGS_DESCRIPTION,
|
||||
DUPLICATES_SETTING_EXCEPTION_TITLE,
|
||||
DUPLICATES_TOGGLE_LABEL,
|
||||
},
|
||||
tableHeaderFields: [
|
||||
{
|
||||
key: 'packageFormat',
|
||||
label: PACKAGE_FORMATS_TABLE_HEADER,
|
||||
thClass: 'gl-bg-gray-10!',
|
||||
},
|
||||
{
|
||||
key: 'allowDuplicates',
|
||||
label: DUPLICATES_TOGGLE_LABEL,
|
||||
thClass: 'gl-bg-gray-10!',
|
||||
},
|
||||
{
|
||||
key: 'exceptions',
|
||||
label: DUPLICATES_SETTING_EXCEPTION_TITLE,
|
||||
thClass: 'gl-bg-gray-10!',
|
||||
},
|
||||
],
|
||||
components: {
|
||||
SettingsBlock,
|
||||
MavenSettings,
|
||||
GenericSettings,
|
||||
DuplicatesSettings,
|
||||
GlTableLite,
|
||||
GlToggle,
|
||||
ExceptionsInput,
|
||||
},
|
||||
inject: ['groupPath'],
|
||||
props: {
|
||||
|
@ -40,6 +63,37 @@ export default {
|
|||
errors: {},
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
tableRows() {
|
||||
return [
|
||||
{
|
||||
id: 'maven-duplicated-settings-regex-input',
|
||||
format: MAVEN_PACKAGE_FORMAT,
|
||||
duplicatesAllowed: this.packageSettings.mavenDuplicatesAllowed,
|
||||
duplicateExceptionRegex: this.packageSettings.mavenDuplicateExceptionRegex,
|
||||
duplicateExceptionRegexError: this.errors.mavenDuplicateExceptionRegex,
|
||||
modelNames: {
|
||||
allowed: 'mavenDuplicatesAllowed',
|
||||
exception: 'mavenDuplicateExceptionRegex',
|
||||
},
|
||||
testid: 'maven-settings',
|
||||
dataQaSelector: 'allow_duplicates_toggle',
|
||||
},
|
||||
{
|
||||
id: 'generic-duplicated-settings-regex-input',
|
||||
format: GENERIC_PACKAGE_FORMAT,
|
||||
duplicatesAllowed: this.packageSettings.genericDuplicatesAllowed,
|
||||
duplicateExceptionRegex: this.packageSettings.genericDuplicateExceptionRegex,
|
||||
duplicateExceptionRegexError: this.errors.genericDuplicateExceptionRegex,
|
||||
modelNames: {
|
||||
allowed: 'genericDuplicatesAllowed',
|
||||
exception: 'genericDuplicateExceptionRegex',
|
||||
},
|
||||
testid: 'generic-settings',
|
||||
},
|
||||
];
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
async updateSettings(payload) {
|
||||
this.errors = {};
|
||||
|
@ -79,6 +133,9 @@ export default {
|
|||
this.$emit('error');
|
||||
}
|
||||
},
|
||||
update(type, value) {
|
||||
this.updateSettings({ [type]: value });
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
@ -92,32 +149,40 @@ export default {
|
|||
</span>
|
||||
</template>
|
||||
<template #default>
|
||||
<maven-settings data-testid="maven-settings">
|
||||
<template #default="{ modelNames }">
|
||||
<duplicates-settings
|
||||
:duplicates-allowed="packageSettings.mavenDuplicatesAllowed"
|
||||
:duplicate-exception-regex="packageSettings.mavenDuplicateExceptionRegex"
|
||||
:duplicate-exception-regex-error="errors.mavenDuplicateExceptionRegex"
|
||||
:model-names="modelNames"
|
||||
:loading="isLoading"
|
||||
toggle-qa-selector="reject_duplicates_toggle"
|
||||
label-qa-selector="reject_duplicates_label"
|
||||
@update="updateSettings"
|
||||
<form>
|
||||
<gl-table-lite
|
||||
:fields="$options.tableHeaderFields"
|
||||
:items="tableRows"
|
||||
stacked="sm"
|
||||
:tbody-tr-attr="(item) => ({ 'data-testid': item.testid })"
|
||||
>
|
||||
<template #cell(packageFormat)="{ item }">
|
||||
<span class="gl-md-pt-3">{{ item.format }}</span>
|
||||
</template>
|
||||
<template #cell(allowDuplicates)="{ item }">
|
||||
<gl-toggle
|
||||
:data-qa-selector="item.dataQaSelector"
|
||||
:label="$options.i18n.DUPLICATES_TOGGLE_LABEL"
|
||||
:value="item.duplicatesAllowed"
|
||||
:disabled="isLoading"
|
||||
label-position="hidden"
|
||||
class="gl-align-items-flex-end gl-sm-align-items-flex-start"
|
||||
@change="update(item.modelNames.allowed, $event)"
|
||||
/>
|
||||
</template>
|
||||
</maven-settings>
|
||||
<generic-settings class="gl-mt-6" data-testid="generic-settings">
|
||||
<template #default="{ modelNames }">
|
||||
<duplicates-settings
|
||||
:duplicates-allowed="packageSettings.genericDuplicatesAllowed"
|
||||
:duplicate-exception-regex="packageSettings.genericDuplicateExceptionRegex"
|
||||
:duplicate-exception-regex-error="errors.genericDuplicateExceptionRegex"
|
||||
:model-names="modelNames"
|
||||
<template #cell(exceptions)="{ item }">
|
||||
<exceptions-input
|
||||
:id="item.id"
|
||||
:duplicates-allowed="item.duplicatesAllowed"
|
||||
:duplicate-exception-regex="item.duplicateExceptionRegex"
|
||||
:duplicate-exception-regex-error="item.duplicateExceptionRegexError"
|
||||
:name="item.modelNames.exception"
|
||||
:loading="isLoading"
|
||||
@update="updateSettings"
|
||||
/>
|
||||
</template>
|
||||
</generic-settings>
|
||||
</gl-table-lite>
|
||||
</form>
|
||||
</template>
|
||||
</settings-block>
|
||||
</template>
|
||||
|
|
|
@ -5,10 +5,11 @@ export const PACKAGE_SETTINGS_HEADER = s__('PackageRegistry|Duplicate packages')
|
|||
export const PACKAGE_SETTINGS_DESCRIPTION = s__(
|
||||
'PackageRegistry|Allow packages with the same name and version to be uploaded to the registry. The newest version of a package is always used when installing.',
|
||||
);
|
||||
export const PACKAGE_FORMATS_TABLE_HEADER = s__('PackageRegistry|Package formats');
|
||||
export const MAVEN_PACKAGE_FORMAT = s__('PackageRegistry|Maven');
|
||||
export const GENERIC_PACKAGE_FORMAT = s__('PackageRegistry|Generic');
|
||||
|
||||
export const DUPLICATES_TOGGLE_LABEL = s__(
|
||||
'PackageRegistry|Reject packages with the same name and version',
|
||||
);
|
||||
export const DUPLICATES_TOGGLE_LABEL = s__('PackageRegistry|Allow duplicates');
|
||||
export const DUPLICATES_SETTING_EXCEPTION_TITLE = __('Exceptions');
|
||||
export const DUPLICATES_SETTINGS_EXCEPTION_LEGEND = s__(
|
||||
'PackageRegistry|Publish packages if their name or version matches this regex.',
|
||||
|
|
|
@ -149,10 +149,14 @@ module MembershipActions
|
|||
[:direct]
|
||||
when 'only'
|
||||
[:inherited]
|
||||
else
|
||||
if Feature.enabled?(:webui_members_inherited_users, current_user)
|
||||
[:inherited, :direct, :shared_from_groups]
|
||||
else
|
||||
[:inherited, :direct]
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
MembershipActions.prepend_mod_with('MembershipActions')
|
||||
|
|
|
@ -47,7 +47,7 @@ class GroupMembersFinder < UnionFinder
|
|||
related_groups << Group.by_id(group.id) if include_relations&.include?(:direct)
|
||||
related_groups << group.ancestors if include_relations&.include?(:inherited)
|
||||
related_groups << group.descendants if include_relations&.include?(:descendants)
|
||||
related_groups << group.shared_with_groups.public_or_visible_to_user(user) if include_relations&.include?(:shared_from_groups)
|
||||
related_groups << Group.shared_into_ancestors(group).public_or_visible_to_user(user) if include_relations&.include?(:shared_from_groups)
|
||||
|
||||
find_union(related_groups, Group)
|
||||
end
|
||||
|
|
|
@ -8,12 +8,9 @@ class CommitStatus < Ci::ApplicationRecord
|
|||
include EnumWithNil
|
||||
include BulkInsertableAssociations
|
||||
include TaggableQueries
|
||||
include IgnorableColumns
|
||||
|
||||
self.table_name = 'ci_builds'
|
||||
|
||||
ignore_column :token, remove_with: '15.4', remove_after: '2022-08-22'
|
||||
|
||||
belongs_to :user
|
||||
belongs_to :project
|
||||
belongs_to :pipeline, class_name: 'Ci::Pipeline', foreign_key: :commit_id
|
||||
|
|
|
@ -186,6 +186,11 @@ class Group < Namespace
|
|||
where(project_creation_level: permitted_levels)
|
||||
end
|
||||
|
||||
scope :shared_into_ancestors, -> (group) do
|
||||
joins(:shared_group_links)
|
||||
.where(group_group_links: { shared_group_id: group.self_and_ancestors })
|
||||
end
|
||||
|
||||
class << self
|
||||
def sort_by_attribute(method)
|
||||
if method == 'storage_size_desc'
|
||||
|
|
|
@ -11,12 +11,12 @@
|
|||
|
||||
= form_tag namespace_project_branches_path, method: :post, id: "new-branch-form", class: "js-create-branch-form js-requires-input" do
|
||||
.form-group.row
|
||||
= label_tag :branch_name, nil, class: 'col-form-label col-sm-2'
|
||||
= label_tag :branch_name, _('Branch name'), class: 'col-form-label col-sm-2'
|
||||
.col-sm-10
|
||||
= text_field_tag :branch_name, params[:branch_name], required: true, autofocus: true, class: 'form-control js-branch-name monospace'
|
||||
.form-text.text-muted.text-danger.js-branch-name-error
|
||||
.form-group.row
|
||||
= label_tag :ref, 'Create from', class: 'col-form-label col-sm-2'
|
||||
= label_tag :ref, _('Create from'), class: 'col-form-label col-sm-2'
|
||||
.col-sm-10.create-from
|
||||
.dropdown
|
||||
= hidden_field_tag :ref, default_ref
|
||||
|
@ -24,7 +24,8 @@
|
|||
.text-left.dropdown-toggle-text= default_ref
|
||||
= sprite_icon('chevron-down', css_class: "dropdown-menu-toggle-icon gl-top-3")
|
||||
= render 'shared/ref_dropdown', dropdown_class: 'wide'
|
||||
.form-text.text-muted Existing branch name, tag, or commit SHA
|
||||
.form-text.text-muted
|
||||
= _('Existing branch name, tag, or commit SHA')
|
||||
.form-actions
|
||||
= render Pajamas::ButtonComponent.new(variant: :confirm, button_options: { type: 'submit', class: 'gl-mr-3' }) do
|
||||
= _('Create branch')
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
---
|
||||
name: webui_members_inherited_users
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/83214
|
||||
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/364078
|
||||
milestone: '15.4'
|
||||
type: development
|
||||
group: group::workspace
|
||||
default_enabled: false
|
|
@ -36,6 +36,7 @@ This is a partial list of the [RSpec metadata](https://relishapp.com/rspec/rspec
|
|||
| `:only` | The test is only to be run in specific execution contexts. See [test execution context selection](execution_context_selection.md) for more information. |
|
||||
| `:orchestrated` | The GitLab instance under test may be [configured by `gitlab-qa`](https://gitlab.com/gitlab-org/gitlab-qa/-/blob/master/docs/what_tests_can_be_run.md#orchestrated-tests) to be different to the default GitLab configuration, or `gitlab-qa` may launch additional services in separate Docker containers, or both. Tests tagged with `:orchestrated` are excluded when testing environments where we can't dynamically modify the GitLab configuration (for example, Staging). |
|
||||
| `:packages` | The test requires a GitLab instance that has the [Package Registry](../../../administration/packages/#gitlab-package-registry-administration) enabled. |
|
||||
| `:product_group` | Specifies what product group the test belongs to. See [Product sections, stages, groups, and categories](https://about.gitlab.com/handbook/product/categories) for the comprehensive groups list. |
|
||||
| `:quarantine` | The test has been [quarantined](https://about.gitlab.com/handbook/engineering/quality/quality-engineering/debugging-qa-test-failures/#quarantining-tests), runs in a separate job that only includes quarantined tests, and is allowed to fail. The test is skipped in its regular job so that if it fails it doesn't hold up the pipeline. Note that you can also [quarantine a test only when it runs in a specific context](execution_context_selection.md#quarantine-a-test-for-a-specific-environment). |
|
||||
| `:relative_url` | The test requires a GitLab instance to be installed under a [relative URL](../../../install/relative_url.md). |
|
||||
| `:reliable` | The test has been [promoted to a reliable test](https://about.gitlab.com/handbook/engineering/quality/quality-engineering/reliable-tests/#promoting-an-existing-test-to-reliable) meaning it passes consistently in all pipelines, including merge requests. |
|
||||
|
|
|
@ -125,12 +125,30 @@ To switch to Semgrep-based scanning early, you can:
|
|||
You can see how Semgrep-based scanning will work in your projects before the GitLab-managed Stable CI/CD template for SAST is updated.
|
||||
We recommend that you test this change in a merge request but continue using the Stable template in your default branch pipeline configuration.
|
||||
|
||||
To preview the new configuration:
|
||||
In GitLab 15.3, we [activated a feature flag](https://gitlab.com/gitlab-org/gitlab/-/issues/362179) to migrate security findings on the default branch from other analyzers to Semgrep.
|
||||
We plan to [plan to remove the deprecated analyzers](https://gitlab.com/gitlab-org/gitlab/-/issues/352554) from the Stable CI/CD template in GitLab 15.4.
|
||||
|
||||
To preview the upcoming changes to the CI/CD configuration:
|
||||
|
||||
1. Open an MR to switch from the Stable CI/CD template, `SAST.gitlab-ci.yaml`, to [the Latest template](https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/ci/templates/Jobs/SAST.latest.gitlab-ci.yml), `SAST.latest.gitlab-ci.yaml`.
|
||||
- On GitLab.com, use the latest template directly:
|
||||
|
||||
```yaml
|
||||
include:
|
||||
template: 'SAST.latest.gitlab-ci.yaml'
|
||||
```
|
||||
|
||||
- On a Self-Managed instance, download the template from GitLab.com:
|
||||
|
||||
```yaml
|
||||
include:
|
||||
remote: 'https://gitlab.com/gitlab-org/gitlab/-/blob/2851f4d5/lib/gitlab/ci/templates/Jobs/SAST.latest.gitlab-ci.yml'
|
||||
```
|
||||
|
||||
1. Verify that scanning jobs succeed in the MR. You'll notice findings from the removed analyzers in _Fixed_ and findings from Semgrep in _New_. (Some findings may show different names, descriptions, and severities, since GitLab manages and edits the Semgrep rulesets.)
|
||||
1. Close the MR.
|
||||
|
||||
- In GitLab 15.3, open an MR to switch from the Stable CI/CD template, `SAST.gitlab-ci.yaml`, to [the Latest template](https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/ci/templates/Jobs/SAST.latest.gitlab-ci.yml), `SAST.latest.gitlab-ci.yaml`.
|
||||
The `latest` template contains all changes that we [plan to release](https://gitlab.com/gitlab-org/gitlab/-/issues/352554) in the Stable template in GitLab 15.4.
|
||||
To learn more about Stable and Latest templates, see documentation on [CI/CD template versioning](../../../development/cicd/templates.md#versioning).
|
||||
- In GitLab 15.3 or earlier versions, follow the [steps to activate Semgrep-based scanning early](#activate-semgrep-based-scanning), but don't merge the MR you create.
|
||||
|
||||
## Customize analyzers
|
||||
|
||||
|
|
|
@ -242,7 +242,8 @@ module Gitlab
|
|||
# in such cases it is fine to ignore such connections
|
||||
return unless db_config
|
||||
|
||||
primary_model = self.database_base_models.fetch(db_config.name.to_sym)
|
||||
db_config_name = db_config.name.delete_suffix(LoadBalancing::LoadBalancer::REPLICA_SUFFIX)
|
||||
primary_model = self.database_base_models.fetch(db_config_name.to_sym)
|
||||
|
||||
self.schemas_to_base_models.select do |_, child_models|
|
||||
child_models.any? do |child_model|
|
||||
|
|
|
@ -24266,9 +24266,6 @@ msgstr ""
|
|||
msgid "Maximum allowable lifetime for access token (days)"
|
||||
msgstr ""
|
||||
|
||||
msgid "Maximum allowable lifetime for personal access token (days)"
|
||||
msgstr ""
|
||||
|
||||
msgid "Maximum allowed lifetime for SSH keys (in days)"
|
||||
msgstr ""
|
||||
|
||||
|
@ -27787,6 +27784,9 @@ msgstr ""
|
|||
msgid "PackageRegistry|Additional metadata"
|
||||
msgstr ""
|
||||
|
||||
msgid "PackageRegistry|Allow duplicates"
|
||||
msgstr ""
|
||||
|
||||
msgid "PackageRegistry|Allow packages with the same name and version to be uploaded to the registry. The newest version of a package is always used when installing."
|
||||
msgstr ""
|
||||
|
||||
|
@ -28005,6 +28005,9 @@ msgstr ""
|
|||
msgid "PackageRegistry|Package file deleted successfully"
|
||||
msgstr ""
|
||||
|
||||
msgid "PackageRegistry|Package formats"
|
||||
msgstr ""
|
||||
|
||||
msgid "PackageRegistry|Package has %{updatesCount} archived update"
|
||||
msgid_plural "PackageRegistry|Package has %{updatesCount} archived updates"
|
||||
msgstr[0] ""
|
||||
|
@ -28037,9 +28040,6 @@ msgstr ""
|
|||
msgid "PackageRegistry|Registry setup"
|
||||
msgstr ""
|
||||
|
||||
msgid "PackageRegistry|Reject packages with the same name and version"
|
||||
msgstr ""
|
||||
|
||||
msgid "PackageRegistry|Remove package"
|
||||
msgstr ""
|
||||
|
||||
|
|
|
@ -8,8 +8,7 @@ module QA
|
|||
|
||||
view 'app/assets/javascripts/packages_and_registries/settings/group/components/packages_settings.vue' do
|
||||
element :package_registry_settings_content
|
||||
element :reject_duplicates_toggle
|
||||
element :reject_duplicates_label
|
||||
element :allow_duplicates_toggle
|
||||
end
|
||||
|
||||
view 'app/assets/javascripts/packages_and_registries/settings/group/components/dependency_proxy_settings.vue' do
|
||||
|
@ -17,32 +16,32 @@ module QA
|
|||
element :dependency_proxy_setting_toggle
|
||||
end
|
||||
|
||||
def set_reject_duplicates_disabled
|
||||
def set_allow_duplicates_disabled
|
||||
within_element :package_registry_settings_content do
|
||||
click_on_reject_duplicates_button if duplicates_disabled?
|
||||
click_on_allow_duplicates_button if duplicates_enabled?
|
||||
end
|
||||
end
|
||||
|
||||
def set_reject_duplicates_enabled
|
||||
def set_allow_duplicates_enabled
|
||||
within_element :package_registry_settings_content do
|
||||
click_on_reject_duplicates_button unless duplicates_disabled?
|
||||
click_on_allow_duplicates_button unless duplicates_enabled?
|
||||
end
|
||||
end
|
||||
|
||||
def click_on_reject_duplicates_button
|
||||
with_reject_duplicates_button do |button|
|
||||
def click_on_allow_duplicates_button
|
||||
with_allow_duplicates_button do |button|
|
||||
button.click
|
||||
end
|
||||
end
|
||||
|
||||
def duplicates_disabled?
|
||||
with_reject_duplicates_button do |button|
|
||||
def duplicates_enabled?
|
||||
with_allow_duplicates_button do |button|
|
||||
button[:class].include?('is-checked')
|
||||
end
|
||||
end
|
||||
|
||||
def with_reject_duplicates_button
|
||||
within_element :reject_duplicates_toggle do
|
||||
def with_allow_duplicates_button
|
||||
within_element :allow_duplicates_toggle do
|
||||
toggle = find('button.gl-toggle:not(.is-disabled)')
|
||||
yield(toggle)
|
||||
end
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
module QA
|
||||
RSpec.describe 'Create' do
|
||||
describe 'Create a new project from a template' do
|
||||
describe 'Create a new project from a template', product_group: :source_code do
|
||||
let(:project) do
|
||||
Resource::Project.fabricate_via_api! do |project|
|
||||
project.name = 'templated-project'
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
module QA
|
||||
RSpec.describe 'Create' do
|
||||
describe 'Default branch name instance setting', :requires_admin, :skip_live_env do
|
||||
describe 'Default branch name instance setting', :requires_admin, :skip_live_env, product_group: :source_code do
|
||||
before(:context) do
|
||||
Runtime::ApplicationSettings.set_application_settings(default_branch_name: 'main')
|
||||
end
|
||||
|
|
|
@ -4,7 +4,7 @@ require 'airborne'
|
|||
|
||||
module QA
|
||||
RSpec.describe 'Create' do
|
||||
describe 'API basics' do
|
||||
describe 'API basics', product_group: :source_code do
|
||||
before(:context) do
|
||||
@api_client = Runtime::API::Client.new(:gitlab)
|
||||
end
|
||||
|
@ -103,7 +103,7 @@ module QA
|
|||
expect(response.headers[:expires]).to eq("Fri, 01 Jan 1990 00:00:00 GMT")
|
||||
expect(response.headers[:content_disposition]).to include("attachment")
|
||||
expect(response.headers[:content_disposition]).not_to include("inline")
|
||||
expect(response.headers[:content_type]).to include("image/svg+xml")
|
||||
expect(response.headers[:content_type]).to include("application/octet-stream")
|
||||
end
|
||||
|
||||
delete_project_request = Runtime::API::Request.new(@api_client, "/projects/#{sanitized_project_path}")
|
||||
|
|
|
@ -5,7 +5,8 @@ require 'digest'
|
|||
|
||||
module QA
|
||||
RSpec.describe 'Create' do
|
||||
describe 'Compare archives of different user projects with the same name and check they\'re different' do
|
||||
describe 'Compare archives of different user projects with the same name and check they\'re different',
|
||||
product_group: :source_code do
|
||||
include Support::API
|
||||
let(:project_name) { "project-archive-download-#{SecureRandom.hex(8)}" }
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
module QA
|
||||
RSpec.describe 'Create' do
|
||||
describe 'PostReceive idempotent' do
|
||||
describe 'PostReceive idempotent', product_group: :source_code do
|
||||
# Tests that a push does not result in multiple changes from repeated PostReceive executions.
|
||||
# One of the consequences would be duplicate push events
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
module QA
|
||||
RSpec.describe 'Create' do
|
||||
describe 'Repository Usage Quota', :skip_live_env, feature_flag: {
|
||||
describe 'Repository Usage Quota', :skip_live_env, product_group: :source_code, feature_flag: {
|
||||
name: 'gitaly_revlist_for_repo_size',
|
||||
scope: :global
|
||||
} do
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
module QA
|
||||
RSpec.describe 'Create' do
|
||||
describe 'Prereceive hook', product_group: :source_code do
|
||||
let(:project) do
|
||||
Resource::Project.fabricate_via_api! do |project|
|
||||
project.initialize_with_readme = true
|
||||
|
@ -21,7 +22,8 @@ module QA
|
|||
:skip_live_env,
|
||||
except: { job: 'review-qa-*' },
|
||||
testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/369053' do
|
||||
expect { project.create_repository_tag('v1.2.3') }.to raise_error
|
||||
expect { project.create_repository_tag('v1.2.3') }
|
||||
.to raise_error
|
||||
.with_message(
|
||||
/rejecting prereceive hook for projects with GL_PROJECT_PATH matching pattern reject-prereceive/
|
||||
)
|
||||
|
@ -30,3 +32,4 @@ module QA
|
|||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
module QA
|
||||
RSpec.describe 'Create' do
|
||||
describe 'File templates' do
|
||||
describe 'File templates', product_group: :source_code do
|
||||
include Runtime::Fixtures
|
||||
|
||||
let(:project) do
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
module QA
|
||||
RSpec.describe 'Create' do
|
||||
describe 'Create, list, and delete branches via web', :requires_admin do
|
||||
describe 'Create, list, and delete branches via web', :requires_admin, product_group: :source_code do
|
||||
master_branch = nil
|
||||
second_branch = 'second-branch'
|
||||
third_branch = 'third-branch'
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
module QA
|
||||
RSpec.describe 'Create' do
|
||||
describe 'Branch with unusual name' do
|
||||
describe 'Branch with unusual name', product_group: :source_code do
|
||||
let(:branch_name) { 'unUsually/named#br--anch' }
|
||||
let(:project) do
|
||||
Resource::Project.fabricate_via_api! do |resource|
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
module QA
|
||||
RSpec.describe 'Create' do
|
||||
describe 'Git clone over HTTP' do
|
||||
describe 'Git clone over HTTP', product_group: :source_code do
|
||||
let(:project) do
|
||||
Resource::Project.fabricate_via_api! do |scenario|
|
||||
scenario.name = 'project-with-code'
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module QA
|
||||
RSpec.describe 'Create', :reliable do
|
||||
context 'File management' do
|
||||
RSpec.describe 'Create', :reliable, product_group: :source_code do
|
||||
describe 'File management' do
|
||||
file_name = 'QA Test - File name'
|
||||
file_content = 'QA Test - File content'
|
||||
commit_message_for_create = 'QA Test - Create new file'
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
module QA
|
||||
RSpec.describe 'Create' do
|
||||
context 'File management' do
|
||||
describe 'File management', product_group: :source_code do
|
||||
let(:file) { Resource::File.fabricate_via_api! }
|
||||
|
||||
commit_message_for_delete = 'QA Test - Delete file'
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
module QA
|
||||
RSpec.describe 'Create', :reliable do
|
||||
context 'File management' do
|
||||
describe 'File management', product_group: :source_code do
|
||||
let(:file) { Resource::File.fabricate_via_api! }
|
||||
|
||||
updated_file_content = 'QA Test - Updated file content'
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
module QA
|
||||
RSpec.describe 'Create' do
|
||||
describe 'File with unusual name' do
|
||||
describe 'File with unusual name', product_group: :source_code do
|
||||
let(:file_name) { '-un:usually;named#file?.md' }
|
||||
let(:project) do
|
||||
Resource::Project.fabricate_via_api! do |resource|
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
module QA
|
||||
RSpec.describe 'Create' do
|
||||
describe 'Repository License Detection' do
|
||||
describe 'Repository License Detection', product_group: :source_code do
|
||||
after do
|
||||
project.remove_via_api!
|
||||
end
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module QA
|
||||
RSpec.describe 'Create', :orchestrated, :repository_storage, :requires_admin do
|
||||
RSpec.describe 'Create', :orchestrated, :repository_storage, :requires_admin, product_group: :source_code do
|
||||
describe 'Gitaly repository storage' do
|
||||
let(:user) { Resource::User.fabricate_or_use(Runtime::Env.gitlab_qa_username_1, Runtime::Env.gitlab_qa_password_1) }
|
||||
let(:parent_project) do
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
module QA
|
||||
RSpec.describe 'Create' do
|
||||
describe 'Repository tags', :reliable do
|
||||
describe 'Repository tags', :reliable, product_group: :source_code do
|
||||
let(:project) do
|
||||
Resource::Project.fabricate_via_api! do |project|
|
||||
project.name = 'project-for-tags'
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
module QA
|
||||
RSpec.describe 'Create' do
|
||||
describe 'Push over HTTP using Git protocol version 2', :requires_git_protocol_v2 do
|
||||
describe 'Push over HTTP using Git protocol version 2', :requires_git_protocol_v2, product_group: :source_code do
|
||||
it 'user pushes to the repository', testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347760' do
|
||||
Flow::Login.sign_in
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
module QA
|
||||
RSpec.describe 'Create' do
|
||||
describe 'Push over SSH using Git protocol version 2', :requires_git_protocol_v2 do
|
||||
describe 'Push over SSH using Git protocol version 2', :requires_git_protocol_v2, product_group: :source_code do
|
||||
# Note: If you run this test against GDK make sure you've enabled sshd and
|
||||
# enabled setting the Git protocol by adding `AcceptEnv GIT_PROTOCOL` to
|
||||
# `sshd_config`
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
module QA
|
||||
RSpec.describe 'Create' do
|
||||
describe 'Git push over HTTP', :smoke do
|
||||
describe 'Git push over HTTP', :smoke, product_group: :source_code do
|
||||
it 'user using a personal access token pushes code to the repository', testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347749' do
|
||||
Flow::Login.sign_in
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module QA
|
||||
RSpec.describe 'Create', quarantine: {
|
||||
RSpec.describe 'Create', product_group: :source_code, quarantine: {
|
||||
issue: 'https://gitlab.com/gitlab-org/gitlab/-/issues/352525',
|
||||
type: :test_environment,
|
||||
only: { job: 'review-qa-*' }
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
module QA
|
||||
RSpec.describe 'Create' do
|
||||
describe 'Push mirror a repository over HTTP' do
|
||||
describe 'Push mirror a repository over HTTP', product_group: :source_code do
|
||||
it 'configures and syncs a (push) mirrored repository', testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347741' do
|
||||
Runtime::Browser.visit(:gitlab, Page::Main::Login)
|
||||
Page::Main::Login.perform(&:sign_in_using_credentials)
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
module QA
|
||||
# This test modifies an instance level setting,
|
||||
# so skipping on live envs to avoid random transient issues
|
||||
RSpec.describe 'Create', :requires_admin, :skip_live_env do
|
||||
RSpec.describe 'Create', :requires_admin, :skip_live_env, product_group: :source_code do
|
||||
describe 'push after setting the file size limit via admin/application_settings' do
|
||||
include Support::API
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
module QA
|
||||
RSpec.describe 'Create' do
|
||||
describe 'Git push over HTTP' do
|
||||
describe 'Git push over HTTP', product_group: :source_code do
|
||||
it 'user pushes code to the repository', :smoke, testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347747' do
|
||||
Flow::Login.sign_in
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
module QA
|
||||
RSpec.describe 'Create' do
|
||||
describe 'SSH key support' do
|
||||
describe 'SSH key support', product_group: :source_code do
|
||||
# Note: If you run these tests against GDK make sure you've enabled sshd
|
||||
# See: https://gitlab.com/gitlab-org/gitlab-qa/blob/master/docs/run_qa_against_gdk.md
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module QA
|
||||
RSpec.describe 'Create', :reliable do
|
||||
RSpec.describe 'Create', :reliable, product_group: :source_code do
|
||||
describe 'Protected branch support' do
|
||||
let(:branch_name) { 'protected-branch' }
|
||||
let(:commit_message) { 'Protected push commit message' }
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module QA
|
||||
RSpec.describe 'Create', only: { subdomain: %i[staging staging-canary] } do
|
||||
RSpec.describe 'Create', only: { subdomain: %i[staging staging-canary] }, product_group: :source_code do
|
||||
describe 'Git push to canary Gitaly node over HTTP' do
|
||||
it 'pushes to a project using a canary specific Gitaly repository storage', :smoke, :requires_admin, testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/351116' do
|
||||
Flow::Login.sign_in_as_admin
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
module QA
|
||||
RSpec.describe 'Create' do
|
||||
describe 'SSH keys support', :smoke do
|
||||
describe 'SSH keys support', :smoke, product_group: :source_code do
|
||||
let(:key_title) { "key for ssh tests #{Time.now.to_f}" }
|
||||
|
||||
key = nil
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
module QA
|
||||
RSpec.describe 'Create' do
|
||||
describe 'Commit data' do
|
||||
describe 'Commit data', product_group: :source_code do
|
||||
before(:context) do
|
||||
# Get the user's details to confirm they're included in the email patch
|
||||
@user = Resource::User.fabricate_via_api! do |user|
|
||||
|
|
|
@ -142,7 +142,7 @@ module QA
|
|||
Page::Group::Menu.perform(&:go_to_package_settings)
|
||||
end
|
||||
|
||||
context 'when enabled' do
|
||||
context 'when disabled' do
|
||||
where do
|
||||
{
|
||||
'using a personal access token' => {
|
||||
|
@ -176,7 +176,7 @@ module QA
|
|||
end
|
||||
|
||||
before do
|
||||
Page::Group::Settings::PackageRegistries.perform(&:set_reject_duplicates_enabled)
|
||||
Page::Group::Settings::PackageRegistries.perform(&:set_allow_duplicates_disabled)
|
||||
end
|
||||
|
||||
it 'prevents users from publishing group level Maven packages duplicates', testcase: params[:testcase] do
|
||||
|
@ -195,7 +195,7 @@ module QA
|
|||
end
|
||||
end
|
||||
|
||||
context 'when disabled' do
|
||||
context 'when enabled' do
|
||||
where do
|
||||
{
|
||||
'using a personal access token' => {
|
||||
|
@ -229,7 +229,7 @@ module QA
|
|||
end
|
||||
|
||||
before do
|
||||
Page::Group::Settings::PackageRegistries.perform(&:set_reject_duplicates_disabled)
|
||||
Page::Group::Settings::PackageRegistries.perform(&:set_allow_duplicates_enabled)
|
||||
end
|
||||
|
||||
it 'allows users to publish group level Maven packages duplicates', testcase: params[:testcase] do
|
||||
|
|
|
@ -97,6 +97,25 @@ RSpec.describe Groups::GroupMembersController do
|
|||
expect(assigns(:members).map(&:user_id)).to contain_exactly(user.id)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when webui_members_inherited_users is disabled' do
|
||||
let_it_be(:shared_group) { create(:group) }
|
||||
let_it_be(:shared_group_user) { create(:user) }
|
||||
let_it_be(:group_link) { create(:group_group_link, shared_group: shared_group, shared_with_group: group) }
|
||||
|
||||
before do
|
||||
group.add_owner(user)
|
||||
shared_group.add_owner(shared_group_user)
|
||||
stub_feature_flags(webui_members_inherited_users: false)
|
||||
sign_in(user)
|
||||
end
|
||||
|
||||
it 'lists inherited group members only' do
|
||||
get :index, params: { group_id: shared_group }
|
||||
|
||||
expect(assigns(:members).map(&:user_id)).to contain_exactly(shared_group_user.id)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'PUT update' do
|
||||
|
|
|
@ -5,6 +5,7 @@ require('spec_helper')
|
|||
RSpec.describe Projects::ProjectMembersController do
|
||||
let_it_be(:user) { create(:user) }
|
||||
let_it_be(:group) { create(:group, :public) }
|
||||
let_it_be(:sub_group) { create(:group, parent: group) }
|
||||
let_it_be(:project, reload: true) { create(:project, :public) }
|
||||
|
||||
before do
|
||||
|
@ -52,7 +53,36 @@ RSpec.describe Projects::ProjectMembersController do
|
|||
end
|
||||
end
|
||||
|
||||
context 'when invited members are present' do
|
||||
context 'when project belongs to a sub-group' do
|
||||
let_it_be(:user_in_group) { create(:user) }
|
||||
let_it_be(:project_in_group) { create(:project, :public, group: sub_group) }
|
||||
|
||||
before do
|
||||
group.add_owner(user_in_group)
|
||||
project_in_group.add_maintainer(user)
|
||||
sign_in(user)
|
||||
end
|
||||
|
||||
it 'lists inherited project members by default' do
|
||||
get :index, params: { namespace_id: project_in_group.namespace, project_id: project_in_group }
|
||||
|
||||
expect(assigns(:project_members).map(&:user_id)).to contain_exactly(user.id, user_in_group.id)
|
||||
end
|
||||
|
||||
it 'lists direct project members only' do
|
||||
get :index, params: { namespace_id: project_in_group.namespace, project_id: project_in_group, with_inherited_permissions: 'exclude' }
|
||||
|
||||
expect(assigns(:project_members).map(&:user_id)).to contain_exactly(user.id)
|
||||
end
|
||||
|
||||
it 'lists inherited project members only' do
|
||||
get :index, params: { namespace_id: project_in_group.namespace, project_id: project_in_group, with_inherited_permissions: 'only' }
|
||||
|
||||
expect(assigns(:project_members).map(&:user_id)).to contain_exactly(user_in_group.id)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when invited project members are present' do
|
||||
let!(:invited_member) { create(:project_member, :invited, project: project) }
|
||||
|
||||
before do
|
||||
|
|
|
@ -60,37 +60,38 @@ RSpec.describe 'Group Packages & Registries settings' do
|
|||
visit_settings_page
|
||||
|
||||
expect(page).to have_content('Duplicate packages')
|
||||
expect(page).to have_content('Allow duplicates')
|
||||
expect(page).to have_content('Exceptions')
|
||||
end
|
||||
|
||||
it 'automatically saves changes to the server', :js do
|
||||
visit_settings_page
|
||||
wait_for_requests
|
||||
|
||||
within '[data-testid="maven-settings"]' do
|
||||
expect(page).to have_content('Reject packages with the same name and version')
|
||||
expect(page).not_to have_content('Exceptions')
|
||||
expect(page).to have_field _('Exceptions'), disabled: true
|
||||
|
||||
find('.gl-toggle').click
|
||||
click_button class: 'gl-toggle'
|
||||
|
||||
expect(page).to have_content('Exceptions')
|
||||
expect(page).to have_field _('Exceptions'), disabled: false
|
||||
|
||||
visit_settings_page
|
||||
|
||||
expect(page).to have_content('Exceptions')
|
||||
expect(page).to have_field _('Exceptions'), disabled: false
|
||||
end
|
||||
end
|
||||
|
||||
it 'shows an error on wrong regex', :js do
|
||||
visit_settings_page
|
||||
wait_for_requests
|
||||
|
||||
within '[data-testid="maven-settings"]' do
|
||||
expect(page).to have_content('Reject packages with the same name and version')
|
||||
click_button class: 'gl-toggle'
|
||||
|
||||
find('.gl-toggle').click
|
||||
|
||||
fill_in 'Exceptions', with: ')'
|
||||
fill_in _('Exceptions'), with: ')'
|
||||
|
||||
# simulate blur event
|
||||
find('#maven-duplicated-settings-regex-input').native.send_keys(:tab)
|
||||
send_keys(:tab)
|
||||
end
|
||||
|
||||
expect(page).to have_content('is an invalid regexp')
|
||||
|
@ -99,13 +100,16 @@ RSpec.describe 'Group Packages & Registries settings' do
|
|||
context 'in a sub group' do
|
||||
it 'works correctly', :js do
|
||||
visit_sub_group_settings_page
|
||||
wait_for_requests
|
||||
|
||||
within '[data-testid="maven-settings"]' do
|
||||
expect(page).to have_content('Reject packages with the same name and version')
|
||||
expect(page).to have_content('Allow duplicates')
|
||||
|
||||
find('.gl-toggle').click
|
||||
expect(page).to have_field _('Exceptions'), disabled: true
|
||||
|
||||
expect(page).to have_content('Exceptions')
|
||||
click_button class: 'gl-toggle'
|
||||
|
||||
expect(page).to have_field _('Exceptions'), disabled: false
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -15,6 +15,16 @@ RSpec.describe "User creates branch", :js do
|
|||
visit(new_project_branch_path(project))
|
||||
end
|
||||
|
||||
context 'on new branch page' do
|
||||
it 'renders I18n supported text' do
|
||||
page.within('#new-branch-form') do
|
||||
expect(page).to have_content(_('Branch name'))
|
||||
expect(page).to have_content(_('Create from'))
|
||||
expect(page).to have_content(_('Existing branch name, tag, or commit SHA'))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
it "creates new branch" do
|
||||
branch_name = "deploy_keys"
|
||||
|
||||
|
|
|
@ -51,7 +51,8 @@ RSpec.describe GroupMembersFinder, '#execute' do
|
|||
user4_sub_group: create(:group_member, :developer, group: sub_group, user: user4, expires_at: 1.day.from_now),
|
||||
user4_group: create(:group_member, :developer, group: group, user: user4, expires_at: 2.days.from_now),
|
||||
user4_public_shared_group: create(:group_member, :developer, group: public_shared_group, user: user4),
|
||||
user4_private_shared_group: create(:group_member, :developer, group: private_shared_group, user: user4)
|
||||
user4_private_shared_group: create(:group_member, :developer, group: private_shared_group, user: user4),
|
||||
user5_private_shared_group: create(:group_member, :developer, group: private_shared_group, user: user5)
|
||||
}
|
||||
end
|
||||
|
||||
|
@ -76,15 +77,15 @@ RSpec.describe GroupMembersFinder, '#execute' do
|
|||
[:direct] | :sub_group | [:user1_sub_group, :user2_sub_group, :user3_sub_group, :user4_sub_group]
|
||||
[:inherited] | :sub_group | [:user1_group, :user2_group, :user3_group, :user4_group]
|
||||
[:descendants] | :sub_group | [:user1_sub_sub_group, :user2_sub_sub_group, :user3_sub_sub_group, :user4_sub_sub_group]
|
||||
[:shared_from_groups] | :sub_group | []
|
||||
[:direct, :inherited, :descendants, :shared_from_groups] | :sub_group | [:user1_sub_sub_group, :user2_group, :user3_sub_group, :user4_group]
|
||||
[:shared_from_groups] | :sub_group | [:user1_public_shared_group, :user2_public_shared_group, :user3_public_shared_group, :user4_public_shared_group]
|
||||
[:direct, :inherited, :descendants, :shared_from_groups] | :sub_group | [:user1_sub_sub_group, :user2_group, :user3_sub_group, :user4_public_shared_group]
|
||||
[] | :sub_sub_group | []
|
||||
GroupMembersFinder::DEFAULT_RELATIONS | :sub_sub_group | [:user1_sub_sub_group, :user2_group, :user3_sub_group, :user4_group]
|
||||
[:direct] | :sub_sub_group | [:user1_sub_sub_group, :user2_sub_sub_group, :user3_sub_sub_group, :user4_sub_sub_group]
|
||||
[:inherited] | :sub_sub_group | [:user1_sub_group, :user2_group, :user3_sub_group, :user4_group]
|
||||
[:descendants] | :sub_sub_group | []
|
||||
[:shared_from_groups] | :sub_sub_group | []
|
||||
[:direct, :inherited, :descendants, :shared_from_groups] | :sub_sub_group | [:user1_sub_sub_group, :user2_group, :user3_sub_group, :user4_group]
|
||||
[:shared_from_groups] | :sub_sub_group | [:user1_public_shared_group, :user2_public_shared_group, :user3_public_shared_group, :user4_public_shared_group]
|
||||
[:direct, :inherited, :descendants, :shared_from_groups] | :sub_sub_group | [:user1_sub_sub_group, :user2_group, :user3_sub_group, :user4_public_shared_group]
|
||||
end
|
||||
|
||||
with_them do
|
||||
|
|
|
@ -0,0 +1,108 @@
|
|||
import { GlSprintf, GlFormGroup, GlFormInput } from '@gitlab/ui';
|
||||
import { shallowMount } from '@vue/test-utils';
|
||||
import component from '~/packages_and_registries/settings/group/components/exceptions_input.vue';
|
||||
|
||||
import { DUPLICATES_SETTING_EXCEPTION_TITLE } from '~/packages_and_registries/settings/group/constants';
|
||||
|
||||
describe('Exceptions Input', () => {
|
||||
let wrapper;
|
||||
|
||||
const defaultProps = {
|
||||
duplicatesAllowed: false,
|
||||
duplicateExceptionRegex: 'foo',
|
||||
id: 'maven-duplicated-settings-regex-input',
|
||||
name: 'exceptionModel',
|
||||
};
|
||||
|
||||
const mountComponent = (propsData = defaultProps) => {
|
||||
wrapper = shallowMount(component, {
|
||||
propsData,
|
||||
stubs: {
|
||||
GlSprintf,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
afterEach(() => {
|
||||
wrapper.destroy();
|
||||
});
|
||||
|
||||
const findInputGroup = () => wrapper.findComponent(GlFormGroup);
|
||||
const findInput = () => wrapper.findComponent(GlFormInput);
|
||||
|
||||
it('shows a form group with an input field', () => {
|
||||
mountComponent();
|
||||
|
||||
expect(findInputGroup().exists()).toBe(true);
|
||||
|
||||
expect(findInputGroup().attributes()).toMatchObject({
|
||||
'label-for': defaultProps.id,
|
||||
label: DUPLICATES_SETTING_EXCEPTION_TITLE,
|
||||
'label-sr-only': '',
|
||||
});
|
||||
});
|
||||
|
||||
it('shows an input field', () => {
|
||||
mountComponent();
|
||||
|
||||
expect(findInput().exists()).toBe(true);
|
||||
|
||||
expect(findInput().attributes()).toMatchObject({
|
||||
id: 'maven-duplicated-settings-regex-input',
|
||||
value: defaultProps.duplicateExceptionRegex,
|
||||
});
|
||||
});
|
||||
|
||||
it('input change event emits an update event', () => {
|
||||
mountComponent();
|
||||
|
||||
findInput().vm.$emit('change', 'bar');
|
||||
|
||||
expect(wrapper.emitted('update')).toStrictEqual([[{ [defaultProps.name]: 'bar' }]]);
|
||||
});
|
||||
|
||||
describe('valid state', () => {
|
||||
beforeEach(() => {
|
||||
mountComponent();
|
||||
});
|
||||
|
||||
it('form group has correct props', () => {
|
||||
expect(findInputGroup().attributes('input-feedback')).toBeUndefined();
|
||||
});
|
||||
|
||||
it('form input has correct props', () => {
|
||||
expect(findInput().attributes('state')).toBe('true');
|
||||
});
|
||||
});
|
||||
|
||||
describe('invalid state', () => {
|
||||
const propsWithError = {
|
||||
...defaultProps,
|
||||
duplicateExceptionRegexError: 'some error string',
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
mountComponent(propsWithError);
|
||||
});
|
||||
|
||||
it('form group has correct props', () => {
|
||||
expect(findInputGroup().attributes('invalid-feedback')).toBe(
|
||||
propsWithError.duplicateExceptionRegexError,
|
||||
);
|
||||
});
|
||||
|
||||
it('form input has correct props', () => {
|
||||
expect(findInput().attributes('state')).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe('loading', () => {
|
||||
beforeEach(() => {
|
||||
mountComponent({ ...defaultProps, loading: true });
|
||||
});
|
||||
|
||||
it('disables the form input', () => {
|
||||
expect(findInput().attributes('disabled')).toBe('true');
|
||||
});
|
||||
});
|
||||
});
|
|
@ -1,13 +1,13 @@
|
|||
import Vue, { nextTick } from 'vue';
|
||||
import { GlToggle } from '@gitlab/ui';
|
||||
import VueApollo from 'vue-apollo';
|
||||
import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
|
||||
import { mountExtended, shallowMountExtended } from 'helpers/vue_test_utils_helper';
|
||||
import createMockApollo from 'helpers/mock_apollo_helper';
|
||||
import waitForPromises from 'helpers/wait_for_promises';
|
||||
import DuplicatesSettings from '~/packages_and_registries/settings/group/components/duplicates_settings.vue';
|
||||
import GenericSettings from '~/packages_and_registries/settings/group/components/generic_settings.vue';
|
||||
import ExceptionsInput from '~/packages_and_registries/settings/group/components/exceptions_input.vue';
|
||||
import component from '~/packages_and_registries/settings/group/components/packages_settings.vue';
|
||||
import MavenSettings from '~/packages_and_registries/settings/group/components/maven_settings.vue';
|
||||
import {
|
||||
DUPLICATES_TOGGLE_LABEL,
|
||||
PACKAGE_SETTINGS_HEADER,
|
||||
PACKAGE_SETTINGS_DESCRIPTION,
|
||||
} from '~/packages_and_registries/settings/group/constants';
|
||||
|
@ -35,6 +35,7 @@ describe('Packages Settings', () => {
|
|||
};
|
||||
|
||||
const mountComponent = ({
|
||||
mountFn = shallowMountExtended,
|
||||
mutationResolver = jest.fn().mockResolvedValue(groupPackageSettingsMutationMock()),
|
||||
} = {}) => {
|
||||
Vue.use(VueApollo);
|
||||
|
@ -43,7 +44,7 @@ describe('Packages Settings', () => {
|
|||
|
||||
apolloProvider = createMockApollo(requestHandlers);
|
||||
|
||||
wrapper = shallowMountExtended(component, {
|
||||
wrapper = mountFn(component, {
|
||||
apolloProvider,
|
||||
provide: defaultProvide,
|
||||
propsData: {
|
||||
|
@ -51,8 +52,6 @@ describe('Packages Settings', () => {
|
|||
},
|
||||
stubs: {
|
||||
SettingsBlock,
|
||||
MavenSettings,
|
||||
GenericSettings,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
@ -63,11 +62,15 @@ describe('Packages Settings', () => {
|
|||
|
||||
const findSettingsBlock = () => wrapper.findComponent(SettingsBlock);
|
||||
const findDescription = () => wrapper.findByTestId('description');
|
||||
const findMavenSettings = () => wrapper.findComponent(MavenSettings);
|
||||
const findMavenDuplicatedSettings = () => findMavenSettings().findComponent(DuplicatesSettings);
|
||||
const findGenericSettings = () => wrapper.findComponent(GenericSettings);
|
||||
const findGenericDuplicatedSettings = () =>
|
||||
findGenericSettings().findComponent(DuplicatesSettings);
|
||||
const findMavenSettings = () => wrapper.findByTestId('maven-settings');
|
||||
const findGenericSettings = () => wrapper.findByTestId('generic-settings');
|
||||
|
||||
const findMavenDuplicatedSettingsToggle = () => findMavenSettings().findComponent(GlToggle);
|
||||
const findGenericDuplicatedSettingsToggle = () => findGenericSettings().findComponent(GlToggle);
|
||||
const findMavenDuplicatedSettingsExceptionsInput = () =>
|
||||
findMavenSettings().findComponent(ExceptionsInput);
|
||||
const findGenericDuplicatedSettingsExceptionsInput = () =>
|
||||
findGenericSettings().findComponent(ExceptionsInput);
|
||||
|
||||
const fillApolloCache = () => {
|
||||
apolloProvider.defaultClient.cache.writeQuery({
|
||||
|
@ -80,7 +83,7 @@ describe('Packages Settings', () => {
|
|||
};
|
||||
|
||||
const emitMavenSettingsUpdate = (override) => {
|
||||
findMavenDuplicatedSettings().vm.$emit('update', {
|
||||
findGenericDuplicatedSettingsExceptionsInput().vm.$emit('update', {
|
||||
mavenDuplicateExceptionRegex: ')',
|
||||
...override,
|
||||
});
|
||||
|
@ -106,27 +109,46 @@ describe('Packages Settings', () => {
|
|||
|
||||
describe('maven settings', () => {
|
||||
it('exists', () => {
|
||||
mountComponent();
|
||||
mountComponent({ mountFn: mountExtended });
|
||||
|
||||
expect(findMavenSettings().exists()).toBe(true);
|
||||
expect(findMavenSettings().find('td').text()).toBe('Maven');
|
||||
});
|
||||
|
||||
it('assigns duplication allowness and exception props', async () => {
|
||||
mountComponent();
|
||||
it('renders toggle', () => {
|
||||
mountComponent({ mountFn: mountExtended });
|
||||
|
||||
const { mavenDuplicatesAllowed } = packageSettings();
|
||||
|
||||
expect(findMavenDuplicatedSettingsToggle().exists()).toBe(true);
|
||||
|
||||
expect(findMavenDuplicatedSettingsToggle().props()).toMatchObject({
|
||||
label: DUPLICATES_TOGGLE_LABEL,
|
||||
value: mavenDuplicatesAllowed,
|
||||
disabled: false,
|
||||
labelPosition: 'hidden',
|
||||
});
|
||||
});
|
||||
|
||||
it('renders ExceptionsInput and assigns duplication allowness and exception props', () => {
|
||||
mountComponent({ mountFn: mountExtended });
|
||||
|
||||
const { mavenDuplicatesAllowed, mavenDuplicateExceptionRegex } = packageSettings();
|
||||
|
||||
expect(findMavenDuplicatedSettings().props()).toMatchObject({
|
||||
expect(findMavenDuplicatedSettingsExceptionsInput().exists()).toBe(true);
|
||||
|
||||
expect(findMavenDuplicatedSettingsExceptionsInput().props()).toMatchObject({
|
||||
duplicatesAllowed: mavenDuplicatesAllowed,
|
||||
duplicateExceptionRegex: mavenDuplicateExceptionRegex,
|
||||
duplicateExceptionRegexError: '',
|
||||
loading: false,
|
||||
name: 'mavenDuplicateExceptionRegex',
|
||||
id: 'maven-duplicated-settings-regex-input',
|
||||
});
|
||||
});
|
||||
|
||||
it('on update event calls the mutation', () => {
|
||||
const mutationResolver = jest.fn().mockResolvedValue(groupPackageSettingsMutationMock());
|
||||
mountComponent({ mutationResolver });
|
||||
mountComponent({ mountFn: mountExtended, mutationResolver });
|
||||
|
||||
fillApolloCache();
|
||||
|
||||
|
@ -140,31 +162,47 @@ describe('Packages Settings', () => {
|
|||
|
||||
describe('generic settings', () => {
|
||||
it('exists', () => {
|
||||
mountComponent();
|
||||
mountComponent({ mountFn: mountExtended });
|
||||
|
||||
expect(findGenericSettings().exists()).toBe(true);
|
||||
expect(findGenericSettings().find('td').text()).toBe('Generic');
|
||||
});
|
||||
|
||||
it('assigns duplication allowness and exception props', async () => {
|
||||
mountComponent();
|
||||
it('renders toggle', () => {
|
||||
mountComponent({ mountFn: mountExtended });
|
||||
|
||||
const { genericDuplicatesAllowed } = packageSettings();
|
||||
|
||||
expect(findGenericDuplicatedSettingsToggle().exists()).toBe(true);
|
||||
expect(findGenericDuplicatedSettingsToggle().props()).toMatchObject({
|
||||
label: DUPLICATES_TOGGLE_LABEL,
|
||||
value: genericDuplicatesAllowed,
|
||||
disabled: false,
|
||||
labelPosition: 'hidden',
|
||||
});
|
||||
});
|
||||
|
||||
it('renders ExceptionsInput and assigns duplication allowness and exception props', async () => {
|
||||
mountComponent({ mountFn: mountExtended });
|
||||
|
||||
const { genericDuplicatesAllowed, genericDuplicateExceptionRegex } = packageSettings();
|
||||
|
||||
expect(findGenericDuplicatedSettings().props()).toMatchObject({
|
||||
expect(findGenericDuplicatedSettingsExceptionsInput().props()).toMatchObject({
|
||||
duplicatesAllowed: genericDuplicatesAllowed,
|
||||
duplicateExceptionRegex: genericDuplicateExceptionRegex,
|
||||
duplicateExceptionRegexError: '',
|
||||
loading: false,
|
||||
name: 'genericDuplicateExceptionRegex',
|
||||
id: 'generic-duplicated-settings-regex-input',
|
||||
});
|
||||
});
|
||||
|
||||
it('on update event calls the mutation', async () => {
|
||||
const mutationResolver = jest.fn().mockResolvedValue(groupPackageSettingsMutationMock());
|
||||
mountComponent({ mutationResolver });
|
||||
mountComponent({ mountFn: mountExtended, mutationResolver });
|
||||
|
||||
fillApolloCache();
|
||||
|
||||
findMavenDuplicatedSettings().vm.$emit('update', {
|
||||
findGenericDuplicatedSettingsExceptionsInput().vm.$emit('update', {
|
||||
genericDuplicateExceptionRegex: ')',
|
||||
});
|
||||
|
||||
|
@ -176,9 +214,11 @@ describe('Packages Settings', () => {
|
|||
|
||||
describe('settings update', () => {
|
||||
describe('success state', () => {
|
||||
it('emits a success event', async () => {
|
||||
mountComponent();
|
||||
beforeEach(() => {
|
||||
mountComponent({ mountFn: mountExtended });
|
||||
});
|
||||
|
||||
it('emits a success event', async () => {
|
||||
fillApolloCache();
|
||||
emitMavenSettingsUpdate();
|
||||
|
||||
|
@ -189,11 +229,12 @@ describe('Packages Settings', () => {
|
|||
|
||||
it('has an optimistic response', () => {
|
||||
const mavenDuplicateExceptionRegex = 'latest[main]something';
|
||||
mountComponent();
|
||||
|
||||
fillApolloCache();
|
||||
|
||||
expect(findMavenDuplicatedSettings().props('duplicateExceptionRegex')).toBe('');
|
||||
expect(
|
||||
findGenericDuplicatedSettingsExceptionsInput().props('duplicateExceptionRegex'),
|
||||
).toBe('');
|
||||
|
||||
emitMavenSettingsUpdate({ mavenDuplicateExceptionRegex });
|
||||
|
||||
|
@ -209,7 +250,7 @@ describe('Packages Settings', () => {
|
|||
// note this is a complex test that covers all the path around errors that are shown in the form
|
||||
// it's one single it case, due to the expensive preparation and execution
|
||||
const mutationResolver = jest.fn().mockResolvedValue(groupPackageSettingsMutationErrorMock);
|
||||
mountComponent({ mutationResolver });
|
||||
mountComponent({ mountFn: mountExtended, mutationResolver });
|
||||
|
||||
fillApolloCache();
|
||||
|
||||
|
@ -218,9 +259,9 @@ describe('Packages Settings', () => {
|
|||
await waitForPromises();
|
||||
|
||||
// errors are bound to the component
|
||||
expect(findMavenDuplicatedSettings().props('duplicateExceptionRegexError')).toBe(
|
||||
groupPackageSettingsMutationErrorMock.errors[0].extensions.problems[0].message,
|
||||
);
|
||||
expect(
|
||||
findMavenDuplicatedSettingsExceptionsInput().props('duplicateExceptionRegexError'),
|
||||
).toBe(groupPackageSettingsMutationErrorMock.errors[0].extensions.problems[0].message);
|
||||
|
||||
// general error message is shown
|
||||
|
||||
|
@ -231,7 +272,9 @@ describe('Packages Settings', () => {
|
|||
await nextTick();
|
||||
|
||||
// errors are reset on mutation call
|
||||
expect(findMavenDuplicatedSettings().props('duplicateExceptionRegexError')).toBe('');
|
||||
expect(
|
||||
findMavenDuplicatedSettingsExceptionsInput().props('duplicateExceptionRegexError'),
|
||||
).toBe('');
|
||||
});
|
||||
|
||||
it.each`
|
||||
|
@ -239,7 +282,7 @@ describe('Packages Settings', () => {
|
|||
${'local'} | ${jest.fn().mockResolvedValue(groupPackageSettingsMutationMock({ errors: ['foo'] }))}
|
||||
${'network'} | ${jest.fn().mockRejectedValue()}
|
||||
`('mutation payload with $type error', async ({ mutationResolver }) => {
|
||||
mountComponent({ mutationResolver });
|
||||
mountComponent({ mountFn: mountExtended, mutationResolver });
|
||||
|
||||
fillApolloCache();
|
||||
emitMavenSettingsUpdate();
|
||||
|
|
|
@ -237,6 +237,26 @@ RSpec.describe Gitlab::Database do
|
|||
end
|
||||
end
|
||||
|
||||
it 'does return a valid schema for a replica connection' do
|
||||
with_replica_pool_for(ActiveRecord::Base) do |main_replica_pool|
|
||||
expect(described_class.gitlab_schemas_for_connection(main_replica_pool.connection)).to include(:gitlab_main, :gitlab_shared)
|
||||
end
|
||||
|
||||
with_replica_pool_for(Ci::ApplicationRecord) do |ci_replica_pool|
|
||||
expect(described_class.gitlab_schemas_for_connection(ci_replica_pool.connection)).to include(:gitlab_ci, :gitlab_shared)
|
||||
end
|
||||
end
|
||||
|
||||
def with_replica_pool_for(base_model)
|
||||
config = Gitlab::Database::LoadBalancing::Configuration.new(base_model, [base_model.connection_pool.db_config.host])
|
||||
lb = Gitlab::Database::LoadBalancing::LoadBalancer.new(config)
|
||||
pool = lb.create_replica_connection_pool(1)
|
||||
|
||||
yield pool
|
||||
ensure
|
||||
pool&.disconnect!
|
||||
end
|
||||
|
||||
context "when there's CI connection", :request_store do
|
||||
before do
|
||||
skip_if_multiple_databases_not_setup
|
||||
|
|
|
@ -156,13 +156,20 @@ RSpec.describe 'getting group members information' do
|
|||
expect_array_response(child_user)
|
||||
end
|
||||
|
||||
it 'returns invited members plus inherited members' do
|
||||
it 'returns invited members and inherited members of a shared group' do
|
||||
fetch_members(group: child_group, args: { relations: [:DIRECT, :INHERITED, :SHARED_FROM_GROUPS] })
|
||||
|
||||
expect(graphql_errors).to be_nil
|
||||
expect_array_response(invited_user, user_1, user_2, child_user)
|
||||
end
|
||||
|
||||
it 'returns invited members and inherited members of an ancestor of a shared group' do
|
||||
fetch_members(group: grandchild_group, args: { relations: [:DIRECT, :INHERITED, :SHARED_FROM_GROUPS] })
|
||||
|
||||
expect(graphql_errors).to be_nil
|
||||
expect_array_response(grandchild_user, invited_user, user_1, user_2, child_user)
|
||||
end
|
||||
|
||||
it 'returns direct and inherited members' do
|
||||
fetch_members(group: child_group, args: { relations: [:DIRECT, :INHERITED] })
|
||||
|
||||
|
|
Loading…
Reference in New Issue