Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2022-04-28 21:09:03 +00:00
parent f14814c981
commit f138af0ccd
99 changed files with 651 additions and 516 deletions

View File

@ -72,6 +72,11 @@ Lint/LastKeywordArgument:
Enabled: true
Safe: false
Lint/EmptyFile:
Exclude:
- 'db/seeds.rb'
- 'ee/db/geo/seeds.rb'
# This cop checks whether some constant value isn't a
# mutable literal (e.g. array or hash).
Style/MutableConstant:

View File

@ -11,13 +11,6 @@ Gitlab/PolicyRuleBoolean:
Exclude:
- 'ee/app/policies/ee/identity_provider_policy.rb'
# Offense count: 2
# Configuration parameters: AllowComments.
Lint/EmptyFile:
Exclude:
- 'db/seeds.rb'
- 'ee/db/geo/seeds.rb'
# Offense count: 200
# Cop supports --auto-correct.
Lint/RedundantCopDisableDirective:

View File

@ -5991,7 +5991,6 @@ Layout/LineLength:
- 'spec/services/environments/reset_auto_stop_service_spec.rb'
- 'spec/services/environments/schedule_to_delete_review_apps_service_spec.rb'
- 'spec/services/environments/stop_service_spec.rb'
- 'spec/services/error_tracking/base_service_spec.rb'
- 'spec/services/events/destroy_service_spec.rb'
- 'spec/services/feature_flags/create_service_spec.rb'
- 'spec/services/feature_flags/update_service_spec.rb'

View File

@ -300,7 +300,6 @@ Layout/SpaceInsideBlockBraces:
- 'spec/controllers/import/manifest_controller_spec.rb'
- 'spec/controllers/projects/blame_controller_spec.rb'
- 'spec/controllers/projects/deploy_keys_controller_spec.rb'
- 'spec/controllers/projects/error_tracking/stack_traces_controller_spec.rb'
- 'spec/controllers/projects/feature_flags_controller_spec.rb'
- 'spec/controllers/projects/jobs_controller_spec.rb'
- 'spec/controllers/projects/labels_controller_spec.rb'

View File

@ -135,9 +135,6 @@ RSpec/ExpectInHook:
- 'spec/controllers/projects/compare_controller_spec.rb'
- 'spec/controllers/projects/deployments_controller_spec.rb'
- 'spec/controllers/projects/environments_controller_spec.rb'
- 'spec/controllers/projects/error_tracking/projects_controller_spec.rb'
- 'spec/controllers/projects/error_tracking/stack_traces_controller_spec.rb'
- 'spec/controllers/projects/error_tracking_controller_spec.rb'
- 'spec/controllers/projects/issues_controller_spec.rb'
- 'spec/controllers/projects/labels_controller_spec.rb'
- 'spec/controllers/projects/merge_requests/content_controller_spec.rb'
@ -414,11 +411,6 @@ RSpec/ExpectInHook:
- 'spec/services/design_management/save_designs_service_spec.rb'
- 'spec/services/discussions/capture_diff_note_position_service_spec.rb'
- 'spec/services/environments/reset_auto_stop_service_spec.rb'
- 'spec/services/error_tracking/base_service_spec.rb'
- 'spec/services/error_tracking/issue_details_service_spec.rb'
- 'spec/services/error_tracking/issue_latest_event_service_spec.rb'
- 'spec/services/error_tracking/issue_update_service_spec.rb'
- 'spec/services/error_tracking/list_projects_service_spec.rb'
- 'spec/services/git/base_hooks_service_spec.rb'
- 'spec/services/git/branch_push_service_spec.rb'
- 'spec/services/git/wiki_push_service/change_spec.rb'
@ -492,7 +484,6 @@ RSpec/ExpectInHook:
- 'spec/services/user_project_access_changed_service_spec.rb'
- 'spec/support/services/issuable_update_service_shared_examples.rb'
- 'spec/support/services/migrate_to_ghost_user_service_shared_examples.rb'
- 'spec/support/shared_contexts/sentry_error_tracking_shared_context.rb'
- 'spec/support/shared_examples/controllers/wiki_actions_shared_examples.rb'
- 'spec/support/shared_examples/csp.rb'
- 'spec/support/shared_examples/features/wiki/user_views_wiki_page_shared_examples.rb'

View File

@ -301,9 +301,6 @@ RSpec/VerifiedDoubles:
- spec/controllers/projects/blob_controller_spec.rb
- spec/controllers/projects/branches_controller_spec.rb
- spec/controllers/projects/clusters_controller_spec.rb
- spec/controllers/projects/error_tracking/projects_controller_spec.rb
- spec/controllers/projects/error_tracking/stack_traces_controller_spec.rb
- spec/controllers/projects/error_tracking_controller_spec.rb
- spec/controllers/projects/import/jira_controller_spec.rb
- spec/controllers/projects/jobs_controller_spec.rb
- spec/controllers/projects/merge_requests/diffs_controller_spec.rb
@ -351,9 +348,6 @@ RSpec/VerifiedDoubles:
- spec/graphql/mutations/merge_requests/accept_spec.rb
- spec/graphql/mutations/merge_requests/create_spec.rb
- spec/graphql/resolvers/design_management/versions_resolver_spec.rb
- spec/graphql/resolvers/error_tracking/sentry_detailed_error_resolver_spec.rb
- spec/graphql/resolvers/error_tracking/sentry_error_collection_resolver_spec.rb
- spec/graphql/resolvers/error_tracking/sentry_errors_resolver_spec.rb
- spec/graphql/resolvers/kas/agent_connections_resolver_spec.rb
- spec/graphql/resolvers/projects/jira_projects_resolver_spec.rb
- spec/graphql/types/ci/detailed_status_type_spec.rb
@ -434,9 +428,6 @@ RSpec/VerifiedDoubles:
- spec/lib/constraints/user_url_constrainer_spec.rb
- spec/lib/csv_builder_spec.rb
- spec/lib/csv_builders/stream_spec.rb
- spec/lib/error_tracking/collector/dsn_spec.rb
- spec/lib/error_tracking/collector/sentry_auth_parser_spec.rb
- spec/lib/error_tracking/collector/sentry_request_parser_spec.rb
- spec/lib/extracts_path_spec.rb
- spec/lib/feature_spec.rb
- spec/lib/gitaly/server_spec.rb
@ -1015,8 +1006,6 @@ RSpec/VerifiedDoubles:
- spec/services/container_expiration_policies/cleanup_service_spec.rb
- spec/services/deployments/create_service_spec.rb
- spec/services/discussions/capture_diff_note_position_service_spec.rb
- spec/services/error_tracking/base_service_spec.rb
- spec/services/error_tracking/issue_update_service_spec.rb
- spec/services/event_create_service_spec.rb
- spec/services/git/base_hooks_service_spec.rb
- spec/services/git/process_ref_changes_service_spec.rb

View File

@ -1,5 +1,5 @@
<script>
import { GlIcon, GlSprintf, GlBadge, GlResizeObserverDirective } from '@gitlab/ui';
import { GlSprintf, GlBadge, GlResizeObserverDirective } from '@gitlab/ui';
import { GlBreakpointInstance } from '@gitlab/ui/dist/utils';
import { numberToHumanSize } from '~/lib/utils/number_utils';
import { __ } from '~/locale';
@ -14,7 +14,6 @@ export default {
name: 'PackageTitle',
components: {
TitleArea,
GlIcon,
GlSprintf,
PackageTags,
MetadataItem,
@ -84,7 +83,6 @@ export default {
data-qa-selector="package_title"
>
<template #sub-header>
<gl-icon name="eye" class="gl-mr-3" />
<span data-testid="sub-header">
<gl-sprintf :message="$options.i18n.packageInfo">
<template #version>

View File

@ -1,8 +1,10 @@
<script>
import { GlTooltipDirective } from '@gitlab/ui';
import { createAlert } from '~/flash';
import { createAlert, VARIANT_SUCCESS } from '~/flash';
import { TYPE_CI_RUNNER } from '~/graphql_shared/constants';
import { convertToGraphQLId } from '~/graphql_shared/utils';
import { redirectTo } from '~/lib/utils/url_utility';
import RunnerDeleteButton from '../components/runner_delete_button.vue';
import RunnerEditButton from '../components/runner_edit_button.vue';
import RunnerPauseButton from '../components/runner_pause_button.vue';
import RunnerHeader from '../components/runner_header.vue';
@ -10,10 +12,12 @@ import RunnerDetails from '../components/runner_details.vue';
import { I18N_FETCH_ERROR } from '../constants';
import runnerQuery from '../graphql/details/runner.query.graphql';
import { captureException } from '../sentry_utils';
import { saveAlertToLocalStorage } from '../local_storage_alert/save_alert_to_local_storage';
export default {
name: 'AdminRunnerShowApp',
components: {
RunnerDeleteButton,
RunnerEditButton,
RunnerPauseButton,
RunnerHeader,
@ -27,6 +31,10 @@ export default {
type: String,
required: true,
},
runnersPath: {
type: String,
required: true,
},
},
data() {
return {
@ -52,6 +60,9 @@ export default {
canUpdate() {
return this.runner.userPermissions?.updateRunner;
},
canDelete() {
return this.runner.userPermissions?.deleteRunner;
},
},
errorCaptured(error) {
this.reportToSentry(error);
@ -60,6 +71,10 @@ export default {
reportToSentry(error) {
captureException({ error, component: this.$options.name });
},
onDeleted({ message }) {
saveAlertToLocalStorage({ message, variant: VARIANT_SUCCESS });
redirectTo(this.runnersPath);
},
},
};
</script>
@ -69,6 +84,12 @@ export default {
<template #actions>
<runner-edit-button v-if="canUpdate && runner.editAdminUrl" :href="runner.editAdminUrl" />
<runner-pause-button v-if="canUpdate" :runner="runner" />
<runner-delete-button
v-if="canDelete"
:runner="runner"
category="secondary"
@deleted="onDeleted"
/>
</template>
</runner-header>

View File

@ -12,7 +12,7 @@ export const initAdminRunnerShow = (selector = '#js-admin-runner-show') => {
return null;
}
const { runnerId } = el.dataset;
const { runnerId, runnersPath } = el.dataset;
const apolloProvider = new VueApollo({
defaultClient: createDefaultClient(),
@ -25,6 +25,7 @@ export const initAdminRunnerShow = (selector = '#js-admin-runner-show') => {
return h(AdminRunnerShowApp, {
props: {
runnerId,
runnersPath,
},
});
},

View File

@ -5,12 +5,15 @@ import { visitUrl } from '~/lib/utils/url_utility';
import { updateOutdatedUrl } from '~/runner/runner_search_utils';
import createDefaultClient from '~/lib/graphql';
import { createLocalState } from '../graphql/list/local_state';
import { showAlertFromLocalStorage } from '../local_storage_alert/show_alert_from_local_storage';
import AdminRunnersApp from './admin_runners_app.vue';
Vue.use(GlToast);
Vue.use(VueApollo);
export const initAdminRunners = (selector = '#js-admin-runners') => {
showAlertFromLocalStorage();
const el = document.querySelector(selector);
if (!el) {

View File

@ -165,6 +165,7 @@ export default {
:loading="deleting"
:disabled="disabled"
variant="danger"
v-bind="$attrs"
>
{{ buttonContent }}
</gl-button>

View File

@ -1,6 +1,7 @@
fragment RunnerDetailsShared on CiRunner {
__typename
id
shortSha
runnerType
active
accessLevel

View File

@ -0,0 +1 @@
export const LOCAL_STORAGE_ALERT_KEY = 'local-storage-alert';

View File

@ -0,0 +1,8 @@
import AccessorUtilities from '~/lib/utils/accessor';
import { LOCAL_STORAGE_ALERT_KEY } from './constants';
export const saveAlertToLocalStorage = (alertOptions) => {
if (AccessorUtilities.canUseLocalStorage()) {
localStorage.setItem(LOCAL_STORAGE_ALERT_KEY, JSON.stringify(alertOptions));
}
};

View File

@ -0,0 +1,18 @@
import AccessorUtilities from '~/lib/utils/accessor';
import { LOCAL_STORAGE_ALERT_KEY } from './constants';
export const showAlertFromLocalStorage = async () => {
if (AccessorUtilities.canUseLocalStorage()) {
const alertOptions = localStorage.getItem(LOCAL_STORAGE_ALERT_KEY);
if (alertOptions) {
try {
const { createAlert } = await import('~/flash');
createAlert(JSON.parse(alertOptions));
} catch {
// ignore when the alert data cannot be parsed
}
}
localStorage.removeItem(LOCAL_STORAGE_ALERT_KEY);
}
};

View File

@ -394,8 +394,7 @@ a.gl-badge.badge-info:active {
background-color: #0b5cad;
}
a.gl-badge.badge-info:active {
box-shadow: inset 0 0 0 1px rgba(51, 51, 51, 0.8),
0 0 0 1px rgba(51, 51, 51, 0.4), 0 0 0 4px rgba(66, 143, 220, 0.48);
box-shadow: 0 0 0 1px #333, 0 0 0 3px #1f75cb;
outline: none;
}
.gl-badge.badge-success {
@ -408,8 +407,7 @@ a.gl-badge.badge-success:active {
background-color: #24663b;
}
a.gl-badge.badge-success:active {
box-shadow: inset 0 0 0 1px rgba(51, 51, 51, 0.8),
0 0 0 1px rgba(51, 51, 51, 0.4), 0 0 0 4px rgba(66, 143, 220, 0.48);
box-shadow: 0 0 0 1px #333, 0 0 0 3px #1f75cb;
outline: none;
}
.gl-badge.badge-warning {
@ -422,8 +420,7 @@ a.gl-badge.badge-warning:active {
background-color: #8f4700;
}
a.gl-badge.badge-warning:active {
box-shadow: inset 0 0 0 1px rgba(51, 51, 51, 0.8),
0 0 0 1px rgba(51, 51, 51, 0.4), 0 0 0 4px rgba(66, 143, 220, 0.48);
box-shadow: 0 0 0 1px #333, 0 0 0 3px #1f75cb;
outline: none;
}
.gl-button .gl-badge {
@ -488,9 +485,8 @@ a.gl-badge.badge-warning:active {
}
.gl-link:active {
text-decoration: underline;
box-shadow: 0 0 0 1px rgba(51, 51, 51, 0.4),
0 0 0 4px rgba(66, 143, 220, 0.48);
outline: none;
outline: 2px solid #1f75cb;
outline-offset: 2px;
}
.gl-button {
display: inline-flex;
@ -519,8 +515,7 @@ a.gl-badge.badge-warning:active {
}
.gl-button.gl-button.btn-default:active,
.gl-button.gl-button.btn-default.active {
box-shadow: inset 0 0 0 2px #bfbfbf, 0 0 0 1px rgba(51, 51, 51, 0.4),
0 0 0 4px rgba(66, 143, 220, 0.48);
box-shadow: inset 0 0 0 1px #bfbfbf, 0 0 0 1px #333, 0 0 0 3px #1f75cb;
outline: none;
background-color: #404040;
}

View File

@ -379,8 +379,7 @@ a.gl-badge.badge-info:active {
background-color: #9dc7f1;
}
a.gl-badge.badge-info:active {
box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.8),
0 0 0 1px rgba(255, 255, 255, 0.4), 0 0 0 4px rgba(31, 117, 203, 0.48);
box-shadow: 0 0 0 1px #fff, 0 0 0 3px #428fdc;
outline: none;
}
.gl-badge.badge-success {
@ -393,8 +392,7 @@ a.gl-badge.badge-success:active {
background-color: #91d4a8;
}
a.gl-badge.badge-success:active {
box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.8),
0 0 0 1px rgba(255, 255, 255, 0.4), 0 0 0 4px rgba(31, 117, 203, 0.48);
box-shadow: 0 0 0 1px #fff, 0 0 0 3px #428fdc;
outline: none;
}
.gl-badge.badge-warning {
@ -407,8 +405,7 @@ a.gl-badge.badge-warning:active {
background-color: #e9be74;
}
a.gl-badge.badge-warning:active {
box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.8),
0 0 0 1px rgba(255, 255, 255, 0.4), 0 0 0 4px rgba(31, 117, 203, 0.48);
box-shadow: 0 0 0 1px #fff, 0 0 0 3px #428fdc;
outline: none;
}
.gl-button .gl-badge {
@ -473,9 +470,8 @@ a.gl-badge.badge-warning:active {
}
.gl-link:active {
text-decoration: underline;
box-shadow: 0 0 0 1px rgba(255, 255, 255, 0.4),
0 0 0 4px rgba(31, 117, 203, 0.48);
outline: none;
outline: 2px solid #428fdc;
outline-offset: 2px;
}
.gl-button {
display: inline-flex;
@ -504,8 +500,7 @@ a.gl-badge.badge-warning:active {
}
.gl-button.gl-button.btn-default:active,
.gl-button.gl-button.btn-default.active {
box-shadow: inset 0 0 0 2px #5e5e5e, 0 0 0 1px rgba(255, 255, 255, 0.4),
0 0 0 4px rgba(31, 117, 203, 0.48);
box-shadow: inset 0 0 0 1px #5e5e5e, 0 0 0 1px #fff, 0 0 0 3px #428fdc;
outline: none;
background-color: #dbdbdb;
}

View File

@ -351,8 +351,7 @@ fieldset:disabled a.btn {
background-color: #fff;
}
.gl-button.gl-button.btn-default:active {
box-shadow: inset 0 0 0 2px #5e5e5e, 0 0 0 1px rgba(255, 255, 255, 0.4),
0 0 0 4px rgba(31, 117, 203, 0.48);
box-shadow: inset 0 0 0 1px #5e5e5e, 0 0 0 1px #fff, 0 0 0 3px #428fdc;
outline: none;
background-color: #dbdbdb;
}
@ -364,8 +363,7 @@ fieldset:disabled a.btn {
box-shadow: inset 0 0 0 1px #1068bf;
}
.gl-button.gl-button.btn-confirm:active {
box-shadow: inset 0 0 0 2px #033464, 0 0 0 1px rgba(255, 255, 255, 0.4),
0 0 0 4px rgba(31, 117, 203, 0.48);
box-shadow: inset 0 0 0 1px #033464, 0 0 0 1px #fff, 0 0 0 3px #428fdc;
outline: none;
background-color: #0b5cad;
}

View File

@ -2,6 +2,8 @@
module Registrations
class WelcomeController < ApplicationController
include OneTrustCSP
layout 'minimal'
skip_before_action :authenticate_user!, :required_signup_info, :check_two_factor_requirement, only: [:show, :update]
before_action :require_current_user

View File

@ -5,4 +5,4 @@
- page_title title
- add_to_breadcrumbs _('Runners'), admin_runners_path
#js-admin-runner-show{ data: {runner_id: @runner.id} }
#js-admin-runner-show{ data: {runner_id: @runner.id, runners_path: admin_runners_path} }

View File

@ -4,6 +4,6 @@ classes:
- ContainerExpirationPolicy
feature_categories:
- container_registry
description: TODO
description: Project level settings for container registry cleanup policies
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/20412
milestone: '12.6'

View File

@ -4,6 +4,6 @@ classes:
- ContainerRepository
feature_categories:
- container_registry
description: TODO
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/commit/01d159b409d8b24d36204979a73de249843d71bf
description: Container registry repositories (images), originally named container_images, renamed in https://gitlab.com/gitlab-org/gitlab/-/commit/01d159b409d8b24d36204979a73de249843d71bf
introduced_by_url: https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/10109
milestone: '9.1'

View File

@ -4,6 +4,6 @@ classes:
- DependencyProxy::Blob
feature_categories:
- dependency_proxy
description: TODO
description: Dependency proxy blob files
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/10676
milestone: '11.11'

View File

@ -4,6 +4,6 @@ classes:
- DependencyProxy::GroupSetting
feature_categories:
- dependency_proxy
description: TODO
description: Group-level settings for the dependency proxy
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/10676
milestone: '11.11'

View File

@ -4,6 +4,6 @@ classes:
- DependencyProxy::ImageTtlGroupPolicy
feature_categories:
- dependency_proxy
description: TODO
description: Group-level settings for dependency proxy cleanup policies
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/68809
milestone: '14.3'

View File

@ -4,6 +4,6 @@ classes:
- DependencyProxy::Manifest
feature_categories:
- dependency_proxy
description: TODO
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/48727
description: Dependency proxy manifest files
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/48535
milestone: '13.7'

View File

@ -4,6 +4,6 @@ classes:
- LfsFileLock
feature_categories:
- git_lfs
description: TODO
description: File locks for LFS objects
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/4091
milestone: '10.5'

View File

@ -4,6 +4,6 @@ classes:
- Geo::LfsObjectState
feature_categories:
- git_lfs
description: TODO
description: Geo verification states for LFS objects
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/63981
milestone: '14.6'

View File

@ -4,6 +4,6 @@ classes:
- LfsObject
feature_categories:
- git_lfs
description: TODO
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/commit/14032d8eb1a60ae5920286249c1044be2fa27278
description: LFS files
introduced_by_url: https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/1727
milestone: '8.2'

View File

@ -5,6 +5,6 @@ classes:
feature_categories:
- git_lfs
- source_code_management
description: TODO
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/commit/14032d8eb1a60ae5920286249c1044be2fa27278
description: Join table relating lfs_objects and projects
introduced_by_url: https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/1727
milestone: '8.2'

View File

@ -4,6 +4,6 @@ classes:
- Namespace::PackageSetting
feature_categories:
- package_registry
description: TODO
description: Namespace and group-level settings for the package registry
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/50104
milestone: '13.8'

View File

@ -4,6 +4,6 @@ classes:
- Packages::BuildInfo
feature_categories:
- package_registry
description: TODO
description: Join table relating packages_packages with ci_pipelines
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/19796
milestone: '12.6'

View File

@ -4,6 +4,6 @@ classes:
- Packages::Composer::CacheFile
feature_categories:
- package_registry
description: TODO
description: Composer packages cached SHA files (deprecated)
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/51509
milestone: '13.9'

View File

@ -4,6 +4,6 @@ classes:
- Packages::Composer::Metadatum
feature_categories:
- package_registry
description: TODO
description: Composer package metadata
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/30448
milestone: '13.1'

View File

@ -4,6 +4,6 @@ classes:
- Packages::Conan::FileMetadatum
feature_categories:
- package_registry
description: TODO
description: Conan package file metadata
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/16418
milestone: '12.5'

View File

@ -4,6 +4,6 @@ classes:
- Packages::Conan::Metadatum
feature_categories:
- package_registry
description: TODO
description: Conan package metadata
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/16418
milestone: '12.5'

View File

@ -4,6 +4,6 @@ classes:
- Packages::Debian::FileMetadatum
feature_categories:
- package_registry
description: TODO
description: Debian package file metadata
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/49692
milestone: '13.8'

View File

@ -4,6 +4,6 @@ classes:
- Packages::Debian::GroupArchitecture
feature_categories:
- package_registry
description: TODO
description: Debian registry group-level architectures
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/51265
milestone: '13.8'

View File

@ -4,6 +4,6 @@ classes:
- Packages::Debian::GroupComponentFile
feature_categories:
- package_registry
description: TODO
description: Debian group-level component files
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/52885
milestone: '13.9'

View File

@ -4,6 +4,6 @@ classes:
- Packages::Debian::GroupComponent
feature_categories:
- package_registry
description: TODO
description: Debian package group-level distribution components
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/51732
milestone: '13.9'

View File

@ -4,6 +4,6 @@ classes:
- Packages::Debian::GroupDistributionKey
feature_categories:
- package_registry
description: TODO
description: Debian group-level distribution keys
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/60993
milestone: '14.0'

View File

@ -4,6 +4,6 @@ classes:
- Packages::Debian::GroupDistribution
feature_categories:
- package_registry
description: TODO
description: Debian registry group level distributions
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/49405
milestone: '13.8'

View File

@ -4,6 +4,6 @@ classes:
- Packages::Debian::ProjectArchitecture
feature_categories:
- package_registry
description: TODO
description: Debian registry group-level architectures
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/51265
milestone: '13.8'

View File

@ -4,6 +4,6 @@ classes:
- Packages::Debian::ProjectComponentFile
feature_categories:
- package_registry
description: TODO
description: Debian project-level component files
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/52885
milestone: '13.9'

View File

@ -4,6 +4,6 @@ classes:
- Packages::Debian::ProjectComponent
feature_categories:
- package_registry
description: TODO
description: Debian package project-level distribution components
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/51732
milestone: '13.9'

View File

@ -4,6 +4,6 @@ classes:
- Packages::Debian::ProjectDistributionKey
feature_categories:
- package_registry
description: TODO
description: Debian project-level distribution keys
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/60993
milestone: '14.0'

View File

@ -4,6 +4,6 @@ classes:
- Packages::Debian::ProjectDistribution
feature_categories:
- package_registry
description: TODO
description: Debian package registry project level distributions
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/49405
milestone: '13.8'

View File

@ -4,6 +4,6 @@ classes:
- Packages::Debian::Publication
feature_categories:
- package_registry
description: TODO
description: Debian package publications relating distributions to packages
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/52916
milestone: '13.9'

View File

@ -4,6 +4,6 @@ classes:
- Packages::Dependency
feature_categories:
- package_registry
description: TODO
description: Information about package dependencies for a set of supported package types
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/20549
milestone: '12.6'

View File

@ -4,6 +4,6 @@ classes:
- Packages::DependencyLink
feature_categories:
- package_registry
description: TODO
description: Join table between packages_packages and packages_dependencies
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/20549
milestone: '12.6'

View File

@ -4,6 +4,6 @@ classes:
- Packages::Event
feature_categories:
- package_registry
description: TODO
description: Package tracking events (deprecated)
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/41846
milestone: '13.5'

View File

@ -4,6 +4,6 @@ classes:
- Packages::Helm::FileMetadatum
feature_categories:
- package_registry
description: TODO
description: Helm package file metadata
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/57017
milestone: '13.12'

View File

@ -4,6 +4,6 @@ classes:
- Packages::Maven::Metadatum
feature_categories:
- package_registry
description: TODO
description: Maven package metadata
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/6607
milestone: '11.3'

View File

@ -4,6 +4,6 @@ classes:
- Packages::Npm::Metadatum
feature_categories:
- package_registry
description: TODO
description: Npm package metadata
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/73639
milestone: '14.5'

View File

@ -4,6 +4,6 @@ classes:
- Packages::Nuget::DependencyLinkMetadatum
feature_categories:
- package_registry
description: TODO
description: Join table between nuget target frameworks and packages_dependency_links
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/30618
milestone: '13.0'

View File

@ -4,6 +4,6 @@ classes:
- Packages::Nuget::Metadatum
feature_categories:
- package_registry
description: TODO
description: Nuget package metadata
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/30994
milestone: '13.0'
milestone: '13.1'

View File

@ -4,6 +4,6 @@ classes:
- Packages::PackageFileBuildInfo
feature_categories:
- package_registry
description: TODO
description: Join table relating packages_package_files and ci_pipelines
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/44348
milestone: '13.6'

View File

@ -4,6 +4,6 @@ classes:
- Packages::PackageFile
feature_categories:
- package_registry
description: TODO
description: Package registry file links and file metadata for all package types
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/6607
milestone: '11.3'

View File

@ -4,6 +4,6 @@ classes:
- Packages::Package
feature_categories:
- package_registry
description: TODO
description: Information for individual packages in the package registry
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/6607
milestone: '11.3'

View File

@ -4,6 +4,6 @@ classes:
- Packages::Pypi::Metadatum
feature_categories:
- package_registry
description: TODO
description: PyPI package metadata
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/27632
milestone: '13.0'

View File

@ -4,6 +4,6 @@ classes:
- Packages::Rubygems::Metadatum
feature_categories:
- package_registry
description: TODO
description: Ruby gems metadata
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/52639
milestone: '13.9'

View File

@ -4,6 +4,6 @@ classes:
- Packages::Tag
feature_categories:
- package_registry
description: TODO
description: Package identifier tags for supported package types. See https://docs.gitlab.com/ee/user/packages/npm_registry/#add-npm-distribution-tags for an example.
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/20636
milestone: '12.7'

View File

@ -62,7 +62,8 @@ Prerequisite:
1. Enter the **Git repository URL**. Include the username
in the URL, if required: `https://MYUSERNAME@github.com/GROUPNAME/PROJECTNAME.git`
1. In **Mirror direction**, select **Pull**.
1. In **Authentication method**, select your authentication method.
1. In **Authentication method**, select your authentication method. To learn more, read
[Authentication methods for mirrors](index.md#authentication-methods-for-mirrors).
1. Select any of the options you need:
- [**Overwrite diverged branches**](#overwrite-diverged-branches)
- [**Trigger pipelines for mirror updates**](#trigger-pipelines-for-mirror-updates)

View File

@ -38,8 +38,8 @@ To set up push mirroring for an existing project:
1. Expand **Mirroring repositories**.
1. Enter a repository URL.
1. In the **Mirror direction** dropdown list, select **Push**.
1. Select an **Authentication method**.
You can authenticate with either a password or an [SSH key](index.md#ssh-authentication).
1. Select an **Authentication method**. To learn more, read
[Authentication methods for mirrors](index.md#authentication-methods-for-mirrors).
1. Select **Only mirror protected branches**, if necessary.
1. Select **Keep divergent refs**, if desired.
1. To save the configuration, select **Mirror repository**.

View File

@ -57,7 +57,7 @@
"@gitlab/at.js": "1.5.7",
"@gitlab/favicon-overlay": "2.0.0",
"@gitlab/svgs": "2.10.0",
"@gitlab/ui": "39.2.1",
"@gitlab/ui": "39.3.1",
"@gitlab/visual-review-tools": "1.7.0",
"@rails/actioncable": "6.1.4-7",
"@rails/ujs": "6.1.4-7",

View File

@ -0,0 +1,64 @@
# frozen_string_literal: true
module QA
RSpec.describe 'Create' do
describe 'Create a new merge request from the event notification after a push' do
let(:branch_name) { "merge-request-test-#{SecureRandom.hex(8)}" }
let(:title) { "Merge from push event notification test #{SecureRandom.hex(8)}" }
let(:project) do
Resource::Project.fabricate_via_api! do |project|
project.initialize_with_readme = true
end
end
before do
Flow::Login.sign_in
end
it(
'creates a merge request after a push via the git CLI',
testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/360489'
) do
Resource::Repository::ProjectPush.fabricate! do |push|
push.project = project
push.branch_name = branch_name
end
project.visit!
Page::Project::Show.perform(&:new_merge_request)
Page::MergeRequest::New.perform do |merge_request|
merge_request.fill_title(title)
merge_request.create_merge_request
end
Page::MergeRequest::Show.perform do |merge_request|
expect(merge_request).to have_title(title)
end
end
it(
'creates a merge request after a push via the API',
testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/360490'
) do
commit = Resource::Repository::Commit.fabricate_via_api! do |resource|
resource.project = project
resource.add_files([{ 'file_path': "file-#{SecureRandom.hex(8)}.txt", 'content': 'MR init' }])
resource.branch = branch_name
resource.start_branch = project.default_branch
end
project.wait_for_push(commit.commit_message)
project.visit!
Page::Project::Show.perform(&:new_merge_request)
Page::MergeRequest::New.perform do |merge_request|
merge_request.fill_title(title)
merge_request.create_merge_request
end
Page::MergeRequest::Show.perform do |merge_request|
expect(merge_request).to have_title(title)
end
end
end
end
end

View File

@ -6,18 +6,21 @@ RSpec.describe Projects::ErrorTracking::ProjectsController do
let_it_be(:project) { create(:project) }
let_it_be(:user) { create(:user) }
before_all do
project.add_maintainer(user)
end
before do
sign_in(user)
project.add_maintainer(user)
end
describe 'GET #index' do
context 'with insufficient permissions' do
before do
project.add_guest(user)
end
let(:user) { create(:user) }
it 'returns 404' do
project.add_guest(user)
get :index, params: list_projects_params
expect(response).to have_gitlab_http_status(:not_found)
@ -37,8 +40,8 @@ RSpec.describe Projects::ErrorTracking::ProjectsController do
end
context 'with authorized user' do
let(:list_projects_service) { spy(:list_projects_service) }
let(:sentry_project) { build(:error_tracking_project) }
let(:list_projects_service) { instance_double('ErrorTracking::ListProjectsService') }
let(:sentry_project) { build_stubbed(:error_tracking_project) }
let(:query_params) do
list_projects_params.slice(:api_host, :token)
@ -50,9 +53,9 @@ RSpec.describe Projects::ErrorTracking::ProjectsController do
.and_return(list_projects_service)
end
context 'service result is successful' do
context 'when service result is successful' do
before do
expect(list_projects_service).to receive(:execute)
allow(list_projects_service).to receive(:execute)
.and_return(status: :success, projects: [sentry_project])
end
@ -65,12 +68,12 @@ RSpec.describe Projects::ErrorTracking::ProjectsController do
end
end
context 'service result is erroneous' do
context 'with service result is erroneous' do
let(:error_message) { 'error message' }
context 'without http_status' do
before do
expect(list_projects_service).to receive(:execute)
allow(list_projects_service).to receive(:execute)
.and_return(status: :error, message: error_message)
end
@ -86,7 +89,7 @@ RSpec.describe Projects::ErrorTracking::ProjectsController do
let(:http_status) { :no_content }
before do
expect(list_projects_service).to receive(:execute).and_return(
allow(list_projects_service).to receive(:execute).and_return(
status: :error,
message: error_message,
http_status: http_status
@ -106,11 +109,7 @@ RSpec.describe Projects::ErrorTracking::ProjectsController do
private
def list_projects_params(opts = {})
project_params(
format: :json,
api_host: 'gitlab.com',
token: 'token'
)
project_params(format: :json, api_host: 'gitlab.com', token: 'token')
end
end

View File

@ -6,30 +6,34 @@ RSpec.describe Projects::ErrorTracking::StackTracesController do
let_it_be(:project) { create(:project) }
let_it_be(:user) { create(:user) }
before_all do
project.add_maintainer(user)
end
before do
sign_in(user)
project.add_maintainer(user)
end
describe 'GET #index' do
let(:issue_id) { non_existing_record_id }
let(:issue_stack_trace_service) { spy(:issue_stack_trace_service) }
let(:issue_latest_event_service) { instance_double('ErrorTracking::IssueLatestEventService') }
subject(:get_stack_trace) do
get :index, params: { namespace_id: project.namespace, project_id: project, issue_id: issue_id, format: :json }
end
before do
expect(ErrorTracking::IssueLatestEventService)
allow(ErrorTracking::IssueLatestEventService)
.to receive(:new).with(project, user, issue_id: issue_id.to_s)
.and_return(issue_stack_trace_service)
expect(issue_stack_trace_service).to receive(:execute).and_return(service_response)
.and_return(issue_latest_event_service)
allow(issue_latest_event_service).to receive(:execute).and_return(service_response)
get_stack_trace
end
context 'awaiting data' do
let(:service_response) { { status: :error, http_status: :no_content }}
context 'when awaiting data' do
let(:service_response) { { status: :error, http_status: :no_content } }
it 'responds with no data' do
expect(response).to have_gitlab_http_status(:no_content)
@ -38,19 +42,14 @@ RSpec.describe Projects::ErrorTracking::StackTracesController do
it_behaves_like 'sets the polling header'
end
context 'service result is successful' do
context 'when service result is successful' do
let(:service_response) { { status: :success, latest_event: error_event } }
let(:error_event) { build(:error_tracking_sentry_error_event) }
it 'responds with success' do
expect(response).to have_gitlab_http_status(:ok)
end
it 'responds with error' do
expect(response).to match_response_schema('error_tracking/issue_stack_trace')
end
let(:error_event) { build_stubbed(:error_tracking_sentry_error_event) }
it 'highlights stack trace source code' do
expect(response).to have_gitlab_http_status(:ok)
expect(response).to match_response_schema('error_tracking/issue_stack_trace')
expect(json_response['error']).to eq(
Gitlab::ErrorTracking::StackTraceHighlightDecorator.decorate(error_event).as_json
)
@ -59,7 +58,7 @@ RSpec.describe Projects::ErrorTracking::StackTracesController do
it_behaves_like 'sets the polling header'
end
context 'service result is erroneous' do
context 'when service result is erroneous' do
let(:error_message) { 'error message' }
context 'without http_status' do
@ -67,9 +66,6 @@ RSpec.describe Projects::ErrorTracking::StackTracesController do
it 'responds with bad request' do
expect(response).to have_gitlab_http_status(:bad_request)
end
it 'responds with error message' do
expect(json_response['message']).to eq(error_message)
end
end
@ -80,9 +76,6 @@ RSpec.describe Projects::ErrorTracking::StackTracesController do
it 'responds with custom http status' do
expect(response).to have_gitlab_http_status(http_status)
end
it 'responds with error message' do
expect(json_response['message']).to eq(error_message)
end
end

View File

@ -6,9 +6,12 @@ RSpec.describe Projects::ErrorTrackingController do
let_it_be(:project) { create(:project) }
let_it_be(:user) { create(:user) }
before_all do
project.add_maintainer(user)
end
before do
sign_in(user)
project.add_maintainer(user)
end
describe 'GET #index' do
@ -46,18 +49,18 @@ RSpec.describe Projects::ErrorTrackingController do
end
describe 'format json' do
let(:list_issues_service) { spy(:list_issues_service) }
let(:list_issues_service) { instance_double('ErrorTracking::ListIssuesService') }
let(:external_url) { 'http://example.com' }
context 'no data' do
context 'with no data' do
let(:permitted_params) { permit_index_parameters!({}) }
before do
expect(ErrorTracking::ListIssuesService)
allow(ErrorTracking::ListIssuesService)
.to receive(:new).with(project, user, permitted_params)
.and_return(list_issues_service)
expect(list_issues_service).to receive(:execute)
allow(list_issues_service).to receive(:execute)
.and_return(status: :error, http_status: :no_content)
end
@ -76,22 +79,22 @@ RSpec.describe Projects::ErrorTrackingController do
let(:permitted_params) { permit_index_parameters!(search_term: search_term, sort: sort, cursor: cursor) }
before do
expect(ErrorTracking::ListIssuesService)
allow(ErrorTracking::ListIssuesService)
.to receive(:new).with(project, user, permitted_params)
.and_return(list_issues_service)
end
context 'service result is successful' do
context 'when service result is successful' do
before do
expect(list_issues_service).to receive(:execute)
allow(list_issues_service).to receive(:execute)
.and_return(status: :success, issues: [error], pagination: {})
expect(list_issues_service).to receive(:external_url)
allow(list_issues_service).to receive(:external_url)
.and_return(external_url)
get :index, params: params
end
let(:error) { build(:error_tracking_sentry_error) }
let(:error) { build_stubbed(:error_tracking_sentry_error) }
it 'returns a list of errors' do
expect(response).to have_gitlab_http_status(:ok)
@ -109,16 +112,16 @@ RSpec.describe Projects::ErrorTrackingController do
context 'without extra params' do
before do
expect(ErrorTracking::ListIssuesService)
allow(ErrorTracking::ListIssuesService)
.to receive(:new).with(project, user, permit_index_parameters!({}))
.and_return(list_issues_service)
end
context 'service result is successful' do
context 'when service result is successful' do
before do
expect(list_issues_service).to receive(:execute)
allow(list_issues_service).to receive(:execute)
.and_return(status: :success, issues: [error], pagination: {})
expect(list_issues_service).to receive(:external_url)
allow(list_issues_service).to receive(:external_url)
.and_return(external_url)
end
@ -137,12 +140,12 @@ RSpec.describe Projects::ErrorTrackingController do
end
end
context 'service result is erroneous' do
context 'when service result is erroneous' do
let(:error_message) { 'error message' }
context 'without http_status' do
before do
expect(list_issues_service).to receive(:execute)
allow(list_issues_service).to receive(:execute)
.and_return(status: :error, message: error_message)
end
@ -158,7 +161,7 @@ RSpec.describe Projects::ErrorTrackingController do
let(:http_status) { :no_content }
before do
expect(list_issues_service).to receive(:execute).and_return(
allow(list_issues_service).to receive(:execute).and_return(
status: :error,
message: error_message,
http_status: http_status
@ -189,7 +192,7 @@ RSpec.describe Projects::ErrorTrackingController do
describe 'GET #issue_details' do
let_it_be(:issue_id) { non_existing_record_id }
let(:issue_details_service) { spy(:issue_details_service) }
let(:issue_details_service) { instance_double('ErrorTracking::IssueDetailsService') }
let(:permitted_params) do
ActionController::Parameters.new(
@ -199,15 +202,15 @@ RSpec.describe Projects::ErrorTrackingController do
end
before do
expect(ErrorTracking::IssueDetailsService)
allow(ErrorTracking::IssueDetailsService)
.to receive(:new).with(project, user, permitted_params)
.and_return(issue_details_service)
end
describe 'format json' do
context 'no data' do
context 'with no data' do
before do
expect(issue_details_service).to receive(:execute)
allow(issue_details_service).to receive(:execute)
.and_return(status: :error, http_status: :no_content)
get :details, params: issue_params(issue_id: issue_id, format: :json)
end
@ -219,15 +222,15 @@ RSpec.describe Projects::ErrorTrackingController do
it_behaves_like 'sets the polling header'
end
context 'service result is successful' do
context 'when service result is successful' do
before do
expect(issue_details_service).to receive(:execute)
allow(issue_details_service).to receive(:execute)
.and_return(status: :success, issue: error)
get :details, params: issue_params(issue_id: issue_id, format: :json)
end
let(:error) { build(:error_tracking_sentry_detailed_error) }
let(:error) { build_stubbed(:error_tracking_sentry_detailed_error) }
it 'returns an error' do
expected_error = error.as_json.except('first_release_version').merge(
@ -245,12 +248,12 @@ RSpec.describe Projects::ErrorTrackingController do
it_behaves_like 'sets the polling header'
end
context 'service result is erroneous' do
context 'when service result is erroneous' do
let(:error_message) { 'error message' }
context 'without http_status' do
before do
expect(issue_details_service).to receive(:execute)
allow(issue_details_service).to receive(:execute)
.and_return(status: :error, message: error_message)
end
@ -266,7 +269,7 @@ RSpec.describe Projects::ErrorTrackingController do
let(:http_status) { :no_content }
before do
expect(issue_details_service).to receive(:execute).and_return(
allow(issue_details_service).to receive(:execute).and_return(
status: :error,
message: error_message,
http_status: http_status
@ -286,7 +289,7 @@ RSpec.describe Projects::ErrorTrackingController do
describe 'PUT #update' do
let(:issue_id) { non_existing_record_id }
let(:issue_update_service) { spy(:issue_update_service) }
let(:issue_update_service) { instance_double('ErrorTracking::IssueUpdateService') }
let(:permitted_params) do
ActionController::Parameters.new(
{ issue_id: issue_id.to_s, status: 'resolved' }
@ -298,15 +301,15 @@ RSpec.describe Projects::ErrorTrackingController do
end
before do
expect(ErrorTracking::IssueUpdateService)
allow(ErrorTracking::IssueUpdateService)
.to receive(:new).with(project, user, permitted_params)
.and_return(issue_update_service)
end
describe 'format json' do
context 'update result is successful' do
context 'when update result is successful' do
before do
expect(issue_update_service).to receive(:execute)
allow(issue_update_service).to receive(:execute)
.and_return(status: :success, updated: true, closed_issue_iid: non_existing_record_iid)
update_issue
@ -318,11 +321,11 @@ RSpec.describe Projects::ErrorTrackingController do
end
end
context 'update result is erroneous' do
context 'when update result is erroneous' do
let(:error_message) { 'error message' }
before do
expect(issue_update_service).to receive(:execute)
allow(issue_update_service).to receive(:execute)
.and_return(status: :error, message: error_message)
update_issue

View File

@ -4,6 +4,7 @@ require 'spec_helper'
RSpec.describe "Admin Runners" do
include Spec::Support::Helpers::Features::RunnersHelpers
include Spec::Support::Helpers::ModalHelpers
let_it_be(:admin) { create(:admin) }
@ -483,6 +484,24 @@ RSpec.describe "Admin Runners" do
expect(page).to have_content 'Tags tag1'
end
end
describe 'when a runner is deleted' do
before do
click_on 'Delete runner'
within_modal do
click_on 'Delete runner'
end
end
it 'deletes runner' do
expect(page.find('[data-testid="alert-success"]')).to have_content('deleted')
end
it 'redirects to runner list' do
expect(current_url).to match(admin_runners_path)
end
end
end
describe "Runner edit page" do

View File

@ -4,7 +4,6 @@ require 'spec_helper'
RSpec.describe 'Recent searches', :js do
include FilteredSearchHelpers
include MobileHelpers
let_it_be(:project_1) { create(:project, :public) }
let_it_be(:project_2) { create(:project, :public) }
@ -14,116 +13,96 @@ RSpec.describe 'Recent searches', :js do
let(:project_1_local_storage_key) { "#{project_1.full_path}-issue-recent-searches" }
before do
Capybara.ignore_hidden_elements = false
stub_feature_flags(vue_issues_list: true)
# Visit any fast-loading page so we can clear local storage without a DOM exception
visit '/404'
remove_recent_searches
end
after do
Capybara.ignore_hidden_elements = true
end
it 'searching adds to recent searches' do
visit project_issues_path(project_1)
input_filtered_search('foo', submit: true)
input_filtered_search('bar', submit: true)
submit_then_clear_search 'foo'
submit_then_clear_search 'bar'
click_button 'Toggle history'
items = all('.filtered-search-history-dropdown-item', visible: false, count: 2)
expect(items[0].text).to eq('bar')
expect(items[1].text).to eq('foo')
expect_recent_searches_history_item 'bar'
expect_recent_searches_history_item 'foo'
end
it 'visiting URL with search params adds to recent searches' do
visit project_issues_path(project_1, label_name: 'foo', search: 'bar')
visit project_issues_path(project_1, label_name: 'qux', search: 'garply')
items = all('.filtered-search-history-dropdown-item', visible: false, count: 2)
click_button 'Toggle history'
expect(items[0].text).to eq('label: = ~qux garply')
expect(items[1].text).to eq('label: = ~foo bar')
expect_recent_searches_history_item 'Label := qux garply'
expect_recent_searches_history_item 'Label := foo bar'
end
it 'saved recent searches are restored last on the list' do
set_recent_searches(project_1_local_storage_key, '["saved1", "saved2"]')
set_recent_searches(project_1_local_storage_key, '[[{"type":"filtered-search-term","value":{"data":"saved1"}}],[{"type":"filtered-search-term","value":{"data":"saved2"}}]]')
visit project_issues_path(project_1, search: 'foo')
click_button 'Toggle history'
items = all('.filtered-search-history-dropdown-item', visible: false, count: 3)
expect(items[0].text).to eq('foo')
expect(items[1].text).to eq('saved1')
expect(items[2].text).to eq('saved2')
expect_recent_searches_history_item 'foo'
expect_recent_searches_history_item 'saved1'
expect_recent_searches_history_item 'saved2'
end
it 'searches are scoped to projects' do
visit project_issues_path(project_1)
input_filtered_search('foo', submit: true)
input_filtered_search('bar', submit: true)
submit_then_clear_search 'foo'
submit_then_clear_search 'bar'
visit project_issues_path(project_2)
input_filtered_search('more', submit: true)
input_filtered_search('things', submit: true)
submit_then_clear_search 'more'
submit_then_clear_search 'things'
click_button 'Toggle history'
items = all('.filtered-search-history-dropdown-item', visible: false, count: 2)
expect(items[0].text).to eq('things')
expect(items[1].text).to eq('more')
expect_recent_searches_history_item 'things'
expect_recent_searches_history_item 'more'
end
it 'clicking item fills search input' do
set_recent_searches(project_1_local_storage_key, '["foo", "bar"]')
set_recent_searches(project_1_local_storage_key, '[[{"type":"filtered-search-term","value":{"data":"foo"}}],[{"type":"filtered-search-term","value":{"data":"bar"}}]]')
visit project_issues_path(project_1)
find('.filtered-search-history-dropdown-toggle-button').click
all('.filtered-search-history-dropdown-item', count: 2)[0].click
wait_for_filtered_search('foo')
click_button 'Toggle history'
click_button 'foo'
expect(find('.filtered-search').value.strip).to eq('foo')
expect_search_term 'foo'
end
it 'clear recent searches button, clears recent searches' do
set_recent_searches(project_1_local_storage_key, '["foo"]')
set_recent_searches(project_1_local_storage_key, '[[{"type":"filtered-search-term","value":{"data":"foo"}}]]')
visit project_issues_path(project_1)
find('.filtered-search-history-dropdown-toggle-button').click
all('.filtered-search-history-dropdown-item', count: 1)
click_button 'Toggle history'
find('.filtered-search-history-clear-button').click
items_after = all('.filtered-search-history-dropdown-item', count: 0)
expect_recent_searches_history_item_count 1
expect(items_after.count).to eq(0)
click_button 'Clear recent searches'
click_button 'Toggle history'
expect(page).to have_text "You don't have any recent searches"
expect_recent_searches_history_item_count 0
end
it 'shows flash error when failed to parse saved history' do
set_recent_searches(project_1_local_storage_key, 'fail')
visit project_issues_path(project_1)
expect(find('[data-testid="alert-danger"]')).to have_text('An error occurred while parsing recent searches')
expect(page).to have_text 'An error occurred while parsing recent searches'
end
context 'on tablet/mobile screen' do
it 'shows only the history icon in the dropdown' do
resize_screen_sm
visit project_issues_path(project_1)
expect(find('.filtered-search-history-dropdown-wrapper')).to have_selector('svg', visible: true)
expect(find('.filtered-search-history-dropdown-wrapper')).to have_selector('span', text: 'Recent searches', visible: false)
end
end
context 'on PC screen' do
it 'shows only the Recent searches text in the dropdown' do
restore_window_size
visit project_issues_path(project_1)
expect(find('.filtered-search-history-dropdown-wrapper')).to have_selector('svg', visible: false)
expect(find('.filtered-search-history-dropdown-wrapper')).to have_selector('span', text: 'Recent searches', visible: true)
end
def submit_then_clear_search(search)
click_filtered_search_bar
send_keys(search, :enter)
click_button 'Clear'
end
end

View File

@ -9,102 +9,74 @@ RSpec.describe 'Search bar', :js do
let_it_be(:user) { create(:user) }
let_it_be(:issue) { create(:issue, project: project) }
let(:filtered_search) { find('.filtered-search') }
before do
stub_feature_flags(vue_issues_list: true)
project.add_maintainer(user)
sign_in(user)
visit project_issues_path(project)
end
def get_left_style(style)
left_style = /left:\s\d*[.]\d*px/.match(style)
left_style.to_s.gsub('left: ', '').to_f
end
describe 'keyboard navigation' do
it 'makes item active' do
filtered_search.native.send_keys(:down)
page.within '#js-dropdown-hint' do
expect(page).to have_selector('.droplab-item-active')
end
end
it 'selects item' do
filtered_search.native.send_keys(:down, :down, :enter)
click_filtered_search_bar
send_keys :down, :enter
expect_tokens([{ name: 'Assignee' }])
expect_filtered_search_input_empty
expect_token_segment 'Assignee'
end
end
describe 'clear search button' do
it 'clears text' do
search_text = 'search_text'
filtered_search.set(search_text)
click_filtered_search_bar
send_keys search_text
expect(filtered_search.value).to eq(search_text)
find('.filtered-search-box .clear-search').click
expect(page).to have_field 'Search', with: search_text
expect(filtered_search.value).to eq('')
click_button 'Clear'
expect(page).to have_field 'Search', with: ''
end
it 'hides by default' do
expect(page).to have_css('.clear-search', visible: false)
expect(page).not_to have_button 'Clear'
end
it 'hides after clicked' do
filtered_search.set('a')
find('.filtered-search-box .clear-search').click
click_filtered_search_bar
send_keys 'a'
expect(page).to have_css('.clear-search', visible: false)
click_button 'Clear'
expect(page).not_to have_button 'Clear'
end
it 'hides when there is no text' do
filtered_search.set('a')
filtered_search.set('')
click_filtered_search_bar
send_keys 'a', :backspace, :backspace
expect(page).to have_css('.clear-search', visible: false)
expect(page).not_to have_button 'Clear'
end
it 'shows when there is text' do
filtered_search.set('a')
click_filtered_search_bar
send_keys 'a'
expect(page).to have_css('.clear-search', visible: true)
expect(page).to have_button 'Clear'
end
it 'resets the dropdown hint filter' do
filtered_search.click
original_size = page.all('#js-dropdown-hint .filter-dropdown .filter-dropdown-item').size
click_filtered_search_bar
original_size = get_suggestion_count
send_keys 'autho'
filtered_search.set('autho')
expect_suggestion_count 1
expect(find('#js-dropdown-hint')).to have_selector('.filter-dropdown .filter-dropdown-item', count: 1)
click_button 'Clear'
click_filtered_search_bar
find('.filtered-search-box .clear-search').click
filtered_search.click
expect(find('#js-dropdown-hint')).to have_selector('.filter-dropdown .filter-dropdown-item', count: original_size)
end
it 'resets the dropdown filters' do
filtered_search.click
hint_offset = get_left_style(find('#js-dropdown-hint')['style'])
filtered_search.set('a')
filtered_search.set('author:')
find('#js-dropdown-hint', visible: false)
find('.filtered-search-box .clear-search').click
filtered_search.click
expect(find('#js-dropdown-hint')).to have_selector('.filter-dropdown .filter-dropdown-item', minimum: 6)
expect(get_left_style(find('#js-dropdown-hint')['style'])).to eq(hint_offset)
expect_suggestion_count(original_size)
end
end
end

View File

@ -14,178 +14,160 @@ RSpec.describe 'Visual tokens', :js do
let_it_be(:cc_label) { create(:label, project: project, title: 'Community Contribution') }
let_it_be(:issue) { create(:issue, project: project) }
let(:filtered_search) { find('.filtered-search') }
let(:filter_author_dropdown) { find("#js-dropdown-author .filter-dropdown") }
def is_input_focused
page.evaluate_script("document.activeElement.classList.contains('filtered-search')")
end
before do
stub_feature_flags(vue_issues_list: true)
project.add_user(user, :maintainer)
project.add_user(user_rock, :maintainer)
sign_in(user)
set_cookie('sidebar_collapsed', 'true')
visit project_issues_path(project)
end
describe 'editing a single token' do
before do
input_filtered_search('author:=@root assignee:=none', submit: false)
first('.tokens-container .filtered-search-token').click
wait_for_requests
select_tokens 'Author', '=', user.username, 'Assignee', '=', 'None'
click_token_segment(user.name)
end
it 'opens author dropdown' do
expect(page).to have_css('#js-dropdown-author', visible: true)
expect_filtered_search_input('@root')
expect_visible_suggestions_list
expect(page).to have_field('Search', with: 'root')
end
it 'filters value' do
filtered_search.send_keys(:backspace)
send_keys :backspace
expect(page).to have_css('#js-dropdown-author .filter-dropdown .filter-dropdown-item', count: 1)
expect_suggestion_count 1
end
it 'ends editing mode when document is clicked' do
find('.js-navbar').click
expect_filtered_search_input_empty
expect(page).to have_css('#js-dropdown-author', visible: false)
expect_empty_search_term
expect_hidden_suggestions_list
end
describe 'selecting different author from dropdown' do
before do
filter_author_dropdown.find('.filter-dropdown-item .dropdown-light-content', text: "@#{user_rock.username}").click
send_keys :backspace, :backspace, :backspace, :backspace
click_on user_rock.name
end
it 'changes value in visual token' do
wait_for_requests
expect(first('.tokens-container .filtered-search-token .value').text).to eq("#{user_rock.name}")
end
it 'moves input to the right' do
expect(is_input_focused).to eq(true)
expect_author_token(user_rock.name)
end
end
end
describe 'editing multiple tokens' do
before do
input_filtered_search('author:=@root assignee:=none', submit: false)
first('.tokens-container .filtered-search-token').click
select_tokens 'Author', '=', user.username, 'Assignee', '=', 'None'
click_token_segment(user.name)
end
it 'opens author dropdown' do
expect(page).to have_css('#js-dropdown-author', visible: true)
expect_visible_suggestions_list
end
it 'opens assignee dropdown' do
find('.tokens-container .filtered-search-token', text: 'Assignee').click
expect(page).to have_css('#js-dropdown-assignee', visible: true)
click_token_segment 'Assignee'
expect_visible_suggestions_list
end
end
describe 'editing a search term while editing another filter token' do
before do
input_filtered_search('foo assignee:=', submit: false)
first('.tokens-container .filtered-search-term').click
click_filtered_search_bar
send_keys 'foo '
select_tokens 'Assignee', '='
click_token_segment 'foo'
send_keys ' '
end
it 'opens author dropdown' do
find('#js-dropdown-hint .filter-dropdown .filter-dropdown-item', text: 'Author').click
click_on 'Author'
expect(page).to have_css('#js-dropdown-operator', visible: true)
expect(page).to have_css('#js-dropdown-author', visible: false)
expect_suggestion '='
expect_suggestion '!='
find('#js-dropdown-operator .filter-dropdown .filter-dropdown-item[data-value="="]').click
click_on '= is'
expect(page).to have_css('#js-dropdown-operator', visible: false)
expect(page).to have_css('#js-dropdown-author', visible: true)
expect_suggestion(user.name)
expect_suggestion(user_rock.name)
end
end
describe 'add new token after editing existing token' do
before do
input_filtered_search('author:=@root assignee:=none', submit: false)
first('.tokens-container .filtered-search-token').click
filtered_search.send_keys(' ')
select_tokens 'Assignee', '=', user.username, 'Label', '=', 'None'
click_token_segment(user.name)
send_keys ' '
end
describe 'opens dropdowns' do
it 'opens hint dropdown' do
expect(page).to have_css('#js-dropdown-hint', visible: true)
expect_visible_suggestions_list
end
it 'opens token dropdown' do
filtered_search.send_keys('author:=')
click_on 'Author'
expect(page).to have_css('#js-dropdown-author', visible: true)
expect_visible_suggestions_list
end
end
describe 'visual tokens' do
it 'creates visual token' do
filtered_search.send_keys('author:=@thomas ')
token = page.all('.tokens-container .filtered-search-token')[1]
click_on 'Author'
click_on '= is'
click_on 'The Rock'
expect(token.find('.name').text).to eq('Author')
expect(token.find('.value').text).to eq('@thomas')
expect_author_token 'The Rock'
end
end
it 'does not tokenize incomplete token' do
filtered_search.send_keys('author:=')
click_on 'Author'
find('.js-navbar').click
token = page.all('.tokens-container .js-visual-token')[1]
expect_filtered_search_input_empty
expect(token.find('.name').text).to eq('Author')
expect_empty_search_term
expect_token_segment 'Assignee'
end
end
describe 'search using incomplete visual tokens' do
before do
input_filtered_search('author:=@root assignee:=none', extra_space: false)
select_tokens 'Author', '=', user.username, 'Assignee', '=', 'None'
end
it 'tokenizes the search term to complete visual token' do
expect_tokens([
author_token(user.name),
assignee_token('None')
])
expect_author_token(user.name)
expect_assignee_token 'None'
end
end
it 'does retain hint token when mix of typing and clicks are performed' do
input_filtered_search('label:', extra_space: false, submit: false)
select_tokens 'Label'
click_on '= is'
expect(page).to have_css('#js-dropdown-operator', visible: true)
find('#js-dropdown-operator li[data-value="="]').click
token = page.all('.tokens-container .js-visual-token')[0]
expect(token.find('.name').text).to eq('Label')
expect(token.find('.operator').text).to eq('=')
expect_token_segment 'Label'
expect_token_segment '='
end
describe 'Any/None option' do
it 'hidden when NOT operator is selected' do
input_filtered_search('milestone:!=', extra_space: false, submit: false)
select_tokens 'Milestone', '!='
expect(page).not_to have_selector("#js-dropdown-milestone", text: 'Any')
expect(page).not_to have_selector("#js-dropdown-milestone", text: 'None')
expect_no_suggestion 'Any'
expect_no_suggestion 'None'
end
it 'shown when EQUAL operator is selected' do
input_filtered_search('milestone:=', extra_space: false, submit: false)
select_tokens 'Milestone', '='
expect(page).to have_selector("#js-dropdown-milestone", text: 'Any')
expect(page).to have_selector("#js-dropdown-milestone", text: 'None')
expect_suggestion 'Any'
expect_suggestion 'None'
end
end
end

View File

@ -2,6 +2,7 @@
exports[`AddContextCommitsModal renders modal with 2 tabs 1`] = `
<gl-modal-stub
arialabel=""
body-class="add-review-item pt-0"
cancel-variant="light"
dismisslabel="Close"

View File

@ -57,6 +57,7 @@ exports[`Alert integration settings form should match the default snapshot 1`] =
</gl-button-stub>
<gl-modal-stub
arialabel=""
dismisslabel="Close"
modalclass=""
modalid="resetWebhookModal"

View File

@ -29,12 +29,6 @@ exports[`PackageTitle renders with tags 1`] = `
<div
class="gl-display-flex gl-align-items-center gl-text-gray-500 gl-mt-3"
>
<gl-icon-stub
class="gl-mr-3"
name="eye"
size="16"
/>
<span
data-testid="sub-header"
>
@ -127,12 +121,6 @@ exports[`PackageTitle renders without tags 1`] = `
<div
class="gl-display-flex gl-align-items-center gl-text-gray-500 gl-mt-3"
>
<gl-icon-stub
class="gl-mr-3"
name="eye"
size="16"
/>
<span
data-testid="sub-header"
>

View File

@ -1,4 +1,4 @@
import { GlIcon, GlSprintf } from '@gitlab/ui';
import { GlSprintf } from '@gitlab/ui';
import { GlBreakpointInstance } from '@gitlab/ui/dist/utils';
import { nextTick } from 'vue';
import { createMockDirective, getBinding } from 'helpers/vue_mock_directive';
@ -46,7 +46,6 @@ describe('PackageTitle', () => {
const findPackageRef = () => wrapper.findByTestId('package-ref');
const findPackageTags = () => wrapper.findComponent(PackageTags);
const findPackageBadges = () => wrapper.findAllByTestId('tag-badge');
const findSubHeaderIcon = () => wrapper.findComponent(GlIcon);
const findSubHeaderText = () => wrapper.findByTestId('sub-header');
const findSubHeaderTimeAgo = () => wrapper.findComponent(TimeAgoTooltip);
@ -120,12 +119,6 @@ describe('PackageTitle', () => {
});
describe('sub-header', () => {
it('has the eye icon', async () => {
await createComponent();
expect(findSubHeaderIcon().props('name')).toBe('eye');
});
it('has a text showing version', async () => {
await createComponent();

View File

@ -31,6 +31,7 @@ exports[`Project remove modal initialized matches the snapshot 1`] = `
<gl-modal-stub
actioncancel="[object Object]"
actionprimary="[object Object]"
arialabel=""
dismisslabel="Close"
footer-class="gl-bg-gray-10 gl-p-5"
modalclass=""

View File

@ -3,24 +3,30 @@ import { mount, shallowMount } from '@vue/test-utils';
import VueApollo from 'vue-apollo';
import createMockApollo from 'helpers/mock_apollo_helper';
import waitForPromises from 'helpers/wait_for_promises';
import { createAlert } from '~/flash';
import { createAlert, VARIANT_SUCCESS } from '~/flash';
import { redirectTo } from '~/lib/utils/url_utility';
import { getIdFromGraphQLId } from '~/graphql_shared/utils';
import RunnerHeader from '~/runner/components/runner_header.vue';
import RunnerPauseButton from '~/runner/components/runner_pause_button.vue';
import RunnerDeleteButton from '~/runner/components/runner_delete_button.vue';
import RunnerEditButton from '~/runner/components/runner_edit_button.vue';
import runnerQuery from '~/runner/graphql/details/runner.query.graphql';
import AdminRunnerShowApp from '~/runner/admin_runner_show/admin_runner_show_app.vue';
import { captureException } from '~/runner/sentry_utils';
import { saveAlertToLocalStorage } from '~/runner/local_storage_alert/save_alert_to_local_storage';
import { runnerData } from '../mock_data';
jest.mock('~/runner/local_storage_alert/save_alert_to_local_storage');
jest.mock('~/flash');
jest.mock('~/runner/sentry_utils');
jest.mock('~/lib/utils/url_utility');
const mockRunner = runnerData.data.runner;
const mockRunnerGraphqlId = mockRunner.id;
const mockRunnerId = `${getIdFromGraphQLId(mockRunnerGraphqlId)}`;
const mockRunnersPath = '/admin/runners';
Vue.use(VueApollo);
@ -29,6 +35,7 @@ describe('AdminRunnerShowApp', () => {
let mockRunnerQuery;
const findRunnerHeader = () => wrapper.findComponent(RunnerHeader);
const findRunnerDeleteButton = () => wrapper.findComponent(RunnerDeleteButton);
const findRunnerEditButton = () => wrapper.findComponent(RunnerEditButton);
const findRunnerPauseButton = () => wrapper.findComponent(RunnerPauseButton);
@ -45,6 +52,7 @@ describe('AdminRunnerShowApp', () => {
apolloProvider: createMockApollo([[runnerQuery, mockRunnerQuery]]),
propsData: {
runnerId: mockRunnerId,
runnersPath: mockRunnersPath,
...props,
},
});
@ -75,6 +83,7 @@ describe('AdminRunnerShowApp', () => {
it('displays the runner edit and pause buttons', async () => {
expect(findRunnerEditButton().exists()).toBe(true);
expect(findRunnerPauseButton().exists()).toBe(true);
expect(findRunnerDeleteButton().exists()).toBe(true);
});
it('shows basic runner details', async () => {
@ -109,6 +118,42 @@ describe('AdminRunnerShowApp', () => {
});
});
describe('when runner cannot be deleted', () => {
beforeEach(async () => {
mockRunnerQueryResult({
userPermissions: {
deleteRunner: false,
},
});
await createComponent({
mountFn: mount,
});
});
it('does not display the runner edit and pause buttons', () => {
expect(findRunnerDeleteButton().exists()).toBe(false);
});
});
describe('when runner is deleted', () => {
beforeEach(async () => {
await createComponent({
mountFn: mount,
});
});
it('redirects to the runner list page', () => {
findRunnerDeleteButton().vm.$emit('deleted', { message: 'Runner deleted' });
expect(saveAlertToLocalStorage).toHaveBeenCalledWith({
message: 'Runner deleted',
variant: VARIANT_SUCCESS,
});
expect(redirectTo).toHaveBeenCalledWith(mockRunnersPath);
});
});
describe('when runner does not have an edit url ', () => {
beforeEach(async () => {
mockRunnerQueryResult({

View File

@ -118,6 +118,12 @@ describe('RunnerDeleteButton', () => {
expect(findBtn().attributes('aria-label')).toBe(undefined);
});
it('Passes other attributes to the button', () => {
createComponent({ props: { category: 'secondary' } });
expect(findBtn().props('category')).toBe('secondary');
});
describe(`Before the delete button is clicked`, () => {
it('The mutation has not been called', () => {
expect(runnerDeleteHandler).toHaveBeenCalledTimes(0);

View File

@ -122,6 +122,7 @@ describe('RunnerUpdateForm', () => {
// Some read-only fields are not submitted
const {
__typename,
shortSha,
ipAddress,
executorName,
runnerType,

View File

@ -0,0 +1,24 @@
import AccessorUtilities from '~/lib/utils/accessor';
import { saveAlertToLocalStorage } from '~/runner/local_storage_alert/save_alert_to_local_storage';
import { LOCAL_STORAGE_ALERT_KEY } from '~/runner/local_storage_alert/constants';
import { useLocalStorageSpy } from 'helpers/local_storage_helper';
const mockAlert = { message: 'Message!' };
describe('saveAlertToLocalStorage', () => {
useLocalStorageSpy();
beforeEach(() => {
jest.spyOn(AccessorUtilities, 'canUseLocalStorage').mockReturnValue(true);
});
it('saves message to local storage', () => {
saveAlertToLocalStorage(mockAlert);
expect(localStorage.setItem).toHaveBeenCalledTimes(1);
expect(localStorage.setItem).toHaveBeenCalledWith(
LOCAL_STORAGE_ALERT_KEY,
JSON.stringify(mockAlert),
);
});
});

View File

@ -0,0 +1,40 @@
import AccessorUtilities from '~/lib/utils/accessor';
import { showAlertFromLocalStorage } from '~/runner/local_storage_alert/show_alert_from_local_storage';
import { LOCAL_STORAGE_ALERT_KEY } from '~/runner/local_storage_alert/constants';
import { useLocalStorageSpy } from 'helpers/local_storage_helper';
import { createAlert } from '~/flash';
jest.mock('~/flash');
describe('showAlertFromLocalStorage', () => {
useLocalStorageSpy();
beforeEach(() => {
jest.spyOn(AccessorUtilities, 'canUseLocalStorage').mockReturnValue(true);
});
it('retrieves message from local storage and displays it', async () => {
const mockAlert = { message: 'Message!' };
localStorage.getItem.mockReturnValueOnce(JSON.stringify(mockAlert));
await showAlertFromLocalStorage();
expect(createAlert).toHaveBeenCalledTimes(1);
expect(createAlert).toHaveBeenCalledWith(mockAlert);
expect(localStorage.removeItem).toHaveBeenCalledTimes(1);
expect(localStorage.removeItem).toHaveBeenCalledWith(LOCAL_STORAGE_ALERT_KEY);
});
it.each(['not a json string', null])('does not fail when stored message is %o', async (item) => {
localStorage.getItem.mockReturnValueOnce(item);
await showAlertFromLocalStorage();
expect(createAlert).not.toHaveBeenCalled();
expect(localStorage.removeItem).toHaveBeenCalledTimes(1);
expect(localStorage.removeItem).toHaveBeenCalledWith(LOCAL_STORAGE_ALERT_KEY);
});
});

View File

@ -63,6 +63,7 @@ exports[`self monitor component When the self monitor project has not been creat
</div>
<gl-modal-stub
arialabel=""
cancel-title="Cancel"
category="primary"
dismisslabel="Close"

View File

@ -10,6 +10,7 @@ exports[`Metrics upload item render the metrics image component 1`] = `
<gl-modal-stub
actioncancel="[object Object]"
actionprimary="[object Object]"
arialabel=""
body-class="gl-pb-0! gl-min-h-6!"
dismisslabel="Close"
modalclass=""
@ -26,6 +27,7 @@ exports[`Metrics upload item render the metrics image component 1`] = `
<gl-modal-stub
actioncancel="[object Object]"
actionprimary="[object Object]"
arialabel=""
data-testid="metric-image-edit-modal"
dismisslabel="Close"
modalclass=""

View File

@ -4,6 +4,7 @@ exports[`RunnerAwsDeploymentsModal renders the modal 1`] = `
<gl-modal-stub
actionprimary="[object Object]"
actionsecondary="[object Object]"
arialabel=""
dismisslabel="Close"
modalclass=""
modalid="runner-aws-deployments-modal"

View File

@ -8,28 +8,23 @@ RSpec.describe Resolvers::ErrorTracking::SentryDetailedErrorResolver do
let_it_be(:project) { create(:project) }
let_it_be(:current_user) { create(:user) }
let(:issue_details_service) { spy('ErrorTracking::IssueDetailsService') }
let(:issue_details_service) { instance_double('ErrorTracking::IssueDetailsService') }
let(:service_response) { {} }
specify do
expect(described_class).to have_nullable_graphql_type(Types::ErrorTracking::SentryDetailedErrorType)
before_all do
project.add_developer(current_user)
end
before do
project.add_developer(current_user)
allow(ErrorTracking::IssueDetailsService)
.to receive(:new)
.and_return issue_details_service
.and_return(issue_details_service)
allow(issue_details_service).to receive(:execute).and_return(service_response)
end
shared_examples 'it resolves to nil' do
it 'resolves to nil' do
allow(issue_details_service).to receive(:execute)
.and_return(issue: nil)
result = resolve_error(args)
expect(result).to be_nil
end
specify do
expect(described_class).to have_nullable_graphql_type(Types::ErrorTracking::SentryDetailedErrorType)
end
describe '#resolve' do
@ -41,13 +36,9 @@ RSpec.describe Resolvers::ErrorTracking::SentryDetailedErrorResolver do
expect(issue_details_service).to have_received(:execute)
end
context 'error matched' do
let(:detailed_error) { build(:error_tracking_sentry_detailed_error) }
before do
allow(issue_details_service).to receive(:execute)
.and_return(issue: detailed_error)
end
context 'when error matches' do
let(:detailed_error) { build_stubbed(:error_tracking_sentry_detailed_error) }
let(:service_response) { { issue: detailed_error } }
it 'resolves to a detailed error' do
expect(resolve_error(args)).to eq detailed_error
@ -58,11 +49,16 @@ RSpec.describe Resolvers::ErrorTracking::SentryDetailedErrorResolver do
end
end
context 'if id does not match issue' do
it_behaves_like 'it resolves to nil'
context 'when id does not match issue' do
let(:service_response) { { issue: nil } }
it 'resolves to nil' do
result = resolve_error(args)
expect(result).to be_nil
end
end
context 'blank id' do
context 'with blank id' do
let(:args) { { id: '' } }
it 'responds with an error' do
@ -71,6 +67,8 @@ RSpec.describe Resolvers::ErrorTracking::SentryDetailedErrorResolver do
end
end
private
def resolve_error(args = {}, context = { current_user: current_user })
resolve(described_class, obj: project, args: args, ctx: context)
end

View File

@ -8,18 +8,22 @@ RSpec.describe Resolvers::ErrorTracking::SentryErrorCollectionResolver do
let_it_be(:project) { create(:project) }
let_it_be(:current_user) { create(:user) }
let(:list_issues_service) { spy('ErrorTracking::ListIssuesService') }
let(:list_issues_service) { instance_double('ErrorTracking::ListIssuesService') }
specify do
expect(described_class).to have_nullable_graphql_type(Types::ErrorTracking::SentryErrorCollectionType)
before_all do
project.add_developer(current_user)
end
before do
project.add_developer(current_user)
allow(ErrorTracking::ListIssuesService)
.to receive(:new)
.and_return list_issues_service
allow(list_issues_service).to receive(:external_url)
end
specify do
expect(described_class).to have_nullable_graphql_type(Types::ErrorTracking::SentryErrorCollectionType)
end
describe '#resolve' do
@ -34,8 +38,7 @@ RSpec.describe Resolvers::ErrorTracking::SentryErrorCollectionResolver do
.to receive(:external_url)
.and_return(fake_url)
result = resolve_error_collection
expect(result.external_url).to eq fake_url
expect(resolve_error_collection.external_url).to eq fake_url
end
it 'provides the project' do

View File

@ -9,7 +9,7 @@ RSpec.describe Resolvers::ErrorTracking::SentryErrorsResolver do
let_it_be(:current_user) { create(:user) }
let_it_be(:error_collection) { Gitlab::ErrorTracking::ErrorCollection.new(project: project) }
let(:list_issues_service) { spy('ErrorTracking::ListIssuesService') }
let(:list_issues_service) { instance_double('ErrorTracking::ListIssuesService') }
let(:issues) { nil }
let(:pagination) { nil }
@ -19,23 +19,25 @@ RSpec.describe Resolvers::ErrorTracking::SentryErrorsResolver do
end
describe '#resolve' do
before do
allow(ErrorTracking::ListIssuesService)
.to receive(:new)
.and_return list_issues_service
allow(list_issues_service).to receive(:execute).and_return({})
end
context 'with insufficient user permission' do
let(:user) { create(:user) }
let(:current_user) { create(:user) }
it 'returns nil' do
context = { current_user: user }
expect(resolve_errors({}, context)).to eq nil
expect(resolve_errors).to eq nil
end
end
context 'with sufficient permission' do
before do
before_all do
project.add_developer(current_user)
allow(ErrorTracking::ListIssuesService)
.to receive(:new)
.and_return list_issues_service
end
context 'when after arg given' do
@ -52,14 +54,9 @@ RSpec.describe Resolvers::ErrorTracking::SentryErrorsResolver do
end
context 'when no issues fetched' do
before do
allow(list_issues_service)
.to receive(:execute)
.and_return(
issues: nil
)
end
it 'returns nil' do
expect(list_issues_service).to receive(:execute).and_return(issues: nil)
expect(resolve_errors).to eq nil
end
end

View File

@ -3,24 +3,32 @@
require 'spec_helper'
RSpec.describe ErrorTracking::Collector::Dsn do
describe '.build__url' do
let(:gitlab) do
double(
describe '.build_url' do
let(:setting) do
{
protocol: 'https',
https: true,
port: 443,
host: 'gitlab.example.com',
port: '4567',
relative_url_root: nil
)
}
end
subject { described_class.build_url('abcdef1234567890', 778) }
it 'returns a valid URL' do
allow(Settings).to receive(:gitlab).and_return(gitlab)
allow(Settings).to receive(:gitlab_on_standard_port?).and_return(false)
it 'returns a valid URL without explicit port' do
stub_config_setting(setting)
is_expected.to eq('https://abcdef1234567890@gitlab.example.com:4567/api/v4/error_tracking/collector/778')
is_expected.to eq('https://abcdef1234567890@gitlab.example.com/api/v4/error_tracking/collector/778')
end
context 'with non-standard port' do
it 'returns a valid URL with custom port' do
setting[:port] = 4567
stub_config_setting(setting)
is_expected.to eq('https://abcdef1234567890@gitlab.example.com:4567/api/v4/error_tracking/collector/778')
end
end
end
end

View File

@ -5,11 +5,11 @@ require 'spec_helper'
RSpec.describe ErrorTracking::Collector::SentryAuthParser do
describe '.parse' do
let(:headers) { { 'X-Sentry-Auth' => "Sentry sentry_key=glet_1fedb514e17f4b958435093deb02048c" } }
let(:request) { double('request', headers: headers) }
let(:request) { instance_double('ActionDispatch::Request', headers: headers) }
subject { described_class.parse(request) }
context 'empty headers' do
context 'with empty headers' do
let(:headers) { {} }
it 'fails with exception' do
@ -17,7 +17,7 @@ RSpec.describe ErrorTracking::Collector::SentryAuthParser do
end
end
context 'missing sentry_key' do
context 'with missing sentry_key' do
let(:headers) { { 'X-Sentry-Auth' => "Sentry foo=bar" } }
it 'returns empty value for public_key' do

View File

@ -9,7 +9,7 @@ RSpec.describe ErrorTracking::Collector::SentryRequestParser do
let(:body) { raw_event }
let(:headers) { { 'Content-Encoding' => '' } }
let(:request) { double('request', headers: headers, body: StringIO.new(body)) }
let(:request) { instance_double('ActionDispatch::Request', headers: headers, body: StringIO.new(body)) }
subject { described_class.parse(request) }
@ -22,7 +22,7 @@ RSpec.describe ErrorTracking::Collector::SentryRequestParser do
end
end
context 'empty body content' do
context 'with empty body content' do
let(:body) { '' }
it 'fails with exception' do
@ -30,7 +30,7 @@ RSpec.describe ErrorTracking::Collector::SentryRequestParser do
end
end
context 'plain text sentry request' do
context 'with plain text sentry request' do
it_behaves_like 'valid parser'
end
end

View File

@ -4,8 +4,8 @@ require 'spec_helper'
RSpec.describe ErrorTracking::BaseService do
describe '#compose_response' do
let(:project) { double('project') }
let(:user) { double('user', id: non_existing_record_id) }
let(:project) { build_stubbed(:project) }
let(:user) { build_stubbed(:user, id: non_existing_record_id) }
let(:service) { described_class.new(project, user) }
it 'returns bad_request error when response has an error key' do
@ -19,7 +19,10 @@ RSpec.describe ErrorTracking::BaseService do
end
it 'returns server error when response has missing key error_type' do
data = { error: 'Unexpected Error', error_type: ErrorTracking::ProjectErrorTrackingSetting::SENTRY_API_ERROR_TYPE_MISSING_KEYS }
data = {
error: 'Unexpected Error',
error_type: ErrorTracking::ProjectErrorTrackingSetting::SENTRY_API_ERROR_TYPE_MISSING_KEYS
}
result = service.send(:compose_response, data)
@ -48,7 +51,7 @@ RSpec.describe ErrorTracking::BaseService do
context 'when parse_response is implemented' do
before do
expect(service).to receive(:parse_response) do |response|
allow(service).to receive(:parse_response) do |response|
{ animal: response[:thing] }
end
end

View File

@ -14,7 +14,7 @@ RSpec.describe ErrorTracking::IssueDetailsService do
let(:params) { { issue_id: detailed_error.id } }
before do
expect(error_tracking_setting)
allow(error_tracking_setting)
.to receive(:issue_details).and_return(issue: detailed_error)
end
@ -40,7 +40,7 @@ RSpec.describe ErrorTracking::IssueDetailsService do
include_examples 'error tracking service sentry error handling', :issue_details
include_examples 'error tracking service http status handling', :issue_details
context 'integrated error tracking' do
context 'with integrated error tracking' do
let_it_be(:error) { create(:error_tracking_error, project: project) }
let(:params) { { issue_id: error.id } }

View File

@ -15,7 +15,7 @@ RSpec.describe ErrorTracking::IssueLatestEventService do
let(:error_event) { build(:error_tracking_sentry_error_event) }
before do
expect(error_tracking_setting)
allow(error_tracking_setting)
.to receive(:issue_latest_event).and_return(latest_event: error_event)
end
@ -28,7 +28,7 @@ RSpec.describe ErrorTracking::IssueLatestEventService do
include_examples 'error tracking service sentry error handling', :issue_latest_event
include_examples 'error tracking service http status handling', :issue_latest_event
context 'integrated error tracking' do
context 'with integrated error tracking' do
let_it_be(:error) { create(:error_tracking_error, project: project) }
let_it_be(:event) { create(:error_tracking_error_event, error: error) }

View File

@ -13,8 +13,7 @@ RSpec.describe ErrorTracking::IssueUpdateService do
it 'does not call the close issue service' do
update_service.execute
expect(issue_close_service)
.not_to have_received(:execute)
expect(issue_close_service).not_to have_received(:execute)
end
it 'does not create system note' do
@ -29,8 +28,7 @@ RSpec.describe ErrorTracking::IssueUpdateService do
let(:update_issue_response) { { updated: true } }
before do
expect(error_tracking_setting)
.to receive(:update_issue).and_return(update_issue_response)
allow(error_tracking_setting).to receive(:update_issue).and_return(update_issue_response)
end
it 'returns the response' do
@ -49,12 +47,11 @@ RSpec.describe ErrorTracking::IssueUpdateService do
result
end
context 'related issue and resolving' do
context 'with related issue and resolving' do
let(:issue) { create(:issue, project: project) }
let(:sentry_issue) { create(:sentry_issue, issue: issue) }
let(:arguments) { { issue_id: sentry_issue.sentry_issue_identifier, status: 'resolved' } }
let(:issue_close_service) { spy(:issue_close_service) }
let(:issue_close_service) { instance_double('Issues::CloseService') }
before do
allow_next_instance_of(SentryIssueFinder) do |finder|
@ -78,11 +75,11 @@ RSpec.describe ErrorTracking::IssueUpdateService do
.with(issue, system_note: false)
end
context 'issues gets closed' do
context 'when issue gets closed' do
let(:closed_issue) { create(:issue, :closed, project: project) }
before do
expect(issue_close_service)
allow(issue_close_service)
.to receive(:execute)
.with(issue, system_note: false)
.and_return(closed_issue)
@ -99,13 +96,13 @@ RSpec.describe ErrorTracking::IssueUpdateService do
end
end
context 'issue is already closed' do
context 'when issue is already closed' do
let(:issue) { create(:issue, :closed, project: project) }
include_examples 'does not perform close issue flow'
end
context 'status is not resolving' do
context 'when status is not resolving' do
let(:arguments) { { issue_id: sentry_issue.sentry_issue_identifier, status: 'ignored' } }
include_examples 'does not perform close issue flow'
@ -115,7 +112,7 @@ RSpec.describe ErrorTracking::IssueUpdateService do
include_examples 'error tracking service sentry error handling', :update_issue
context 'integrated error tracking' do
context 'with integrated error tracking' do
let(:error) { create(:error_tracking_error, project: project) }
let(:arguments) { { issue_id: error.id, status: 'resolved' } }
let(:update_issue_response) { { updated: true, status: :success, closed_issue_iid: nil } }

View File

@ -221,6 +221,10 @@ module FilteredSearchHelpers
find('.gl-filtered-search-last-item').click
end
def click_token_segment(value)
find('.gl-filtered-search-token-segment', text: value).click
end
def expect_visible_suggestions_list
expect(page).to have_css('.gl-filtered-search-suggestion-list')
end
@ -233,6 +237,10 @@ module FilteredSearchHelpers
expect(page).to have_css('.gl-filtered-search-suggestion', text: value)
end
def expect_no_suggestion(value)
expect(page).not_to have_css('.gl-filtered-search-suggestion', text: value)
end
def expect_suggestion_count(count)
expect(page).to have_css('.gl-filtered-search-suggestion', count: count)
end
@ -245,6 +253,12 @@ module FilteredSearchHelpers
expect(page).to have_css '.gl-filtered-search-token', text: "Author = #{value}"
end
def expect_search_term(value)
value.split(' ').each do |term|
expect(page).to have_css '.gl-filtered-search-term', text: term
end
end
def expect_empty_search_term
expect(page).to have_css '.gl-filtered-search-term', text: ''
end
@ -252,4 +266,12 @@ module FilteredSearchHelpers
def expect_token_segment(value)
expect(page).to have_css '.gl-filtered-search-token-segment', text: value
end
def expect_recent_searches_history_item(value)
expect(page).to have_css '.gl-search-box-by-click-history-item', text: value
end
def expect_recent_searches_history_item_count(count)
expect(page).to have_css '.gl-search-box-by-click-history-item', count: count
end
end

View File

@ -14,7 +14,7 @@ RSpec.shared_context 'sentry error tracking context' do
end
before do
expect(project).to receive(:error_tracking_setting).at_least(:once).and_return(error_tracking_setting)
allow(project).to receive(:error_tracking_setting).at_least(:once).and_return(error_tracking_setting)
project.add_reporter(user)
end

View File

@ -845,11 +845,6 @@
dependencies:
regenerator-runtime "^0.13.4"
"@babel/standalone@^7.0.0":
version "7.10.2"
resolved "https://registry.yarnpkg.com/@babel/standalone/-/standalone-7.10.2.tgz#49dbbadcbc4b199df064d7d8b3e21c915b84abdb"
integrity sha512-PNQuj9oQH6BL/3l9iiL8hJLQwX14woA2/FHcPtNIZAc7IgFZYJdtMBMXiy4xcefADHTSvoBnmc2AybrHRW1IKQ==
"@babel/template@^7.10.1", "@babel/template@^7.16.7", "@babel/template@^7.3.3":
version "7.16.7"
resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.16.7.tgz#8d126c8701fde4d66b264b3eba3d96f07666d155"
@ -973,22 +968,17 @@
resolved "https://registry.yarnpkg.com/@gitlab/svgs/-/svgs-2.10.0.tgz#223d3ab592b06aff330a05aa2e7b590624ab8a08"
integrity sha512-XohQ/abgalEHNaNR/HCTS761GUSzlLAMSyO/37eaSykjMqeX8v2iEwUm+0JtmI70N/7mp/tFZv5gzGIDt5oWgQ==
"@gitlab/ui@39.2.1":
version "39.2.1"
resolved "https://registry.yarnpkg.com/@gitlab/ui/-/ui-39.2.1.tgz#e1930ccfd85ff3b378f2d4b4e769df6022d778f8"
integrity sha512-CwArf5CLS9VhnpGJMPtROzLEtUHudaXVSo2HusKQS6rMUWDsc0SFF5nC07uIlp0oI8iS3zt3O9Ma9uAJBlxLgA==
"@gitlab/ui@39.3.1":
version "39.3.1"
resolved "https://registry.yarnpkg.com/@gitlab/ui/-/ui-39.3.1.tgz#333d10724748da9356af2b1150cee58d86264b90"
integrity sha512-6Tqa2sPxPBkC6svKwgyqRo/ONo8tuO7rITHecKuPYcnoRfZjxxzzEK2aPI+0Cdi/Jl478WWh6lT/T1jB065vNw==
dependencies:
"@babel/standalone" "^7.0.0"
bootstrap-vue "2.20.1"
copy-to-clipboard "^3.0.8"
dompurify "^2.3.6"
echarts "^5.2.1"
highlight.js "^10.6.0"
iframe-resizer "^4.3.2"
js-beautify "^1.8.8"
lodash "^4.17.20"
portal-vue "^2.1.6"
url-search-params-polyfill "^5.0.0"
vue-runtime-helpers "^1.1.2"
"@gitlab/visual-review-tools@1.7.0":
@ -3879,13 +3869,6 @@ copy-descriptor@^0.1.0:
resolved "https://registry.yarnpkg.com/copy-descriptor/-/copy-descriptor-0.1.1.tgz#676f6eb3c39997c2ee1ac3a924fd6124748f578d"
integrity sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=
copy-to-clipboard@^3.0.8:
version "3.2.0"
resolved "https://registry.yarnpkg.com/copy-to-clipboard/-/copy-to-clipboard-3.2.0.tgz#d2724a3ccbfed89706fac8a894872c979ac74467"
integrity sha512-eOZERzvCmxS8HWzugj4Uxl8OJxa7T2k1Gi0X5qavwydHIfuSHq2dTD09LOg/XyGq4Zpb5IsR/2OJ5lbOegz78w==
dependencies:
toggle-selection "^1.0.6"
copy-webpack-plugin@^6.4.1:
version "6.4.1"
resolved "https://registry.yarnpkg.com/copy-webpack-plugin/-/copy-webpack-plugin-6.4.1.tgz#138cd9b436dbca0a6d071720d5414848992ec47e"
@ -6636,16 +6619,16 @@ he@^1.1.0:
resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f"
integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==
highlight.js@^10.6.0, highlight.js@~10.7.0:
version "10.7.2"
resolved "https://registry.yarnpkg.com/highlight.js/-/highlight.js-10.7.2.tgz#89319b861edc66c48854ed1e6da21ea89f847360"
integrity sha512-oFLl873u4usRM9K63j4ME9u3etNF0PLiJhSQ8rdfuL51Wn3zkD6drf9ZW0dOzjnZI22YYG24z30JcmfCZjMgYg==
highlight.js@^11.3.1, highlight.js@~11.4.0:
version "11.4.0"
resolved "https://registry.yarnpkg.com/highlight.js/-/highlight.js-11.4.0.tgz#34ceadd49e1596ee5aba3d99346cdfd4845ee05a"
integrity sha512-nawlpCBCSASs7EdvZOYOYVkJpGmAOKMYZgZtUqSRqodZE0GRVcFKwo1RcpeOemqh9hyttTdd5wDBwHkuSyUfnA==
highlight.js@~10.7.0:
version "10.7.2"
resolved "https://registry.yarnpkg.com/highlight.js/-/highlight.js-10.7.2.tgz#89319b861edc66c48854ed1e6da21ea89f847360"
integrity sha512-oFLl873u4usRM9K63j4ME9u3etNF0PLiJhSQ8rdfuL51Wn3zkD6drf9ZW0dOzjnZI22YYG24z30JcmfCZjMgYg==
hmac-drbg@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/hmac-drbg/-/hmac-drbg-1.0.1.tgz#d2745701025a6c775a6c545793ed502fc0c649a1"
@ -7830,7 +7813,7 @@ jquery.caret@^0.3.1:
resolved "https://registry.yarnpkg.com/jquery/-/jquery-3.6.0.tgz#c72a09f15c1bdce142f49dbf1170bdf8adac2470"
integrity sha512-JVzAR/AjBvVt2BmYhxRCSYysDsPcssdmTFnzyLEts9qNwmjmu4JTAMYubEfwVOSwpQ1I1sKKFcxhZCI2buerfw==
js-beautify@^1.6.12, js-beautify@^1.8.8:
js-beautify@^1.6.12:
version "1.11.0"
resolved "https://registry.yarnpkg.com/js-beautify/-/js-beautify-1.11.0.tgz#afb873dc47d58986360093dcb69951e8bcd5ded2"
integrity sha512-a26B+Cx7USQGSWnz9YxgJNMmML/QG2nqIaL7VVYPCXbqiKz8PN0waSNvroMtvAK6tY7g/wPdNWGEP+JTNIBr6A==
@ -12030,11 +12013,6 @@ to-regex@^3.0.1, to-regex@^3.0.2:
regex-not "^1.0.2"
safe-regex "^1.1.0"
toggle-selection@^1.0.6:
version "1.0.6"
resolved "https://registry.yarnpkg.com/toggle-selection/-/toggle-selection-1.0.6.tgz#6e45b1263f2017fa0acc7d89d78b15b8bf77da32"
integrity sha1-bkWxJj8gF/oKzH2J14sVuL932jI=
toidentifier@1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.1.tgz#3be34321a88a820ed1bd80dfaa33e479fbb8dd35"
@ -12471,11 +12449,6 @@ url-parse-lax@^3.0.0:
dependencies:
prepend-http "^2.0.0"
url-search-params-polyfill@^5.0.0:
version "5.1.0"
resolved "https://registry.yarnpkg.com/url-search-params-polyfill/-/url-search-params-polyfill-5.1.0.tgz#f0405dcc2e921bf7f5fdf8c4e616f1e8088ef31b"
integrity sha512-yjFY7uw2xRf9e8Mg4ZVkZwtp8dMCC4cbBkEIZiTDpuSY2WJ9+Quw0wRhxncv32qaMQwmBQT+P847rO8PrFhhDA==
url@0.10.3:
version "0.10.3"
resolved "https://registry.yarnpkg.com/url/-/url-0.10.3.tgz#021e4d9c7705f21bbf37d03ceb58767402774c64"