Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2022-08-26 03:12:38 +00:00
parent 34f4e3a054
commit c593b347c9
55 changed files with 611 additions and 187 deletions

View File

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

View File

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

View File

@ -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"
/>
</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"
:loading="isLoading"
@update="updateSettings"
/>
</template>
</generic-settings>
<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>
<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>
</gl-table-lite>
</form>
</template>
</settings-block>
</template>

View File

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

View File

@ -150,7 +150,11 @@ module MembershipActions
when 'only'
[:inherited]
else
[:inherited, :direct]
if Feature.enabled?(:webui_members_inherited_users, current_user)
[:inherited, :direct, :shared_from_groups]
else
[:inherited, :direct]
end
end
end
end

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -15,27 +15,28 @@ This is a partial list of the [RSpec metadata](https://relishapp.com/rspec/rspec
|-----------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `:elasticsearch` | The test requires an Elasticsearch service. It is used by the [instance-level scenario](https://gitlab.com/gitlab-org/gitlab-qa#definitions) [`Test::Integration::Elasticsearch`](https://gitlab.com/gitlab-org/gitlab/-/blob/72b62b51bdf513e2936301cb6c7c91ec27c35b4d/qa/qa/ee/scenario/test/integration/elasticsearch.rb) to include only tests that require Elasticsearch. |
| `:except` | The test is to be run in their typical execution contexts _except_ as specified. See [test execution context selection](execution_context_selection.md) for more information. |
| `:feature_flag` | The test uses a feature flag and therefore requires an administrator account to run. When `scope` is set to `:global`, the test will be skipped on all live .com environments. Otherwise, it will be skipped only on Canary, Production, and Preprod. See [testing with feature flags](../../../development/testing_guide/end_to_end/feature_flags.md) for more details. |
| `:feature_flag` | The test uses a feature flag and therefore requires an administrator account to run. When `scope` is set to `:global`, the test will be skipped on all live .com environments. Otherwise, it will be skipped only on Canary, Production, and Preprod. See [testing with feature flags](../../../development/testing_guide/end_to_end/feature_flags.md) for more details. |
| `:geo` | The test requires two GitLab Geo instances - a primary and a secondary - to be spun up. |
| `:gitaly_cluster` | The test runs against a GitLab instance where repositories are stored on redundant Gitaly nodes behind a Praefect node. All nodes are [separate containers](../../../administration/gitaly/praefect.md#requirements). Tests that use this tag have a longer setup time since there are three additional containers that need to be started. |
| `:github` | The test requires a GitHub personal access token. |
| `:group_saml` | The test requires a GitLab instance that has SAML SSO enabled at the group level. Interacts with an external SAML identity provider. Paired with the `:orchestrated` tag. |
| `:instance_saml` | The test requires a GitLab instance that has SAML SSO enabled at the instance level. Interacts with an external SAML identity provider. Paired with the `:orchestrated` tag. |
| `:integrations` | This aims to test the available [integrations](../../../user/project/integrations/index.md#available-integrations). The test requires Docker to be installed in the run context. It will provision the containers and can be run against a local instance or using the `gitlab-qa` scenario `Test::Integration::Integrations` |
| `:integrations` | This aims to test the available [integrations](../../../user/project/integrations/index.md#available-integrations). The test requires Docker to be installed in the run context. It will provision the containers and can be run against a local instance or using the `gitlab-qa` scenario `Test::Integration::Integrations` |
| `:issue`, `:issue_${num}` | Optional links to issues which might be related to the spec. Helps keep track of related issues and can also be used by tools that create test reports. Currently added automatically to `Allure` test report. Multiple tags can be used by adding an optional numeric suffix like `issue_1`, `issue_2` etc. |
| `:service_ping_disabled` | The test interacts with the GitLab configuration service ping at the instance level to turn Admin Area setting service ping checkbox on or off. This tag will have the test run only in the `service_ping_disabled` job and must be paired with the `:orchestrated` and `:requires_admin` tags. |
| `:service_ping_disabled` | The test interacts with the GitLab configuration service ping at the instance level to turn Admin Area setting service ping checkbox on or off. This tag will have the test run only in the `service_ping_disabled` job and must be paired with the `:orchestrated` and `:requires_admin` tags. |
| `:jira` | The test requires a Jira Server. [GitLab-QA](https://gitlab.com/gitlab-org/gitlab-qa) provisions the Jira Server in a Docker container when the `Test::Integration::Jira` test scenario is run. |
| `:kubernetes` | The test includes a GitLab instance that is configured to be run behind an SSH tunnel, allowing a TLS-accessible GitLab. This test also includes provisioning of at least one Kubernetes cluster to test against. _This tag is often be paired with `:orchestrated`._ |
| `:ldap_no_server` | The test requires a GitLab instance to be configured to use LDAP. To be used with the `:orchestrated` tag. It does not spin up an LDAP server at orchestration time. Instead, it creates the LDAP server at runtime. |
| `:ldap_no_tls` | The test requires a GitLab instance to be configured to use an external LDAP server with TLS not enabled. |
| `:ldap_tls` | The test requires a GitLab instance to be configured to use an external LDAP server with TLS enabled. |
| `:mattermost` | The test requires a GitLab Mattermost service on the GitLab instance. |
| `:metrics` | The test requires a GitLab instance where [dedicated metrics exporters](../../../administration/monitoring/prometheus/web_exporter.md) are running alongside Puma and Sidekiq. |
| `:metrics` | The test requires a GitLab instance where [dedicated metrics exporters](../../../administration/monitoring/prometheus/web_exporter.md) are running alongside Puma and Sidekiq. |
| `:mixed_env` | The test should only be executed in environments that have a paired canary version available through traffic routing based on the existence of the `gitlab_canary=true` cookie. Tests in this category are switching the cookie mid-test to validate mixed deployment environments. |
| `:object_storage` | The test requires a GitLab instance to be configured to use multiple [object storage types](../../../administration/object_storage.md). Uses MinIO as the object storage server. |
| `: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. |
@ -44,7 +45,7 @@ This is a partial list of the [RSpec metadata](https://relishapp.com/rspec/rspec
| `:requires_git_protocol_v2` | The test requires that Git protocol version 2 is enabled on the server. It's assumed to be enabled by default but if not the test can be skipped by setting `QA_CAN_TEST_GIT_PROTOCOL_V2` to `false`. |
| `:requires_praefect` | The test requires that the GitLab instance uses [Gitaly Cluster](../../../administration/gitaly/praefect.md) (a.k.a. Praefect) as the repository storage . It's assumed to be used by default but if not the test can be skipped by setting `QA_CAN_TEST_PRAEFECT` to `false`. |
| `:runner` | The test depends on and sets up a GitLab Runner instance, typically to run a pipeline. |
| `:sanity_feature_flags` | The test verifies the functioning of the feature flag handling part of the test framework |
| `:sanity_feature_flags` | The test verifies the functioning of the feature flag handling part of the test framework |
| `:skip_live_env` | The test is excluded when run against live deployed environments such as Staging, Canary, and Production. |
| `:skip_fips_env` | The test is excluded when run against an environment in FIPS mode. |
| `:skip_signup_disabled` | The test uses UI to sign up a new user and is skipped in any environment that does not allow new user registration via the UI. |

View File

@ -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.
- 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.
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.
To learn more about Stable and Latest templates, see documentation on [CI/CD template versioning](../../../development/cicd/templates.md#versioning).
## Customize analyzers

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -2,29 +2,32 @@
module QA
RSpec.describe 'Create' do
let(:project) do
Resource::Project.fabricate_via_api! do |project|
project.initialize_with_readme = true
end
end
context 'when creating a tag for a ref' do
context 'when it triggers a prereceive hook configured with a custom error' do
before do
# The configuration test prereceive hook must match a specific naming pattern
# In this test we create a project with a different name and then change the path.
# Otherwise we wouldn't be able create any commits to be tagged due to the hook.
project.change_path("project-reject-prereceive-#{SecureRandom.hex(8)}")
describe 'Prereceive hook', product_group: :source_code do
let(:project) do
Resource::Project.fabricate_via_api! do |project|
project.initialize_with_readme = true
end
end
it 'returns a custom server hook error',
: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
.with_message(
/rejecting prereceive hook for projects with GL_PROJECT_PATH matching pattern reject-prereceive/
)
context 'when creating a tag for a ref' do
context 'when it triggers a prereceive hook configured with a custom error' do
before do
# The configuration test prereceive hook must match a specific naming pattern
# In this test we create a project with a different name and then change the path.
# Otherwise we wouldn't be able create any commits to be tagged due to the hook.
project.change_path("project-reject-prereceive-#{SecureRandom.hex(8)}")
end
it 'returns a custom server hook error',
: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
.with_message(
/rejecting prereceive hook for projects with GL_PROJECT_PATH matching pattern reject-prereceive/
)
end
end
end
end

View File

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

View File

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

View File

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

View File

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

View File

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

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

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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