Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
1a397155d6
commit
2705a15dea
|
@ -11,12 +11,6 @@ Gitlab/PolicyRuleBoolean:
|
|||
Exclude:
|
||||
- 'ee/app/policies/ee/identity_provider_policy.rb'
|
||||
|
||||
# Offense count: 218
|
||||
# Cop supports --auto-correct.
|
||||
# Configuration parameters: PreferredName.
|
||||
Naming/RescuedExceptionsVariableName:
|
||||
Enabled: false
|
||||
|
||||
# Offense count: 29
|
||||
# Configuration parameters: MinSize.
|
||||
Performance/CollectionLiteralInLoop:
|
||||
|
|
|
@ -0,0 +1,222 @@
|
|||
---
|
||||
# Cop supports --auto-correct.
|
||||
Naming/RescuedExceptionsVariableName:
|
||||
# Offense count: 269
|
||||
# Temporarily disabled due to too many offenses
|
||||
Enabled: false
|
||||
Exclude:
|
||||
- 'app/controllers/admin/projects_controller.rb'
|
||||
- 'app/controllers/projects/google_cloud/deployments_controller.rb'
|
||||
- 'app/controllers/projects/google_cloud/service_accounts_controller.rb'
|
||||
- 'app/controllers/projects/merge_requests/drafts_controller.rb'
|
||||
- 'app/controllers/projects/milestones_controller.rb'
|
||||
- 'app/controllers/projects/mirrors_controller.rb'
|
||||
- 'app/controllers/projects/repositories_controller.rb'
|
||||
- 'app/controllers/projects_controller.rb'
|
||||
- 'app/finders/repositories/changelog_tag_finder.rb'
|
||||
- 'app/graphql/mutations/issues/move.rb'
|
||||
- 'app/graphql/resolvers/ci/config_resolver.rb'
|
||||
- 'app/graphql/resolvers/environments_resolver.rb'
|
||||
- 'app/helpers/application_helper.rb'
|
||||
- 'app/models/application_setting.rb'
|
||||
- 'app/models/blob_viewer/metrics_dashboard_yml.rb'
|
||||
- 'app/models/ci/build.rb'
|
||||
- 'app/models/ci/deleted_object.rb'
|
||||
- 'app/models/clusters/concerns/elasticsearch_client.rb'
|
||||
- 'app/models/concerns/prometheus_adapter.rb'
|
||||
- 'app/models/concerns/repository_storage_movable.rb'
|
||||
- 'app/models/concerns/x509_serial_number_attribute.rb'
|
||||
- 'app/models/integrations/base_issue_tracker.rb'
|
||||
- 'app/models/integrations/discord.rb'
|
||||
- 'app/models/integrations/jenkins.rb'
|
||||
- 'app/models/integrations/jira.rb'
|
||||
- 'app/models/integrations/packagist.rb'
|
||||
- 'app/models/integrations/pipelines_email.rb'
|
||||
- 'app/models/integrations/prometheus.rb'
|
||||
- 'app/models/performance_monitoring/prometheus_dashboard.rb'
|
||||
- 'app/models/personal_access_token.rb'
|
||||
- 'app/models/project.rb'
|
||||
- 'app/models/repository.rb'
|
||||
- 'app/models/snippet_repository.rb'
|
||||
- 'app/models/u2f_registration.rb'
|
||||
- 'app/models/wiki.rb'
|
||||
- 'app/services/branches/delete_service.rb'
|
||||
- 'app/services/branches/validate_new_service.rb'
|
||||
- 'app/services/ci/job_artifacts/create_service.rb'
|
||||
- 'app/services/ci/parse_dotenv_artifact_service.rb'
|
||||
- 'app/services/ci/register_job_service.rb'
|
||||
- 'app/services/ci/stuck_builds/drop_helpers.rb'
|
||||
- 'app/services/clusters/applications/prometheus_update_service.rb'
|
||||
- 'app/services/commits/change_service.rb'
|
||||
- 'app/services/commits/create_service.rb'
|
||||
- 'app/services/dependency_proxy/head_manifest_service.rb'
|
||||
- 'app/services/dependency_proxy/request_token_service.rb'
|
||||
- 'app/services/design_management/copy_design_collection/copy_service.rb'
|
||||
- 'app/services/git/base_hooks_service.rb'
|
||||
- 'app/services/grafana/proxy_service.rb'
|
||||
- 'app/services/groups/update_shared_runners_service.rb'
|
||||
- 'app/services/issues/relative_position_rebalancing_service.rb'
|
||||
- 'app/services/jira/requests/base.rb'
|
||||
- 'app/services/jira_import/start_import_service.rb'
|
||||
- 'app/services/jira_import/users_importer.rb'
|
||||
- 'app/services/lfs/lock_file_service.rb'
|
||||
- 'app/services/lfs/locks_finder_service.rb'
|
||||
- 'app/services/lfs/push_service.rb'
|
||||
- 'app/services/lfs/unlock_file_service.rb'
|
||||
- 'app/services/merge_requests/merge_to_ref_service.rb'
|
||||
- 'app/services/merge_requests/mergeability_check_service.rb'
|
||||
- 'app/services/metrics/dashboard/base_service.rb'
|
||||
- 'app/services/metrics/dashboard/panel_preview_service.rb'
|
||||
- 'app/services/projects/cleanup_service.rb'
|
||||
- 'app/services/projects/destroy_service.rb'
|
||||
- 'app/services/projects/hashed_storage/base_repository_service.rb'
|
||||
- 'app/services/projects/transfer_service.rb'
|
||||
- 'app/services/prometheus/proxy_service.rb'
|
||||
- 'app/services/resource_access_tokens/revoke_service.rb'
|
||||
- 'app/services/tags/create_service.rb'
|
||||
- 'app/services/tags/destroy_service.rb'
|
||||
- 'app/services/users/validate_manual_otp_service.rb'
|
||||
- 'app/services/users/validate_push_otp_service.rb'
|
||||
- 'app/services/verify_pages_domain_service.rb'
|
||||
- 'app/validators/js_regex_validator.rb'
|
||||
- 'app/workers/concerns/limited_capacity/worker.rb'
|
||||
- 'app/workers/gitlab/jira_import/import_issue_worker.rb'
|
||||
- 'app/workers/issuable_export_csv_worker.rb'
|
||||
- 'app/workers/namespaces/root_statistics_worker.rb'
|
||||
- 'app/workers/namespaces/schedule_aggregation_worker.rb'
|
||||
- 'app/workers/packages/go/sync_packages_worker.rb'
|
||||
- 'app/workers/project_destroy_worker.rb'
|
||||
- 'app/workers/project_service_worker.rb'
|
||||
- 'app/workers/projects/git_garbage_collect_worker.rb'
|
||||
- 'app/workers/remove_expired_members_worker.rb'
|
||||
- 'app/workers/users/create_statistics_worker.rb'
|
||||
- 'config/initializers/rspec_profiling.rb'
|
||||
- 'config/initializers/wikicloth_redos_patch.rb'
|
||||
- 'db/post_migrate/20210606143426_add_index_for_container_registry_access_level.rb'
|
||||
- 'db/post_migrate/20211206162601_cleanup_after_add_primary_email_to_emails_if_user_confirmed.rb'
|
||||
- 'db/post_migrate/20220318111729_cleanup_after_fixing_issue_when_admin_changed_primary_email.rb'
|
||||
- 'db/post_migrate/20220504083836_cleanup_after_fixing_regression_with_new_users_emails.rb'
|
||||
- 'ee/app/finders/projects/integrations/jira/by_ids_finder.rb'
|
||||
- 'ee/app/graphql/mutations/issues/promote_to_epic.rb'
|
||||
- 'ee/app/graphql/mutations/issues/set_epic.rb'
|
||||
- 'ee/app/helpers/ee/kerberos_spnego_helper.rb'
|
||||
- 'ee/app/models/concerns/geo/replicable_model.rb'
|
||||
- 'ee/app/models/integrations/github.rb'
|
||||
- 'ee/app/services/app_sec/dast/profiles/create_service.rb'
|
||||
- 'ee/app/services/app_sec/dast/profiles/update_service.rb'
|
||||
- 'ee/app/services/app_sec/dast/scans/create_service.rb'
|
||||
- 'ee/app/services/app_sec/dast/site_validations/find_or_create_service.rb'
|
||||
- 'ee/app/services/app_sec/dast/site_validations/revoke_service.rb'
|
||||
- 'ee/app/services/app_sec/fuzzing/coverage/corpuses/create_service.rb'
|
||||
- 'ee/app/services/arkose/user_verification_service.rb'
|
||||
- 'ee/app/services/ci/sync_reports_to_approval_rules_service.rb'
|
||||
- 'ee/app/services/elastic/process_bookkeeping_service.rb'
|
||||
- 'ee/app/services/geo/file_registry_removal_service.rb'
|
||||
- 'ee/app/services/geo/framework_repository_sync_service.rb'
|
||||
- 'ee/app/services/geo/move_repository_service.rb'
|
||||
- 'ee/app/services/geo/repository_base_sync_service.rb'
|
||||
- 'ee/app/services/incident_management/oncall_rotations/create_service.rb'
|
||||
- 'ee/app/services/incident_management/oncall_rotations/edit_service.rb'
|
||||
- 'ee/app/services/namespaces/deactivate_members_over_limit_service.rb'
|
||||
- 'ee/app/services/namespaces/remove_project_group_links_outside_hierarchy_service.rb'
|
||||
- 'ee/app/services/namespaces/update_prevent_sharing_outside_hierarchy_service.rb'
|
||||
- 'ee/app/services/projects/licenses/create_policy_service.rb'
|
||||
- 'ee/app/services/projects/licenses/update_policy_service.rb'
|
||||
- 'ee/app/services/security/ingestion/ingest_report_service.rb'
|
||||
- 'ee/app/services/security/orchestration/assign_service.rb'
|
||||
- 'ee/app/services/security/store_grouped_scans_service.rb'
|
||||
- 'ee/app/services/security/store_scan_service.rb'
|
||||
- 'ee/app/services/security/token_revocation_service.rb'
|
||||
- 'ee/app/services/software_license_policies/create_service.rb'
|
||||
- 'ee/app/services/software_license_policies/update_service.rb'
|
||||
- 'ee/app/workers/adjourned_project_deletion_worker.rb'
|
||||
- 'ee/app/workers/geo/file_removal_worker.rb'
|
||||
- 'ee/app/workers/geo/repositories_clean_up_worker.rb'
|
||||
- 'ee/app/workers/geo/scheduler/scheduler_worker.rb'
|
||||
- 'ee/app/workers/namespaces/free_user_cap_worker.rb'
|
||||
- 'ee/app/workers/refresh_license_compliance_checks_worker.rb'
|
||||
- 'ee/app/workers/repository_update_mirror_worker.rb'
|
||||
- 'ee/app/workers/sync_seat_link_request_worker.rb'
|
||||
- 'ee/lib/ee/gitlab/background_migration/populate_uuids_for_security_findings.rb'
|
||||
- 'ee/lib/elastic/instance_proxy_util.rb'
|
||||
- 'ee/lib/gitlab/audit/auditor.rb'
|
||||
- 'ee/lib/gitlab/auth/smartcard/base.rb'
|
||||
- 'ee/lib/gitlab/ci/parsers/license_compliance/license_scanning.rb'
|
||||
- 'ee/lib/gitlab/elastic/bulk_indexer.rb'
|
||||
- 'ee/lib/gitlab/spdx/catalogue_gateway.rb'
|
||||
- 'ee/lib/tasks/gitlab/seed/metrics.rake'
|
||||
- 'lib/api/environments.rb'
|
||||
- 'lib/api/helpers.rb'
|
||||
- 'lib/api/helpers/label_helpers.rb'
|
||||
- 'lib/api/issues.rb'
|
||||
- 'lib/api/project_milestones.rb'
|
||||
- 'lib/api/projects.rb'
|
||||
- 'lib/api/repositories.rb'
|
||||
- 'lib/api/v3/github.rb'
|
||||
- 'lib/gitaly/server.rb'
|
||||
- 'lib/gitlab/auth/ldap/adapter.rb'
|
||||
- 'lib/gitlab/auth/otp/strategies/forti_authenticator/manual_otp.rb'
|
||||
- 'lib/gitlab/auth/otp/strategies/forti_authenticator/push_otp.rb'
|
||||
- 'lib/gitlab/background_migration/copy_ci_builds_columns_to_security_scans.rb'
|
||||
- 'lib/gitlab/background_migration/recalculate_vulnerabilities_occurrences_uuid.rb'
|
||||
- 'lib/gitlab/ci/config/external/file/artifact.rb'
|
||||
- 'lib/gitlab/ci/pipeline/chain/config/process.rb'
|
||||
- 'lib/gitlab/ci/pipeline/chain/validate/external.rb'
|
||||
- 'lib/gitlab/ci/reports/codequality_reports.rb'
|
||||
- 'lib/gitlab/database/background_migration/batched_job.rb'
|
||||
- 'lib/gitlab/database/background_migration/batched_migration_wrapper.rb'
|
||||
- 'lib/gitlab/database/batch_counter.rb'
|
||||
- 'lib/gitlab/database/load_balancing/load_balancer.rb'
|
||||
- 'lib/gitlab/database/load_balancing/service_discovery.rb'
|
||||
- 'lib/gitlab/database/reindexing/grafana_notifier.rb'
|
||||
- 'lib/gitlab/git/keep_around.rb'
|
||||
- 'lib/gitlab/gitaly_client/call.rb'
|
||||
- 'lib/gitlab/gitaly_client/commit_service.rb'
|
||||
- 'lib/gitlab/gitaly_client/operation_service.rb'
|
||||
- 'lib/gitlab/gitaly_client/ref_service.rb'
|
||||
- 'lib/gitlab/gitaly_client/repository_service.rb'
|
||||
- 'lib/gitlab/hashed_storage/migrator.rb'
|
||||
- 'lib/gitlab/health_checks/base_abstract_check.rb'
|
||||
- 'lib/gitlab/import_export/merge_request_parser.rb'
|
||||
- 'lib/gitlab/instrumentation/redis_interceptor.rb'
|
||||
- 'lib/gitlab/issuables_count_for_state.rb'
|
||||
- 'lib/gitlab/jira_import/issues_importer.rb'
|
||||
- 'lib/gitlab/json.rb'
|
||||
- 'lib/gitlab/jwt_token.rb'
|
||||
- 'lib/gitlab/kubernetes/namespace.rb'
|
||||
- 'lib/gitlab/metrics/dashboard/stages/panel_ids_inserter.rb'
|
||||
- 'lib/gitlab/metrics/rack_middleware.rb'
|
||||
- 'lib/gitlab/middleware/handle_ip_spoof_attack_error.rb'
|
||||
- 'lib/gitlab/prometheus/queries/validate_query.rb'
|
||||
- 'lib/gitlab/prometheus_client.rb'
|
||||
- 'lib/gitlab/sanitizers/exif.rb'
|
||||
- 'lib/gitlab/sidekiq_logging/structured_logger.rb'
|
||||
- 'lib/gitlab/tcp_checker.rb'
|
||||
- 'lib/gitlab/template_parser/parser.rb'
|
||||
- 'lib/gitlab/tracking.rb'
|
||||
- 'lib/gitlab/url_blocker.rb'
|
||||
- 'lib/gitlab/usage/metrics/aggregates/aggregate.rb'
|
||||
- 'lib/gitlab/usage_data.rb'
|
||||
- 'lib/gitlab/utils/usage_data.rb'
|
||||
- 'lib/gitlab/verify/batch_verifier.rb'
|
||||
- 'lib/gitlab/wiki_pages/front_matter_parser.rb'
|
||||
- 'lib/microsoft_teams/notifier.rb'
|
||||
- 'lib/system_check/incoming_email/imap_authentication_check.rb'
|
||||
- 'lib/tasks/gitlab/db/validate_config.rake'
|
||||
- 'lib/tasks/gitlab/setup.rake'
|
||||
- 'lib/tasks/gitlab/storage.rake'
|
||||
- 'lib/tasks/lint.rake'
|
||||
- 'qa/qa/resource/user_gpg.rb'
|
||||
- 'scripts/review_apps/automated_cleanup.rb'
|
||||
- 'scripts/trigger-build.rb'
|
||||
- 'spec/commands/metrics_server/metrics_server_spec.rb'
|
||||
- 'spec/db/docs_spec.rb'
|
||||
- 'spec/lib/bulk_imports/network_error_spec.rb'
|
||||
- 'spec/lib/gitlab/database/load_balancing/host_spec.rb'
|
||||
- 'spec/lib/gitlab/database/load_balancing/load_balancer_spec.rb'
|
||||
- 'spec/lib/gitlab/error_tracking_spec.rb'
|
||||
- 'spec/lib/gitlab/sanitizers/exception_message_spec.rb'
|
||||
- 'spec/support/capybara.rb'
|
||||
- 'spec/support/helpers/capybara_helpers.rb'
|
||||
- 'tooling/lib/tooling/helm3_client.rb'
|
||||
- 'tooling/lib/tooling/kubernetes_client.rb'
|
|
@ -1,9 +1,12 @@
|
|||
## Contributor license agreement
|
||||
## Contributor License Agreement and Developer Certificate of Origin
|
||||
|
||||
By submitting code as an individual you agree to the
|
||||
[individual contributor license agreement](doc/legal/individual_contributor_license_agreement.md).
|
||||
By submitting code as an entity you agree to the
|
||||
[corporate contributor license agreement](doc/legal/corporate_contributor_license_agreement.md).
|
||||
Contributions to this repository are subject to the [Developer Certificate of Origin](https://docs.gitlab.com/ee/legal/developer_certificate_of_origin.html#developer-certificate-of-origin-version-11), or the [Individual](https://docs.gitlab.com/ee/legal/individual_contributor_license_agreement.html) or [Corporate](https://docs.gitlab.com/ee/legal/corporate_contributor_license_agreement.html) Contributor License Agreement, depending on where the contribution is made and on whose behalf:
|
||||
|
||||
- By submitting code contributions as an individual to the [`/ee` subdirectory](/ee) of this repository, you agree to the [Individual Contributor License Agreement](https://docs.gitlab.com/ee/legal/individual_contributor_license_agreement.html).
|
||||
|
||||
- By submitting code contributions on behalf of a corporation to the [`/ee` subdirectory](/ee) of this repository, you agree to the [Corporate Contributor License Agreement](https://docs.gitlab.com/ee/legal/corporate_contributor_license_agreement.html).
|
||||
|
||||
- By submitting code contributions as an individual or on behalf of a corporation to any directory in this repository outside of the [`/ee` subdirectory](/ee), you agree to the [Developer Certificate of Origin](https://docs.gitlab.com/ee/legal/developer_certificate_of_origin.html#developer-certificate-of-origin-version-11).
|
||||
|
||||
All Documentation content that resides under the [`doc/` directory](/doc) of this
|
||||
repository is licensed under Creative Commons:
|
||||
|
|
|
@ -55,7 +55,7 @@ export default {
|
|||
return contacts.slice().sort((a, b) => a.firstName.localeCompare(b.firstName));
|
||||
},
|
||||
getIssuesPath(path, value) {
|
||||
return `${path}?scope=all&state=opened&crm_contact_id=${value}`;
|
||||
return `${path}?crm_contact_id=${value}`;
|
||||
},
|
||||
getEditRoute(id) {
|
||||
return { name: this.$options.EDIT_ROUTE_NAME, params: { id } };
|
||||
|
|
|
@ -55,7 +55,7 @@ export default {
|
|||
return organizations.slice().sort((a, b) => a.name.localeCompare(b.name));
|
||||
},
|
||||
getIssuesPath(path, value) {
|
||||
return `${path}?scope=all&state=opened&crm_organization_id=${value}`;
|
||||
return `${path}?crm_organization_id=${value}`;
|
||||
},
|
||||
getEditRoute(id) {
|
||||
return { name: this.$options.EDIT_ROUTE_NAME, params: { id } };
|
||||
|
|
|
@ -1,37 +1,48 @@
|
|||
<script>
|
||||
import { GlIcon } from '@gitlab/ui';
|
||||
import { GlBadge, GlIcon } from '@gitlab/ui';
|
||||
import Vue from 'vue';
|
||||
import glFeatureFlagMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
|
||||
import { fetchPolicies } from '~/lib/graphql';
|
||||
import { __ } from '~/locale';
|
||||
import { IssuableType } from '~/issues/constants';
|
||||
import { IssuableStates } from '~/vue_shared/issuable/list/constants';
|
||||
|
||||
export const statusBoxState = Vue.observable({
|
||||
export const badgeState = Vue.observable({
|
||||
state: '',
|
||||
updateStatus: null,
|
||||
});
|
||||
|
||||
const CLASSES = {
|
||||
opened: 'status-box-open',
|
||||
merge_request_opened: 'badge-success',
|
||||
locked: 'status-box-open',
|
||||
merge_request_locked: 'badge-success',
|
||||
closed: 'status-box-mr-closed',
|
||||
merge_request_closed: 'badge-danger',
|
||||
merged: 'badge-info',
|
||||
opened: 'issuable-status-badge-open',
|
||||
locked: 'issuable-status-badge-open',
|
||||
closed: 'issuable-status-badge-closed',
|
||||
merged: 'issuable-status-badge-merged',
|
||||
};
|
||||
|
||||
const ISSUE_ICONS = {
|
||||
opened: 'issues',
|
||||
locked: 'issues',
|
||||
closed: 'issue-closed',
|
||||
};
|
||||
|
||||
const MERGE_REQUEST_ICONS = {
|
||||
opened: 'merge-request-open',
|
||||
locked: 'merge-request-open',
|
||||
closed: 'merge-request-close',
|
||||
merged: 'merge',
|
||||
};
|
||||
|
||||
const STATUS = {
|
||||
opened: [__('Open'), 'issue-open-m'],
|
||||
locked: [__('Open'), 'issue-open-m'],
|
||||
closed: [__('Closed'), 'issue-close'],
|
||||
merged: [__('Merged'), 'git-merge'],
|
||||
opened: __('Open'),
|
||||
locked: __('Open'),
|
||||
closed: __('Closed'),
|
||||
merged: __('Merged'),
|
||||
};
|
||||
|
||||
export default {
|
||||
components: {
|
||||
GlBadge,
|
||||
GlIcon,
|
||||
},
|
||||
mixins: [glFeatureFlagMixin()],
|
||||
inject: {
|
||||
query: { default: null },
|
||||
projectPath: { default: null },
|
||||
|
@ -51,39 +62,41 @@ export default {
|
|||
},
|
||||
data() {
|
||||
if (this.initialState) {
|
||||
statusBoxState.state = this.initialState;
|
||||
badgeState.state = this.initialState;
|
||||
}
|
||||
|
||||
return statusBoxState;
|
||||
return badgeState;
|
||||
},
|
||||
computed: {
|
||||
isMergeRequest() {
|
||||
return this.issuableType === 'merge_request' && this.glFeatures.updatedMrHeader;
|
||||
badgeClass() {
|
||||
return CLASSES[this.state];
|
||||
},
|
||||
statusBoxClass() {
|
||||
return [
|
||||
CLASSES[`${this.issuableType}_${this.state}`] || CLASSES[this.state],
|
||||
{
|
||||
'badge badge-pill gl-badge gl-mr-3': this.isMergeRequest,
|
||||
'issuable-status-box status-box': !this.isMergeRequest,
|
||||
badgeVariant() {
|
||||
if (this.state === IssuableStates.Opened) {
|
||||
return 'success';
|
||||
} else if (this.state === IssuableStates.Closed) {
|
||||
return this.issuableType === IssuableType.MergeRequest ? 'danger' : 'info';
|
||||
}
|
||||
return 'info';
|
||||
},
|
||||
];
|
||||
badgeText() {
|
||||
return STATUS[this.state];
|
||||
},
|
||||
statusHumanName() {
|
||||
return (STATUS[`${this.issuableType}_${this.state}`] || STATUS[this.state])[0];
|
||||
},
|
||||
statusIconName() {
|
||||
return (STATUS[`${this.issuableType}_${this.state}`] || STATUS[this.state])[1];
|
||||
badgeIcon() {
|
||||
if (this.issuableType === IssuableType.Issue) {
|
||||
return ISSUE_ICONS[this.state];
|
||||
}
|
||||
return MERGE_REQUEST_ICONS[this.state];
|
||||
},
|
||||
},
|
||||
created() {
|
||||
if (!statusBoxState.updateStatus) {
|
||||
statusBoxState.updateStatus = this.fetchState;
|
||||
if (!badgeState.updateStatus) {
|
||||
badgeState.updateStatus = this.fetchState;
|
||||
}
|
||||
},
|
||||
beforeDestroy() {
|
||||
if (statusBoxState.updateStatus && this.query) {
|
||||
statusBoxState.updateStatus = null;
|
||||
if (badgeState.updateStatus && this.query) {
|
||||
badgeState.updateStatus = null;
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
|
@ -97,21 +110,15 @@ export default {
|
|||
fetchPolicy: fetchPolicies.NO_CACHE,
|
||||
});
|
||||
|
||||
statusBoxState.state = data?.workspace?.issuable?.state;
|
||||
badgeState.state = data?.workspace?.issuable?.state;
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div :class="statusBoxClass">
|
||||
<gl-icon
|
||||
v-if="!isMergeRequest"
|
||||
:name="statusIconName"
|
||||
class="gl-display-block gl-sm-display-none!"
|
||||
/>
|
||||
<span :class="{ 'gl-display-none gl-sm-display-block': !isMergeRequest }">
|
||||
{{ statusHumanName }}
|
||||
</span>
|
||||
</div>
|
||||
<gl-badge class="issuable-status-badge gl-mr-3" :class="badgeClass" :variant="badgeVariant">
|
||||
<gl-icon :name="badgeIcon" />
|
||||
<span class="gl-display-none gl-sm-display-block gl-ml-2">{{ badgeText }}</span>
|
||||
</gl-badge>
|
||||
</template>
|
||||
|
|
|
@ -49,8 +49,8 @@ export default class Issue {
|
|||
issueFailMessage = __('Unable to update this issue at this time.'),
|
||||
) {
|
||||
if ('id' in data) {
|
||||
const isClosedBadge = $('div.status-box-issue-closed');
|
||||
const isOpenBadge = $('div.status-box-open');
|
||||
const isClosedBadge = $('.issuable-status-badge-closed');
|
||||
const isOpenBadge = $('.issuable-status-badge-open');
|
||||
const projectIssuesCounter = $('.issue_counter');
|
||||
|
||||
isClosedBadge.toggleClass('hidden', !isClosed);
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<script>
|
||||
import { GlIcon, GlIntersectionObserver, GlTooltipDirective } from '@gitlab/ui';
|
||||
import { GlIcon, GlBadge, GlIntersectionObserver, GlTooltipDirective } from '@gitlab/ui';
|
||||
import Visibility from 'visibilityjs';
|
||||
import createFlash from '~/flash';
|
||||
import {
|
||||
|
@ -27,6 +27,7 @@ export default {
|
|||
WorkspaceType,
|
||||
components: {
|
||||
GlIcon,
|
||||
GlBadge,
|
||||
GlIntersectionObserver,
|
||||
titleComponent,
|
||||
editedComponent,
|
||||
|
@ -267,7 +268,10 @@ export default {
|
|||
: '';
|
||||
},
|
||||
statusIcon() {
|
||||
return this.isClosed ? 'issue-close' : 'issue-open-m';
|
||||
return this.isClosed ? 'issue-closed' : 'issues';
|
||||
},
|
||||
statusVariant() {
|
||||
return this.isClosed ? 'info' : 'success';
|
||||
},
|
||||
statusText() {
|
||||
return IssuableStatusText[this.issuableStatus];
|
||||
|
@ -517,13 +521,9 @@ export default {
|
|||
<div
|
||||
class="issue-sticky-header-text gl-display-flex gl-align-items-center gl-mx-auto gl-px-5"
|
||||
>
|
||||
<p
|
||||
class="issuable-status-box status-box gl-white-space-nowrap gl-my-0"
|
||||
:class="[isClosed ? 'status-box-issue-closed' : 'status-box-open']"
|
||||
<gl-badge :icon="statusIcon" :variant="statusVariant" class="gl-mr-2"
|
||||
><span class="gl-display-none gl-sm-display-block">{{ statusText }}</span></gl-badge
|
||||
>
|
||||
<gl-icon :name="statusIcon" class="gl-display-block d-sm-none gl-h-6!" />
|
||||
<span class="gl-display-none d-sm-block">{{ statusText }}</span>
|
||||
</p>
|
||||
<span v-if="isLocked" data-testid="locked" class="issuable-warning-icon">
|
||||
<gl-icon name="lock" :aria-label="__('Locked')" />
|
||||
</span>
|
||||
|
|
|
@ -6,7 +6,7 @@ import { mapActions, mapGetters, mapState } from 'vuex';
|
|||
import Autosave from '~/autosave';
|
||||
import { refreshUserMergeRequestCounts } from '~/commons/nav/user_merge_requests';
|
||||
import createFlash from '~/flash';
|
||||
import { statusBoxState } from '~/issuable/components/status_box.vue';
|
||||
import { badgeState } from '~/issuable/components/status_box.vue';
|
||||
import httpStatusCodes from '~/lib/utils/http_status';
|
||||
import {
|
||||
capitalizeFirstCharacter,
|
||||
|
@ -266,7 +266,7 @@ export default {
|
|||
const toggleState = this.isOpen ? this.closeIssuable : this.reopenIssuable;
|
||||
|
||||
toggleState()
|
||||
.then(() => statusBoxState.updateStatus && statusBoxState.updateStatus())
|
||||
.then(() => badgeState.updateStatus && badgeState.updateStatus())
|
||||
.then(refreshUserMergeRequestCounts)
|
||||
.catch(() =>
|
||||
createFlash({
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { flattenDeep, clone } from 'lodash';
|
||||
import { match } from '~/diffs/utils/diff_file';
|
||||
import { statusBoxState } from '~/issuable/components/status_box.vue';
|
||||
import { badgeState } from '~/issuable/components/status_box.vue';
|
||||
import { isInMRPage } from '~/lib/utils/common_utils';
|
||||
import * as constants from '../constants';
|
||||
import { collapseSystemNotes } from './collapse_utils';
|
||||
|
@ -85,8 +85,7 @@ export const getBlockedByIssues = (state) => state.noteableData.blocked_by_issue
|
|||
|
||||
export const userCanReply = (state) => Boolean(state.noteableData.current_user.can_create_note);
|
||||
|
||||
export const openState = (state) =>
|
||||
isInMRPage() ? statusBoxState.state : state.noteableData.state;
|
||||
export const openState = (state) => (isInMRPage() ? badgeState.state : state.noteableData.state);
|
||||
|
||||
export const getUserData = (state) => state.userData || {};
|
||||
|
||||
|
|
|
@ -20,23 +20,25 @@ export default function initMergeRequestShow() {
|
|||
initAwardsApp(document.getElementById('js-vue-awards-block'));
|
||||
|
||||
const el = document.querySelector('.js-mr-status-box');
|
||||
const { iid, issuableType, projectPath } = el.dataset;
|
||||
const apolloProvider = new VueApollo({
|
||||
defaultClient: createDefaultClient(),
|
||||
});
|
||||
// eslint-disable-next-line no-new
|
||||
new Vue({
|
||||
el,
|
||||
name: 'IssuableStatusBoxRoot',
|
||||
apolloProvider,
|
||||
provide: {
|
||||
query: getStateQuery,
|
||||
projectPath: el.dataset.projectPath,
|
||||
iid: el.dataset.iid,
|
||||
iid,
|
||||
projectPath,
|
||||
},
|
||||
render(h) {
|
||||
return h(StatusBox, {
|
||||
props: {
|
||||
initialState: el.dataset.state,
|
||||
issuableType: 'merge_request',
|
||||
issuableType,
|
||||
},
|
||||
});
|
||||
},
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
import { GlIcon, GlLink, GlPopover, GlTooltipDirective } from '@gitlab/ui';
|
||||
import { __, n__, sprintf } from '~/locale';
|
||||
import createFlash from '~/flash';
|
||||
import { convertToGraphQLId } from '~/graphql_shared/utils';
|
||||
import { convertToGraphQLId, getIdFromGraphQLId } from '~/graphql_shared/utils';
|
||||
import { TYPE_ISSUE } from '~/graphql_shared/constants';
|
||||
import getIssueCrmContactsQuery from './queries/get_issue_crm_contacts.query.graphql';
|
||||
import issueCrmContactsSubscription from './queries/issue_crm_contacts.subscription.graphql';
|
||||
|
@ -21,6 +21,10 @@ export default {
|
|||
type: String,
|
||||
required: true,
|
||||
},
|
||||
groupIssuesPath: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
|
@ -85,6 +89,10 @@ export default {
|
|||
Boolean,
|
||||
);
|
||||
},
|
||||
getIssuesPath(contactId) {
|
||||
const id = getIdFromGraphQLId(contactId);
|
||||
return `${this.groupIssuesPath}?crm_contact_id=${id}`;
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
@ -110,8 +118,8 @@ export default {
|
|||
:key="index"
|
||||
class="gl-pr-2"
|
||||
>
|
||||
<span :id="`contact_${index}`" class="gl-font-weight-bold"
|
||||
>{{ contact.firstName }} {{ contact.lastName }}{{ divider(index) }}</span
|
||||
<gl-link :id="`contact_${index}`" :href="getIssuesPath(contact.id)"
|
||||
>{{ contact.firstName }} {{ contact.lastName }}{{ divider(index) }}</gl-link
|
||||
>
|
||||
<gl-popover
|
||||
v-if="shouldShowPopover(contact)"
|
||||
|
|
|
@ -218,7 +218,7 @@ function mountCrmContactsComponent() {
|
|||
|
||||
if (!el) return;
|
||||
|
||||
const { issueId } = el.dataset;
|
||||
const { issueId, groupIssuesPath } = el.dataset;
|
||||
// eslint-disable-next-line no-new
|
||||
new Vue({
|
||||
el,
|
||||
|
@ -231,6 +231,7 @@ function mountCrmContactsComponent() {
|
|||
createElement('crm-contacts', {
|
||||
props: {
|
||||
issueId,
|
||||
groupIssuesPath,
|
||||
},
|
||||
}),
|
||||
});
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import getStateKey from 'ee_else_ce/vue_merge_request_widget/stores/get_state_key';
|
||||
import { statusBoxState } from '~/issuable/components/status_box.vue';
|
||||
import { badgeState } from '~/issuable/components/status_box.vue';
|
||||
import { formatDate, getTimeago } from '~/lib/utils/datetime_utility';
|
||||
import { machine } from '~/lib/utils/finite_state_machine';
|
||||
import {
|
||||
|
@ -221,8 +221,8 @@ export default class MergeRequestStore {
|
|||
}
|
||||
|
||||
updateStatusState(state) {
|
||||
if (this.mergeRequestState !== state && statusBoxState.updateStatus) {
|
||||
statusBoxState.updateStatus();
|
||||
if (this.mergeRequestState !== state && badgeState.updateStatus) {
|
||||
badgeState.updateStatus();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,14 +1,23 @@
|
|||
<script>
|
||||
import { GlIcon, GlButton, GlTooltipDirective, GlAvatarLink, GlAvatarLabeled } from '@gitlab/ui';
|
||||
import {
|
||||
GlIcon,
|
||||
GlBadge,
|
||||
GlButton,
|
||||
GlTooltipDirective,
|
||||
GlAvatarLink,
|
||||
GlAvatarLabeled,
|
||||
} from '@gitlab/ui';
|
||||
|
||||
import { getIdFromGraphQLId } from '~/graphql_shared/utils';
|
||||
import { isExternal } from '~/lib/utils/url_utility';
|
||||
import { n__, sprintf } from '~/locale';
|
||||
import TimeAgoTooltip from '~/vue_shared/components/time_ago_tooltip.vue';
|
||||
import { IssuableStates } from '~/vue_shared/issuable/list/constants';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
GlIcon,
|
||||
GlBadge,
|
||||
GlButton,
|
||||
GlAvatarLink,
|
||||
GlAvatarLabeled,
|
||||
|
@ -26,6 +35,11 @@ export default {
|
|||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
issuableState: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: '',
|
||||
},
|
||||
statusBadgeClass: {
|
||||
type: String,
|
||||
required: false,
|
||||
|
@ -36,6 +50,11 @@ export default {
|
|||
required: false,
|
||||
default: '',
|
||||
},
|
||||
statusIconClass: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: '',
|
||||
},
|
||||
blocked: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
|
@ -53,6 +72,9 @@ export default {
|
|||
},
|
||||
},
|
||||
computed: {
|
||||
badgeVariant() {
|
||||
return this.issuableState === IssuableStates.Opened ? 'success' : 'info';
|
||||
},
|
||||
authorId() {
|
||||
return getIdFromGraphQLId(`${this.author.id}`);
|
||||
},
|
||||
|
@ -91,10 +113,15 @@ export default {
|
|||
<template>
|
||||
<div class="detail-page-header">
|
||||
<div class="detail-page-header-body">
|
||||
<div data-testid="status" class="issuable-status-box status-box" :class="statusBadgeClass">
|
||||
<gl-icon v-if="statusIcon" :name="statusIcon" class="d-block d-sm-none" />
|
||||
<span class="d-none d-sm-block"><slot name="status-badge"></slot></span>
|
||||
</div>
|
||||
<gl-badge
|
||||
data-testid="status"
|
||||
class="issuable-status-badge gl-mr-3"
|
||||
:class="statusBadgeClass"
|
||||
:variant="badgeVariant"
|
||||
>
|
||||
<gl-icon v-if="statusIcon" :name="statusIcon" :class="statusIconClass" />
|
||||
<span class="gl-display-none gl-sm-display-block"><slot name="status-badge"></slot></span>
|
||||
</gl-badge>
|
||||
<div class="issuable-meta gl-display-flex gl-align-items-center d-md-inline-block">
|
||||
<div v-if="blocked || confidential" class="gl-display-inline-block">
|
||||
<div v-if="blocked" data-testid="blocked" class="issuable-warning-icon inline">
|
||||
|
|
|
@ -27,6 +27,11 @@ export default {
|
|||
required: false,
|
||||
default: '',
|
||||
},
|
||||
statusIconClass: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: '',
|
||||
},
|
||||
enableEdit: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
|
@ -102,8 +107,10 @@ export default {
|
|||
<template>
|
||||
<div class="issuable-show-container" data-qa-selector="issuable_show_container">
|
||||
<issuable-header
|
||||
:issuable-state="issuable.state"
|
||||
:status-badge-class="statusBadgeClass"
|
||||
:status-icon="statusIcon"
|
||||
:status-icon-class="statusIconClass"
|
||||
:blocked="issuable.blocked"
|
||||
:confidential="issuable.confidential"
|
||||
:created-at="issuable.createdAt"
|
||||
|
@ -122,6 +129,7 @@ export default {
|
|||
:issuable="issuable"
|
||||
:status-badge-class="statusBadgeClass"
|
||||
:status-icon="statusIcon"
|
||||
:status-icon-class="statusIconClass"
|
||||
:enable-edit="enableEdit"
|
||||
:enable-autocomplete="enableAutocomplete"
|
||||
:enable-autosave="enableAutosave"
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
.detail-page-header-body {
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex: 1 1;
|
||||
min-width: 0;
|
||||
|
||||
|
|
|
@ -69,6 +69,7 @@ module BadgesHelper
|
|||
icon_only = options[:icon_only]
|
||||
variant_class = VARIANT_CLASSES[options.fetch(:variant, :muted)]
|
||||
size_class = SIZE_CLASSES[options.fetch(:size, :md)]
|
||||
icon_classes = GL_ICON_CLASSES.dup << options.fetch(:icon_classes, nil)
|
||||
|
||||
html_options = html_options.merge(
|
||||
class: [
|
||||
|
@ -85,7 +86,6 @@ module BadgesHelper
|
|||
end
|
||||
|
||||
if options[:icon]
|
||||
icon_classes = GL_ICON_CLASSES.dup
|
||||
icon_classes << "gl-mr-2" unless icon_only
|
||||
icon = sprite_icon(options[:icon], css_class: icon_classes.join(' '))
|
||||
|
||||
|
|
|
@ -341,14 +341,20 @@ module IssuablesHelper
|
|||
end
|
||||
|
||||
def state_name_with_icon(issuable)
|
||||
if issuable.is_a?(MergeRequest) && issuable.merged?
|
||||
[_("Merged"), "git-merge"]
|
||||
elsif issuable.is_a?(MergeRequest) && issuable.closed?
|
||||
[_("Closed"), "close"]
|
||||
elsif issuable.closed?
|
||||
[_("Closed"), "mobile-issue-close"]
|
||||
if issuable.is_a?(MergeRequest)
|
||||
if issuable.open?
|
||||
[_("Open"), "merge-request-open"]
|
||||
elsif issuable.merged?
|
||||
[_("Merged"), "merge"]
|
||||
else
|
||||
[_("Open"), "issue-open-m"]
|
||||
[_("Closed"), "merge-request-close"]
|
||||
end
|
||||
else
|
||||
if issuable.open?
|
||||
[_("Open"), "issues"]
|
||||
else
|
||||
[_("Closed"), "issue-closed"]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -35,8 +35,16 @@ module Routing
|
|||
end
|
||||
|
||||
def issue_url(entity, *args)
|
||||
if use_work_items_path?(entity)
|
||||
work_item_url(entity, *args)
|
||||
else
|
||||
project_issue_url(entity.project, entity, *args)
|
||||
end
|
||||
end
|
||||
|
||||
def work_item_url(entity, *args)
|
||||
project_work_items_url(entity.project, entity.id, *args)
|
||||
end
|
||||
|
||||
def merge_request_url(entity, *args)
|
||||
project_merge_request_url(entity.project, entity, *args)
|
||||
|
@ -77,5 +85,11 @@ module Routing
|
|||
toggle_subscription_project_merge_request_path(entity.project, entity)
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def use_work_items_path?(issue)
|
||||
issue.issue_type == 'task' && issue.project.work_items_feature_flag_enabled?
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -4,9 +4,7 @@ class IssuePresenter < Gitlab::View::Presenter::Delegated
|
|||
presents ::Issue, as: :issue
|
||||
|
||||
def issue_path
|
||||
return url_builder.build(issue, only_path: true) unless use_work_items_path?
|
||||
|
||||
project_work_items_path(issue.project, work_items_path: issue.id)
|
||||
web_path
|
||||
end
|
||||
|
||||
delegator_override :subscribed?
|
||||
|
@ -17,18 +15,6 @@ class IssuePresenter < Gitlab::View::Presenter::Delegated
|
|||
def project_emails_disabled?
|
||||
issue.project.emails_disabled?
|
||||
end
|
||||
|
||||
def web_url
|
||||
return super unless use_work_items_path?
|
||||
|
||||
project_work_items_url(issue.project, work_items_path: issue.id)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def use_work_items_path?
|
||||
issue.issue_type == 'task' && issue.project.work_items_feature_flag_enabled?
|
||||
end
|
||||
end
|
||||
|
||||
IssuePresenter.prepend_mod_with('IssuePresenter')
|
||||
|
|
|
@ -37,7 +37,7 @@ class IssueBoardEntity < Grape::Entity
|
|||
end
|
||||
|
||||
expose :real_path, if: -> (issue) { issue.project } do |issue|
|
||||
project_issue_path(issue.project, issue)
|
||||
Gitlab::UrlBuilder.build(issue, only_path: true)
|
||||
end
|
||||
|
||||
expose :issue_sidebar_endpoint, if: -> (issue) { issue.project } do |issue|
|
||||
|
|
|
@ -31,7 +31,7 @@ class IssueEntity < IssuableEntity
|
|||
end
|
||||
|
||||
expose :web_url do |issue|
|
||||
project_issue_path(issue.project, issue)
|
||||
Gitlab::UrlBuilder.build(issue, only_path: true)
|
||||
end
|
||||
|
||||
expose :current_user do
|
||||
|
|
|
@ -18,7 +18,7 @@ class LinkedIssueEntity < Grape::Entity
|
|||
end
|
||||
|
||||
expose :path do |link|
|
||||
project_issue_path(link.project, link.iid)
|
||||
Gitlab::UrlBuilder.build(link, only_path: true)
|
||||
end
|
||||
|
||||
expose :relation_path
|
||||
|
|
|
@ -82,7 +82,13 @@ module Members
|
|||
if member.request?
|
||||
approve_request
|
||||
else
|
||||
member.save
|
||||
# Calling #save triggers callbacks even if there is no change on object.
|
||||
# This previously caused an incident due to the hard to predict
|
||||
# behaviour caused by the large number of callbacks.
|
||||
# See https://gitlab.com/gitlab-com/gl-infra/production/-/issues/6351
|
||||
# and https://gitlab.com/gitlab-org/gitlab/-/merge_requests/80920#note_911569038
|
||||
# for details.
|
||||
member.save if member.changed?
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
.detail-page-description.py-2
|
||||
- if Feature.enabled?(:updated_mr_header, @project)
|
||||
- state_human_name, _ = state_name_with_icon(@merge_request)
|
||||
.badge.badge-pill.gl-badge.gl-mr-3.js-mr-status-box{ class: status_box_class(@merge_request), data: { project_path: @merge_request.project.path_with_namespace, iid: @merge_request.iid, state: @merge_request.state } }>
|
||||
= state_human_name
|
||||
= render 'shared/issuable/status_box', issuable: @merge_request
|
||||
.gl-display-inline.gl-vertical-align-top
|
||||
= merge_request_header(@project, @merge_request)
|
||||
- else
|
||||
%h2.title.mb-0{ data: { qa_selector: 'title_content' } }
|
||||
|
|
|
@ -45,7 +45,7 @@
|
|||
|
||||
- if issuable_sidebar[:show_crm_contacts]
|
||||
.block.contact
|
||||
#js-issue-crm-contacts{ data: { issue_id: issuable_sidebar[:id] } }
|
||||
#js-issue-crm-contacts{ data: { issue_id: issuable_sidebar[:id], group_issues_path: issues_group_path(@project.group) } }
|
||||
|
||||
= render_if_exists 'shared/issuable/sidebar_weight', issuable_sidebar: issuable_sidebar, can_edit: can_edit_issuable.to_s, project_path: issuable_sidebar[:project_full_path], issue_iid: issuable_sidebar[:iid]
|
||||
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
- state_human_name, state_icon_name = state_name_with_icon(issuable)
|
||||
- badge_text = state_name_with_icon(issuable)[0]
|
||||
- badge_icon = state_name_with_icon(issuable)[1]
|
||||
- badge_variant = issuable.open? ? :success : issuable.merged? ? :info : :danger
|
||||
- badge_status_class = issuable.open? ? 'issuable-status-badge-open' : issuable.merged? ? 'issuable-status-badge-merged' : 'issuable-status-badge-closed'
|
||||
- badge_classes = "js-mr-status-box issuable-status-badge gl-mr-3 #{badge_status_class}"
|
||||
|
||||
.issuable-status-box.status-box.js-mr-status-box{ class: status_box_class(issuable), data: { project_path: issuable.project.path_with_namespace, iid: issuable.iid, state: issuable.state } }
|
||||
= sprite_icon(state_icon_name, css_class: 'gl-display-block gl-sm-display-none!')
|
||||
%span.gl-display-none.gl-sm-display-block
|
||||
= state_human_name
|
||||
= gl_badge_tag({ variant: badge_variant, icon: badge_icon, icon_classes: 'gl-mr-0!' }, { class: badge_classes, data: { project_path: issuable.project.path_with_namespace, iid: issuable.iid, issuable_type: 'merge_request', state: issuable.state } }) do
|
||||
%span.gl-display-none.gl-sm-display-block.gl-ml-2
|
||||
= badge_text
|
||||
|
|
|
@ -1,17 +1,16 @@
|
|||
- link = issue_closed_link(@issue, current_user, css_class: 'text-white text-underline')
|
||||
- badge_classes = 'issuable-status-badge gl-mr-3'
|
||||
|
||||
.detail-page-header
|
||||
.detail-page-header-body
|
||||
.issuable-status-box.status-box.status-box-issue-closed{ class: issue_status_visibility(issuable, status_box: :closed) }
|
||||
= sprite_icon('issue-close', css_class: 'gl-display-block gl-sm-display-none!')
|
||||
.gl-display-none.gl-sm-display-block
|
||||
= gl_badge_tag({ variant: :info, icon: 'issue-closed', icon_classes: 'gl-mr-0!' }, { class: "#{issue_status_visibility(issuable, status_box: :closed)} #{badge_classes} issuable-status-badge-closed" }) do
|
||||
.gl-display-none.gl-sm-display-block.gl-ml-2
|
||||
= issue_closed_text(issuable, current_user)
|
||||
- if link
|
||||
%span.text-white.gl-pl-2.gl-sm-display-none
|
||||
= "(#{link})"
|
||||
.issuable-status-box.status-box.status-box-open{ class: issue_status_visibility(issuable, status_box: :open) }
|
||||
= sprite_icon('issue-open-m', css_class: 'gl-display-block gl-sm-display-none!')
|
||||
%span.gl-display-none.gl-sm-display-block
|
||||
= gl_badge_tag({ variant: :success, icon: 'issues', icon_classes: 'gl-mr-0!' }, { class: "#{issue_status_visibility(issuable, status_box: :open)} #{badge_classes} issuable-status-badge-open" }) do
|
||||
%span.gl-display-none.gl-sm-display-block.gl-ml-2
|
||||
= _('Open')
|
||||
|
||||
.issuable-meta
|
||||
|
|
|
@ -5,4 +5,4 @@ rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/352123
|
|||
milestone: '14.8'
|
||||
type: development
|
||||
group: group::product planning
|
||||
default_enabled: false
|
||||
default_enabled: true
|
||||
|
|
|
@ -5,4 +5,4 @@ rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/346082
|
|||
milestone: '14.3'
|
||||
type: development
|
||||
group: group::product planning
|
||||
default_enabled: false
|
||||
default_enabled: true
|
||||
|
|
|
@ -200,10 +200,31 @@ Conversely, a shared runner that executes jobs for public projects could have a
|
|||
### Monthly reset of CI/CD minutes
|
||||
|
||||
On the first day of each calendar month, the accumulated usage of CI/CD minutes is reset to `0`
|
||||
for all namespaces that use shared runners.
|
||||
for all namespaces that use shared runners. This means your full quota is available, and
|
||||
calculations start again from `0`.
|
||||
|
||||
For example, if you have a monthly quota of `10,000` CI/CD minutes:
|
||||
|
||||
- On **1st April**, you have `10,000` minutes.
|
||||
- During April, you use only `6,000` of the `10,000` minutes.
|
||||
- On **1st May**, the accumulated usage of minutes resets to `0`, and you have `10,000` minutes to use again
|
||||
during May.
|
||||
|
||||
Usage data for the previous month is kept to show historical view of the consumption over time.
|
||||
|
||||
### Monthly rollover of purchased CI/CD minutes
|
||||
|
||||
If you purchase additional CI/CD minutes and don't use the full amount, the remaining amount rolls over to
|
||||
the next month.
|
||||
|
||||
For example:
|
||||
|
||||
- On **1st April**, you purchase `5,000` CI/CD minutes.
|
||||
- During April, you use only `3,000` of the `5,000` minutes.
|
||||
- On **1st May**, the remaining `2,000` minutes roll over and are added to your monthly quota.
|
||||
|
||||
Additional CI/CD minutes are a one-time purchase and do not renew or refresh each month.
|
||||
|
||||
## What happens when you exceed the quota
|
||||
|
||||
When the quota of CI/CD minutes is used for the current month, GitLab stops
|
||||
|
|
|
@ -28,10 +28,10 @@ install GitLab:
|
|||
| Installation method | Description | When to choose |
|
||||
|----------------------------------------------------------------|-------------|----------------|
|
||||
| [Linux package](https://docs.gitlab.com/omnibus/installation/) | The official deb/rpm packages (also known as Omnibus GitLab) that contains a bundle of GitLab and the components it depends on, including PostgreSQL, Redis, and Sidekiq. | This method is recommended for getting started. The Linux packages are mature, scalable, and are used today on GitLab.com. If you need additional flexibility and resilience, we recommend deploying GitLab as described in the [reference architecture documentation](../administration/reference_architectures/index.md). |
|
||||
| [Helm charts](https://docs.gitlab.com/charts/) | The cloud native Helm chart for installing GitLab and all of its components on Kubernetes. | When installing GitLab on Kubernetes, there are some trade-offs that you need to be aware of: <br/>- Administration and troubleshooting requires Kubernetes knowledge.<br/>- It can be more expensive for smaller installations. The default installation requires more resources than a single node Linux package deployment, as most services are deployed in a redundant fashion.<br/><br/> Use this method if your infrastructure is built on Kubernetes and you're familiar with how it works. The methods for management, observability, and some concepts are different than traditional deployments. |
|
||||
| [Helm charts](https://docs.gitlab.com/charts/) | The cloud native Helm chart for installing GitLab and all of its components on Kubernetes. | When installing GitLab on Kubernetes, it has some trade-offs that you must be aware of: <br/>- Administration and troubleshooting requires Kubernetes knowledge.<br/>- It can be more expensive for smaller installations. The default installation requires more resources than a single node Linux package deployment, as most services are deployed in a redundant fashion.<br/><br/> Use this method if your infrastructure is built on Kubernetes and you're familiar with how it works. The methods for management, observability, and some concepts are different than traditional deployments. |
|
||||
| [Docker](docker.md) | The GitLab packages, Dockerized. | Use this method if you're familiar with Docker. |
|
||||
| [Source](installation.md) | Install GitLab and all of its components from scratch. | Use this method if none of the previous methods are available for your platform. Useful for unsupported systems like \*BSD.|
|
||||
| [GitLab Environment Toolkit (GET)](https://gitlab.com/gitlab-org/gitlab-environment-toolkit#documentation) | The GitLab Environment Toolkit provides a set of automation tools to deploy a [reference architecture](../administration/reference_architectures/index.md) on most major cloud providers. | Customers are very welcome to trial and evaluate GET today, however be aware of [key limitations](https://gitlab.com/gitlab-org/gitlab-environment-toolkit#missing-features-to-be-aware-of) of the current iteration. For production environments further manual setup will be required based on your specific requirements. |
|
||||
| [Source](installation.md) | Install GitLab and all of its components from scratch. | Use this method if none of the previous methods are available for your platform. Can be used for unsupported systems like \*BSD.|
|
||||
| [GitLab Environment Toolkit (GET)](https://gitlab.com/gitlab-org/gitlab-environment-toolkit#documentation) | The GitLab Environment toolkit provides a set of automation tools to deploy a [reference architecture](../administration/reference_architectures/index.md) on most major cloud providers. | Customers are very welcome to trial and evaluate GET today, however be aware of [key limitations](https://gitlab.com/gitlab-org/gitlab-environment-toolkit#missing-features-to-be-aware-of) of the current iteration. For production environments further manual setup is required based on your specific requirements. |
|
||||
| [GitLab Operator](https://docs.gitlab.com/operator/) | The GitLab Operator provides an installation and management method for GitLab following the [Kubernetes Operator pattern](https://kubernetes.io/docs/concepts/extend-kubernetes/operator/). | Use the GitLab Operator to run GitLab in an [OpenShift](openshift_and_gitlab/index.md) environment. |
|
||||
|
||||
## Install GitLab on cloud providers
|
||||
|
|
|
@ -8,7 +8,7 @@ info: To determine the technical writer assigned to the Stage/Group associated w
|
|||
|
||||
## Description
|
||||
|
||||
The target website returns AspNet header(s) along with version information of this website. By
|
||||
The target website returns AspNet headers along with version information of this website. By
|
||||
exposing these values attackers may attempt to identify if the target software is vulnerable to known
|
||||
vulnerabilities. Or catalog known sites running particular versions to exploit in the future when a
|
||||
vulnerability is identified in the particular version.
|
||||
|
|
|
@ -1220,3 +1220,21 @@ gemnasium-python-dependency_scanning:
|
|||
before_script:
|
||||
- pip install setuptools==57.5.0
|
||||
```
|
||||
|
||||
### Dependency Scanning of projects using psycopg2 fails with `pg_config executable not found` error
|
||||
|
||||
Scanning a Python project that depends on `psycopg2` can fail with this message:
|
||||
|
||||
```plaintext
|
||||
Error: pg_config executable not found.
|
||||
```
|
||||
|
||||
[psycopg2](https://pypi.org/project/psycopg2/) depends on the `libpq-dev` Debian package,
|
||||
which is not installed in the `gemnasium-python` Docker image. To work around this error,
|
||||
install the `libpq-dev` package in a `before_script`:
|
||||
|
||||
```yaml
|
||||
gemnasium-python-dependency_scanning:
|
||||
before_script:
|
||||
- apt-get update && apt-get install -y libpq-dev
|
||||
```
|
||||
|
|
|
@ -6,12 +6,13 @@ info: To determine the technical writer assigned to the Stage/Group associated w
|
|||
|
||||
# Customer relations management (CRM) **(FREE)**
|
||||
|
||||
FLAG:
|
||||
On self-managed GitLab, by default this feature is not available. To make it available, ask an administrator to [enable the feature flag](../../administration/feature_flags.md) named `customer_relations`.
|
||||
On GitLab.com, this feature is not available.
|
||||
|
||||
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/2256) in GitLab 14.6 [with a flag](../../administration/feature_flags.md) named `customer_relations`. Disabled by default.
|
||||
> - In GitLab 14.8 and later, you can [create contacts and organizations only in root groups](https://gitlab.com/gitlab-org/gitlab/-/issues/350634).
|
||||
> - [Enabled on GitLab.com and self-managed](https://gitlab.com/gitlab-org/gitlab/-/issues/346082) in GitLab 15.0.
|
||||
|
||||
FLAG:
|
||||
On self-managed GitLab, by default this feature is available. To hide the feature, ask an administrator to [disable the feature flag](../../administration/feature_flags.md) named `customer_relations`.
|
||||
On GitLab.com, this feature is available.
|
||||
|
||||
With customer relations management (CRM) you can create a record of contacts
|
||||
(individuals) and organizations (companies) and relate them to issues.
|
||||
|
@ -118,7 +119,7 @@ organizations using the GraphQL API.
|
|||
|
||||
### View issues linked to a contact
|
||||
|
||||
To view a contact's issues:
|
||||
To view a contact's issues, select a contact from the issue sidebar, or:
|
||||
|
||||
1. On the top bar, select **Menu > Groups** and find your group.
|
||||
1. On the left sidebar, select **Customer relations > Contacts**.
|
||||
|
@ -166,11 +167,12 @@ API.
|
|||
|
||||
## Autocomplete contacts **(FREE SELF)**
|
||||
|
||||
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/2256) in GitLab 14.8 [with a flag](../../administration/feature_flags.md) named `contacts_autocomplete`. Disabled by default.
|
||||
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/2256) in GitLab 14.8 [with a flag](../../administration/feature_flags.md) named `contacts_autocomplete`. Disabled by default.
|
||||
> - [Enabled on GitLab.com and self-managed](https://gitlab.com/gitlab-org/gitlab/-/issues/352123) in GitLab 15.0.
|
||||
|
||||
FLAG:
|
||||
On self-managed GitLab, by default this feature is not available. To make it available, ask an administrator to [enable the feature flag](../../administration/feature_flags.md) named `contacts_autocomplete`.
|
||||
On GitLab.com, this feature is not available.
|
||||
On self-managed GitLab, by default this feature is available. To hide the feature, ask an administrator to [disable the feature flag](../../administration/feature_flags.md) named `contacts_autocomplete`.
|
||||
On GitLab.com, this feature is available.
|
||||
This feature is not ready for production use.
|
||||
|
||||
When you use the `/add_contacts` or `/remove_contacts` quick actions, follow them with `[contact:` and an autocomplete list appears:
|
||||
|
|
|
@ -446,7 +446,7 @@ The following table lists group permissions available for each role:
|
|||
### Subgroup permissions
|
||||
|
||||
When you add a member to a subgroup, they inherit the membership and
|
||||
permission level from the parent group(s). This model allows access to
|
||||
permission level from the parent groups. This model allows access to
|
||||
nested groups if you have membership in one of its parents.
|
||||
|
||||
To learn more, read through the documentation on
|
||||
|
|
|
@ -226,7 +226,7 @@ on the running pod.
|
|||
If you are using a self-managed GitLab instance, you need to configure
|
||||
Amazon credentials. GitLab uses these credentials to assume an Amazon IAM role to create your cluster.
|
||||
|
||||
Create an IAM user and ensure it has permissions to assume the role(s) that
|
||||
Create an IAM user and ensure it has permissions to assume the roles that
|
||||
your users need to create EKS clusters.
|
||||
|
||||
For example, the following policy document allows assuming a role whose name starts with
|
||||
|
|
|
@ -24,8 +24,8 @@ The following list illustrates the main differences between CVS and Git:
|
|||
whole, or they fail without any changes. In CVS, commits (and other operations)
|
||||
are not atomic. If an operation on the repository is interrupted in the middle,
|
||||
the repository can be left in an inconsistent state.
|
||||
- **Storage method.** Changes in CVS are per file (changeset), while in Git
|
||||
a committed file(s) is stored in its entirety (snapshot). That means it's
|
||||
- **Storage method.** Changes in CVS are per file (changeset), while in Git,
|
||||
committed files are stored in their entirety (snapshot). This means it is
|
||||
very easy in Git to revert or undo a whole change.
|
||||
- **Revision IDs.** The fact that in CVS changes are per files, the revision ID
|
||||
is depicted by version numbers, for example `1.4` reflects how many times a
|
||||
|
|
|
@ -541,7 +541,7 @@ This can be due to multiple reasons:
|
|||
- If no [degradation or error is detected](https://docs.codeclimate.com/docs/maintainability#section-checks),
|
||||
nothing is displayed.
|
||||
- The [`artifacts:expire_in`](../../../ci/yaml/index.md#artifactsexpire_in) CI/CD
|
||||
setting can cause the Code Quality artifact(s) to expire faster than desired.
|
||||
setting can cause the Code Quality artifacts to expire faster than desired.
|
||||
- The widgets use the pipeline of the latest commit to the target branch. If commits are made to the default branch that do not run the code quality job, this may cause the merge request widget to have no base report for comparison.
|
||||
- If you use the [`REPORT_STDOUT` environment variable](https://gitlab.com/gitlab-org/ci-cd/codequality#environment-variables), no report file is generated and nothing displays in the merge request.
|
||||
- Large `gl-code-quality-report.json` files (esp. >10 MB) are [known to prevent the report from being displayed](https://gitlab.com/gitlab-org/gitlab/-/issues/2737).
|
||||
|
|
|
@ -203,7 +203,7 @@ These features are associated with merge requests:
|
|||
- [Fast-forward merge requests](../methods/index.md#fast-forward-merge):
|
||||
For a linear Git history and a way to accept merge requests without creating merge commits
|
||||
- [Find the merge request that introduced a change](../versions.md):
|
||||
When viewing the commit details page, GitLab links to the merge request(s) containing that commit.
|
||||
When viewing the commit details page, GitLab links to the merge requests containing that commit.
|
||||
- [Merge requests versions](../versions.md):
|
||||
Select and compare the different versions of merge request diffs
|
||||
- [Resolve conflicts](../conflicts.md):
|
||||
|
|
|
@ -148,7 +148,7 @@ These shortcuts are available when editing a file with the [Web IDE](project/web
|
|||
| <kbd>Shift</kbd> + <kbd>Option</kbd> + <kbd>↓</kbd> | <kbd>Control</kbd> + <kbd>Shift</kbd> + <kbd>Alt</kbd> + <kbd>↓</kbd> | Copy line down |
|
||||
| <kbd>Shift</kbd> + <kbd>Option</kbd> + <kbd>↑</kbd> | <kbd>Control</kbd> + <kbd>Shift</kbd> + <kbd>Alt</kbd> + <kbd>↑</kbd> | Copy line up [(Linux note)](#linux-shortcuts) |
|
||||
| <kbd>Command</kbd> + <kbd>U</kbd> | <kbd>Control</kbd> + <kbd>U</kbd> | Cursor undo |
|
||||
| <kbd>Command</kbd> + <kbd>Backspace<kbd> | <kbd>Control</kbd> + <kbd>Shift</kbd> + <kbd>Backspace</kbd> | Delete all left |
|
||||
| <kbd>Command</kbd> + <kbd>Backspace</kbd> | <kbd>Control</kbd> + <kbd>Shift</kbd> + <kbd>Backspace</kbd> | Delete all left |
|
||||
| <kbd>Control</kbd> + <kbd>K</kbd> | | Delete all right |
|
||||
| <kbd>Shift</kbd> + <kbd>Command</kbd> + <kbd>K</kbd> | <kbd>Control</kbd> + <kbd>Shift</kbd> + <kbd>K</kbd> | Delete line |
|
||||
| | <kbd>Control</kbd> + <kbd>Backspace</kbd> | Delete word |
|
||||
|
|
|
@ -28,6 +28,8 @@ module Gitlab
|
|||
compare_url(object, **options)
|
||||
when Group
|
||||
instance.group_canonical_url(object, **options)
|
||||
when WorkItem
|
||||
instance.work_item_url(object, **options)
|
||||
when Issue
|
||||
instance.issue_url(object, **options)
|
||||
when MergeRequest
|
||||
|
|
|
@ -5995,6 +5995,9 @@ msgstr ""
|
|||
msgid "Billing|Free groups on GitLab are limited to %{maxNamespaceSeats} seats"
|
||||
msgstr ""
|
||||
|
||||
msgid "Billing|From June 22, 2022 (GitLab 15.1), free groups will be limited to 5 members"
|
||||
msgstr ""
|
||||
|
||||
msgid "Billing|Group invite"
|
||||
msgstr ""
|
||||
|
||||
|
@ -6031,6 +6034,9 @@ msgstr ""
|
|||
msgid "Billing|You are about to remove user %{username} from your subscription. If you continue, the user will be removed from the %{namespace} group and all its subgroups and projects. This action can't be undone."
|
||||
msgstr ""
|
||||
|
||||
msgid "Billing|You can begin moving members in %{namespaceName} now. A member loses access to the group when you turn off %{strongStart}In a seat%{strongEnd}. If over 5 members have %{strongStart}In a seat%{strongEnd} enabled after June 22, 2022, we'll select the 5 members who maintain access. We'll first count members that have Owner and Maintainer roles, then the most recently active members until we reach 5 members. The remaining members will get a status of Over limit and lose access to the group."
|
||||
msgstr ""
|
||||
|
||||
msgid "Bitbucket Server Import"
|
||||
msgstr ""
|
||||
|
||||
|
|
|
@ -4,7 +4,6 @@ plugins {
|
|||
}
|
||||
|
||||
repositories {
|
||||
jcenter()
|
||||
maven {
|
||||
url "<%= gitlab_address_with_port %>/api/v4/projects/<%= package_project.id %>/packages/maven"
|
||||
name "GitLab"
|
||||
|
|
|
@ -1,32 +1,27 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module QA
|
||||
RSpec.describe 'Create', :requires_admin do
|
||||
# This test modifies an instance level setting,
|
||||
# so skipping on live envs to avoid random transient issues
|
||||
RSpec.describe 'Create', :requires_admin, :skip_live_env do
|
||||
describe 'push after setting the file size limit via admin/application_settings' do
|
||||
# Note: The file size limits in this test should be greater than the limits in
|
||||
# ee/browser_ui/3_create/repository/push_rules_spec to prevent that test from
|
||||
# triggering the limit set in this test (which can happen on Staging where the
|
||||
# tests are run in parallel).
|
||||
# See: https://gitlab.com/gitlab-org/gitlab/-/issues/218620#note_361634705
|
||||
|
||||
include Support::API
|
||||
|
||||
before(:context) do
|
||||
@project = Resource::Project.fabricate_via_api! do |p|
|
||||
let!(:project) do
|
||||
Resource::Project.fabricate_via_api! do |p|
|
||||
p.name = 'project-test-push-limit'
|
||||
p.initialize_with_readme = true
|
||||
end
|
||||
|
||||
@api_client = Runtime::API::Client.as_admin
|
||||
end
|
||||
|
||||
after(:context) do
|
||||
# need to set the default value after test
|
||||
# default value for file size limit is empty
|
||||
set_file_size_limit(nil)
|
||||
end
|
||||
|
||||
it 'push successful when the file size is under the limit', testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347758' do
|
||||
it(
|
||||
'push successful when the file size is under the limit',
|
||||
testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347758'
|
||||
) do
|
||||
set_file_size_limit(5)
|
||||
|
||||
retry_on_fail do
|
||||
|
@ -36,7 +31,10 @@ module QA
|
|||
end
|
||||
end
|
||||
|
||||
it 'push fails when the file size is above the limit', testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347759' do
|
||||
it(
|
||||
'push fails when the file size is above the limit',
|
||||
testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347759'
|
||||
) do
|
||||
set_file_size_limit(2)
|
||||
|
||||
retry_on_fail do
|
||||
|
@ -46,7 +44,7 @@ module QA
|
|||
end
|
||||
|
||||
def set_file_size_limit(limit)
|
||||
request = Runtime::API::Request.new(@api_client, '/application/settings')
|
||||
request = Runtime::API::Request.new(Runtime::API::Client.as_admin, '/application/settings')
|
||||
response = put request.url, receive_max_input_size: limit
|
||||
|
||||
expect(response.code).to eq(200)
|
||||
|
@ -56,13 +54,13 @@ module QA
|
|||
def push_new_file(file_name, wait_for_push: true)
|
||||
commit_message = 'Adding a new file'
|
||||
output = Resource::Repository::Push.fabricate! do |p|
|
||||
p.repository_http_uri = @project.repository_http_location.uri
|
||||
p.repository_http_uri = project.repository_http_location.uri
|
||||
p.file_name = file_name
|
||||
p.file_content = SecureRandom.random_bytes(3000000)
|
||||
p.commit_message = commit_message
|
||||
p.new_branch = false
|
||||
end
|
||||
@project.wait_for_push commit_message
|
||||
project.wait_for_push commit_message
|
||||
|
||||
output
|
||||
end
|
||||
|
@ -77,10 +75,8 @@ module QA
|
|||
# under a minute, i.e., in fewer than 6 attempts with a 10 second sleep
|
||||
# between attempts.
|
||||
# See https://gitlab.com/gitlab-org/gitlab-foss/merge_requests/30233#note_188616863
|
||||
def retry_on_fail
|
||||
Support::Retrier.retry_on_exception(max_attempts: 6, reload_page: nil, sleep_interval: 10) do
|
||||
yield
|
||||
end
|
||||
def retry_on_fail(&block)
|
||||
Support::Retrier.retry_on_exception(max_attempts: 6, reload_page: false, sleep_interval: 10, &block)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -105,7 +105,7 @@ describe('Customer relations contacts root app', () => {
|
|||
|
||||
const issueLink = findIssuesLinks().at(0);
|
||||
expect(issueLink.exists()).toBe(true);
|
||||
expect(issueLink.attributes('href')).toBe('/issues?scope=all&state=opened&crm_contact_id=16');
|
||||
expect(issueLink.attributes('href')).toBe('/issues?crm_contact_id=16');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -102,9 +102,7 @@ describe('Customer relations organizations root app', () => {
|
|||
|
||||
const issueLink = findIssuesLinks().at(0);
|
||||
expect(issueLink.exists()).toBe(true);
|
||||
expect(issueLink.attributes('href')).toBe(
|
||||
'/issues?scope=all&state=opened&crm_organization_id=2',
|
||||
);
|
||||
expect(issueLink.attributes('href')).toBe('/issues?crm_organization_id=2');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,65 +1,53 @@
|
|||
import { GlSprintf } from '@gitlab/ui';
|
||||
import { GlBadge, GlIcon } from '@gitlab/ui';
|
||||
import { shallowMount } from '@vue/test-utils';
|
||||
import StatusBox from '~/issuable/components/status_box.vue';
|
||||
|
||||
let wrapper;
|
||||
|
||||
function factory(propsData) {
|
||||
wrapper = shallowMount(StatusBox, {
|
||||
propsData,
|
||||
stubs: { GlSprintf },
|
||||
provide: { glFeatures: { updatedMrHeader: true } },
|
||||
});
|
||||
wrapper = shallowMount(StatusBox, { propsData, stubs: { GlBadge } });
|
||||
}
|
||||
|
||||
const testCases = [
|
||||
{
|
||||
name: 'Open',
|
||||
state: 'opened',
|
||||
class: 'badge-success',
|
||||
},
|
||||
{
|
||||
name: 'Open',
|
||||
state: 'locked',
|
||||
class: 'badge-success',
|
||||
},
|
||||
{
|
||||
name: 'Closed',
|
||||
state: 'closed',
|
||||
class: 'badge-danger',
|
||||
},
|
||||
{
|
||||
name: 'Merged',
|
||||
state: 'merged',
|
||||
class: 'badge-info',
|
||||
},
|
||||
];
|
||||
|
||||
describe('Merge request status box component', () => {
|
||||
const findBadge = () => wrapper.findComponent(GlBadge);
|
||||
|
||||
afterEach(() => {
|
||||
wrapper.destroy();
|
||||
wrapper = null;
|
||||
});
|
||||
|
||||
testCases.forEach((testCase) => {
|
||||
describe(`when merge request is ${testCase.name}`, () => {
|
||||
it('renders human readable test', () => {
|
||||
describe.each`
|
||||
issuableType | badgeText | initialState | badgeClass | badgeVariant | badgeIcon
|
||||
${'merge_request'} | ${'Open'} | ${'opened'} | ${'issuable-status-badge-open'} | ${'success'} | ${'merge-request-open'}
|
||||
${'merge_request'} | ${'Closed'} | ${'closed'} | ${'issuable-status-badge-closed'} | ${'danger'} | ${'merge-request-close'}
|
||||
${'merge_request'} | ${'Merged'} | ${'merged'} | ${'issuable-status-badge-merged'} | ${'info'} | ${'merge'}
|
||||
${'issue'} | ${'Open'} | ${'opened'} | ${'issuable-status-badge-open'} | ${'success'} | ${'issues'}
|
||||
${'issue'} | ${'Closed'} | ${'closed'} | ${'issuable-status-badge-closed'} | ${'info'} | ${'issue-closed'}
|
||||
`(
|
||||
'with issuableType set to "$issuableType" and state set to "$initialState"',
|
||||
({ issuableType, badgeText, initialState, badgeClass, badgeVariant, badgeIcon }) => {
|
||||
beforeEach(() => {
|
||||
factory({
|
||||
initialState: testCase.state,
|
||||
issuableType: 'merge_request',
|
||||
initialState,
|
||||
issuableType,
|
||||
});
|
||||
});
|
||||
|
||||
expect(wrapper.text()).toContain(testCase.name);
|
||||
it(`renders badge with text '${badgeText}'`, () => {
|
||||
expect(findBadge().text()).toBe(badgeText);
|
||||
});
|
||||
|
||||
it('sets css class', () => {
|
||||
factory({
|
||||
initialState: testCase.state,
|
||||
issuableType: 'merge_request',
|
||||
it(`sets badge css class as '${badgeClass}'`, () => {
|
||||
expect(findBadge().classes()).toContain(badgeClass);
|
||||
});
|
||||
|
||||
expect(wrapper.classes()).toContain(testCase.class);
|
||||
});
|
||||
it(`sets badge variant as '${badgeVariant}`, () => {
|
||||
expect(findBadge().props('variant')).toBe(badgeVariant);
|
||||
});
|
||||
|
||||
it(`sets badge icon as '${badgeIcon}'`, () => {
|
||||
expect(findBadge().findComponent(GlIcon).props('name')).toBe(badgeIcon);
|
||||
});
|
||||
},
|
||||
);
|
||||
});
|
||||
|
|
|
@ -24,11 +24,11 @@ describe('Issue', () => {
|
|||
const getIssueCounter = () => document.querySelector('.issue_counter');
|
||||
const getOpenStatusBox = () =>
|
||||
getByText(document, (_, el) => el.textContent.match(/Open/), {
|
||||
selector: '.status-box-open',
|
||||
selector: '.issuable-status-badge-open',
|
||||
});
|
||||
const getClosedStatusBox = () =>
|
||||
getByText(document, (_, el) => el.textContent.match(/Closed/), {
|
||||
selector: '.status-box-issue-closed',
|
||||
selector: '.issuable-status-badge-closed',
|
||||
});
|
||||
|
||||
describe.each`
|
||||
|
|
|
@ -33,7 +33,7 @@ describe('Issue crm contacts component', () => {
|
|||
[issueCrmContactsSubscription, subscriptionHandler],
|
||||
]);
|
||||
wrapper = shallowMountExtended(CrmContacts, {
|
||||
propsData: { issueId: '123' },
|
||||
propsData: { issueId: '123', groupIssuesPath: '/groups/flightjs/-/issues' },
|
||||
apolloProvider: fakeApollo,
|
||||
});
|
||||
};
|
||||
|
@ -71,8 +71,14 @@ describe('Issue crm contacts component', () => {
|
|||
await waitForPromises();
|
||||
|
||||
expect(wrapper.find('#contact_0').text()).toContain('Someone Important');
|
||||
expect(wrapper.find('#contact_0').attributes('href')).toBe(
|
||||
'/groups/flightjs/-/issues?crm_contact_id=1',
|
||||
);
|
||||
expect(wrapper.find('#contact_container_0').text()).toContain('si@gitlab.com');
|
||||
expect(wrapper.find('#contact_1').text()).toContain('Marty McFly');
|
||||
expect(wrapper.find('#contact_1').attributes('href')).toBe(
|
||||
'/groups/flightjs/-/issues?crm_contact_id=5',
|
||||
);
|
||||
});
|
||||
|
||||
it('renders correct results after subscription update', async () => {
|
||||
|
@ -83,5 +89,8 @@ describe('Issue crm contacts component', () => {
|
|||
contact.forEach((property) => {
|
||||
expect(wrapper.find('#contact_container_0').text()).toContain(property);
|
||||
});
|
||||
expect(wrapper.find('#contact_0').attributes('href')).toBe(
|
||||
'/groups/flightjs/-/issues?crm_contact_id=13',
|
||||
);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -69,9 +69,11 @@ describe('IssuableHeader', () => {
|
|||
it('renders issuable status icon and text', () => {
|
||||
createComponent();
|
||||
const statusBoxEl = wrapper.findByTestId('status');
|
||||
const statusIconEl = statusBoxEl.findComponent(GlIcon);
|
||||
|
||||
expect(statusBoxEl.exists()).toBe(true);
|
||||
expect(statusBoxEl.find(GlIcon).props('name')).toBe(mockIssuableShowProps.statusIcon);
|
||||
expect(statusIconEl.props('name')).toBe(mockIssuableShowProps.statusIcon);
|
||||
expect(statusIconEl.attributes('class')).toBe(mockIssuableShowProps.statusIconClass);
|
||||
expect(statusBoxEl.text()).toContain('Open');
|
||||
});
|
||||
|
||||
|
|
|
@ -49,6 +49,7 @@ describe('IssuableShowRoot', () => {
|
|||
const {
|
||||
statusBadgeClass,
|
||||
statusIcon,
|
||||
statusIconClass,
|
||||
enableEdit,
|
||||
enableAutocomplete,
|
||||
editFormVisible,
|
||||
|
@ -56,7 +57,7 @@ describe('IssuableShowRoot', () => {
|
|||
descriptionHelpPath,
|
||||
taskCompletionStatus,
|
||||
} = mockIssuableShowProps;
|
||||
const { blocked, confidential, createdAt, author } = mockIssuable;
|
||||
const { state, blocked, confidential, createdAt, author } = mockIssuable;
|
||||
|
||||
it('renders component container element with class `issuable-show-container`', () => {
|
||||
expect(wrapper.classes()).toContain('issuable-show-container');
|
||||
|
@ -67,15 +68,17 @@ describe('IssuableShowRoot', () => {
|
|||
|
||||
expect(issuableHeader.exists()).toBe(true);
|
||||
expect(issuableHeader.props()).toMatchObject({
|
||||
issuableState: state,
|
||||
statusBadgeClass,
|
||||
statusIcon,
|
||||
statusIconClass,
|
||||
blocked,
|
||||
confidential,
|
||||
createdAt,
|
||||
author,
|
||||
taskCompletionStatus,
|
||||
});
|
||||
expect(issuableHeader.find('.issuable-status-box').text()).toContain('Open');
|
||||
expect(issuableHeader.find('.issuable-status-badge').text()).toContain('Open');
|
||||
expect(issuableHeader.find('.detail-page-header-actions button.js-close').exists()).toBe(
|
||||
true,
|
||||
);
|
||||
|
|
|
@ -36,8 +36,9 @@ export const mockIssuableShowProps = {
|
|||
enableTaskList: true,
|
||||
enableEdit: true,
|
||||
showFieldTitle: false,
|
||||
statusBadgeClass: 'status-box-open',
|
||||
statusIcon: 'issue-open-m',
|
||||
statusBadgeClass: 'issuable-status-badge-open',
|
||||
statusIcon: 'issues',
|
||||
statusIconClass: 'gl-sm-display-none',
|
||||
taskCompletionStatus: {
|
||||
completedCount: 0,
|
||||
count: 5,
|
||||
|
|
|
@ -89,16 +89,16 @@ RSpec.describe BadgesHelper do
|
|||
end
|
||||
|
||||
describe 'icons' do
|
||||
let(:spacing_class_regex) { %r{<svg .*class=".*gl-mr-2.*".*>.*</svg>} }
|
||||
let(:spacing_class_regex) { %r{<svg .*class=".*my-icon-class gl-mr-2".*>.*</svg>} }
|
||||
|
||||
describe 'with text' do
|
||||
subject { helper.gl_badge_tag(label, icon: "question-o") }
|
||||
subject { helper.gl_badge_tag(label, icon: "question-o", icon_classes: 'my-icon-class') }
|
||||
|
||||
it 'renders an icon' do
|
||||
expect(subject).to match(%r{<svg .*#question-o".*>.*</svg>})
|
||||
end
|
||||
|
||||
it 'adds a spacing class to the icon' do
|
||||
it 'adds a spacing class and any custom classes to the icon' do
|
||||
expect(subject).to match(spacing_class_regex)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -464,6 +464,41 @@ RSpec.describe IssuablesHelper do
|
|||
end
|
||||
end
|
||||
|
||||
describe '#state_name_with_icon' do
|
||||
let_it_be(:project) { create(:project, :repository) }
|
||||
|
||||
context 'for an issue' do
|
||||
let_it_be(:issue) { create(:issue, project: project) }
|
||||
let_it_be(:issue_closed) { create(:issue, :closed, project: project) }
|
||||
|
||||
it 'returns the correct state name and icon when issue is open' do
|
||||
expect(helper.state_name_with_icon(issue)).to match_array([_('Open'), 'issues'])
|
||||
end
|
||||
|
||||
it 'returns the correct state name and icon when issue is closed' do
|
||||
expect(helper.state_name_with_icon(issue_closed)).to match_array([_('Closed'), 'issue-closed'])
|
||||
end
|
||||
end
|
||||
|
||||
context 'for a merge request' do
|
||||
let_it_be(:merge_request) { create(:merge_request, source_project: project) }
|
||||
let_it_be(:merge_request_merged) { create(:merge_request, :merged, source_project: project) }
|
||||
let_it_be(:merge_request_closed) { create(:merge_request, :closed, source_project: project) }
|
||||
|
||||
it 'returns the correct state name and icon when merge request is open' do
|
||||
expect(helper.state_name_with_icon(merge_request)).to match_array([_('Open'), 'merge-request-open'])
|
||||
end
|
||||
|
||||
it 'returns the correct state name and icon when merge request is merged' do
|
||||
expect(helper.state_name_with_icon(merge_request_merged)).to match_array([_('Merged'), 'merge'])
|
||||
end
|
||||
|
||||
it 'returns the correct state name and icon when merge request is closed' do
|
||||
expect(helper.state_name_with_icon(merge_request_closed)).to match_array([_('Closed'), 'merge-request-close'])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#issuable_display_type' do
|
||||
using RSpec::Parameterized::TableSyntax
|
||||
|
||||
|
|
|
@ -5,31 +5,6 @@ require 'spec_helper'
|
|||
RSpec.describe MergeRequestsHelper do
|
||||
include ProjectForksHelper
|
||||
|
||||
describe '#state_name_with_icon' do
|
||||
using RSpec::Parameterized::TableSyntax
|
||||
|
||||
let(:merge_request) { MergeRequest.new }
|
||||
|
||||
where(:state, :expected_name, :expected_icon) do
|
||||
:merged? | 'Merged' | 'git-merge'
|
||||
:closed? | 'Closed' | 'close'
|
||||
:opened? | 'Open' | 'issue-open-m'
|
||||
end
|
||||
|
||||
with_them do
|
||||
before do
|
||||
allow(merge_request).to receive(state).and_return(true)
|
||||
end
|
||||
|
||||
it 'returns name and icon' do
|
||||
name, icon = helper.state_name_with_icon(merge_request)
|
||||
|
||||
expect(name).to eq(expected_name)
|
||||
expect(icon).to eq(expected_icon)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#format_mr_branch_names' do
|
||||
describe 'within the same project' do
|
||||
let(:merge_request) { create(:merge_request) }
|
||||
|
|
|
@ -22,6 +22,8 @@ RSpec.describe Gitlab::UrlBuilder do
|
|||
:group_board | ->(board) { "/groups/#{board.group.full_path}/-/boards/#{board.id}" }
|
||||
:commit | ->(commit) { "/#{commit.project.full_path}/-/commit/#{commit.id}" }
|
||||
:issue | ->(issue) { "/#{issue.project.full_path}/-/issues/#{issue.iid}" }
|
||||
[:issue, :task] | ->(issue) { "/#{issue.project.full_path}/-/work_items/#{issue.id}" }
|
||||
:work_item | ->(work_item) { "/#{work_item.project.full_path}/-/work_items/#{work_item.id}" }
|
||||
:merge_request | ->(merge_request) { "/#{merge_request.project.full_path}/-/merge_requests/#{merge_request.iid}" }
|
||||
:project_milestone | ->(milestone) { "/#{milestone.project.full_path}/-/milestones/#{milestone.iid}" }
|
||||
:project_snippet | ->(snippet) { "/#{snippet.project.full_path}/-/snippets/#{snippet.id}" }
|
||||
|
@ -57,7 +59,7 @@ RSpec.describe Gitlab::UrlBuilder do
|
|||
end
|
||||
|
||||
with_them do
|
||||
let(:object) { build_stubbed(factory) }
|
||||
let(:object) { build_stubbed(*Array(factory)) }
|
||||
let(:path) { path_generator.call(object) }
|
||||
|
||||
it 'returns the full URL' do
|
||||
|
@ -69,6 +71,18 @@ RSpec.describe Gitlab::UrlBuilder do
|
|||
end
|
||||
end
|
||||
|
||||
context 'when work_items feature flag is disabled' do
|
||||
before do
|
||||
stub_feature_flags(work_items: false)
|
||||
end
|
||||
|
||||
it 'returns an issue path for an issue of type task' do
|
||||
task = create(:issue, :task)
|
||||
|
||||
expect(subject.build(task, only_path: true)).to eq("/#{task.project.full_path}/-/issues/#{task.iid}")
|
||||
end
|
||||
end
|
||||
|
||||
context 'when passing a compare' do
|
||||
# NOTE: The Compare requires an actual repository, which isn't available
|
||||
# with the `build_stubbed` strategy used by the table tests above
|
||||
|
|
|
@ -24,6 +24,17 @@ RSpec.describe Projects::IssueLinksController do
|
|||
expect(response).to have_gitlab_http_status(:ok)
|
||||
expect(json_response).to eq(list_service_response.as_json)
|
||||
end
|
||||
|
||||
context 'when linked issue is a task' do
|
||||
let(:issue_b) { create :issue, :task, project: project }
|
||||
|
||||
it 'returns a work item path for the linked task' do
|
||||
get namespace_project_issue_links_path(issue_links_params)
|
||||
|
||||
expect(json_response.count).to eq(1)
|
||||
expect(json_response.first).to include('path' => project_work_items_path(issue_b.project, issue_b.id))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'POST /*namespace_id/:project_id/issues/:issue_id/links' do
|
||||
|
|
|
@ -3,6 +3,8 @@
|
|||
require 'spec_helper'
|
||||
|
||||
RSpec.describe IssueBoardEntity do
|
||||
include Gitlab::Routing.url_helpers
|
||||
|
||||
let_it_be(:project) { create(:project) }
|
||||
let_it_be(:resource) { create(:issue, project: project) }
|
||||
let_it_be(:user) { create(:user) }
|
||||
|
@ -40,4 +42,18 @@ RSpec.describe IssueBoardEntity do
|
|||
|
||||
expect(subject).to include(labels: array_including(hash_including(:id, :title, :color, :description, :text_color, :priority)))
|
||||
end
|
||||
|
||||
describe 'real_path' do
|
||||
it 'has an issue path' do
|
||||
expect(subject[:real_path]).to eq(project_issue_path(project, resource.iid))
|
||||
end
|
||||
|
||||
context 'when issue is of type task' do
|
||||
let(:resource) { create(:issue, :task, project: project) }
|
||||
|
||||
it 'has a work item path' do
|
||||
expect(subject[:real_path]).to eq(project_work_items_path(project, resource.id))
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -3,6 +3,8 @@
|
|||
require 'spec_helper'
|
||||
|
||||
RSpec.describe IssueEntity do
|
||||
include Gitlab::Routing.url_helpers
|
||||
|
||||
let(:project) { create(:project) }
|
||||
let(:resource) { create(:issue, project: project) }
|
||||
let(:user) { create(:user) }
|
||||
|
@ -11,6 +13,17 @@ RSpec.describe IssueEntity do
|
|||
|
||||
subject { described_class.new(resource, request: request).as_json }
|
||||
|
||||
describe 'web_url' do
|
||||
context 'when issue is of type task' do
|
||||
let(:resource) { create(:issue, :task, project: project) }
|
||||
|
||||
# This was already a path and not a url when the work items change was introduced
|
||||
it 'has a work item path' do
|
||||
expect(subject[:web_url]).to eq(project_work_items_path(project, resource.id))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
it 'has Issuable attributes' do
|
||||
expect(subject).to include(:id, :iid, :author_id, :description, :lock_version, :milestone_id,
|
||||
:title, :updated_by_id, :created_at, :updated_at, :milestone, :labels)
|
||||
|
|
|
@ -3,6 +3,8 @@
|
|||
require 'spec_helper'
|
||||
|
||||
RSpec.describe LinkedProjectIssueEntity do
|
||||
include Gitlab::Routing.url_helpers
|
||||
|
||||
let_it_be(:user) { create(:user) }
|
||||
let_it_be(:project) { create(:project) }
|
||||
let_it_be(:issue_link) { create(:issue_link) }
|
||||
|
@ -17,7 +19,25 @@ RSpec.describe LinkedProjectIssueEntity do
|
|||
issue_link.target.project.add_developer(user)
|
||||
end
|
||||
|
||||
subject(:serialized_entity) { entity.as_json }
|
||||
|
||||
describe 'issue_link_type' do
|
||||
it { expect(entity.as_json).to include(link_type: 'relates_to') }
|
||||
it { is_expected.to include(link_type: 'relates_to') }
|
||||
end
|
||||
|
||||
describe 'path' do
|
||||
it 'returns an issue path' do
|
||||
expect(serialized_entity).to include(path: project_issue_path(related_issue.project, related_issue.iid))
|
||||
end
|
||||
|
||||
context 'when related issue is a task' do
|
||||
before do
|
||||
related_issue.update!(issue_type: :task, work_item_type: WorkItems::Type.default_by_type(:task))
|
||||
end
|
||||
|
||||
it 'returns a work items path' do
|
||||
expect(serialized_entity).to include(path: project_work_items_path(related_issue.project, related_issue.id))
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -3,14 +3,28 @@
|
|||
require 'spec_helper'
|
||||
|
||||
RSpec.describe Members::Groups::CreatorService do
|
||||
it_behaves_like 'member creation' do
|
||||
let_it_be(:source, reload: true) { create(:group, :public) }
|
||||
let_it_be(:member_type) { GroupMember }
|
||||
end
|
||||
|
||||
describe '.access_levels' do
|
||||
it 'returns Gitlab::Access.options_with_owner' do
|
||||
expect(described_class.access_levels).to eq(Gitlab::Access.sym_options_with_owner)
|
||||
end
|
||||
end
|
||||
|
||||
describe '#execute' do
|
||||
let_it_be(:source, reload: true) { create(:group, :public) }
|
||||
let_it_be(:user) { create(:user) }
|
||||
|
||||
it_behaves_like 'member creation' do
|
||||
let_it_be(:member_type) { GroupMember }
|
||||
end
|
||||
|
||||
context 'authorized projects update' do
|
||||
it 'schedules a single project authorization update job when called multiple times' do
|
||||
expect(AuthorizedProjectsWorker).to receive(:bulk_perform_and_wait).once
|
||||
|
||||
1.upto(3) do
|
||||
described_class.new(source, user, :maintainer).execute
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -3,14 +3,28 @@
|
|||
require 'spec_helper'
|
||||
|
||||
RSpec.describe Members::Projects::CreatorService do
|
||||
it_behaves_like 'member creation' do
|
||||
let_it_be(:source, reload: true) { create(:project, :public) }
|
||||
let_it_be(:member_type) { ProjectMember }
|
||||
end
|
||||
|
||||
describe '.access_levels' do
|
||||
it 'returns Gitlab::Access.sym_options_with_owner' do
|
||||
expect(described_class.access_levels).to eq(Gitlab::Access.sym_options_with_owner)
|
||||
end
|
||||
end
|
||||
|
||||
describe '#execute' do
|
||||
let_it_be(:source, reload: true) { create(:project, :public) }
|
||||
let_it_be(:user) { create(:user) }
|
||||
|
||||
it_behaves_like 'member creation' do
|
||||
let_it_be(:member_type) { ProjectMember }
|
||||
end
|
||||
|
||||
context 'authorized projects update' do
|
||||
it 'schedules a single project authorization update job when called multiple times' do
|
||||
expect(AuthorizedProjectUpdate::UserRefreshFromReplicaWorker).to receive(:bulk_perform_in).once
|
||||
|
||||
1.upto(3) do
|
||||
described_class.new(source, user, :maintainer).execute
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module TrialStatusWidgetTestHelper
|
||||
def purchase_href(group)
|
||||
new_subscriptions_path(namespace_id: group.id, plan_id: 'ultimate-plan-id')
|
||||
end
|
||||
end
|
||||
|
||||
TrialStatusWidgetTestHelper.prepend_mod
|
|
@ -77,10 +77,8 @@ RSpec.shared_examples '#valid_level_roles' do |entity_name|
|
|||
end
|
||||
|
||||
RSpec.shared_examples_for "member creation" do
|
||||
let_it_be(:user) { create(:user) }
|
||||
let_it_be(:admin) { create(:admin) }
|
||||
|
||||
describe '#execute' do
|
||||
it 'returns a Member object', :aggregate_failures do
|
||||
member = described_class.new(source, user, :maintainer).execute
|
||||
|
||||
|
@ -386,7 +384,6 @@ RSpec.shared_examples_for "member creation" do
|
|||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
RSpec.shared_examples_for "bulk member creation" do
|
||||
let_it_be(:user) { create(:user) }
|
||||
|
|
|
@ -26,14 +26,14 @@ RSpec.describe 'projects/issues/show' do
|
|||
it 'shows "Closed (moved)" if an issue has been moved and closed' do
|
||||
render
|
||||
|
||||
expect(rendered).to have_selector('.status-box-issue-closed:not(.hidden)', text: 'Closed (moved)')
|
||||
expect(rendered).to have_selector('.issuable-status-badge-closed:not(.hidden)', text: 'Closed (moved)')
|
||||
end
|
||||
|
||||
it 'shows "Closed (moved)" if an issue has been moved and discussion is locked' do
|
||||
allow(issue).to receive(:discussion_locked).and_return(true)
|
||||
render
|
||||
|
||||
expect(rendered).to have_selector('.status-box-issue-closed:not(.hidden)', text: 'Closed (moved)')
|
||||
expect(rendered).to have_selector('.issuable-status-badge-closed:not(.hidden)', text: 'Closed (moved)')
|
||||
end
|
||||
|
||||
it 'links "moved" to the new issue the original issue was moved to' do
|
||||
|
@ -47,7 +47,7 @@ RSpec.describe 'projects/issues/show' do
|
|||
|
||||
render
|
||||
|
||||
expect(rendered).not_to have_selector('.status-box-issue-closed:not(.hidden)', text: 'Closed (moved)')
|
||||
expect(rendered).not_to have_selector('.issuable-status-badge-closed:not(.hidden)', text: 'Closed (moved)')
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -75,7 +75,7 @@ RSpec.describe 'projects/issues/show' do
|
|||
it 'shows "Closed (duplicated)" if an issue has been duplicated' do
|
||||
render
|
||||
|
||||
expect(rendered).to have_selector('.status-box-issue-closed:not(.hidden)', text: 'Closed (duplicated)')
|
||||
expect(rendered).to have_selector('.issuable-status-badge-closed:not(.hidden)', text: 'Closed (duplicated)')
|
||||
end
|
||||
|
||||
it 'links "duplicated" to the new issue the original issue was duplicated to' do
|
||||
|
@ -97,14 +97,14 @@ RSpec.describe 'projects/issues/show' do
|
|||
it 'shows "Closed" if an issue has not been moved or duplicated' do
|
||||
render
|
||||
|
||||
expect(rendered).to have_selector('.status-box-issue-closed:not(.hidden)', text: 'Closed')
|
||||
expect(rendered).to have_selector('.issuable-status-badge-closed:not(.hidden)', text: 'Closed')
|
||||
end
|
||||
|
||||
it 'shows "Closed" if discussion is locked' do
|
||||
allow(issue).to receive(:discussion_locked).and_return(true)
|
||||
render
|
||||
|
||||
expect(rendered).to have_selector('.status-box-issue-closed:not(.hidden)', text: 'Closed')
|
||||
expect(rendered).to have_selector('.issuable-status-badge-closed:not(.hidden)', text: 'Closed')
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -117,14 +117,14 @@ RSpec.describe 'projects/issues/show' do
|
|||
it 'shows "Open" if an issue has been moved' do
|
||||
render
|
||||
|
||||
expect(rendered).to have_selector('.status-box-open:not(.hidden)', text: 'Open')
|
||||
expect(rendered).to have_selector('.issuable-status-badge-open:not(.hidden)', text: 'Open')
|
||||
end
|
||||
|
||||
it 'shows "Open" if discussion is locked' do
|
||||
allow(issue).to receive(:discussion_locked).and_return(true)
|
||||
render
|
||||
|
||||
expect(rendered).to have_selector('.status-box-open:not(.hidden)', text: 'Open')
|
||||
expect(rendered).to have_selector('.issuable-status-badge-open:not(.hidden)', text: 'Open')
|
||||
end
|
||||
end
|
||||
|
||||
|
|
Loading…
Reference in New Issue