Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
e023756b39
commit
d592691fa5
|
|
@ -526,7 +526,6 @@ Layout/LineLength:
|
|||
- 'ee/app/helpers/ee/notes_helper.rb'
|
||||
- 'ee/app/helpers/ee/profiles_helper.rb'
|
||||
- 'ee/app/helpers/ee/subscribable_banner_helper.rb'
|
||||
- 'ee/app/helpers/epics_helper.rb'
|
||||
- 'ee/app/helpers/gitlab_subscriptions/upcoming_reconciliation_helper.rb'
|
||||
- 'ee/app/helpers/license_helper.rb'
|
||||
- 'ee/app/helpers/projects/on_demand_scans_helper.rb'
|
||||
|
|
@ -1222,7 +1221,6 @@ Layout/LineLength:
|
|||
- 'ee/spec/lib/gitlab/custom_file_templates_spec.rb'
|
||||
- 'ee/spec/lib/gitlab/data_builder/vulnerability_spec.rb'
|
||||
- 'ee/spec/lib/gitlab/elastic/group_search_results_spec.rb'
|
||||
- 'ee/spec/lib/gitlab/elastic/snippet_search_results_spec.rb'
|
||||
- 'ee/spec/lib/gitlab/email/handler/create_note_handler_spec.rb'
|
||||
- 'ee/spec/lib/gitlab/expiring_subscription_message_spec.rb'
|
||||
- 'ee/spec/lib/gitlab/geo/event_gap_tracking_spec.rb'
|
||||
|
|
|
|||
|
|
@ -318,7 +318,6 @@ RSpec/ContextWording:
|
|||
- 'ee/spec/lib/gitlab/code_owners/users_loader_spec.rb'
|
||||
- 'ee/spec/lib/gitlab/custom_file_templates_spec.rb'
|
||||
- 'ee/spec/lib/gitlab/elastic/group_search_results_spec.rb'
|
||||
- 'ee/spec/lib/gitlab/elastic/snippet_search_results_spec.rb'
|
||||
- 'ee/spec/lib/gitlab/email/handler/create_note_handler_spec.rb'
|
||||
- 'ee/spec/lib/gitlab/expiring_subscription_message_spec.rb'
|
||||
- 'ee/spec/lib/gitlab/geo/cron_manager_spec.rb'
|
||||
|
|
|
|||
|
|
@ -553,7 +553,6 @@ RSpec/FeatureCategory:
|
|||
- 'ee/spec/lib/gitlab/custom_file_templates_spec.rb'
|
||||
- 'ee/spec/lib/gitlab/customers_dot/jwt_spec.rb'
|
||||
- 'ee/spec/lib/gitlab/elastic/elasticsearch_enabled_cache_spec.rb'
|
||||
- 'ee/spec/lib/gitlab/elastic/snippet_search_results_spec.rb'
|
||||
- 'ee/spec/lib/gitlab/email/handler/create_note_handler_spec.rb'
|
||||
- 'ee/spec/lib/gitlab/expiring_subscription_message_spec.rb'
|
||||
- 'ee/spec/lib/gitlab/favicon_spec.rb'
|
||||
|
|
|
|||
|
|
@ -273,7 +273,6 @@ RSpec/NamedSubject:
|
|||
- 'ee/spec/lib/ee/sidebars/projects/menus/security_compliance_menu_spec.rb'
|
||||
- 'ee/spec/lib/ee/sidebars/projects/menus/settings_menu_spec.rb'
|
||||
- 'ee/spec/lib/elastic/latest/application_instance_proxy_spec.rb'
|
||||
- 'ee/spec/lib/elastic/latest/epic_class_proxy_spec.rb'
|
||||
- 'ee/spec/lib/elastic/latest/epic_instance_proxy_spec.rb'
|
||||
- 'ee/spec/lib/elastic/latest/git_instance_proxy_spec.rb'
|
||||
- 'ee/spec/lib/elastic/latest/routing_spec.rb'
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import { __ } from '~/locale';
|
||||
import groupsEmptyStateIllustration from '@gitlab/svgs/dist/illustrations/empty-state/empty-groups-md.svg?url';
|
||||
import { s__, __ } from '~/locale';
|
||||
import {
|
||||
SORT_LABEL_NAME,
|
||||
SORT_LABEL_CREATED,
|
||||
|
|
@ -7,6 +8,7 @@ import {
|
|||
} from '~/groups_projects/constants';
|
||||
import GroupsList from '~/vue_shared/components/groups_list/groups_list.vue';
|
||||
import { formatGraphQLGroups } from '~/vue_shared/components/groups_list/formatter';
|
||||
import ResourceListsEmptyState from '~/vue_shared/components/resource_lists/empty_state.vue';
|
||||
import adminGroupsQuery from './graphql/queries/groups.query.graphql';
|
||||
|
||||
const baseTab = {
|
||||
|
|
@ -24,6 +26,7 @@ const baseTab = {
|
|||
listItemClass: 'gl-px-5',
|
||||
showGroupIcon: true,
|
||||
},
|
||||
emptyStateComponent: ResourceListsEmptyState,
|
||||
query: adminGroupsQuery,
|
||||
queryPath: 'groups',
|
||||
};
|
||||
|
|
@ -34,6 +37,14 @@ export const ACTIVE_TAB = {
|
|||
value: 'active',
|
||||
variables: { active: true },
|
||||
countsQueryPath: 'active',
|
||||
emptyStateComponentProps: {
|
||||
svgPath: groupsEmptyStateIllustration,
|
||||
title: s__("Groups|You don't have any active groups yet."),
|
||||
description: s__(
|
||||
'Organization|A group is a collection of several projects. If you organize your projects under a group, it works like a folder.',
|
||||
),
|
||||
'data-testid': 'groups-empty-state',
|
||||
},
|
||||
};
|
||||
|
||||
export const INACTIVE_TAB = {
|
||||
|
|
@ -42,6 +53,11 @@ export const INACTIVE_TAB = {
|
|||
value: 'inactive',
|
||||
variables: { active: false },
|
||||
countsQueryPath: 'inactive',
|
||||
emptyStateComponentProps: {
|
||||
svgPath: groupsEmptyStateIllustration,
|
||||
title: s__("Groups|You don't have any inactive groups."),
|
||||
description: s__('Groups|Groups that are archived or pending deletion will appear here.'),
|
||||
},
|
||||
};
|
||||
|
||||
export const SORT_OPTION_NAME = {
|
||||
|
|
|
|||
|
|
@ -2,12 +2,14 @@
|
|||
import { RUNNER_TYPES } from '../constants';
|
||||
import RequiredFields from './runner_create_wizard_required_fields.vue';
|
||||
import OptionalFields from './runner_create_wizard_optional_fields.vue';
|
||||
import RunnerRegistration from './runner_create_wizard_registration.vue';
|
||||
|
||||
export default {
|
||||
name: 'RunnerCreateWizard',
|
||||
components: {
|
||||
RequiredFields,
|
||||
OptionalFields,
|
||||
RunnerRegistration,
|
||||
},
|
||||
props: {
|
||||
runnerType: {
|
||||
|
|
@ -58,4 +60,9 @@ export default {
|
|||
@next="onNext"
|
||||
@back="onBack"
|
||||
/>
|
||||
<runner-registration
|
||||
v-else-if="currentStep === 3"
|
||||
:current-step="currentStep"
|
||||
:steps-total="$options.stepsTotal"
|
||||
/>
|
||||
</template>
|
||||
|
|
|
|||
|
|
@ -1,12 +1,19 @@
|
|||
<script>
|
||||
import { GlForm, GlButton, GlFormGroup, GlFormInput, GlFormTextarea } from '@gitlab/ui';
|
||||
import { createAlert } from '~/alert';
|
||||
import MultiStepFormTemplate from '~/vue_shared/components/multi_step_form_template.vue';
|
||||
import MultipleChoiceSelector from '~/vue_shared/components/multiple_choice_selector.vue';
|
||||
import MultipleChoiceSelectorItem from '~/vue_shared/components/multiple_choice_selector_item.vue';
|
||||
import runnerCreateMutation from '~/ci/runner/graphql/new/runner_create.mutation.graphql';
|
||||
import { modelToUpdateMutationVariables } from 'ee_else_ce/ci/runner/runner_update_form_utils';
|
||||
import { captureException } from '../sentry_utils';
|
||||
import {
|
||||
DEFAULT_ACCESS_LEVEL,
|
||||
ACCESS_LEVEL_NOT_PROTECTED,
|
||||
ACCESS_LEVEL_REF_PROTECTED,
|
||||
GROUP_TYPE,
|
||||
PROJECT_TYPE,
|
||||
I18N_CREATE_ERROR,
|
||||
} from '../constants';
|
||||
|
||||
export default {
|
||||
|
|
@ -53,10 +60,30 @@ export default {
|
|||
runUntagged: this.runUntagged,
|
||||
locked: false,
|
||||
tagList: this.tags,
|
||||
maximumTimeout: '',
|
||||
maximumTimeout: null,
|
||||
},
|
||||
saving: false,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
mutationInput() {
|
||||
const { input } = modelToUpdateMutationVariables(this.runner);
|
||||
|
||||
if (this.runnerType === GROUP_TYPE) {
|
||||
return {
|
||||
...input,
|
||||
groupId: this.groupId,
|
||||
};
|
||||
}
|
||||
if (this.runnerType === PROJECT_TYPE) {
|
||||
return {
|
||||
...input,
|
||||
projectId: this.projectId,
|
||||
};
|
||||
}
|
||||
return input;
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
onCheckboxesInput(checked) {
|
||||
if (checked.includes(ACCESS_LEVEL_REF_PROTECTED))
|
||||
|
|
@ -65,12 +92,55 @@ export default {
|
|||
|
||||
this.runner.paused = checked.includes('paused');
|
||||
},
|
||||
async onSubmit() {
|
||||
this.saving = true;
|
||||
this.runner.maximumTimeout = parseInt(this.runner.maximumTimeout, 10);
|
||||
|
||||
try {
|
||||
const {
|
||||
data: {
|
||||
runnerCreate: { errors, runner },
|
||||
},
|
||||
} = await this.$apollo.mutate({
|
||||
mutation: runnerCreateMutation,
|
||||
variables: {
|
||||
input: this.mutationInput,
|
||||
},
|
||||
});
|
||||
|
||||
if (errors?.length) {
|
||||
this.onError(new Error(errors.join(' ')), true);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!runner?.ephemeralRegisterUrl) {
|
||||
// runner is missing information, report issue and
|
||||
// fail navigation to register page.
|
||||
this.onError(new Error(I18N_CREATE_ERROR));
|
||||
return;
|
||||
}
|
||||
|
||||
this.$emit('next');
|
||||
// destroy the alert
|
||||
createAlert({ message: null }).dismiss();
|
||||
} catch (error) {
|
||||
this.onError(error);
|
||||
}
|
||||
},
|
||||
onError(error, isValidationError = false) {
|
||||
if (!isValidationError) {
|
||||
captureException({ error, component: this.$options.name });
|
||||
}
|
||||
|
||||
createAlert({ message: error.message });
|
||||
this.saving = false;
|
||||
},
|
||||
},
|
||||
ACCESS_LEVEL_REF_PROTECTED,
|
||||
};
|
||||
</script>
|
||||
<template>
|
||||
<gl-form>
|
||||
<gl-form @submit.prevent="onSubmit">
|
||||
<multi-step-form-template
|
||||
:title="s__('Runners|Optional configuration details')"
|
||||
:current-step="currentStep"
|
||||
|
|
@ -79,7 +149,7 @@ export default {
|
|||
<template #form>
|
||||
<multiple-choice-selector class="gl-mb-5" @input="onCheckboxesInput">
|
||||
<multiple-choice-selector-item
|
||||
:value="ACCESS_LEVEL_REF_PROTECTED"
|
||||
:value="$options.ACCESS_LEVEL_REF_PROTECTED"
|
||||
:title="s__('Runners|Protected')"
|
||||
:description="s__('Runners|Use the runner on pipelines for protected branches only.')"
|
||||
/>
|
||||
|
|
@ -143,12 +213,12 @@ export default {
|
|||
</gl-form-group>
|
||||
</template>
|
||||
<template #next>
|
||||
<!-- [Next step] button will be un-disabled in https://gitlab.com/gitlab-org/gitlab/-/issues/396544 -->
|
||||
<gl-button
|
||||
category="primary"
|
||||
variant="confirm"
|
||||
:disabled="true"
|
||||
type="submit"
|
||||
class="js-no-auto-disable"
|
||||
:loading="saving"
|
||||
data-testid="next-button"
|
||||
>
|
||||
{{ __('Next step') }}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,37 @@
|
|||
<script>
|
||||
import { GlButton } from '@gitlab/ui';
|
||||
import MultiStepFormTemplate from '~/vue_shared/components/multi_step_form_template.vue';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
GlButton,
|
||||
MultiStepFormTemplate,
|
||||
},
|
||||
props: {
|
||||
currentStep: {
|
||||
type: Number,
|
||||
required: true,
|
||||
},
|
||||
stepsTotal: {
|
||||
type: Number,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
<template>
|
||||
<multi-step-form-template
|
||||
:title="s__('Runners|Register your new runner')"
|
||||
:current-step="currentStep"
|
||||
:steps-total="stepsTotal"
|
||||
>
|
||||
<template #form>
|
||||
<!-- Content will be added in https://gitlab.com/gitlab-org/gitlab/-/issues/396544 -->
|
||||
</template>
|
||||
<template #back>
|
||||
<gl-button category="primary" variant="default" :disabled="true">
|
||||
{{ s__('Runners|View runners') }}
|
||||
</gl-button>
|
||||
</template>
|
||||
</multi-step-form-template>
|
||||
</template>
|
||||
|
|
@ -11,6 +11,7 @@ import {
|
|||
GlTooltipDirective as GlTooltip,
|
||||
} from '@gitlab/ui';
|
||||
import { getMarkType, getMarkRange } from '@tiptap/core';
|
||||
import { joinPaths } from '~/lib/utils/url_utility';
|
||||
import Link from '../../extensions/link';
|
||||
import EditorStateObserver from '../editor_state_observer.vue';
|
||||
import BubbleMenu from './bubble_menu.vue';
|
||||
|
|
@ -167,7 +168,11 @@ export default {
|
|||
},
|
||||
|
||||
copyLinkHref() {
|
||||
navigator.clipboard.writeText(this.linkCanonicalSrc);
|
||||
const fullUrl = this.linkCanonicalSrc.startsWith('http')
|
||||
? this.linkCanonicalSrc
|
||||
: joinPaths(gon.gitlab_url, this.linkHref);
|
||||
|
||||
navigator.clipboard.writeText(fullUrl);
|
||||
},
|
||||
|
||||
removeLink() {
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ export const OptionsMenuAdapter = {
|
|||
clicks: {
|
||||
toggleOptionsMenu(event, button) {
|
||||
const menuContainer = this.diffElement.querySelector('[data-options-menu]');
|
||||
if (!menuContainer) return;
|
||||
const items = getMenuItems(menuContainer);
|
||||
// eslint-disable-next-line no-new
|
||||
new Vue({
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
<script>
|
||||
import { GlCard, GlIcon, GlLink, GlButton, GlToggle, GlAlert } from '@gitlab/ui';
|
||||
import { GlCard, GlIcon, GlLink, GlButton, GlToggle, GlAlert, GlExperimentBadge } from '@gitlab/ui';
|
||||
import { s__ } from '~/locale';
|
||||
import ManageViaMr from '~/vue_shared/security_configuration/components/manage_via_mr.vue';
|
||||
import SetValidityChecks from '~/security_configuration/graphql/set_validity_checks.graphql';
|
||||
|
|
@ -15,6 +15,7 @@ export default {
|
|||
GlButton,
|
||||
GlToggle,
|
||||
GlAlert,
|
||||
GlExperimentBadge,
|
||||
ManageViaMr,
|
||||
},
|
||||
inject: [
|
||||
|
|
@ -177,9 +178,10 @@ export default {
|
|||
|
||||
<h4 class="gl-mb-3 gl-text-base gl-font-bold">
|
||||
{{ s__('SecretDetection|Validity checks') }}
|
||||
<gl-experiment-badge />
|
||||
</h4>
|
||||
|
||||
<p class="gl-mb-4 gl-text-secondary">
|
||||
<p class="gl-mb-4 gl-text-base">
|
||||
{{
|
||||
s__(
|
||||
'SecretDetection|Validate tokens using third-party API calls. When the pipeline is complete, your tokens are labeled Active, Possibly active, or Inactive. You must have pipeline secret detection enabled.',
|
||||
|
|
|
|||
|
|
@ -91,6 +91,11 @@
|
|||
font-weight: normal;
|
||||
}
|
||||
|
||||
.rd-diff-file-link {
|
||||
font: inherit;
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
.rd-diff-file-header-submodule,
|
||||
.rd-diff-file-title,
|
||||
.rd-file-mode-change,
|
||||
|
|
|
|||
|
|
@ -28,18 +28,20 @@
|
|||
- if @diff_file.renamed_file?
|
||||
- old_path, new_path = helpers.mark_inline_diffs(@diff_file.old_path, @diff_file.new_path)
|
||||
%h2.rd-diff-file-title{ id: heading_id, aria: { label: moved_title_label } }><
|
||||
= old_path
|
||||
%span.rd-diff-file-moved>< →
|
||||
= new_path
|
||||
= link_to file_link, { class: 'rd-diff-file-link', target: '_blank' } do
|
||||
= old_path
|
||||
%span.rd-diff-file-moved>< →
|
||||
= new_path
|
||||
- else
|
||||
%h2.rd-diff-file-title{ id: heading_id }><
|
||||
- chunks = file_title_chunks
|
||||
- chunks[:path_parts].each do |part|
|
||||
= part
|
||||
= '/'
|
||||
-# allow paths to wrap around '/' symbols for better visuals
|
||||
%wbr><
|
||||
= chunks[:filename]
|
||||
= link_to file_link, { class: 'rd-diff-file-link', target: '_blank' } do
|
||||
- chunks = file_title_chunks
|
||||
- chunks[:path_parts].each do |part|
|
||||
= part
|
||||
= '/'
|
||||
-# allow paths to wrap around '/' symbols for better visuals
|
||||
%wbr><
|
||||
= chunks[:filename]
|
||||
- if @diff_file.deleted_file?
|
||||
%span.rd-diff-file-deleted><= _("deleted")
|
||||
= copy_path_button
|
||||
|
|
@ -52,11 +54,12 @@
|
|||
%span.rd-lines-added +#{@diff_file.added_lines}
|
||||
%span.rd-lines-removed −#{@diff_file.removed_lines}
|
||||
.rd-diff-file-options-menu
|
||||
%div{ data: { options_menu: true } }
|
||||
-# <script> here is likely the most effective way to minimize bytes:
|
||||
-# https://gitlab.com/gitlab-org/gitlab/-/merge_requests/182850#note_2387011092
|
||||
-# haml-lint:disable InlineJavaScript
|
||||
%script{ type: "application/json" }
|
||||
= menu_items.map { |item| item.except(:position) }.to_json.html_safe
|
||||
- button_params = { icon: 'ellipsis_v', button_options: { data: { click: 'toggleOptionsMenu' }, aria: { label: s_('RapidDiffs|Show options') } } }
|
||||
= render Pajamas::ButtonComponent.new(category: :tertiary, size: :small, **button_params)
|
||||
- unless menu_items.empty?
|
||||
%div{ data: { options_menu: true } }
|
||||
-# <script> here is likely the most effective way to minimize bytes:
|
||||
-# https://gitlab.com/gitlab-org/gitlab/-/merge_requests/182850#note_2387011092
|
||||
-# haml-lint:disable InlineJavaScript
|
||||
%script{ type: "application/json" }
|
||||
= menu_items.map { |item| item.except(:position) }.to_json.html_safe
|
||||
- button_params = { icon: 'ellipsis_v', button_options: { data: { click: 'toggleOptionsMenu' }, aria: { label: s_('RapidDiffs|Show options') } } }
|
||||
= render Pajamas::ButtonComponent.new(category: :tertiary, size: :small, **button_params)
|
||||
|
|
|
|||
|
|
@ -16,6 +16,13 @@ module RapidDiffs
|
|||
{ path_parts: parts, filename: last }
|
||||
end
|
||||
|
||||
def file_link
|
||||
helpers.project_blob_path(
|
||||
@diff_file.repository.project,
|
||||
helpers.tree_join(@diff_file.content_sha, @diff_file.new_path)
|
||||
)
|
||||
end
|
||||
|
||||
def copy_path_button
|
||||
clipboard_button(
|
||||
text: @diff_file.file_path,
|
||||
|
|
@ -29,21 +36,7 @@ module RapidDiffs
|
|||
end
|
||||
|
||||
def menu_items
|
||||
base_items = [
|
||||
{
|
||||
text: helpers.safe_format(
|
||||
_('View file @ %{commitSha}'),
|
||||
commitSha: Commit.truncate_sha(@diff_file.content_sha)
|
||||
),
|
||||
href: helpers.project_blob_path(
|
||||
@diff_file.repository.project,
|
||||
helpers.tree_join(@diff_file.content_sha, @diff_file.new_path)
|
||||
),
|
||||
position: 0
|
||||
}
|
||||
]
|
||||
|
||||
[*base_items, *@additional_menu_items].sort_by { |item| item[:position] || Float::INFINITY }
|
||||
@additional_menu_items.sort_by { |item| item[:position] || Float::INFINITY }
|
||||
end
|
||||
|
||||
def heading_id
|
||||
|
|
|
|||
|
|
@ -302,7 +302,6 @@ module Gitlab
|
|||
config.assets.precompile << "page_bundles/dev_ops_reports.css"
|
||||
config.assets.precompile << "page_bundles/editor.css"
|
||||
config.assets.precompile << "page_bundles/environments.css"
|
||||
config.assets.precompile << "page_bundles/epics.css"
|
||||
config.assets.precompile << "page_bundles/error_tracking_details.css"
|
||||
config.assets.precompile << "page_bundles/escalation_policies.css"
|
||||
config.assets.precompile << "page_bundles/graph_charts.css"
|
||||
|
|
|
|||
|
|
@ -9,14 +9,6 @@ description: Information related to Test Reports, which relate historical test o
|
|||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/31643
|
||||
milestone: '13.0'
|
||||
gitlab_schema: gitlab_main_cell
|
||||
desired_sharding_key:
|
||||
project_id:
|
||||
references: projects
|
||||
backfill_via:
|
||||
parent:
|
||||
foreign_key: issue_id
|
||||
table: issues
|
||||
sharding_key: project_id
|
||||
belongs_to: requirement_issue
|
||||
sharding_key:
|
||||
project_id: projects
|
||||
table_size: small
|
||||
desired_sharding_key_migration_job_name: BackfillRequirementsManagementTestReportsProjectId
|
||||
|
|
|
|||
|
|
@ -0,0 +1,14 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class AddRequirementsManagementTestReportsProjectIdNotNull < Gitlab::Database::Migration[2.3]
|
||||
milestone '18.2'
|
||||
disable_ddl_transaction!
|
||||
|
||||
def up
|
||||
add_not_null_constraint :requirements_management_test_reports, :project_id
|
||||
end
|
||||
|
||||
def down
|
||||
remove_not_null_constraint :requirements_management_test_reports, :project_id
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1 @@
|
|||
a848ae3e33bbb8077385785d8c70b6e1fbd90474b859c8e2c44fc2bef0a9c16a
|
||||
|
|
@ -22552,7 +22552,8 @@ CREATE TABLE requirements_management_test_reports (
|
|||
build_id bigint,
|
||||
issue_id bigint,
|
||||
uses_legacy_iid boolean DEFAULT true NOT NULL,
|
||||
project_id bigint
|
||||
project_id bigint,
|
||||
CONSTRAINT check_715b56da9a CHECK ((project_id IS NOT NULL))
|
||||
);
|
||||
|
||||
CREATE SEQUENCE requirements_management_test_reports_id_seq
|
||||
|
|
|
|||
|
|
@ -900,7 +900,7 @@ Query by `iid` field and document type. Requires `type` and `iid` fields.
|
|||
|
||||
#### `by_full_text`
|
||||
|
||||
Performs a full text search. This query will use `by_multi_match_query` or `by_simple_query_string` if Advanced search syntax is used in the query string. `by_multi_match_query` is behind the `search_uses_match_queries` feature flag.
|
||||
Performs a full text search. This query will use `by_multi_match_query` or `by_simple_query_string` if Advanced search syntax is used in the query string.
|
||||
|
||||
#### `by_multi_match_query`
|
||||
|
||||
|
|
|
|||
|
|
@ -11,7 +11,17 @@ module Gitlab
|
|||
ip = RequestContext.instance.client_ip
|
||||
unique_ips = update_and_return_ips_count(user_id, ip)
|
||||
|
||||
raise TooManyIps.new(user_id, ip, unique_ips) if unique_ips > config.unique_ips_limit_per_user
|
||||
if unique_ips > config.unique_ips_limit_per_user
|
||||
Gitlab::AuthLogger.error(
|
||||
message: 'too_many_ips',
|
||||
remote_ip: ip,
|
||||
unique_ips_count: unique_ips,
|
||||
user_id: user_id,
|
||||
**Gitlab::ApplicationContext.current
|
||||
)
|
||||
|
||||
raise TooManyIps.new(user_id, ip, unique_ips)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -31110,6 +31110,9 @@ msgstr ""
|
|||
msgid "Groups|This will create a project %{path} and add a README.md."
|
||||
msgstr ""
|
||||
|
||||
msgid "Groups|You don't have any active groups yet."
|
||||
msgstr ""
|
||||
|
||||
msgid "Groups|You don't have any inactive groups."
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -53380,6 +53383,9 @@ msgstr ""
|
|||
msgid "Runners|Register runner"
|
||||
msgstr ""
|
||||
|
||||
msgid "Runners|Register your new runner"
|
||||
msgstr ""
|
||||
|
||||
msgid "Runners|Registration token"
|
||||
msgstr ""
|
||||
|
||||
|
|
|
|||
|
|
@ -60,7 +60,7 @@
|
|||
"@gitlab/application-sdk-browser": "^0.3.4",
|
||||
"@gitlab/at.js": "1.5.7",
|
||||
"@gitlab/cluster-client": "^3.0.0",
|
||||
"@gitlab/duo-ui": "^8.23.0",
|
||||
"@gitlab/duo-ui": "^8.24.0",
|
||||
"@gitlab/favicon-overlay": "2.0.0",
|
||||
"@gitlab/fonts": "^1.3.0",
|
||||
"@gitlab/query-language-rust": "0.11.4",
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module QA
|
||||
RSpec.describe 'Manage', product_group: :import_and_integrate do
|
||||
RSpec.describe 'Manage', product_group: :import do
|
||||
describe 'GitHub import' do
|
||||
include_context 'with github import'
|
||||
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ module QA
|
|||
custom_test_metrics: {
|
||||
tags: { import_type: ENV["QA_IMPORT_TYPE"], import_repo: ENV["QA_LARGE_IMPORT_REPO"] || "rspec/rspec-core" }
|
||||
} do
|
||||
describe 'Project import', product_group: :import_and_integrate do
|
||||
describe 'Project import', product_group: :import do
|
||||
let!(:api_client) { Runtime::API::Client.as_admin }
|
||||
let!(:user) { create(:user) }
|
||||
let!(:user_api_client) do
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ module QA
|
|||
:requires_admin,
|
||||
:integrations,
|
||||
:orchestrated,
|
||||
product_group: :import_and_integrate,
|
||||
product_group: :import,
|
||||
feature_flag: { name: :auto_disabling_web_hooks }
|
||||
) do
|
||||
before(:context) do
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module QA
|
||||
RSpec.describe "Manage", product_group: :import_and_integrate do
|
||||
RSpec.describe "Manage", product_group: :import do
|
||||
include_context "with gitlab group migration"
|
||||
|
||||
describe "Gitlab migration", :import, :orchestrated, requires_admin: 'creates a user via API' do
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module QA
|
||||
RSpec.describe 'Manage', product_group: :import_and_integrate do
|
||||
RSpec.describe 'Manage', product_group: :import do
|
||||
describe 'Gitlab migration', :import, :orchestrated, requires_admin: 'creates a user via API' do
|
||||
include_context 'with gitlab project migration'
|
||||
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
|
||||
# rubocop:disable Rails/Pluck, Layout/LineLength, RSpec/MultipleMemoizedHelpers
|
||||
module QA
|
||||
RSpec.describe "Manage", :skip_live_env, product_group: :import_and_integrate,
|
||||
RSpec.describe "Manage", :skip_live_env, product_group: :import,
|
||||
only: { condition: -> { ENV["CI_PROJECT_NAME"] == "import-metrics" } },
|
||||
custom_test_metrics: {
|
||||
tags: { import_type: ENV["QA_IMPORT_TYPE"], import_repo: ENV["QA_LARGE_IMPORT_REPO"] || "migration-test-project" }
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module QA
|
||||
RSpec.describe 'Manage', product_group: :import_and_integrate do
|
||||
RSpec.describe 'Manage', product_group: :import do
|
||||
describe 'Gitlab migration', :import, :orchestrated, requires_admin: 'creates a user via API' do
|
||||
include_context 'with gitlab project migration'
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module QA
|
||||
RSpec.describe 'Manage', product_group: :import_and_integrate do
|
||||
RSpec.describe 'Manage', product_group: :import do
|
||||
describe 'Gitlab migration', :import, :orchestrated, requires_admin: 'creates a user via API' do
|
||||
include_context 'with gitlab project migration'
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module QA
|
||||
RSpec.describe 'Manage', product_group: :import_and_integrate do
|
||||
RSpec.describe 'Manage', product_group: :import do
|
||||
describe 'Gitlab migration', :import, :orchestrated, requires_admin: 'creates a user via API' do
|
||||
include_context 'with gitlab project migration'
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module QA
|
||||
RSpec.describe 'Manage', product_group: :import_and_integrate do
|
||||
RSpec.describe 'Manage', product_group: :import do
|
||||
describe 'Gitlab migration', :import, :orchestrated, requires_admin: 'creates a user via API' do
|
||||
include_context 'with gitlab project migration'
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module QA
|
||||
RSpec.describe 'Manage', product_group: :import_and_integrate do
|
||||
RSpec.describe 'Manage', product_group: :import do
|
||||
describe 'Gitlab migration', :import, :orchestrated, requires_admin: 'creates a user via API' do
|
||||
include_context 'with gitlab project migration'
|
||||
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ module QA
|
|||
RSpec.describe 'Manage', :requires_admin, :skip_live_env, only: {
|
||||
condition: -> { ENV['QA_RUN_TYPE']&.match?("e2e-test-on-omnibus") }
|
||||
} do
|
||||
describe 'rate limits', product_group: :import_and_integrate do
|
||||
describe 'rate limits', product_group: :import do
|
||||
let(:rate_limited_user) { create(:user, :with_personal_access_token) }
|
||||
let(:api_client) { rate_limited_user.api_client }
|
||||
let!(:request) { Runtime::API::Request.new(api_client, '/users') }
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module QA
|
||||
RSpec.describe 'Manage', :github, :requires_admin, product_group: :import_and_integrate do
|
||||
RSpec.describe 'Manage', :github, :requires_admin, product_group: :import do
|
||||
describe 'GitHub import',
|
||||
quarantine: {
|
||||
type: :investigating,
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
module QA
|
||||
RSpec.describe 'Manage', :requires_admin, :skip_live_env do
|
||||
describe 'Jenkins integration', product_group: :import_and_integrate do
|
||||
describe 'Jenkins integration', product_group: :import do
|
||||
let(:jenkins_server) { Service::DockerRun::Jenkins.new }
|
||||
|
||||
let(:jenkins_client) do
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ module QA
|
|||
RSpec.describe 'Manage' do
|
||||
include Support::API
|
||||
|
||||
describe 'Jira integration', :jira, :orchestrated, :requires_admin, product_group: :import_and_integrate do
|
||||
describe 'Jira integration', :jira, :orchestrated, :requires_admin, product_group: :import do
|
||||
let(:jira_project_key) { 'JITP' }
|
||||
let(:project) { create(:project, name: 'project_with_jira_integration') }
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
module QA
|
||||
RSpec.describe 'Manage' do
|
||||
describe 'Jira issue import', :jira, :orchestrated, :requires_admin, product_group: :import_and_integrate do
|
||||
describe 'Jira issue import', :jira, :orchestrated, :requires_admin, product_group: :import do
|
||||
let(:jira_project_key) { "JITD" }
|
||||
let(:jira_issue_title) { "[#{jira_project_key}-1] Jira to GitLab Test Issue" }
|
||||
let(:jira_issue_description) { "This issue is for testing importing Jira issues to GitLab." }
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ module QA
|
|||
end
|
||||
end
|
||||
|
||||
RSpec.describe 'Manage', :orchestrated, :requires_admin, :smtp, product_group: :import_and_integrate do
|
||||
RSpec.describe 'Manage', :orchestrated, :requires_admin, :smtp, product_group: :import do
|
||||
describe 'Pipeline status emails' do
|
||||
let(:executor) { "qa-runner-#{SecureRandom.hex(6)}" }
|
||||
let(:emails) { %w[foo@bar.com baz@buzz.com] }
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
module QA
|
||||
RSpec.describe 'Manage', only: { subdomain: "staging-ref" } do
|
||||
describe 'Slack app integration', :slack, product_group: :import_and_integrate do
|
||||
describe 'Slack app integration', :slack, product_group: :import do
|
||||
context 'when using Slash commands' do
|
||||
# state to be seeded in the Slack UI
|
||||
let(:title) { "Issue - #{SecureRandom.hex(5)}" }
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module QA
|
||||
describe 'Manage', product_group: :import_and_integrate do
|
||||
describe 'Manage', product_group: :import do
|
||||
describe 'Gitlab migration', :import, :orchestrated, requires_admin: 'creates a user via API' do
|
||||
include_context "with gitlab group migration"
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module QA
|
||||
describe 'Manage', product_group: :import_and_integrate do
|
||||
describe 'Manage', product_group: :import do
|
||||
describe 'Gitlab migration',
|
||||
feature_flag: {
|
||||
name: [:importer_user_mapping, :bulk_import_importer_user_mapping],
|
||||
|
|
|
|||
|
|
@ -7,8 +7,13 @@ RSpec.describe RapidDiffs::DiffFileHeaderComponent, type: :component, feature_ca
|
|||
let(:header) { page.find('[data-testid="rd-diff-file-header"]') }
|
||||
|
||||
it "renders file path" do
|
||||
project = diff_file.repository.project
|
||||
namespace = project.namespace
|
||||
href = "/#{namespace.to_param}/#{project.to_param}/-/blob/#{diff_file.content_sha}/#{diff_file.new_path}"
|
||||
render_component
|
||||
expect(header).to have_css('h2', text: diff_file.file_path)
|
||||
link = header.find('h2 a')
|
||||
expect(link.text).to eq(diff_file.file_path)
|
||||
expect(link[:href]).to eq(href)
|
||||
end
|
||||
|
||||
it "renders file toggle" do
|
||||
|
|
@ -48,7 +53,7 @@ RSpec.describe RapidDiffs::DiffFileHeaderComponent, type: :component, feature_ca
|
|||
allow(diff_file).to receive(:old_path).and_return(old)
|
||||
allow(diff_file).to receive(:new_path).and_return(new)
|
||||
render_component
|
||||
expect(header).to have_css("h2[aria-label=\"File moved from #{old} to #{new}\"]", text: "#{old}→#{new}")
|
||||
expect(header).to have_css("h2[aria-label=\"File moved from #{old} to #{new}\"] a", text: "#{old}→#{new}")
|
||||
end
|
||||
|
||||
it "renders mode change" do
|
||||
|
|
@ -83,20 +88,10 @@ RSpec.describe RapidDiffs::DiffFileHeaderComponent, type: :component, feature_ca
|
|||
allow(diff_file).to receive(:content_sha).and_return(content_sha)
|
||||
end
|
||||
|
||||
it 'renders menu toggle' do
|
||||
it 'does not render menu toggle without options' do
|
||||
render_component
|
||||
|
||||
expect(page).to have_css('button[data-click="toggleOptionsMenu"][aria-label="Show options"]')
|
||||
end
|
||||
|
||||
it 'renders default menu items' do
|
||||
render_component
|
||||
|
||||
options_menu_items = Gitlab::Json.parse(page.find('script', visible: false).text)
|
||||
|
||||
expect(options_menu_items.length).to eq(1)
|
||||
expect(options_menu_items[0]['text']).to eq("View file @ #{content_sha}")
|
||||
expect(options_menu_items[0]).not_to have_key('position')
|
||||
expect(page).not_to have_css('button[data-click="toggleOptionsMenu"][aria-label="Show options"]')
|
||||
end
|
||||
|
||||
it 'renders additional menu items with respective order' do
|
||||
|
|
@ -104,12 +99,12 @@ RSpec.describe RapidDiffs::DiffFileHeaderComponent, type: :component, feature_ca
|
|||
{
|
||||
text: 'First item',
|
||||
href: '/first',
|
||||
position: -1
|
||||
position: -100
|
||||
},
|
||||
{
|
||||
text: 'Last item',
|
||||
href: '/last',
|
||||
position: 2
|
||||
position: 100
|
||||
}
|
||||
]
|
||||
|
||||
|
|
@ -117,10 +112,9 @@ RSpec.describe RapidDiffs::DiffFileHeaderComponent, type: :component, feature_ca
|
|||
|
||||
options_menu_items = Gitlab::Json.parse(page.find('script', visible: false).text)
|
||||
|
||||
expect(options_menu_items.length).to eq(3)
|
||||
expect(options_menu_items[0]['text']).to eq('First item')
|
||||
expect(options_menu_items[1]['text']).to eq("View file @ #{content_sha}")
|
||||
expect(options_menu_items[2]['text']).to eq('Last item')
|
||||
expect(page).to have_css('button[data-click="toggleOptionsMenu"][aria-label="Show options"]')
|
||||
expect(options_menu_items.first['text']).to eq('First item')
|
||||
expect(options_menu_items.last['text']).to eq('Last item')
|
||||
|
||||
options_menu_items.each do |item|
|
||||
expect(item).not_to have_key('position')
|
||||
|
|
|
|||
|
|
@ -36,25 +36,8 @@ RSpec.describe RapidDiffs::MergeRequestDiffFileComponent, type: :component, feat
|
|||
|
||||
options_menu_items = Gitlab::Json.parse(page.find('script', visible: false).text)
|
||||
|
||||
expect(options_menu_items.length).to eq(2)
|
||||
expect(options_menu_items[0]['text']).to eq('View file @ abc123')
|
||||
expect(options_menu_items[1]['text']).to eq('Edit in single-file editor')
|
||||
expect(options_menu_items[1]['href']).to include("#{edit_path_base}#{merge_request.iid}")
|
||||
end
|
||||
end
|
||||
|
||||
context 'with non text diff file' do
|
||||
before do
|
||||
allow(diff_file).to receive(:text?).and_return(false)
|
||||
end
|
||||
|
||||
it 'renders no additional options' do
|
||||
render_component
|
||||
|
||||
options_menu_items = Gitlab::Json.parse(page.find('script', visible: false).text)
|
||||
|
||||
expect(options_menu_items.length).to eq(1)
|
||||
expect(options_menu_items[0]['text']).to eq('View file @ abc123')
|
||||
expect(options_menu_items[0]['text']).to eq('Edit in single-file editor')
|
||||
expect(options_menu_items[0]['href']).to include("#{edit_path_base}#{merge_request.iid}")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -872,6 +872,23 @@ RSpec.describe ApplicationController, feature_category: :shared do
|
|||
end
|
||||
end
|
||||
|
||||
describe 'rescue_from Gitlab::Auth::TooManyIps' do
|
||||
controller(described_class) do
|
||||
skip_before_action :authenticate_user!
|
||||
|
||||
def index
|
||||
raise Gitlab::Auth::TooManyIps.new(1, '1.2.3.4', 10)
|
||||
end
|
||||
end
|
||||
|
||||
it 'returns a 403' do
|
||||
get :index
|
||||
|
||||
expect(response).to have_gitlab_http_status(:forbidden)
|
||||
expect(response.headers['Retry-After']).to eq(Gitlab::Auth::UniqueIpsLimiter.config.unique_ips_limit_time_window.to_s)
|
||||
end
|
||||
end
|
||||
|
||||
describe '#set_current_context' do
|
||||
controller(described_class) do
|
||||
feature_category :team_planning
|
||||
|
|
|
|||
|
|
@ -1,5 +1,10 @@
|
|||
import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
|
||||
import Vue from 'vue';
|
||||
import VueApollo from 'vue-apollo';
|
||||
import VueRouter from 'vue-router';
|
||||
import { GlEmptyState } from '@gitlab/ui';
|
||||
import { shallowMountExtended, mountExtended } from 'helpers/vue_test_utils_helper';
|
||||
import AdminGroupsApp from '~/admin/groups/index/components/app.vue';
|
||||
import { createRouter } from '~/admin/groups/index/index';
|
||||
import TabsWithList from '~/groups_projects/components/tabs_with_list.vue';
|
||||
import { PAGINATION_TYPE_KEYSET } from '~/groups_projects/constants';
|
||||
import { RECENT_SEARCHES_STORAGE_KEY_GROUPS } from '~/filtered_search/recent_searches_storage_keys';
|
||||
|
|
@ -11,25 +16,57 @@ import {
|
|||
FILTERED_SEARCH_NAMESPACE,
|
||||
ADMIN_GROUPS_TABS,
|
||||
FIRST_TAB_ROUTE_NAMES,
|
||||
ADMIN_GROUPS_ROUTE_NAME,
|
||||
} from '~/admin/groups/index/constants';
|
||||
import adminGroupCountsQuery from '~/admin/groups/index/graphql/queries/group_counts.query.graphql';
|
||||
import {
|
||||
TIMESTAMP_TYPE_CREATED_AT,
|
||||
TIMESTAMP_TYPE_UPDATED_AT,
|
||||
} from '~/vue_shared/components/resource_lists/constants';
|
||||
import adminGroupsQuery from '~/admin/groups/index/graphql/queries/groups.query.graphql';
|
||||
import createMockApollo from 'helpers/mock_apollo_helper';
|
||||
import waitForPromises from 'helpers/wait_for_promises';
|
||||
|
||||
jest.mock(
|
||||
'@gitlab/svgs/dist/illustrations/empty-state/empty-groups-md.svg?url',
|
||||
() => 'empty-groups-mocked-illustration',
|
||||
);
|
||||
|
||||
Vue.use(VueRouter);
|
||||
Vue.use(VueApollo);
|
||||
|
||||
const defaultRoute = {
|
||||
name: ADMIN_GROUPS_ROUTE_NAME,
|
||||
};
|
||||
|
||||
describe('AdminGroupsApp', () => {
|
||||
let wrapper;
|
||||
let mockApollo;
|
||||
|
||||
const createComponent = () => {
|
||||
wrapper = shallowMountExtended(AdminGroupsApp);
|
||||
const createComponent = async ({
|
||||
mountFn = shallowMountExtended,
|
||||
adminGroupsQueryHandler = jest.fn(),
|
||||
route = defaultRoute,
|
||||
stubs = {},
|
||||
} = {}) => {
|
||||
mockApollo = createMockApollo([[adminGroupsQuery, adminGroupsQueryHandler]]);
|
||||
const router = createRouter();
|
||||
await router.push(route);
|
||||
|
||||
wrapper = mountFn(AdminGroupsApp, { stubs, apolloProvider: mockApollo, router });
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
createComponent();
|
||||
const findTabByName = (name) =>
|
||||
wrapper.findAllByRole('tab').wrappers.find((tab) => tab.text().includes(name));
|
||||
const findEmptyState = () => wrapper.findComponent(GlEmptyState);
|
||||
|
||||
afterEach(() => {
|
||||
mockApollo = null;
|
||||
});
|
||||
|
||||
it('renders TabsWithList component and passes correct props', () => {
|
||||
it('renders TabsWithList component and passes correct props', async () => {
|
||||
await createComponent();
|
||||
|
||||
expect(wrapper.findComponent(TabsWithList).props()).toMatchObject({
|
||||
tabs: ADMIN_GROUPS_TABS,
|
||||
filteredSearchTermKey: FILTERED_SEARCH_TERM_KEY,
|
||||
|
|
@ -50,4 +87,37 @@ describe('AdminGroupsApp', () => {
|
|||
firstTabRouteNames: FIRST_TAB_ROUTE_NAMES,
|
||||
});
|
||||
});
|
||||
|
||||
describe('when there are no groups', () => {
|
||||
beforeEach(async () => {
|
||||
await createComponent({
|
||||
mountFn: mountExtended,
|
||||
adminGroupsQueryHandler: jest
|
||||
.fn()
|
||||
.mockResolvedValue({ data: { groups: { nodes: [], pageInfo: {} } } }),
|
||||
});
|
||||
|
||||
await waitForPromises();
|
||||
});
|
||||
|
||||
it('renders empty state on Active tab', () => {
|
||||
expect(findEmptyState().props()).toMatchObject({
|
||||
title: "You don't have any active groups yet.",
|
||||
description:
|
||||
'A group is a collection of several projects. If you organize your projects under a group, it works like a folder.',
|
||||
svgPath: 'empty-groups-mocked-illustration',
|
||||
});
|
||||
});
|
||||
|
||||
it('renders empty state on Inactive tab', async () => {
|
||||
await findTabByName('Inactive').trigger('click');
|
||||
await waitForPromises();
|
||||
|
||||
expect(findEmptyState().props()).toMatchObject({
|
||||
title: "You don't have any inactive groups.",
|
||||
description: 'Groups that are archived or pending deletion will appear here.',
|
||||
svgPath: 'empty-groups-mocked-illustration',
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
import { GlForm } from '@gitlab/ui';
|
||||
import MultiStepFormTemplate from '~/vue_shared/components/multi_step_form_template.vue';
|
||||
import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
|
||||
import RunnerCreateWizardOptionalFields from '~/ci/runner/components/runner_create_wizard_optional_fields.vue';
|
||||
|
|
@ -10,6 +11,9 @@ describe('Create Runner Optional Fields', () => {
|
|||
propsData: {
|
||||
currentStep: 2,
|
||||
stepsTotal: 3,
|
||||
tags: 'tag1, tag2',
|
||||
runUntagged: false,
|
||||
runnerType: 'INSTANCE_TYPE',
|
||||
},
|
||||
});
|
||||
};
|
||||
|
|
@ -18,6 +22,7 @@ describe('Create Runner Optional Fields', () => {
|
|||
createComponent();
|
||||
});
|
||||
|
||||
const findForm = () => wrapper.findComponent(GlForm);
|
||||
const findMultiStepFormTemplate = () => wrapper.findComponent(MultiStepFormTemplate);
|
||||
const findNextButton = () => wrapper.findByTestId('next-button');
|
||||
const findBackButton = () => wrapper.findByTestId('back-button');
|
||||
|
|
@ -30,11 +35,15 @@ describe('Create Runner Optional Fields', () => {
|
|||
stepsTotal: 3,
|
||||
});
|
||||
});
|
||||
|
||||
it('renders GlForm', () => {
|
||||
expect(findForm().exists()).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
it('renders the Next step button', () => {
|
||||
expect(findNextButton().text()).toBe('Next step');
|
||||
expect(findNextButton().props('disabled')).toBe(true);
|
||||
expect(findNextButton().attributes('type')).toBe('submit');
|
||||
});
|
||||
|
||||
describe('back button', () => {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,32 @@
|
|||
import MultiStepFormTemplate from '~/vue_shared/components/multi_step_form_template.vue';
|
||||
import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
|
||||
import RunnerCreateWizardRegistration from '~/ci/runner/components/runner_create_wizard_registration.vue';
|
||||
|
||||
describe('Create New Runner Registration', () => {
|
||||
let wrapper;
|
||||
|
||||
const createComponent = () => {
|
||||
wrapper = shallowMountExtended(RunnerCreateWizardRegistration, {
|
||||
propsData: {
|
||||
currentStep: 2,
|
||||
stepsTotal: 3,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
createComponent();
|
||||
});
|
||||
|
||||
const findMultiStepFormTemplate = () => wrapper.findComponent(MultiStepFormTemplate);
|
||||
|
||||
describe('form', () => {
|
||||
it('passes the correct props to MultiStepFormTemplate', () => {
|
||||
expect(findMultiStepFormTemplate().props()).toMatchObject({
|
||||
title: 'Register your new runner',
|
||||
currentStep: 2,
|
||||
stepsTotal: 3,
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
import { GlLink, GlForm } from '@gitlab/ui';
|
||||
import { nextTick } from 'vue';
|
||||
import { mountExtended } from 'helpers/vue_test_utils_helper';
|
||||
import { createGon } from 'helpers/gon_helper';
|
||||
import LinkBubbleMenu from '~/content_editor/components/bubble_menus/link_bubble_menu.vue';
|
||||
import EditorStateObserver from '~/content_editor/components/editor_state_observer.vue';
|
||||
import eventHubFactory from '~/helpers/event_hub_factory';
|
||||
|
|
@ -62,6 +63,8 @@ describe('content_editor/components/bubble_menus/link_bubble_menu', () => {
|
|||
beforeEach(() => {
|
||||
buildEditor();
|
||||
|
||||
window.gon = createGon(false);
|
||||
|
||||
tiptapEditor
|
||||
.chain()
|
||||
.setContent(
|
||||
|
|
@ -190,14 +193,32 @@ describe('content_editor/components/bubble_menus/link_bubble_menu', () => {
|
|||
});
|
||||
|
||||
describe('copy button', () => {
|
||||
it('copies the canonical link to clipboard', async () => {
|
||||
it('copies the contextualized link to clipboard for project uploads', async () => {
|
||||
document.body.dataset.page = 'projects:issues:show';
|
||||
window.gon.current_project_id = '123';
|
||||
|
||||
await buildWrapperAndDisplayMenu();
|
||||
|
||||
jest.spyOn(navigator.clipboard, 'writeText');
|
||||
|
||||
await wrapper.findByTestId('copy-link-url').vm.$emit('click');
|
||||
|
||||
expect(navigator.clipboard.writeText).toHaveBeenCalledWith('uploads/my_file.pdf');
|
||||
const expectedUrl = `${window.gon.gitlab_url}/path/to/project/-/wikis/uploads/my_file.pdf`;
|
||||
expect(navigator.clipboard.writeText).toHaveBeenCalledWith(expectedUrl);
|
||||
});
|
||||
|
||||
it('copies the contextualized link to clipboard for group uploads', async () => {
|
||||
document.body.dataset.page = 'groups:epics:show';
|
||||
window.gon.current_group_id = '456';
|
||||
|
||||
await buildWrapperAndDisplayMenu();
|
||||
|
||||
jest.spyOn(navigator.clipboard, 'writeText');
|
||||
|
||||
await wrapper.findByTestId('copy-link-url').vm.$emit('click');
|
||||
|
||||
const expectedUrl = `${window.gon.gitlab_url}/path/to/project/-/wikis/uploads/my_file.pdf`;
|
||||
expect(navigator.clipboard.writeText).toHaveBeenCalledWith(expectedUrl);
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { GlCard, GlIcon, GlLink, GlButton, GlAlert } from '@gitlab/ui';
|
||||
import { GlCard, GlIcon, GlLink, GlButton, GlAlert, GlExperimentBadge } from '@gitlab/ui';
|
||||
import VueApollo from 'vue-apollo';
|
||||
import Vue from 'vue';
|
||||
import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
|
||||
|
|
@ -84,6 +84,7 @@ describe('PipelineSecretDetectionFeatureCard component', () => {
|
|||
const findValidityChecksSection = () => wrapper.findByTestId('validity-checks-section');
|
||||
const findValidityChecksToggle = () => wrapper.findByTestId('validity-checks-toggle');
|
||||
const findValidityChecksAlert = () => wrapper.findComponent(GlAlert);
|
||||
const findExperimentBadge = () => wrapper.findComponent(GlExperimentBadge);
|
||||
|
||||
afterEach(() => {
|
||||
feature = undefined;
|
||||
|
|
@ -250,6 +251,7 @@ describe('PipelineSecretDetectionFeatureCard component', () => {
|
|||
createComponent({}, { validityChecksAvailable });
|
||||
|
||||
expect(findValidityChecksSection().exists()).toBe(shouldRender);
|
||||
expect(findExperimentBadge().exists()).toBe(shouldRender);
|
||||
},
|
||||
);
|
||||
|
||||
|
|
|
|||
|
|
@ -54,6 +54,13 @@ RSpec.describe Gitlab::Auth::UniqueIpsLimiter, :clean_gitlab_redis_shared_state
|
|||
expect(described_class.limit_user! { user }).to eq(user)
|
||||
|
||||
change_ip('ip3')
|
||||
|
||||
expect(Gitlab::AuthLogger).to receive(:error).with(hash_including(
|
||||
message: 'too_many_ips',
|
||||
remote_ip: 'ip3',
|
||||
unique_ips_count: 3,
|
||||
user_id: user.id
|
||||
)).and_call_original
|
||||
expect { described_class.limit_user! { user } }.to raise_error(Gitlab::Auth::TooManyIps)
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -950,7 +950,6 @@
|
|||
- './ee/spec/lib/gitlab/elastic/bulk_indexer_spec.rb'
|
||||
- './ee/spec/lib/gitlab/elastic/group_search_results_spec.rb'
|
||||
- './ee/spec/lib/gitlab/elastic/project_search_results_spec.rb'
|
||||
- './ee/spec/lib/gitlab/elastic/snippet_search_results_spec.rb'
|
||||
- './ee/spec/lib/gitlab/email/handler/create_note_handler_spec.rb'
|
||||
- './ee/spec/lib/gitlab/exclusive_lease_spec.rb'
|
||||
- './ee/spec/lib/gitlab/expiring_subscription_message_spec.rb'
|
||||
|
|
|
|||
|
|
@ -227,4 +227,47 @@ RSpec.shared_examples 'rich text editor - links' do
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'copy link functionality for uploaded files' do
|
||||
before do
|
||||
switch_to_content_editor
|
||||
click_attachment_button
|
||||
end
|
||||
|
||||
it 'copies the full contextualized URL for uploaded files', :js do
|
||||
upload_asset 'sample.pdf'
|
||||
wait_for_requests
|
||||
|
||||
expect(page).to have_css('[data-testid="content_editor_editablebox"] a[href]')
|
||||
|
||||
page.find('[data-testid="content_editor_editablebox"] a[href]').click
|
||||
|
||||
expect(page).to have_css('[data-testid="link-bubble-menu"]')
|
||||
|
||||
expect(page).to have_css('[data-testid="copy-link-url"]')
|
||||
|
||||
link_href = page.find('[data-testid="content_editor_editablebox"] a[href]')['href']
|
||||
|
||||
expect(link_href).to match(%r{https?://.*/-/(project|group)/\d+/uploads/\h{32}/sample\.pdf})
|
||||
end
|
||||
end
|
||||
|
||||
describe 'copy link functionality for external URLs' do
|
||||
before do
|
||||
switch_to_content_editor
|
||||
end
|
||||
|
||||
it 'copies external URLs as-is', :js do
|
||||
type_in_content_editor 'Link to [GitLab](https://gitlab.com)'
|
||||
|
||||
page.find('[data-testid="content_editor_editablebox"] a[href="https://gitlab.com"]').click
|
||||
|
||||
expect(page).to have_css('[data-testid="link-bubble-menu"]')
|
||||
|
||||
expect(page).to have_css('[data-testid="copy-link-url"]')
|
||||
|
||||
link_href = page.find('[data-testid="content_editor_editablebox"] a[href="https://gitlab.com"]')['href']
|
||||
expect(link_href).to eq('https://gitlab.com/')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -1397,10 +1397,10 @@
|
|||
core-js "^3.29.1"
|
||||
mitt "^3.0.1"
|
||||
|
||||
"@gitlab/duo-ui@^8.23.0":
|
||||
version "8.23.0"
|
||||
resolved "https://registry.yarnpkg.com/@gitlab/duo-ui/-/duo-ui-8.23.0.tgz#eedfa98fa902f52b6cf7970399528f5d876d7371"
|
||||
integrity sha512-QOurIDNzSrgRg40omKvdQbhHbuBuFc5dweH2NZjpXjj/hksWGWJntUuzF6LsksucBJ912hQ8Z34+i57BNBQO8g==
|
||||
"@gitlab/duo-ui@^8.24.0":
|
||||
version "8.24.0"
|
||||
resolved "https://registry.yarnpkg.com/@gitlab/duo-ui/-/duo-ui-8.24.0.tgz#096b410ea2483e0f632a23b69f97a7ae9ffc97c5"
|
||||
integrity sha512-Vn0AEdy0+Bc4YBPwY8w07Em1n/E7vjQIsWP6AAztSQv4VO5JUVNRouZb/ZUSFJD0s8fGcv+tIH8uJNlFFim9Xw==
|
||||
dependencies:
|
||||
"@floating-ui/dom" "1.7.1"
|
||||
echarts "^5.3.2"
|
||||
|
|
|
|||
Loading…
Reference in New Issue