summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPhil Hughes <me@iamphill.com>2017-06-23 10:44:10 +0100
committerPhil Hughes <me@iamphill.com>2017-06-23 10:44:10 +0100
commit6fb7fb5c1a44926511914080334774b21a5ee9af (patch)
tree4fdf8530c98576fd19c306a3c74540998bee9fd0
parent3b53b553b78d20268d228afa596b53d18046f98e (diff)
parent801cf92310e9f6950dddba848ef1e6a3d1e48ef0 (diff)
downloadgitlab-ce-6fb7fb5c1a44926511914080334774b21a5ee9af.tar.gz
Merge branch 'master' into experimental-top-nav
-rw-r--r--.gitlab-ci.yml1
-rw-r--r--.gitlab/issue_templates/Feature Proposal.md24
-rw-r--r--.rubocop.yml5
-rw-r--r--.rubocop_todo.yml7
-rw-r--r--GITALY_SERVER_VERSION2
-rw-r--r--GITLAB_WORKHORSE_VERSION2
-rw-r--r--Gemfile4
-rw-r--r--Gemfile.lock10
-rw-r--r--app/assets/javascripts/awards_handler.js753
-rw-r--r--app/assets/javascripts/boards/components/board_new_issue.js10
-rw-r--r--app/assets/javascripts/boards/models/list.js3
-rw-r--r--app/assets/javascripts/environments/components/environment_item.vue15
-rw-r--r--app/assets/javascripts/filtered_search/filtered_search_manager.js5
-rw-r--r--app/assets/javascripts/groups/stores/groups_store.js42
-rw-r--r--app/assets/javascripts/issue_show/components/app.vue6
-rw-r--r--app/assets/javascripts/issue_show/stores/index.js10
-rw-r--r--app/assets/javascripts/lib/utils/common_utils.js11
-rw-r--r--app/assets/javascripts/locale/es/app.js2
-rw-r--r--app/assets/javascripts/locale/zh_CN/app.js2
-rw-r--r--app/assets/javascripts/merge_request_tabs.js8
-rw-r--r--app/assets/javascripts/notes.js2730
-rw-r--r--app/assets/javascripts/prometheus_metrics/constants.js5
-rw-r--r--app/assets/javascripts/prometheus_metrics/index.js6
-rw-r--r--app/assets/javascripts/prometheus_metrics/prometheus_metrics.js109
-rw-r--r--app/assets/javascripts/right_sidebar.js4
-rw-r--r--app/assets/javascripts/settings_panels.js2
-rw-r--r--app/assets/javascripts/users_select.js2
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/mr_widget_related_links.js41
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_merged.js10
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/mr_widget_options.js11
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/stores/mr_widget_store.js13
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/stores/state_maps.js13
-rw-r--r--app/assets/stylesheets/framework/dropdowns.scss5
-rw-r--r--app/assets/stylesheets/framework/header.scss1
-rw-r--r--app/assets/stylesheets/framework/nav.scss27
-rw-r--r--app/assets/stylesheets/framework/timeline.scss4
-rw-r--r--app/assets/stylesheets/framework/variables.scss1
-rw-r--r--app/assets/stylesheets/pages/environments.scss1
-rw-r--r--app/assets/stylesheets/pages/members.scss4
-rw-r--r--app/assets/stylesheets/pages/merge_requests.scss6
-rw-r--r--app/assets/stylesheets/pages/note_form.scss16
-rw-r--r--app/assets/stylesheets/pages/notes.scss83
-rw-r--r--app/assets/stylesheets/pages/pipelines.scss2
-rw-r--r--app/assets/stylesheets/pages/projects.scss2
-rw-r--r--app/assets/stylesheets/pages/settings.scss63
-rw-r--r--app/assets/stylesheets/pages/tree.scss85
-rw-r--r--app/controllers/application_controller.rb4
-rw-r--r--app/controllers/concerns/creates_commit.rb4
-rw-r--r--app/controllers/concerns/membership_actions.rb8
-rw-r--r--app/controllers/dashboard/projects_controller.rb8
-rw-r--r--app/controllers/explore/projects_controller.rb4
-rw-r--r--app/controllers/jwt_controller.rb4
-rw-r--r--app/controllers/omniauth_callbacks_controller.rb4
-rw-r--r--app/controllers/profiles_controller.rb6
-rw-r--r--app/controllers/projects/application_controller.rb14
-rw-r--r--app/controllers/projects/blob_controller.rb4
-rw-r--r--app/controllers/projects/branches_controller.rb4
-rw-r--r--app/controllers/projects/commits_controller.rb8
-rw-r--r--app/controllers/projects/compare_controller.rb4
-rw-r--r--app/controllers/projects/deployments_controller.rb16
-rw-r--r--app/controllers/projects/discussions_controller.rb6
-rw-r--r--app/controllers/projects/environments_controller.rb10
-rw-r--r--app/controllers/projects/issues_controller.rb4
-rw-r--r--app/controllers/projects/labels_controller.rb8
-rw-r--r--app/controllers/projects/merge_requests_controller.rb30
-rw-r--r--app/controllers/projects/milestones_controller.rb8
-rw-r--r--app/controllers/projects/prometheus_controller.rb24
-rw-r--r--app/controllers/projects/settings/ci_cd_controller.rb4
-rw-r--r--app/controllers/projects/snippets_controller.rb6
-rw-r--r--app/controllers/projects/tags_controller.rb4
-rw-r--r--app/controllers/sessions_controller.rb4
-rw-r--r--app/controllers/sherlock/application_controller.rb4
-rw-r--r--app/controllers/users_controller.rb10
-rw-r--r--app/finders/events_finder.rb4
-rw-r--r--app/finders/group_members_finder.rb6
-rw-r--r--app/helpers/commits_helper.rb6
-rw-r--r--app/helpers/form_helper.rb8
-rw-r--r--app/helpers/projects_helper.rb4
-rw-r--r--app/helpers/search_helper.rb4
-rw-r--r--app/helpers/users_helper.rb10
-rw-r--r--app/helpers/wiki_helper.rb6
-rw-r--r--app/models/award_emoji.rb6
-rw-r--r--app/models/ci/build.rb27
-rw-r--r--app/models/ci/pipeline.rb4
-rw-r--r--app/models/ci/runner.rb4
-rw-r--r--app/models/concerns/issuable.rb12
-rw-r--r--app/models/concerns/relative_positioning.rb16
-rw-r--r--app/models/concerns/routable.rb16
-rw-r--r--app/models/concerns/sortable.rb12
-rw-r--r--app/models/concerns/subscribable.rb18
-rw-r--r--app/models/deployment.rb33
-rw-r--r--app/models/environment.rb16
-rw-r--r--app/models/event.rb10
-rw-r--r--app/models/group.rb12
-rw-r--r--app/models/issue.rb4
-rw-r--r--app/models/issue_collection.rb6
-rw-r--r--app/models/label.rb12
-rw-r--r--app/models/member.rb6
-rw-r--r--app/models/merge_request.rb4
-rw-r--r--app/models/merge_requests_closing_issues.rb6
-rw-r--r--app/models/milestone.rb10
-rw-r--r--app/models/namespace.rb20
-rw-r--r--app/models/note.rb6
-rw-r--r--app/models/project.rb36
-rw-r--r--app/models/project_authorization.rb6
-rw-r--r--app/models/project_services/mattermost_slash_commands_service.rb4
-rw-r--r--app/models/project_services/prometheus_service.rb40
-rw-r--r--app/models/project_team.rb8
-rw-r--r--app/models/repository.rb8
-rw-r--r--app/models/todo.rb6
-rw-r--r--app/models/user.rb56
-rw-r--r--app/models/wiki_page.rb10
-rw-r--r--app/services/ci/create_pipeline_service.rb4
-rw-r--r--app/services/ci/create_trigger_request_service.rb4
-rw-r--r--app/services/ci/register_job_service.rb14
-rw-r--r--app/services/concerns/issues/resolve_discussions.rb6
-rw-r--r--app/services/create_deployment_service.rb16
-rw-r--r--app/services/files/update_service.rb4
-rw-r--r--app/services/git_push_service.rb4
-rw-r--r--app/services/issuable_base_service.rb4
-rw-r--r--app/services/issues/create_service.rb4
-rw-r--r--app/services/labels/promote_service.rb28
-rw-r--r--app/services/labels/transfer_service.rb20
-rw-r--r--app/services/members/authorized_destroy_service.rb30
-rw-r--r--app/services/merge_requests/conflicts/resolve_service.rb8
-rw-r--r--app/services/merge_requests/merge_service.rb4
-rw-r--r--app/services/merge_requests/refresh_service.rb10
-rw-r--r--app/services/notes/quick_actions_service.rb4
-rw-r--r--app/services/tags/create_service.rb4
-rw-r--r--app/validators/dynamic_path_validator.rb2
-rw-r--r--app/views/groups/merge_requests.html.haml3
-rw-r--r--app/views/profiles/show.html.haml107
-rw-r--r--app/views/projects/_find_file_link.html.haml2
-rw-r--r--app/views/projects/blob/_breadcrumb.html.haml35
-rw-r--r--app/views/projects/commits/_commits.html.haml6
-rw-r--r--app/views/projects/commits/show.html.haml10
-rw-r--r--app/views/projects/deploy_keys/_index.html.haml2
-rw-r--r--app/views/projects/deployments/_deployment.html.haml17
-rw-r--r--app/views/projects/edit.html.haml66
-rw-r--r--app/views/projects/hooks/_index.html.haml4
-rw-r--r--app/views/projects/pipelines_settings/_badge.html.haml4
-rw-r--r--app/views/projects/pipelines_settings/_show.html.haml4
-rw-r--r--app/views/projects/project_members/_index.html.haml4
-rw-r--r--app/views/projects/protected_branches/_index.html.haml2
-rw-r--r--app/views/projects/protected_tags/_index.html.haml2
-rw-r--r--app/views/projects/services/_form.html.haml4
-rw-r--r--app/views/projects/services/_index.html.haml4
-rw-r--r--app/views/projects/services/prometheus/_show.html.haml45
-rw-r--r--app/views/projects/settings/ci_cd/show.html.haml1
-rw-r--r--app/views/projects/settings/integrations/show.html.haml1
-rw-r--r--app/views/projects/settings/members/show.html.haml2
-rw-r--r--app/views/projects/tree/_tree_header.html.haml146
-rw-r--r--app/views/projects/triggers/_index.html.haml4
-rw-r--r--app/views/projects/variables/_index.html.haml4
-rw-r--r--app/views/shared/icons/_icon_empty_metrics.svg5
-rw-r--r--app/views/shared/issuable/_nav.html.haml19
-rw-r--r--app/views/shared/notes/_notes_with_form.html.haml13
-rw-r--r--app/workers/merge_worker.rb4
-rw-r--r--app/workers/process_commit_worker.rb8
-rw-r--r--app/workers/project_cache_worker.rb6
-rw-r--r--app/workers/propagate_service_template_worker.rb6
-rw-r--r--app/workers/prune_old_events_worker.rb8
-rw-r--r--app/workers/repository_check/batch_worker.rb8
-rw-r--r--app/workers/update_user_activity_worker.rb4
-rw-r--r--changelogs/unreleased/25102-files-view-button.yml4
-rw-r--r--changelogs/unreleased/28717-support-additional-prometheus-metrics.yml4
-rw-r--r--changelogs/unreleased/32301-filter-archive-project-on-param-present.yml4
-rw-r--r--changelogs/unreleased/33441-supplement_simplified_chinese_translation_of_i18n.yml4
-rw-r--r--changelogs/unreleased/33917-mr-comment-system-note-highlight-don-t-have-the-same-width.yml4
-rw-r--r--changelogs/unreleased/34008-fix-CI_ENVIRONMENT_URL-2.yml4
-rw-r--r--changelogs/unreleased/34010-fix-linking-to-parallel-diff-line-number-creating-gray-box.yml4
-rw-r--r--changelogs/unreleased/dm-unnecessary-top-padding.yml4
-rw-r--r--changelogs/unreleased/doc-gitaly-network.yml4
-rw-r--r--changelogs/unreleased/fix-33259.yml4
-rw-r--r--config/boot.rb12
-rw-r--r--config/initializers/8_metrics.rb4
-rw-r--r--config/initializers/active_record_data_types.rb73
-rw-r--r--config/initializers/active_record_table_definition.rb22
-rw-r--r--config/initializers/bootstrap_form.rb7
-rw-r--r--config/initializers/flipper.rb4
-rw-r--r--config/prometheus/additional_metrics.yml32
-rw-r--r--config/routes/project.rb6
-rw-r--r--config/webpack.config.js5
-rw-r--r--db/migrate/20161124141322_migrate_process_commit_worker_jobs.rb6
-rw-r--r--db/migrate/20161207231620_fixup_environment_name_uniqueness.rb32
-rw-r--r--db/migrate/20161207231626_add_environment_slug.rb8
-rw-r--r--db/migrate/20170316163800_rename_system_namespaces.rb10
-rw-r--r--db/migrate/20170503140202_turn_nested_groups_into_regular_groups_for_mysql.rb14
-rw-r--r--db/migrate/20170526185602_add_stage_id_to_ci_builds.rb8
-rw-r--r--db/post_migrate/20161109150329_fix_project_records_with_invalid_visibility.rb22
-rw-r--r--db/post_migrate/20161221153951_rename_reserved_project_names.rb12
-rw-r--r--db/post_migrate/20170104150317_requeue_pending_delete_projects.rb12
-rw-r--r--db/post_migrate/20170106142508_fill_authorized_projects.rb4
-rw-r--r--db/post_migrate/20170313133418_rename_more_reserved_project_names.rb12
-rw-r--r--db/post_migrate/20170324160416_migrate_user_activities_to_users_last_activity_on.rb10
-rw-r--r--db/post_migrate/20170502070007_enable_auto_cancel_pending_pipelines_for_all.rb2
-rw-r--r--db/post_migrate/20170502101023_cleanup_namespaceless_pending_delete_projects.rb12
-rw-r--r--db/post_migrate/20170508170547_add_head_pipeline_for_each_merge_request.rb10
-rw-r--r--db/post_migrate/20170526185901_remove_stage_id_index_from_builds.rb18
-rw-r--r--db/post_migrate/20170526185921_migrate_build_stage_reference.rb22
-rw-r--r--db/post_migrate/20170526190000_migrate_build_stage_reference_again.rb27
-rw-r--r--db/post_migrate/20170621102400_add_stage_id_index_to_builds.rb21
-rw-r--r--db/schema.rb2
-rw-r--r--doc/administration/gitaly/index.md139
-rw-r--r--doc/administration/high_availability/nfs.md46
-rw-r--r--doc/administration/high_availability/redis_source.md1
-rw-r--r--doc/install/requirements.md6
-rw-r--r--lib/api/access_requests.rb4
-rw-r--r--lib/api/branches.rb8
-rw-r--r--lib/api/entities.rb6
-rw-r--r--lib/api/notes.rb4
-rw-r--r--lib/api/tags.rb16
-rw-r--r--lib/api/users.rb26
-rw-r--r--lib/api/v3/branches.rb8
-rw-r--r--lib/api/v3/entities.rb6
-rw-r--r--lib/api/v3/helpers.rb5
-rw-r--r--lib/api/v3/notes.rb4
-rw-r--r--lib/api/v3/projects.rb2
-rw-r--r--lib/api/v3/tags.rb4
-rw-r--r--lib/api/v3/users.rb22
-rw-r--r--lib/banzai/reference_extractor.rb4
-rw-r--r--lib/banzai/reference_parser/issue_parser.rb4
-rw-r--r--lib/banzai/reference_parser/user_parser.rb4
-rw-r--r--lib/ci/charts.rb26
-rw-r--r--lib/feature.rb2
-rw-r--r--lib/github/import.rb30
-rw-r--r--lib/github/representation/branch.rb14
-rw-r--r--lib/github/representation/pull_request.rb54
-rw-r--r--lib/gitlab/background_migration.rb4
-rw-r--r--lib/gitlab/cache/ci/project_pipeline_status.rb4
-rw-r--r--lib/gitlab/ci/pipeline_duration.rb4
-rw-r--r--lib/gitlab/conflict/file_collection.rb6
-rw-r--r--lib/gitlab/contributions_calendar.rb46
-rw-r--r--lib/gitlab/cycle_analytics/base_query.rb18
-rw-r--r--lib/gitlab/database/median.rb22
-rw-r--r--lib/gitlab/database/migration_helpers.rb30
-rw-r--r--lib/gitlab/database/rename_reserved_paths_migration/v1/rename_base.rb4
-rw-r--r--lib/gitlab/database/rename_reserved_paths_migration/v1/rename_namespaces.rb14
-rw-r--r--lib/gitlab/downtime_check.rb4
-rw-r--r--lib/gitlab/git/blob.rb45
-rw-r--r--lib/gitlab/git/diff.rb2
-rw-r--r--lib/gitlab/gitaly_client/commit.rb20
-rw-r--r--lib/gitlab/gitaly_client/diff_stitcher.rb5
-rw-r--r--lib/gitlab/group_hierarchy.rb26
-rw-r--r--lib/gitlab/highlight.rb4
-rw-r--r--lib/gitlab/ldap/user.rb6
-rw-r--r--lib/gitlab/metrics/influx_db.rb4
-rw-r--r--lib/gitlab/metrics/system.rb8
-rw-r--r--lib/gitlab/other_markup.rb4
-rw-r--r--lib/gitlab/performance_bar/peek_query_tracker.rb4
-rw-r--r--lib/gitlab/project_authorizations/with_nested_groups.rb68
-rw-r--r--lib/gitlab/project_authorizations/without_nested_groups.rb6
-rw-r--r--lib/gitlab/prometheus/additional_metrics_parser.rb34
-rw-r--r--lib/gitlab/prometheus/metric.rb16
-rw-r--r--lib/gitlab/prometheus/metric_group.rb14
-rw-r--r--lib/gitlab/prometheus/parsing_error.rb5
-rw-r--r--lib/gitlab/prometheus/queries/additional_metrics_deployment_query.rb22
-rw-r--r--lib/gitlab/prometheus/queries/additional_metrics_environment_query.rb22
-rw-r--r--lib/gitlab/prometheus/queries/base_query.rb2
-rw-r--r--lib/gitlab/prometheus/queries/deployment_query.rb43
-rw-r--r--lib/gitlab/prometheus/queries/environment_query.rb35
-rw-r--r--lib/gitlab/prometheus/queries/matched_metrics_query.rb80
-rw-r--r--lib/gitlab/prometheus/queries/query_additional_metrics.rb73
-rw-r--r--lib/gitlab/prometheus_client.rb8
-rw-r--r--lib/gitlab/shell.rb4
-rw-r--r--lib/gitlab/sherlock/line_profiler.rb4
-rw-r--r--lib/gitlab/sherlock/query.rb8
-rw-r--r--lib/gitlab/sql/recursive_cte.rb8
-rw-r--r--locale/es/gitlab.po38
-rw-r--r--locale/gitlab.pot32
-rw-r--r--locale/zh_CN/gitlab.po896
-rw-r--r--spec/controllers/admin/users_controller_spec.rb4
-rw-r--r--spec/controllers/application_controller_spec.rb30
-rw-r--r--spec/controllers/import/bitbucket_controller_spec.rb82
-rw-r--r--spec/controllers/import/github_controller_spec.rb8
-rw-r--r--spec/controllers/import/gitlab_controller_spec.rb82
-rw-r--r--spec/controllers/projects/blob_controller_spec.rb4
-rw-r--r--spec/controllers/projects/branches_controller_spec.rb12
-rw-r--r--spec/controllers/projects/commit_controller_spec.rb4
-rw-r--r--spec/controllers/projects/deployments_controller_spec.rb64
-rw-r--r--spec/controllers/projects/environments_controller_spec.rb56
-rw-r--r--spec/controllers/projects/issues_controller_spec.rb12
-rw-r--r--spec/controllers/projects/mattermosts_controller_spec.rb4
-rw-r--r--spec/controllers/projects/merge_requests_controller_spec.rb26
-rw-r--r--spec/controllers/projects/prometheus_controller_spec.rb59
-rw-r--r--spec/controllers/projects/raw_controller_spec.rb8
-rw-r--r--spec/controllers/projects/services_controller_spec.rb4
-rw-r--r--spec/controllers/projects/snippets_controller_spec.rb32
-rw-r--r--spec/controllers/projects/tree_controller_spec.rb12
-rw-r--r--spec/controllers/sent_notifications_controller_spec.rb8
-rw-r--r--spec/controllers/sessions_controller_spec.rb16
-rw-r--r--spec/controllers/snippets_controller_spec.rb32
-rw-r--r--spec/factories/projects.rb4
-rw-r--r--spec/factories/services.rb8
-rw-r--r--spec/features/admin/admin_projects_spec.rb4
-rw-r--r--spec/features/admin/admin_users_spec.rb8
-rw-r--r--spec/features/atom/dashboard_spec.rb4
-rw-r--r--spec/features/atom/issues_spec.rb12
-rw-r--r--spec/features/atom/users_spec.rb4
-rw-r--r--spec/features/dashboard/issues_spec.rb5
-rw-r--r--spec/features/gitlab_flavored_markdown_spec.rb4
-rw-r--r--spec/features/login_spec.rb4
-rw-r--r--spec/features/markdown_spec.rb16
-rw-r--r--spec/features/merge_requests/closes_issues_spec.rb16
-rw-r--r--spec/features/merge_requests/user_posts_notes_spec.rb8
-rw-r--r--spec/features/snippets/notes_on_personal_snippets_spec.rb4
-rw-r--r--spec/fixtures/api/schemas/prometheus/additional_metrics_query_result.json58
-rw-r--r--spec/helpers/application_helper_spec.rb48
-rw-r--r--spec/helpers/broadcast_messages_helper_spec.rb4
-rw-r--r--spec/helpers/commits_helper_spec.rb8
-rw-r--r--spec/helpers/form_helper_spec.rb12
-rw-r--r--spec/helpers/groups_helper_spec.rb4
-rw-r--r--spec/helpers/import_helper_spec.rb20
-rw-r--r--spec/helpers/issuables_helper_spec.rb40
-rw-r--r--spec/helpers/issues_helper_spec.rb4
-rw-r--r--spec/helpers/labels_helper_spec.rb4
-rw-r--r--spec/helpers/markup_helper_spec.rb16
-rw-r--r--spec/helpers/merge_requests_helper_spec.rb4
-rw-r--r--spec/helpers/page_layout_helper_spec.rb4
-rw-r--r--spec/helpers/preferences_helper_spec.rb16
-rw-r--r--spec/javascripts/boards/board_new_issue_spec.js203
-rw-r--r--spec/javascripts/boards/list_spec.js37
-rw-r--r--spec/javascripts/filtered_search/filtered_search_manager_spec.js61
-rw-r--r--spec/javascripts/fixtures/merge_requests.rb13
-rw-r--r--spec/javascripts/fixtures/prometheus_service.rb30
-rw-r--r--spec/javascripts/groups/groups_spec.js24
-rw-r--r--spec/javascripts/issue_show/components/app_spec.js85
-rw-r--r--spec/javascripts/merge_request_notes_spec.js2
-rw-r--r--spec/javascripts/merge_request_tabs_spec.js138
-rw-r--r--spec/javascripts/prometheus_metrics/mock_data.js41
-rw-r--r--spec/javascripts/prometheus_metrics/prometheus_metrics_spec.js158
-rw-r--r--spec/javascripts/test_bundle.js17
-rw-r--r--spec/javascripts/vue_mr_widget/components/mr_widget_related_links_spec.js246
-rw-r--r--spec/javascripts/vue_mr_widget/mr_widget_options_spec.js7
-rw-r--r--spec/javascripts/vue_mr_widget/stores/mr_widget_store_spec.js12
-rw-r--r--spec/lib/banzai/cross_project_reference_spec.rb4
-rw-r--r--spec/lib/banzai/filter/abstract_reference_filter_spec.rb28
-rw-r--r--spec/lib/banzai/filter/commit_range_reference_filter_spec.rb36
-rw-r--r--spec/lib/banzai/filter/commit_reference_filter_spec.rb8
-rw-r--r--spec/lib/banzai/filter/external_issue_reference_filter_spec.rb4
-rw-r--r--spec/lib/banzai/filter/issue_reference_filter_spec.rb70
-rw-r--r--spec/lib/banzai/filter/label_reference_filter_spec.rb76
-rw-r--r--spec/lib/banzai/filter/merge_request_reference_filter_spec.rb20
-rw-r--r--spec/lib/banzai/filter/milestone_reference_filter_spec.rb68
-rw-r--r--spec/lib/banzai/filter/redactor_filter_spec.rb6
-rw-r--r--spec/lib/banzai/filter/reference_filter_spec.rb4
-rw-r--r--spec/lib/banzai/filter/relative_link_filter_spec.rb48
-rw-r--r--spec/lib/banzai/filter/sanitization_filter_spec.rb4
-rw-r--r--spec/lib/banzai/filter/snippet_reference_filter_spec.rb20
-rw-r--r--spec/lib/banzai/filter/upload_link_filter_spec.rb24
-rw-r--r--spec/lib/banzai/note_renderer_spec.rb12
-rw-r--r--spec/lib/banzai/redactor_spec.rb22
-rw-r--r--spec/lib/banzai/reference_parser/base_parser_spec.rb140
-rw-r--r--spec/lib/banzai/reference_parser/commit_parser_spec.rb32
-rw-r--r--spec/lib/banzai/reference_parser/commit_range_parser_spec.rb24
-rw-r--r--spec/lib/banzai/reference_parser/issue_parser_spec.rb12
-rw-r--r--spec/lib/banzai/reference_parser/user_parser_spec.rb24
-rw-r--r--spec/lib/container_registry/blob_spec.rb10
-rw-r--r--spec/lib/container_registry/client_spec.rb22
-rw-r--r--spec/lib/container_registry/tag_spec.rb28
-rw-r--r--spec/lib/extracts_path_spec.rb4
-rw-r--r--spec/lib/feature_spec.rb8
-rw-r--r--spec/lib/gitlab/background_migration_spec.rb18
-rw-r--r--spec/lib/gitlab/bitbucket_import/importer_spec.rb8
-rw-r--r--spec/lib/gitlab/cache/ci/project_pipeline_status_spec.rb16
-rw-r--r--spec/lib/gitlab/ci/build/artifacts/metadata_spec.rb12
-rw-r--r--spec/lib/gitlab/closing_issue_extractor_spec.rb32
-rw-r--r--spec/lib/gitlab/conflict/file_spec.rb24
-rw-r--r--spec/lib/gitlab/conflict/parser_spec.rb64
-rw-r--r--spec/lib/gitlab/data_builder/push_spec.rb4
-rw-r--r--spec/lib/gitlab/database/migration_helpers_spec.rb212
-rw-r--r--spec/lib/gitlab/database/rename_reserved_paths_migration/v1/rename_namespaces_spec.rb34
-rw-r--r--spec/lib/gitlab/database/rename_reserved_paths_migration/v1/rename_projects_spec.rb30
-rw-r--r--spec/lib/gitlab/database/rename_reserved_paths_migration/v1_spec.rb26
-rw-r--r--spec/lib/gitlab/database_spec.rb8
-rw-r--r--spec/lib/gitlab/downtime_check_spec.rb32
-rw-r--r--spec/lib/gitlab/email/reply_parser_spec.rb36
-rw-r--r--spec/lib/gitlab/etag_caching/middleware_spec.rb4
-rw-r--r--spec/lib/gitlab/file_detector_spec.rb8
-rw-r--r--spec/lib/gitlab/git/attributes_spec.rb44
-rw-r--r--spec/lib/gitlab/git/blob_spec.rb18
-rw-r--r--spec/lib/gitlab/git/branch_spec.rb4
-rw-r--r--spec/lib/gitlab/git/diff_collection_spec.rb16
-rw-r--r--spec/lib/gitlab/git/diff_spec.rb16
-rw-r--r--spec/lib/gitlab/git/repository_spec.rb40
-rw-r--r--spec/lib/gitlab/gitaly_client/notifications_spec.rb4
-rw-r--r--spec/lib/gitlab/gitaly_client/ref_spec.rb40
-rw-r--r--spec/lib/gitlab/gitlab_import/importer_spec.rb4
-rw-r--r--spec/lib/gitlab/highlight_spec.rb4
-rw-r--r--spec/lib/gitlab/identifier_spec.rb12
-rw-r--r--spec/lib/gitlab/job_waiter_spec.rb10
-rw-r--r--spec/lib/gitlab/ldap/authentication_spec.rb12
-rw-r--r--spec/lib/gitlab/ldap/user_spec.rb4
-rw-r--r--spec/lib/gitlab/metrics/instrumentation_spec.rb32
-rw-r--r--spec/lib/gitlab/metrics/rack_middleware_spec.rb12
-rw-r--r--spec/lib/gitlab/metrics/sampler_spec.rb54
-rw-r--r--spec/lib/gitlab/metrics/sidekiq_middleware_spec.rb36
-rw-r--r--spec/lib/gitlab/metrics/subscribers/action_view_spec.rb8
-rw-r--r--spec/lib/gitlab/metrics/subscribers/active_record_spec.rb18
-rw-r--r--spec/lib/gitlab/metrics/subscribers/rails_cache_spec.rb80
-rw-r--r--spec/lib/gitlab/metrics/transaction_spec.rb28
-rw-r--r--spec/lib/gitlab/metrics_spec.rb48
-rw-r--r--spec/lib/gitlab/project_authorizations_spec.rb4
-rw-r--r--spec/lib/gitlab/prometheus/additional_metrics_parser_spec.rb246
-rw-r--r--spec/lib/gitlab/prometheus/queries/additional_metrics_deployment_query_spec.rb25
-rw-r--r--spec/lib/gitlab/prometheus/queries/additional_metrics_environment_query_spec.rb21
-rw-r--r--spec/lib/gitlab/prometheus/queries/matched_metrics_query_spec.rb134
-rw-r--r--spec/lib/gitlab/prometheus_client_spec.rb30
-rw-r--r--spec/lib/gitlab/route_map_spec.rb24
-rw-r--r--spec/lib/gitlab/sherlock/file_sample_spec.rb4
-rw-r--r--spec/lib/gitlab/sherlock/line_profiler_spec.rb6
-rw-r--r--spec/lib/gitlab/sherlock/middleware_spec.rb4
-rw-r--r--spec/lib/gitlab/sherlock/query_spec.rb4
-rw-r--r--spec/lib/gitlab/sherlock/transaction_spec.rb28
-rw-r--r--spec/lib/gitlab/sidekiq_status/client_middleware_spec.rb4
-rw-r--r--spec/lib/gitlab/sidekiq_status/server_middleware_spec.rb4
-rw-r--r--spec/lib/gitlab/url_builder_spec.rb4
-rw-r--r--spec/lib/gitlab/view/presenter/delegated_spec.rb4
-rw-r--r--spec/lib/gitlab/visibility_level_spec.rb22
-rw-r--r--spec/lib/gitlab/workhorse_spec.rb1
-rw-r--r--spec/lib/mattermost/command_spec.rb16
-rw-r--r--spec/lib/mattermost/session_spec.rb14
-rw-r--r--spec/lib/mattermost/team_spec.rb12
-rw-r--r--spec/mailers/abuse_report_mailer_spec.rb4
-rw-r--r--spec/migrations/migrate_build_stage_reference_again_spec.rb (renamed from spec/migrations/migrate_build_stage_reference_spec.rb)4
-rw-r--r--spec/migrations/migrate_process_commit_worker_jobs_spec.rb54
-rw-r--r--spec/migrations/turn_nested_groups_into_regular_groups_for_mysql_spec.rb18
-rw-r--r--spec/models/ability_spec.rb56
-rw-r--r--spec/models/abuse_report_spec.rb4
-rw-r--r--spec/models/ci/build_spec.rb52
-rw-r--r--spec/models/ci/pipeline_schedule_spec.rb12
-rw-r--r--spec/models/ci/pipeline_spec.rb16
-rw-r--r--spec/models/ci/variable_spec.rb4
-rw-r--r--spec/models/commit_range_spec.rb6
-rw-r--r--spec/models/concerns/case_sensitivity_spec.rb148
-rw-r--r--spec/models/concerns/has_status_spec.rb4
-rw-r--r--spec/models/concerns/issuable_spec.rb28
-rw-r--r--spec/models/concerns/resolvable_discussion_spec.rb12
-rw-r--r--spec/models/deployment_spec.rb32
-rw-r--r--spec/models/environment_spec.rb93
-rw-r--r--spec/models/event_spec.rb4
-rw-r--r--spec/models/group_spec.rb4
-rw-r--r--spec/models/issue_collection_spec.rb4
-rw-r--r--spec/models/issue_spec.rb20
-rw-r--r--spec/models/key_spec.rb8
-rw-r--r--spec/models/label_spec.rb4
-rw-r--r--spec/models/member_spec.rb8
-rw-r--r--spec/models/members/group_member_spec.rb8
-rw-r--r--spec/models/merge_request_spec.rb50
-rw-r--r--spec/models/milestone_spec.rb8
-rw-r--r--spec/models/namespace_spec.rb4
-rw-r--r--spec/models/note_spec.rb16
-rw-r--r--spec/models/project_authorization_spec.rb4
-rw-r--r--spec/models/project_services/mattermost_slash_commands_service_spec.rb24
-rw-r--r--spec/models/project_services/prometheus_service_spec.rb5
-rw-r--r--spec/models/project_spec.rb64
-rw-r--r--spec/models/project_team_spec.rb4
-rw-r--r--spec/models/project_wiki_spec.rb24
-rw-r--r--spec/models/repository_spec.rb166
-rw-r--r--spec/models/upload_spec.rb4
-rw-r--r--spec/models/user_spec.rb8
-rw-r--r--spec/models/wiki_page_spec.rb4
-rw-r--r--spec/policies/project_policy_spec.rb4
-rw-r--r--spec/presenters/ci/build_presenter_spec.rb4
-rw-r--r--spec/requests/api/files_spec.rb4
-rw-r--r--spec/requests/api/groups_spec.rb4
-rw-r--r--spec/requests/api/merge_requests_spec.rb4
-rw-r--r--spec/requests/api/notes_spec.rb4
-rw-r--r--spec/requests/api/project_snippets_spec.rb32
-rw-r--r--spec/requests/api/projects_spec.rb36
-rw-r--r--spec/requests/api/runner_spec.rb4
-rw-r--r--spec/requests/api/snippets_spec.rb32
-rw-r--r--spec/requests/api/users_spec.rb32
-rw-r--r--spec/requests/api/v3/files_spec.rb4
-rw-r--r--spec/requests/api/v3/groups_spec.rb4
-rw-r--r--spec/requests/api/v3/merge_requests_spec.rb4
-rw-r--r--spec/requests/api/v3/notes_spec.rb4
-rw-r--r--spec/requests/api/v3/project_snippets_spec.rb32
-rw-r--r--spec/requests/api/v3/projects_spec.rb62
-rw-r--r--spec/requests/api/v3/snippets_spec.rb12
-rw-r--r--spec/requests/ci/api/builds_spec.rb8
-rw-r--r--spec/requests/git_http_spec.rb4
-rw-r--r--spec/rubocop/cop/migration/update_column_in_batches_spec.rb4
-rw-r--r--spec/services/create_deployment_service_spec.rb55
-rw-r--r--spec/services/files/update_service_spec.rb4
-rw-r--r--spec/services/git_push_service_spec.rb46
-rw-r--r--spec/services/issues/close_service_spec.rb16
-rw-r--r--spec/services/labels/promote_service_spec.rb6
-rw-r--r--spec/services/members/destroy_service_spec.rb4
-rw-r--r--spec/services/merge_requests/close_service_spec.rb4
-rw-r--r--spec/services/merge_requests/conflicts/resolve_service_spec.rb29
-rw-r--r--spec/services/merge_requests/create_service_spec.rb6
-rw-r--r--spec/services/merge_requests/merge_service_spec.rb6
-rw-r--r--spec/services/merge_requests/refresh_service_spec.rb16
-rw-r--r--spec/services/merge_requests/reopen_service_spec.rb4
-rw-r--r--spec/services/merge_requests/update_service_spec.rb8
-rw-r--r--spec/services/projects/housekeeping_service_spec.rb12
-rw-r--r--spec/services/projects/import_service_spec.rb8
-rw-r--r--spec/services/projects/propagate_service_template_spec.rb12
-rw-r--r--spec/services/projects/transfer_service_spec.rb14
-rw-r--r--spec/services/projects/unlink_fork_service_spec.rb6
-rw-r--r--spec/services/submit_usage_ping_service_spec.rb4
-rw-r--r--spec/services/system_note_service_spec.rb52
-rw-r--r--spec/services/tags/create_service_spec.rb12
-rw-r--r--spec/services/user_project_access_changed_service_spec.rb4
-rw-r--r--spec/services/users/activity_service_spec.rb6
-rw-r--r--spec/services/users/refresh_authorized_projects_service_spec.rb24
-rw-r--r--spec/support/api/schema_matcher.rb14
-rw-r--r--spec/support/capybara.rb5
-rw-r--r--spec/support/controllers/githubish_import_controller_shared_examples.rb100
-rw-r--r--spec/support/filtered_search_helpers.rb3
-rw-r--r--spec/support/mentionable_shared_examples.rb12
-rw-r--r--spec/support/prometheus/additional_metrics_shared_examples.rb101
-rw-r--r--spec/support/prometheus/metric_builders.rb27
-rw-r--r--spec/support/prometheus_helpers.rb59
-rw-r--r--spec/support/reactive_caching_helpers.rb6
-rw-r--r--spec/support/services_shared_context.rb6
-rw-r--r--spec/support/slack_mattermost_notifications_shared_examples.rb42
-rw-r--r--spec/support/stub_configuration.rb4
-rw-r--r--spec/support/stub_gitlab_calls.rb38
-rw-r--r--spec/support/test_env.rb8
-rw-r--r--spec/support/update_invalid_issuable.rb4
-rw-r--r--spec/support/user_activities_helpers.rb4
-rw-r--r--spec/tasks/gitlab/backup_rake_spec.rb20
-rw-r--r--spec/tasks/gitlab/gitaly_rake_spec.rb12
-rw-r--r--spec/tasks/gitlab/task_helpers_spec.rb16
-rw-r--r--spec/tasks/gitlab/workhorse_rake_spec.rb8
-rw-r--r--spec/validators/dynamic_path_validator_spec.rb9
-rw-r--r--spec/views/devise/shared/_signin_box.html.haml_spec.rb4
-rw-r--r--spec/workers/background_migration_worker_spec.rb6
-rw-r--r--spec/workers/delete_user_worker_spec.rb8
-rw-r--r--spec/workers/every_sidekiq_worker_spec.rb10
-rw-r--r--spec/workers/expire_pipeline_cache_worker_spec.rb4
-rw-r--r--spec/workers/git_garbage_collect_worker_spec.rb4
-rw-r--r--spec/workers/new_note_worker_spec.rb4
-rw-r--r--spec/workers/post_receive_spec.rb6
-rw-r--r--spec/workers/process_commit_worker_spec.rb24
-rw-r--r--spec/workers/project_cache_worker_spec.rb28
-rw-r--r--spec/workers/propagate_service_template_worker_spec.rb4
-rw-r--r--spec/workers/repository_fork_worker_spec.rb8
-rw-r--r--spec/workers/repository_import_worker_spec.rb4
541 files changed, 9148 insertions, 5779 deletions
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index f0c266485b6..76a95ad6e0a 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -461,6 +461,7 @@ karma:
- coverage-javascript/
codeclimate:
+ <<: *except-docs
before_script: []
image: docker:latest
stage: test
diff --git a/.gitlab/issue_templates/Feature Proposal.md b/.gitlab/issue_templates/Feature Proposal.md
index 85ca1bca623..1278061a410 100644
--- a/.gitlab/issue_templates/Feature Proposal.md
+++ b/.gitlab/issue_templates/Feature Proposal.md
@@ -27,12 +27,24 @@ Please remove this notice if you're confident your issue isn't a duplicate.
### Documentation blurb
-(Write the start of the documentation of this feature here, include:
+#### Overview
-1. Why should someone use it; what's the underlying problem.
-2. What is the solution.
-3. How does someone use this
+What is it?
+Why should someone use this feature?
+What is the underlying (business) problem?
+How do you use this feature?
-During implementation, this can then be copied and used as a starter for the documentation.)
+#### Use cases
-/label ~"feature proposal"
+Who is this for? Provide one or more use cases.
+
+### Feature checklist
+
+Make sure these are completed before closing the issue,
+with a link to the relevant commit.
+
+- [ ] [Feature assurance](https://about.gitlab.com/handbook/product/#feature-assurance)
+- [ ] Documentation
+- [ ] Added to [features.yml](https://gitlab.com/gitlab-com/www-gitlab-com/blob/master/data/features.yml)
+
+/label ~"feature proposal" \ No newline at end of file
diff --git a/.rubocop.yml b/.rubocop.yml
index 4537e710dd4..32ec60f540b 100644
--- a/.rubocop.yml
+++ b/.rubocop.yml
@@ -164,6 +164,11 @@ Style/DefWithParentheses:
Style/Documentation:
Enabled: false
+# Multi-line method chaining should be done with leading dots.
+Style/DotPosition:
+ Enabled: true
+ EnforcedStyle: leading
+
# This cop checks for uses of double negation (!!) to convert something
# to a boolean value. As this is both cryptic and usually redundant, it
# should be avoided.
diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml
index e2d9c37479d..5ab4692dd60 100644
--- a/.rubocop_todo.yml
+++ b/.rubocop_todo.yml
@@ -88,13 +88,6 @@ Security/YAMLLoad:
Style/BarePercentLiterals:
Enabled: false
-# Offense count: 1403
-# Cop supports --auto-correct.
-# Configuration parameters: EnforcedStyle, SupportedStyles.
-# SupportedStyles: leading, trailing
-Style/DotPosition:
- Enabled: false
-
# Offense count: 5
# Cop supports --auto-correct.
Style/EachWithObject:
diff --git a/GITALY_SERVER_VERSION b/GITALY_SERVER_VERSION
index bc859cbd6d9..54d1a4f2a4a 100644
--- a/GITALY_SERVER_VERSION
+++ b/GITALY_SERVER_VERSION
@@ -1 +1 @@
-0.11.2
+0.13.0
diff --git a/GITLAB_WORKHORSE_VERSION b/GITLAB_WORKHORSE_VERSION
index 3e3c2f1e5ed..ccbccc3dc62 100644
--- a/GITLAB_WORKHORSE_VERSION
+++ b/GITLAB_WORKHORSE_VERSION
@@ -1 +1 @@
-2.1.1
+2.2.0
diff --git a/Gemfile b/Gemfile
index 61c70ce6b8a..6c53bfe6efe 100644
--- a/Gemfile
+++ b/Gemfile
@@ -2,7 +2,6 @@ source 'https://rubygems.org'
gem 'rails', '4.2.8'
gem 'rails-deprecated_sanitizer', '~> 1.0.3'
-gem 'bootsnap', '~> 1.0.0'
# Responders respond_to and respond_with
gem 'responders', '~> 2.0'
@@ -123,6 +122,7 @@ gem 'asciidoctor', '~> 1.5.2'
gem 'asciidoctor-plantuml', '0.0.7'
gem 'rouge', '~> 2.0'
gem 'truncato', '~> 0.7.8'
+gem 'bootstrap_form', '~> 2.7.0'
# See https://groups.google.com/forum/#!topic/ruby-security-ann/aSbgDiwb24s
# and https://groups.google.com/forum/#!topic/ruby-security-ann/Dy7YiKb_pMM
@@ -384,7 +384,7 @@ gem 'vmstat', '~> 2.3.0'
gem 'sys-filesystem', '~> 1.1.6'
# Gitaly GRPC client
-gem 'gitaly', '~> 0.8.0'
+gem 'gitaly', '~> 0.9.0'
gem 'toml-rb', '~> 0.3.15', require: false
diff --git a/Gemfile.lock b/Gemfile.lock
index 7ca330b6a59..4715363c5b8 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -83,11 +83,10 @@ GEM
bindata (2.3.5)
binding_of_caller (0.7.2)
debug_inspector (>= 0.0.1)
- bootsnap (1.0.0)
- msgpack (~> 1.0)
bootstrap-sass (3.3.6)
autoprefixer-rails (>= 5.2.1)
sass (>= 3.3.4)
+ bootstrap_form (2.7.0)
brakeman (3.6.1)
browser (2.2.0)
builder (3.2.3)
@@ -277,7 +276,7 @@ GEM
po_to_json (>= 1.0.0)
rails (>= 3.2.0)
gherkin-ruby (0.3.2)
- gitaly (0.8.0)
+ gitaly (0.9.0)
google-protobuf (~> 3.1)
grpc (~> 1.0)
github-linguist (4.7.6)
@@ -464,7 +463,6 @@ GEM
minitest (5.7.0)
mmap2 (2.2.6)
mousetrap-rails (1.4.6)
- msgpack (1.1.0)
multi_json (1.12.1)
multi_xml (0.6.0)
multipart-post (2.0.0)
@@ -928,8 +926,8 @@ DEPENDENCIES
benchmark-ips (~> 2.3.0)
better_errors (~> 2.1.0)
binding_of_caller (~> 0.7.2)
- bootsnap (~> 1.0.0)
bootstrap-sass (~> 3.3.0)
+ bootstrap_form (~> 2.7.0)
brakeman (~> 3.6.0)
browser (~> 2.2)
bullet (~> 5.5.0)
@@ -977,7 +975,7 @@ DEPENDENCIES
gettext (~> 3.2.2)
gettext_i18n_rails (~> 1.8.0)
gettext_i18n_rails_js (~> 1.2.0)
- gitaly (~> 0.8.0)
+ gitaly (~> 0.9.0)
github-linguist (~> 4.7.0)
gitlab-flowdock-git-hook (~> 1.0.1)
gitlab-markup (~> 1.5.1)
diff --git a/app/assets/javascripts/awards_handler.js b/app/assets/javascripts/awards_handler.js
index adb45b0606d..ebe722061d7 100644
--- a/app/assets/javascripts/awards_handler.js
+++ b/app/assets/javascripts/awards_handler.js
@@ -1,3 +1,4 @@
+/* eslint-disable class-methods-use-this */
/* global Flash */
import Cookies from 'js-cookie';
@@ -68,141 +69,140 @@ function renderCategory(name, emojiList, opts = {}) {
`;
}
-function AwardsHandler() {
- this.eventListeners = [];
- this.aliases = emojiAliases;
- // If the user shows intent let's pre-build the menu
- this.registerEventListener('one', $(document), 'mouseenter focus', '.js-add-award', 'mouseenter focus', () => {
- const $menu = $('.emoji-menu');
- if ($menu.length === 0) {
- requestAnimationFrame(() => {
- this.createEmojiMenu();
- });
- }
- // Prebuild the categoryMap
- categoryMap = categoryMap || buildCategoryMap();
- });
- this.registerEventListener('on', $(document), 'click', '.js-add-award', (e) => {
- e.stopPropagation();
- e.preventDefault();
- this.showEmojiMenu($(e.currentTarget));
- });
+export default class AwardsHandler {
+ constructor() {
+ this.eventListeners = [];
+ this.aliases = emojiAliases;
+ // If the user shows intent let's pre-build the menu
+ this.registerEventListener('one', $(document), 'mouseenter focus', '.js-add-award', 'mouseenter focus', () => {
+ const $menu = $('.emoji-menu');
+ if ($menu.length === 0) {
+ requestAnimationFrame(() => {
+ this.createEmojiMenu();
+ });
+ }
+ // Prebuild the categoryMap
+ categoryMap = categoryMap || buildCategoryMap();
+ });
+ this.registerEventListener('on', $(document), 'click', '.js-add-award', (e) => {
+ e.stopPropagation();
+ e.preventDefault();
+ this.showEmojiMenu($(e.currentTarget));
+ });
- this.registerEventListener('on', $('html'), 'click', (e) => {
- const $target = $(e.target);
- if (!$target.closest('.emoji-menu-content').length) {
- $('.js-awards-block.current').removeClass('current');
- }
- if (!$target.closest('.emoji-menu').length) {
- if ($('.emoji-menu').is(':visible')) {
- $('.js-add-award.is-active').removeClass('is-active');
- $('.emoji-menu').removeClass('is-visible');
+ this.registerEventListener('on', $('html'), 'click', (e) => {
+ const $target = $(e.target);
+ if (!$target.closest('.emoji-menu-content').length) {
+ $('.js-awards-block.current').removeClass('current');
}
- }
- });
- this.registerEventListener('on', $(document), 'click', '.js-emoji-btn', (e) => {
- e.preventDefault();
- const $target = $(e.currentTarget);
- const $glEmojiElement = $target.find('gl-emoji');
- const $spriteIconElement = $target.find('.icon');
- const emoji = ($glEmojiElement.length ? $glEmojiElement : $spriteIconElement).data('name');
-
- $target.closest('.js-awards-block').addClass('current');
- this.addAward(this.getVotesBlock(), this.getAwardUrl(), emoji);
- });
-}
+ if (!$target.closest('.emoji-menu').length) {
+ if ($('.emoji-menu').is(':visible')) {
+ $('.js-add-award.is-active').removeClass('is-active');
+ $('.emoji-menu').removeClass('is-visible');
+ }
+ }
+ });
+ this.registerEventListener('on', $(document), 'click', '.js-emoji-btn', (e) => {
+ e.preventDefault();
+ const $target = $(e.currentTarget);
+ const $glEmojiElement = $target.find('gl-emoji');
+ const $spriteIconElement = $target.find('.icon');
+ const emoji = ($glEmojiElement.length ? $glEmojiElement : $spriteIconElement).data('name');
+
+ $target.closest('.js-awards-block').addClass('current');
+ this.addAward(this.getVotesBlock(), this.getAwardUrl(), emoji);
+ });
+ }
-AwardsHandler.prototype.registerEventListener = function registerEventListener(method = 'on', element, ...args) {
- element[method].call(element, ...args);
- this.eventListeners.push({
- element,
- args,
- });
-};
+ registerEventListener(method = 'on', element, ...args) {
+ element[method].call(element, ...args);
+ this.eventListeners.push({
+ element,
+ args,
+ });
+ }
-AwardsHandler.prototype.showEmojiMenu = function showEmojiMenu($addBtn) {
- if ($addBtn.hasClass('js-note-emoji')) {
- $addBtn.closest('.note').find('.js-awards-block').addClass('current');
- } else {
- $addBtn.closest('.js-awards-block').addClass('current');
- }
-
- const $menu = $('.emoji-menu');
- const $thumbsBtn = $menu.find('[data-name="thumbsup"], [data-name="thumbsdown"]').parent();
- const $userAuthored = this.isUserAuthored($addBtn);
- if ($menu.length) {
- if ($menu.is('.is-visible')) {
- $addBtn.removeClass('is-active');
- $menu.removeClass('is-visible');
- $('.js-emoji-menu-search').blur();
+ showEmojiMenu($addBtn) {
+ if ($addBtn.hasClass('js-note-emoji')) {
+ $addBtn.closest('.note').find('.js-awards-block').addClass('current');
} else {
- $addBtn.addClass('is-active');
- this.positionMenu($menu, $addBtn);
- $menu.addClass('is-visible');
- $('.js-emoji-menu-search').focus();
+ $addBtn.closest('.js-awards-block').addClass('current');
}
- } else {
- $addBtn.addClass('is-loading is-active');
- this.createEmojiMenu(() => {
- const $createdMenu = $('.emoji-menu');
- $addBtn.removeClass('is-loading');
- this.positionMenu($createdMenu, $addBtn);
- return setTimeout(() => {
- $createdMenu.addClass('is-visible');
+
+ const $menu = $('.emoji-menu');
+ const $thumbsBtn = $menu.find('[data-name="thumbsup"], [data-name="thumbsdown"]').parent();
+ const $userAuthored = this.isUserAuthored($addBtn);
+ if ($menu.length) {
+ if ($menu.is('.is-visible')) {
+ $addBtn.removeClass('is-active');
+ $menu.removeClass('is-visible');
+ $('.js-emoji-menu-search').blur();
+ } else {
+ $addBtn.addClass('is-active');
+ this.positionMenu($menu, $addBtn);
+ $menu.addClass('is-visible');
$('.js-emoji-menu-search').focus();
- }, 200);
- });
+ }
+ } else {
+ $addBtn.addClass('is-loading is-active');
+ this.createEmojiMenu(() => {
+ const $createdMenu = $('.emoji-menu');
+ $addBtn.removeClass('is-loading');
+ this.positionMenu($createdMenu, $addBtn);
+ return setTimeout(() => {
+ $createdMenu.addClass('is-visible');
+ $('.js-emoji-menu-search').focus();
+ }, 200);
+ });
+ }
+
+ $thumbsBtn.toggleClass('disabled', $userAuthored);
}
- $thumbsBtn.toggleClass('disabled', $userAuthored);
-};
+ // Create the emoji menu with the first category of emojis.
+ // Then render the remaining categories of emojis one by one to avoid jank.
+ createEmojiMenu(callback) {
+ if (this.isCreatingEmojiMenu) {
+ return;
+ }
+ this.isCreatingEmojiMenu = true;
-// Create the emoji menu with the first category of emojis.
-// Then render the remaining categories of emojis one by one to avoid jank.
-AwardsHandler.prototype.createEmojiMenu = function createEmojiMenu(callback) {
- if (this.isCreatingEmojiMenu) {
- return;
- }
- this.isCreatingEmojiMenu = true;
-
- // Render the first category
- categoryMap = categoryMap || buildCategoryMap();
- const categoryNameKey = Object.keys(categoryMap)[0];
- const emojisInCategory = categoryMap[categoryNameKey];
- const firstCategory = renderCategory(categoryLabelMap[categoryNameKey], emojisInCategory);
-
- // Render the frequently used
- const frequentlyUsedEmojis = this.getFrequentlyUsedEmojis();
- let frequentlyUsedCatgegory = '';
- if (frequentlyUsedEmojis.length > 0) {
- frequentlyUsedCatgegory = renderCategory('Frequently used', frequentlyUsedEmojis, {
- menuListClass: 'frequent-emojis',
- });
- }
+ // Render the first category
+ categoryMap = categoryMap || buildCategoryMap();
+ const categoryNameKey = Object.keys(categoryMap)[0];
+ const emojisInCategory = categoryMap[categoryNameKey];
+ const firstCategory = renderCategory(categoryLabelMap[categoryNameKey], emojisInCategory);
+
+ // Render the frequently used
+ const frequentlyUsedEmojis = this.getFrequentlyUsedEmojis();
+ let frequentlyUsedCatgegory = '';
+ if (frequentlyUsedEmojis.length > 0) {
+ frequentlyUsedCatgegory = renderCategory('Frequently used', frequentlyUsedEmojis, {
+ menuListClass: 'frequent-emojis',
+ });
+ }
- const emojiMenuMarkup = `
- <div class="emoji-menu">
- <input type="text" name="emoji-menu-search" value="" class="js-emoji-menu-search emoji-search search-input form-control" placeholder="Search emoji" />
+ const emojiMenuMarkup = `
+ <div class="emoji-menu">
+ <input type="text" name="emoji-menu-search" value="" class="js-emoji-menu-search emoji-search search-input form-control" placeholder="Search emoji" />
- <div class="emoji-menu-content">
- ${frequentlyUsedCatgegory}
- ${firstCategory}
+ <div class="emoji-menu-content">
+ ${frequentlyUsedCatgegory}
+ ${firstCategory}
+ </div>
</div>
- </div>
- `;
+ `;
- document.body.insertAdjacentHTML('beforeend', emojiMenuMarkup);
+ document.body.insertAdjacentHTML('beforeend', emojiMenuMarkup);
- this.addRemainingEmojiMenuCategories();
- this.setupSearch();
- if (callback) {
- callback();
+ this.addRemainingEmojiMenuCategories();
+ this.setupSearch();
+ if (callback) {
+ callback();
+ }
}
-};
-AwardsHandler
- .prototype
- .addRemainingEmojiMenuCategories = function addRemainingEmojiMenuCategories() {
+ addRemainingEmojiMenuCategories() {
if (this.isAddingRemainingEmojiMenuCategories) {
return;
}
@@ -243,176 +243,174 @@ AwardsHandler
emojiContentElement.insertAdjacentHTML('beforeend', '<p>We encountered an error while adding the remaining categories</p>');
throw new Error(`Error occurred in addRemainingEmojiMenuCategories: ${err.message}`);
});
- };
-
-AwardsHandler.prototype.positionMenu = function positionMenu($menu, $addBtn) {
- const position = $addBtn.data('position');
- // The menu could potentially be off-screen or in a hidden overflow element
- // So we position the element absolute in the body
- const css = {
- top: `${$addBtn.offset().top + $addBtn.outerHeight()}px`,
- };
- if (position === 'right') {
- css.left = `${($addBtn.offset().left - $menu.outerWidth()) + 20}px`;
- $menu.addClass('is-aligned-right');
- } else {
- css.left = `${$addBtn.offset().left}px`;
- $menu.removeClass('is-aligned-right');
- }
- return $menu.css(css);
-};
-
-AwardsHandler.prototype.addAward = function addAward(
- votesBlock,
- awardUrl,
- emoji,
- checkMutuality,
- callback,
-) {
- const normalizedEmoji = this.normalizeEmojiName(emoji);
- const $emojiButton = this.findEmojiIcon(votesBlock, normalizedEmoji).parent();
- this.postEmoji($emojiButton, awardUrl, normalizedEmoji, () => {
- this.addAwardToEmojiBar(votesBlock, normalizedEmoji, checkMutuality);
- return typeof callback === 'function' ? callback() : undefined;
- });
- $('.emoji-menu').removeClass('is-visible');
- $('.js-add-award.is-active').removeClass('is-active');
-};
+ }
-AwardsHandler.prototype.addAwardToEmojiBar = function addAwardToEmojiBar(
- votesBlock,
- emoji,
- checkForMutuality,
-) {
- if (checkForMutuality || checkForMutuality === null) {
- this.checkMutuality(votesBlock, emoji);
- }
- this.addEmojiToFrequentlyUsedList(emoji);
- const normalizedEmoji = this.normalizeEmojiName(emoji);
- const $emojiButton = this.findEmojiIcon(votesBlock, normalizedEmoji).parent();
- if ($emojiButton.length > 0) {
- if (this.isActive($emojiButton)) {
- this.decrementCounter($emojiButton, normalizedEmoji);
+ positionMenu($menu, $addBtn) {
+ const position = $addBtn.data('position');
+ // The menu could potentially be off-screen or in a hidden overflow element
+ // So we position the element absolute in the body
+ const css = {
+ top: `${$addBtn.offset().top + $addBtn.outerHeight()}px`,
+ };
+ if (position === 'right') {
+ css.left = `${($addBtn.offset().left - $menu.outerWidth()) + 20}px`;
+ $menu.addClass('is-aligned-right');
} else {
- const counter = $emojiButton.find('.js-counter');
- counter.text(parseInt(counter.text(), 10) + 1);
- $emojiButton.addClass('active');
- this.addYouToUserList(votesBlock, normalizedEmoji);
- this.animateEmoji($emojiButton);
+ css.left = `${$addBtn.offset().left}px`;
+ $menu.removeClass('is-aligned-right');
}
- } else {
- votesBlock.removeClass('hidden');
- this.createEmoji(votesBlock, normalizedEmoji);
+ return $menu.css(css);
}
-};
-AwardsHandler.prototype.getVotesBlock = function getVotesBlock() {
- const currentBlock = $('.js-awards-block.current');
- let resultantVotesBlock = currentBlock;
- if (currentBlock.length === 0) {
- resultantVotesBlock = $('.js-awards-block').eq(0);
+ addAward(
+ votesBlock,
+ awardUrl,
+ emoji,
+ checkMutuality,
+ callback,
+ ) {
+ const normalizedEmoji = this.normalizeEmojiName(emoji);
+ const $emojiButton = this.findEmojiIcon(votesBlock, normalizedEmoji).parent();
+ this.postEmoji($emojiButton, awardUrl, normalizedEmoji, () => {
+ this.addAwardToEmojiBar(votesBlock, normalizedEmoji, checkMutuality);
+ return typeof callback === 'function' ? callback() : undefined;
+ });
+ $('.emoji-menu').removeClass('is-visible');
+ $('.js-add-award.is-active').removeClass('is-active');
}
- return resultantVotesBlock;
-};
+ addAwardToEmojiBar(
+ votesBlock,
+ emoji,
+ checkForMutuality,
+ ) {
+ if (checkForMutuality || checkForMutuality === null) {
+ this.checkMutuality(votesBlock, emoji);
+ }
+ this.addEmojiToFrequentlyUsedList(emoji);
+ const normalizedEmoji = this.normalizeEmojiName(emoji);
+ const $emojiButton = this.findEmojiIcon(votesBlock, normalizedEmoji).parent();
+ if ($emojiButton.length > 0) {
+ if (this.isActive($emojiButton)) {
+ this.decrementCounter($emojiButton, normalizedEmoji);
+ } else {
+ const counter = $emojiButton.find('.js-counter');
+ counter.text(parseInt(counter.text(), 10) + 1);
+ $emojiButton.addClass('active');
+ this.addYouToUserList(votesBlock, normalizedEmoji);
+ this.animateEmoji($emojiButton);
+ }
+ } else {
+ votesBlock.removeClass('hidden');
+ this.createEmoji(votesBlock, normalizedEmoji);
+ }
+ }
-AwardsHandler.prototype.getAwardUrl = function getAwardUrl() {
- return this.getVotesBlock().data('award-url');
-};
+ getVotesBlock() {
+ const currentBlock = $('.js-awards-block.current');
+ let resultantVotesBlock = currentBlock;
+ if (currentBlock.length === 0) {
+ resultantVotesBlock = $('.js-awards-block').eq(0);
+ }
+
+ return resultantVotesBlock;
+ }
-AwardsHandler.prototype.checkMutuality = function checkMutuality(votesBlock, emoji) {
- const awardUrl = this.getAwardUrl();
- if (emoji === 'thumbsup' || emoji === 'thumbsdown') {
- const mutualVote = emoji === 'thumbsup' ? 'thumbsdown' : 'thumbsup';
- const $emojiButton = votesBlock.find(`[data-name="${mutualVote}"]`).parent();
- const isAlreadyVoted = $emojiButton.hasClass('active');
- if (isAlreadyVoted) {
- this.addAward(votesBlock, awardUrl, mutualVote, false);
+ getAwardUrl() {
+ return this.getVotesBlock().data('award-url');
+ }
+
+ checkMutuality(votesBlock, emoji) {
+ const awardUrl = this.getAwardUrl();
+ if (emoji === 'thumbsup' || emoji === 'thumbsdown') {
+ const mutualVote = emoji === 'thumbsup' ? 'thumbsdown' : 'thumbsup';
+ const $emojiButton = votesBlock.find(`[data-name="${mutualVote}"]`).parent();
+ const isAlreadyVoted = $emojiButton.hasClass('active');
+ if (isAlreadyVoted) {
+ this.addAward(votesBlock, awardUrl, mutualVote, false);
+ }
}
}
-};
-AwardsHandler.prototype.isActive = function isActive($emojiButton) {
- return $emojiButton.hasClass('active');
-};
+ isActive($emojiButton) {
+ return $emojiButton.hasClass('active');
+ }
-AwardsHandler.prototype.isUserAuthored = function isUserAuthored($button) {
- return $button.hasClass('js-user-authored');
-};
+ isUserAuthored($button) {
+ return $button.hasClass('js-user-authored');
+ }
-AwardsHandler.prototype.decrementCounter = function decrementCounter($emojiButton, emoji) {
- const counter = $('.js-counter', $emojiButton);
- const counterNumber = parseInt(counter.text(), 10);
- if (counterNumber > 1) {
- counter.text(counterNumber - 1);
- this.removeYouFromUserList($emojiButton);
- } else if (emoji === 'thumbsup' || emoji === 'thumbsdown') {
- $emojiButton.tooltip('destroy');
- counter.text('0');
- this.removeYouFromUserList($emojiButton);
- if ($emojiButton.parents('.note').length) {
+ decrementCounter($emojiButton, emoji) {
+ const counter = $('.js-counter', $emojiButton);
+ const counterNumber = parseInt(counter.text(), 10);
+ if (counterNumber > 1) {
+ counter.text(counterNumber - 1);
+ this.removeYouFromUserList($emojiButton);
+ } else if (emoji === 'thumbsup' || emoji === 'thumbsdown') {
+ $emojiButton.tooltip('destroy');
+ counter.text('0');
+ this.removeYouFromUserList($emojiButton);
+ if ($emojiButton.parents('.note').length) {
+ this.removeEmoji($emojiButton);
+ }
+ } else {
this.removeEmoji($emojiButton);
}
- } else {
- this.removeEmoji($emojiButton);
+ return $emojiButton.removeClass('active');
}
- return $emojiButton.removeClass('active');
-};
-AwardsHandler.prototype.removeEmoji = function removeEmoji($emojiButton) {
- $emojiButton.tooltip('destroy');
- $emojiButton.remove();
- const $votesBlock = this.getVotesBlock();
- if ($votesBlock.find('.js-emoji-btn').length === 0) {
- $votesBlock.addClass('hidden');
+ removeEmoji($emojiButton) {
+ $emojiButton.tooltip('destroy');
+ $emojiButton.remove();
+ const $votesBlock = this.getVotesBlock();
+ if ($votesBlock.find('.js-emoji-btn').length === 0) {
+ $votesBlock.addClass('hidden');
+ }
}
-};
-
-AwardsHandler.prototype.getAwardTooltip = function getAwardTooltip($awardBlock) {
- return $awardBlock.attr('data-original-title') || $awardBlock.attr('data-title') || '';
-};
-AwardsHandler.prototype.toSentence = function toSentence(list) {
- let sentence;
- if (list.length <= 2) {
- sentence = list.join(' and ');
- } else {
- sentence = `${list.slice(0, -1).join(', ')}, and ${list[list.length - 1]}`;
+ getAwardTooltip($awardBlock) {
+ return $awardBlock.attr('data-original-title') || $awardBlock.attr('data-title') || '';
}
- return sentence;
-};
+ toSentence(list) {
+ let sentence;
+ if (list.length <= 2) {
+ sentence = list.join(' and ');
+ } else {
+ sentence = `${list.slice(0, -1).join(', ')}, and ${list[list.length - 1]}`;
+ }
-AwardsHandler.prototype.removeYouFromUserList = function removeYouFromUserList($emojiButton) {
- const awardBlock = $emojiButton;
- const originalTitle = this.getAwardTooltip(awardBlock);
- const authors = originalTitle.split(FROM_SENTENCE_REGEX);
- authors.splice(authors.indexOf('You'), 1);
- return awardBlock
- .closest('.js-emoji-btn')
- .removeData('title')
- .removeAttr('data-title')
- .removeAttr('data-original-title')
- .attr('title', this.toSentence(authors))
- .tooltip('fixTitle');
-};
+ return sentence;
+ }
-AwardsHandler.prototype.addYouToUserList = function addYouToUserList(votesBlock, emoji) {
- const awardBlock = this.findEmojiIcon(votesBlock, emoji).parent();
- const origTitle = this.getAwardTooltip(awardBlock);
- let users = [];
- if (origTitle) {
- users = origTitle.trim().split(FROM_SENTENCE_REGEX);
- }
- users.unshift('You');
- return awardBlock
- .attr('title', this.toSentence(users))
- .tooltip('fixTitle');
-};
+ removeYouFromUserList($emojiButton) {
+ const awardBlock = $emojiButton;
+ const originalTitle = this.getAwardTooltip(awardBlock);
+ const authors = originalTitle.split(FROM_SENTENCE_REGEX);
+ authors.splice(authors.indexOf('You'), 1);
+ return awardBlock
+ .closest('.js-emoji-btn')
+ .removeData('title')
+ .removeAttr('data-title')
+ .removeAttr('data-original-title')
+ .attr('title', this.toSentence(authors))
+ .tooltip('fixTitle');
+ }
+
+ addYouToUserList(votesBlock, emoji) {
+ const awardBlock = this.findEmojiIcon(votesBlock, emoji).parent();
+ const origTitle = this.getAwardTooltip(awardBlock);
+ let users = [];
+ if (origTitle) {
+ users = origTitle.trim().split(FROM_SENTENCE_REGEX);
+ }
+ users.unshift('You');
+ return awardBlock
+ .attr('title', this.toSentence(users))
+ .tooltip('fixTitle');
+ }
-AwardsHandler
- .prototype
- .createAwardButtonForVotesBlock = function createAwardButtonForVotesBlock(votesBlock, emojiName) {
+ createAwardButtonForVotesBlock(votesBlock, emojiName) {
const buttonHtml = `
<button class="btn award-control js-emoji-btn has-tooltip active" title="You" data-placement="bottom">
${glEmojiTag(emojiName)}
@@ -424,144 +422,141 @@ AwardsHandler
this.animateEmoji($emojiButton);
$('.award-control').tooltip();
votesBlock.removeClass('current');
- };
+ }
-AwardsHandler.prototype.animateEmoji = function animateEmoji($emoji) {
- const className = 'pulse animated once short';
- $emoji.addClass(className);
+ animateEmoji($emoji) {
+ const className = 'pulse animated once short';
+ $emoji.addClass(className);
- this.registerEventListener('on', $emoji, animationEndEventString, (e) => {
- $(e.currentTarget).removeClass(className);
- });
-};
+ this.registerEventListener('on', $emoji, animationEndEventString, (e) => {
+ $(e.currentTarget).removeClass(className);
+ });
+ }
-AwardsHandler.prototype.createEmoji = function createEmoji(votesBlock, emoji) {
- if ($('.emoji-menu').length) {
- this.createAwardButtonForVotesBlock(votesBlock, emoji);
+ createEmoji(votesBlock, emoji) {
+ if ($('.emoji-menu').length) {
+ this.createAwardButtonForVotesBlock(votesBlock, emoji);
+ }
+ this.createEmojiMenu(() => {
+ this.createAwardButtonForVotesBlock(votesBlock, emoji);
+ });
}
- this.createEmojiMenu(() => {
- this.createAwardButtonForVotesBlock(votesBlock, emoji);
- });
-};
-AwardsHandler.prototype.postEmoji = function postEmoji($emojiButton, awardUrl, emoji, callback) {
- if (this.isUserAuthored($emojiButton)) {
- this.userAuthored($emojiButton);
- } else {
- $.post(awardUrl, {
- name: emoji,
- }, (data) => {
- if (data.ok) {
- callback();
- }
- }).fail(() => new Flash('Something went wrong on our end.'));
+ postEmoji($emojiButton, awardUrl, emoji, callback) {
+ if (this.isUserAuthored($emojiButton)) {
+ this.userAuthored($emojiButton);
+ } else {
+ $.post(awardUrl, {
+ name: emoji,
+ }, (data) => {
+ if (data.ok) {
+ callback();
+ }
+ }).fail(() => new Flash('Something went wrong on our end.'));
+ }
}
-};
-AwardsHandler.prototype.findEmojiIcon = function findEmojiIcon(votesBlock, emoji) {
- return votesBlock.find(`.js-emoji-btn [data-name="${emoji}"]`);
-};
+ findEmojiIcon(votesBlock, emoji) {
+ return votesBlock.find(`.js-emoji-btn [data-name="${emoji}"]`);
+ }
-AwardsHandler.prototype.userAuthored = function userAuthored($emojiButton) {
- const oldTitle = this.getAwardTooltip($emojiButton);
- const newTitle = 'You cannot vote on your own issue, MR and note';
- gl.utils.updateTooltipTitle($emojiButton, newTitle).tooltip('show');
- // Restore tooltip back to award list
- return setTimeout(() => {
- $emojiButton.tooltip('hide');
- gl.utils.updateTooltipTitle($emojiButton, oldTitle);
- }, 2800);
-};
+ userAuthored($emojiButton) {
+ const oldTitle = this.getAwardTooltip($emojiButton);
+ const newTitle = 'You cannot vote on your own issue, MR and note';
+ gl.utils.updateTooltipTitle($emojiButton, newTitle).tooltip('show');
+ // Restore tooltip back to award list
+ return setTimeout(() => {
+ $emojiButton.tooltip('hide');
+ gl.utils.updateTooltipTitle($emojiButton, oldTitle);
+ }, 2800);
+ }
-AwardsHandler.prototype.scrollToAwards = function scrollToAwards() {
- const options = {
- scrollTop: $('.awards').offset().top - 110,
- };
- return $('body, html').animate(options, 200);
-};
+ scrollToAwards() {
+ const options = {
+ scrollTop: $('.awards').offset().top - 110,
+ };
+ return $('body, html').animate(options, 200);
+ }
-AwardsHandler.prototype.normalizeEmojiName = function normalizeEmojiName(emoji) {
- return Object.prototype.hasOwnProperty.call(this.aliases, emoji) ? this.aliases[emoji] : emoji;
-};
+ normalizeEmojiName(emoji) {
+ return Object.prototype.hasOwnProperty.call(this.aliases, emoji) ? this.aliases[emoji] : emoji;
+ }
-AwardsHandler
- .prototype
- .addEmojiToFrequentlyUsedList = function addEmojiToFrequentlyUsedList(emoji) {
+ addEmojiToFrequentlyUsedList(emoji) {
if (isEmojiNameValid(emoji)) {
this.frequentlyUsedEmojis = _.uniq(this.getFrequentlyUsedEmojis().concat(emoji));
Cookies.set('frequently_used_emojis', this.frequentlyUsedEmojis.join(','), { expires: 365 });
}
- };
-
-AwardsHandler.prototype.getFrequentlyUsedEmojis = function getFrequentlyUsedEmojis() {
- return this.frequentlyUsedEmojis || (() => {
- const frequentlyUsedEmojis = _.uniq((Cookies.get('frequently_used_emojis') || '').split(','));
- this.frequentlyUsedEmojis = frequentlyUsedEmojis.filter(
- inputName => isEmojiNameValid(inputName),
- );
+ }
- return this.frequentlyUsedEmojis;
- })();
-};
+ getFrequentlyUsedEmojis() {
+ return this.frequentlyUsedEmojis || (() => {
+ const frequentlyUsedEmojis = _.uniq((Cookies.get('frequently_used_emojis') || '').split(','));
+ this.frequentlyUsedEmojis = frequentlyUsedEmojis.filter(
+ inputName => isEmojiNameValid(inputName),
+ );
-AwardsHandler.prototype.setupSearch = function setupSearch() {
- const $search = $('.js-emoji-menu-search');
+ return this.frequentlyUsedEmojis;
+ })();
+ }
- this.registerEventListener('on', $search, 'input', (e) => {
- const term = $(e.target).val().trim();
- this.searchEmojis(term);
- });
+ setupSearch() {
+ const $search = $('.js-emoji-menu-search');
- const $menu = $('.emoji-menu');
- this.registerEventListener('on', $menu, transitionEndEventString, (e) => {
- if (e.target === e.currentTarget) {
- // Clear the search
- this.searchEmojis('');
- }
- });
-};
+ this.registerEventListener('on', $search, 'input', (e) => {
+ const term = $(e.target).val().trim();
+ this.searchEmojis(term);
+ });
-AwardsHandler.prototype.searchEmojis = function searchEmojis(term) {
- const $search = $('.js-emoji-menu-search');
- $search.val(term);
-
- // Clean previous search results
- $('ul.emoji-menu-search, h5.emoji-search-title').remove();
- if (term.length > 0) {
- // Generate a search result block
- const h5 = $('<h5 class="emoji-search-title"/>').text('Search results');
- const foundEmojis = this.findMatchingEmojiElements(term).show();
- const ul = $('<ul>').addClass('emoji-menu-list emoji-menu-search').append(foundEmojis);
- $('.emoji-menu-content ul, .emoji-menu-content h5').hide();
- $('.emoji-menu-content').append(h5).append(ul);
- } else {
- $('.emoji-menu-content').children().show();
+ const $menu = $('.emoji-menu');
+ this.registerEventListener('on', $menu, transitionEndEventString, (e) => {
+ if (e.target === e.currentTarget) {
+ // Clear the search
+ this.searchEmojis('');
+ }
+ });
}
-};
-
-AwardsHandler.prototype.findMatchingEmojiElements = function findMatchingEmojiElements(term) {
- const safeTerm = term.toLowerCase();
- const namesMatchingAlias = [];
- Object.keys(emojiAliases).forEach((alias) => {
- if (alias.indexOf(safeTerm) >= 0) {
- namesMatchingAlias.push(emojiAliases[alias]);
+ searchEmojis(term) {
+ const $search = $('.js-emoji-menu-search');
+ $search.val(term);
+
+ // Clean previous search results
+ $('ul.emoji-menu-search, h5.emoji-search-title').remove();
+ if (term.length > 0) {
+ // Generate a search result block
+ const h5 = $('<h5 class="emoji-search-title"/>').text('Search results');
+ const foundEmojis = this.findMatchingEmojiElements(term).show();
+ const ul = $('<ul>').addClass('emoji-menu-list emoji-menu-search').append(foundEmojis);
+ $('.emoji-menu-content ul, .emoji-menu-content h5').hide();
+ $('.emoji-menu-content').append(h5).append(ul);
+ } else {
+ $('.emoji-menu-content').children().show();
}
- });
- const $matchingElements = namesMatchingAlias.concat(safeTerm)
- .reduce(
- ($result, searchTerm) =>
- $result.add($(`.emoji-menu-list:not(.frequent-emojis) [data-name*="${searchTerm}"]`)),
- $([]),
- );
- return $matchingElements.closest('li').clone();
-};
+ }
-AwardsHandler.prototype.destroy = function destroy() {
- this.eventListeners.forEach((entry) => {
- entry.element.off.call(entry.element, ...entry.args);
- });
- $('.emoji-menu').remove();
-};
+ findMatchingEmojiElements(term) {
+ const safeTerm = term.toLowerCase();
+
+ const namesMatchingAlias = [];
+ Object.keys(emojiAliases).forEach((alias) => {
+ if (alias.indexOf(safeTerm) >= 0) {
+ namesMatchingAlias.push(emojiAliases[alias]);
+ }
+ });
+ const $matchingElements = namesMatchingAlias.concat(safeTerm)
+ .reduce(
+ ($result, searchTerm) =>
+ $result.add($(`.emoji-menu-list:not(.frequent-emojis) [data-name*="${searchTerm}"]`)),
+ $([]),
+ );
+ return $matchingElements.closest('li').clone();
+ }
-export default AwardsHandler;
+ destroy() {
+ this.eventListeners.forEach((entry) => {
+ entry.element.off.call(entry.element, ...entry.args);
+ });
+ $('.emoji-menu').remove();
+ }
+}
diff --git a/app/assets/javascripts/boards/components/board_new_issue.js b/app/assets/javascripts/boards/components/board_new_issue.js
index b1c47b09c35..4af8b0c7713 100644
--- a/app/assets/javascripts/boards/components/board_new_issue.js
+++ b/app/assets/javascripts/boards/components/board_new_issue.js
@@ -17,7 +17,7 @@ export default {
methods: {
submit(e) {
e.preventDefault();
- if (this.title.trim() === '') return;
+ if (this.title.trim() === '') return Promise.resolve();
this.error = false;
@@ -29,7 +29,10 @@ export default {
assignees: [],
});
- this.list.newIssue(issue)
+ eventHub.$emit(`scroll-board-list-${this.list.id}`);
+ this.cancel();
+
+ return this.list.newIssue(issue)
.then(() => {
// Need this because our jQuery very kindly disables buttons on ALL form submissions
$(this.$refs.submitButton).enable();
@@ -47,9 +50,6 @@ export default {
// Show error message
this.error = true;
});
-
- eventHub.$emit(`scroll-board-list-${this.list.id}`);
- this.cancel();
},
cancel() {
this.title = '';
diff --git a/app/assets/javascripts/boards/models/list.js b/app/assets/javascripts/boards/models/list.js
index 548de1a4c52..b4b09b3876e 100644
--- a/app/assets/javascripts/boards/models/list.js
+++ b/app/assets/javascripts/boards/models/list.js
@@ -112,8 +112,7 @@ class List {
.then((resp) => {
const data = resp.json();
issue.id = data.iid;
- })
- .then(() => {
+
if (this.issuesSize > 1) {
const moveBeforeIid = this.issues[1].id;
gl.boardService.moveIssue(issue.id, null, null, null, moveBeforeIid);
diff --git a/app/assets/javascripts/environments/components/environment_item.vue b/app/assets/javascripts/environments/components/environment_item.vue
index 809c147bf25..b25113e0fc6 100644
--- a/app/assets/javascripts/environments/components/environment_item.vue
+++ b/app/assets/javascripts/environments/components/environment_item.vue
@@ -403,6 +403,14 @@ export default {
return '';
},
+ displayEnvironmentActions() {
+ return this.hasManualActions ||
+ this.externalURL ||
+ this.monitoringUrl ||
+ this.hasStopAction ||
+ this.canRetry;
+ },
+
/**
* Constructs folder URL based on the current location and the folder id.
*
@@ -535,9 +543,12 @@ export default {
</span>
</div>
- <div class="table-section section-30 table-button-footer" role="gridcell">
+ <div
+ v-if="!model.isFolder && displayEnvironmentActions"
+ class="table-section section-30 table-button-footer"
+ role="gridcell">
+
<div
- v-if="!model.isFolder"
class="btn-group table-action-buttons"
role="group">
diff --git a/app/assets/javascripts/filtered_search/filtered_search_manager.js b/app/assets/javascripts/filtered_search/filtered_search_manager.js
index 8f547bd8f1f..1425769d2de 100644
--- a/app/assets/javascripts/filtered_search/filtered_search_manager.js
+++ b/app/assets/javascripts/filtered_search/filtered_search_manager.js
@@ -40,6 +40,10 @@ class FilteredSearchManager {
return [];
})
.then((searches) => {
+ if (!searches) {
+ return;
+ }
+
// Put any searches that may have come in before
// we fetched the saved searches ahead of the already saved ones
const resultantSearches = this.recentSearchesStore.setRecentSearches(
@@ -487,6 +491,7 @@ class FilteredSearchManager {
}
searchState(e) {
+ e.preventDefault();
const target = e.currentTarget;
// remove focus outline after click
target.blur();
diff --git a/app/assets/javascripts/groups/stores/groups_store.js b/app/assets/javascripts/groups/stores/groups_store.js
index f6dc4290fd5..6eab6083e8f 100644
--- a/app/assets/javascripts/groups/stores/groups_store.js
+++ b/app/assets/javascripts/groups/stores/groups_store.js
@@ -47,8 +47,8 @@ export default class GroupsStore {
// Map groups to an object
groups.map((group) => {
- mappedGroups[group.id] = group;
- mappedGroups[group.id].subGroups = {};
+ mappedGroups[`id${group.id}`] = group;
+ mappedGroups[`id${group.id}`].subGroups = {};
return group;
});
@@ -56,26 +56,27 @@ export default class GroupsStore {
const currentGroup = mappedGroups[key];
if (currentGroup.parentId) {
// If the group is not at the root level, add it to its parent array of subGroups.
- const findParentGroup = mappedGroups[currentGroup.parentId];
+ const findParentGroup = mappedGroups[`id${currentGroup.parentId}`];
if (findParentGroup) {
- mappedGroups[currentGroup.parentId].subGroups[currentGroup.id] = currentGroup;
- mappedGroups[currentGroup.parentId].isOpen = true; // Expand group if it has subgroups
+ mappedGroups[`id${currentGroup.parentId}`].subGroups[`id${currentGroup.id}`] = currentGroup;
+ mappedGroups[`id${currentGroup.parentId}`].isOpen = true; // Expand group if it has subgroups
} else if (parentGroup && parentGroup.id === currentGroup.parentId) {
- tree[currentGroup.id] = currentGroup;
+ tree[`id${currentGroup.id}`] = currentGroup;
} else {
- // Means the groups hast no direct parent.
- // Save for later processing, we will add them to its corresponding base group
+ // No parent found. We save it for later processing
orphans.push(currentGroup);
+
+ // Add to tree to preserve original order
+ tree[`id${currentGroup.id}`] = currentGroup;
}
} else {
- // If the group is at the root level, add it to first level elements array.
- tree[currentGroup.id] = currentGroup;
+ // If the group is at the top level, add it to first level elements array.
+ tree[`id${currentGroup.id}`] = currentGroup;
}
return key;
});
- // Hopefully this array will be empty for most cases
if (orphans.length) {
orphans.map((orphan) => {
let found = false;
@@ -83,11 +84,23 @@ export default class GroupsStore {
Object.keys(tree).map((key) => {
const group = tree[key];
- if (currentOrphan.fullPath.lastIndexOf(group.fullPath) === 0) {
+
+ if (
+ group &&
+ currentOrphan.fullPath.lastIndexOf(group.fullPath) === 0 &&
+ // Make sure the currently selected orphan is not the same as the group
+ // we are checking here otherwise it will end up in an infinite loop
+ currentOrphan.id !== group.id
+ ) {
group.subGroups[currentOrphan.id] = currentOrphan;
group.isOpen = true;
currentOrphan.isOrphan = true;
found = true;
+
+ // Delete if group was put at the top level. If not the group will be displayed twice.
+ if (tree[`id${currentOrphan.id}`]) {
+ delete tree[`id${currentOrphan.id}`];
+ }
}
return key;
@@ -95,7 +108,8 @@ export default class GroupsStore {
if (!found) {
currentOrphan.isOrphan = true;
- tree[currentOrphan.id] = currentOrphan;
+
+ tree[`id${currentOrphan.id}`] = currentOrphan;
}
return orphan;
@@ -140,7 +154,7 @@ export default class GroupsStore {
// eslint-disable-next-line class-methods-use-this
removeGroup(group, collection) {
- Vue.delete(collection, group.id);
+ Vue.delete(collection, `id${group.id}`);
}
// eslint-disable-next-line class-methods-use-this
diff --git a/app/assets/javascripts/issue_show/components/app.vue b/app/assets/javascripts/issue_show/components/app.vue
index 8473a81bc88..3d5fb7f441c 100644
--- a/app/assets/javascripts/issue_show/components/app.vue
+++ b/app/assets/javascripts/issue_show/components/app.vue
@@ -204,13 +204,7 @@ export default {
method: 'getData',
successCallback: (res) => {
const data = res.json();
- const shouldUpdate = this.store.stateShouldUpdate(data);
-
this.store.updateState(data);
-
- if (this.showForm && (shouldUpdate.title || shouldUpdate.description)) {
- this.store.formState.lockedWarningVisible = true;
- }
},
errorCallback(err) {
throw new Error(err);
diff --git a/app/assets/javascripts/issue_show/stores/index.js b/app/assets/javascripts/issue_show/stores/index.js
index f2b822f3cbb..0c8bd6f1cc3 100644
--- a/app/assets/javascripts/issue_show/stores/index.js
+++ b/app/assets/javascripts/issue_show/stores/index.js
@@ -12,6 +12,10 @@ export default class Store {
}
updateState(data) {
+ if (this.stateShouldUpdate(data)) {
+ this.formState.lockedWarningVisible = true;
+ }
+
this.state.titleHtml = data.title;
this.state.titleText = data.title_text;
this.state.descriptionHtml = data.description;
@@ -23,10 +27,8 @@ export default class Store {
}
stateShouldUpdate(data) {
- return {
- title: this.state.titleText !== data.title_text,
- description: this.state.descriptionText !== data.description_text,
- };
+ return this.state.titleText !== data.title_text ||
+ this.state.descriptionText !== data.description_text;
}
setFormState(state) {
diff --git a/app/assets/javascripts/lib/utils/common_utils.js b/app/assets/javascripts/lib/utils/common_utils.js
index 2aca86189fd..122ec138c59 100644
--- a/app/assets/javascripts/lib/utils/common_utils.js
+++ b/app/assets/javascripts/lib/utils/common_utils.js
@@ -86,18 +86,25 @@
// This is required to handle non-unicode characters in hash
hash = decodeURIComponent(hash);
+ var fixedTabs = document.querySelector('.js-tabs-affix');
+ var fixedNav = document.querySelector('.navbar-gitlab');
+
+ var adjustment = 0;
+ if (fixedNav) adjustment -= fixedNav.offsetHeight;
+
// scroll to user-generated markdown anchor if we cannot find a match
if (document.getElementById(hash) === null) {
var target = document.getElementById('user-content-' + hash);
if (target && target.scrollIntoView) {
target.scrollIntoView(true);
+ window.scrollBy(0, adjustment);
}
} else {
// only adjust for fixedTabs when not targeting user-generated content
- var fixedTabs = document.querySelector('.js-tabs-affix');
if (fixedTabs) {
- window.scrollBy(0, -fixedTabs.offsetHeight);
+ adjustment -= fixedTabs.offsetHeight;
}
+ window.scrollBy(0, adjustment);
}
};
diff --git a/app/assets/javascripts/locale/es/app.js b/app/assets/javascripts/locale/es/app.js
index eafcd15acf9..8d951928849 100644
--- a/app/assets/javascripts/locale/es/app.js
+++ b/app/assets/javascripts/locale/es/app.js
@@ -1 +1 @@
-var locales = locales || {}; locales['es'] = {"domain":"app","locale_data":{"app":{"":{"Project-Id-Version":"gitlab 1.0.0","Report-Msgid-Bugs-To":"","PO-Revision-Date":"2017-06-15 21:59-0500","Language-Team":"Spanish","Language":"es","MIME-Version":"1.0","Content-Type":"text/plain; charset=UTF-8","Content-Transfer-Encoding":"8bit","Plural-Forms":"nplurals=2; plural=n != 1;","Last-Translator":"Bob Van Landuyt <bob@gitlab.com>","X-Generator":"Poedit 2.0.2","POT-Creation-Date":"2017-06-15 21:59-0500","lang":"es","domain":"app","plural_forms":"nplurals=2; plural=n != 1;"},"%{commit_author_link} committed %{commit_timeago}":["%{commit_author_link} cambió %{commit_timeago}"],"About auto deploy":["Acerca del auto despliegue"],"Active":["Activo"],"Activity":["Actividad"],"Add Changelog":["Agregar Changelog"],"Add Contribution guide":["Agregar guía de contribución"],"Add License":["Agregar Licencia"],"Add an SSH key to your profile to pull or push via SSH.":["Agregar una clave SSH a tu perfil para actualizar o enviar a través de SSH."],"Add new directory":["Agregar nuevo directorio"],"Archived project! Repository is read-only":["¡Proyecto archivado! El repositorio es de solo lectura"],"Are you sure you want to delete this pipeline schedule?":["¿Estás seguro que deseas eliminar esta programación del pipeline?"],"Attach a file by drag &amp; drop or %{upload_link}":["Adjunte un archivo arrastrando &amp; soltando o %{upload_link}"],"Branch":["Rama","Ramas"],"Branch <strong>%{branch_name}</strong> was created. To set up auto deploy, choose a GitLab CI Yaml template and commit your changes. %{link_to_autodeploy_doc}":["La rama <strong>%{branch_name}</strong> fue creada. Para configurar el auto despliegue, escoge una plantilla Yaml para GitLab CI y envía tus cambios. %{link_to_autodeploy_doc}"],"Branches":["Ramas"],"Browse files":["Examinar los archivos"],"ByAuthor|by":["por"],"CI configuration":["Configuración de CI"],"Cancel":["Cancelar"],"ChangeTypeActionLabel|Pick into branch":["Escoger en la rama"],"ChangeTypeActionLabel|Revert in branch":["Revertir en la rama"],"ChangeTypeAction|Cherry-pick":["Cherry-pick"],"ChangeTypeAction|Revert":["Revertir"],"Changelog":["Changelog"],"Charts":["Gráficos"],"Cherry-pick this commit":["Escoger este cambio"],"Cherry-pick this merge request":["Escoger esta solicitud de fusión"],"CiStatusLabel|canceled":["cancelado"],"CiStatusLabel|created":["creado"],"CiStatusLabel|failed":["fallido"],"CiStatusLabel|manual action":["acción manual"],"CiStatusLabel|passed":["pasó"],"CiStatusLabel|passed with warnings":["pasó con advertencias"],"CiStatusLabel|pending":["pendiente"],"CiStatusLabel|skipped":["omitido"],"CiStatusLabel|waiting for manual action":["esperando acción manual"],"CiStatusText|blocked":["bloqueado"],"CiStatusText|canceled":["cancelado"],"CiStatusText|created":["creado"],"CiStatusText|failed":["fallado"],"CiStatusText|manual":["manual"],"CiStatusText|passed":["pasó"],"CiStatusText|pending":["pendiente"],"CiStatusText|skipped":["omitido"],"CiStatus|running":["en ejecución"],"Commit":["Cambio","Cambios"],"Commit message":["Mensaje del cambio"],"CommitBoxTitle|Commit":["Cambio"],"CommitMessage|Add %{file_name}":["Agregar %{file_name}"],"Commits":["Cambios"],"Commits|History":["Historial"],"Committed by":["Enviado por"],"Compare":["Comparar"],"Contribution guide":["Guía de contribución"],"Contributors":["Contribuidores"],"Copy URL to clipboard":["Copiar URL al portapapeles"],"Copy commit SHA to clipboard":["Copiar SHA del cambio al portapapeles"],"Create New Directory":["Crear Nuevo Directorio"],"Create directory":["Crear directorio"],"Create empty bare repository":["Crear repositorio vacío"],"Create merge request":["Crear solicitud de fusión"],"Create new...":["Crear nuevo..."],"CreateNewFork|Fork":["Bifurcar"],"CreateTag|Tag":["Etiqueta"],"Cron Timezone":["Zona horaria del Cron"],"Cron syntax":["Sintaxis de Cron"],"Custom notification events":["Eventos de notificaciones personalizadas"],"Custom notification levels are the same as participating levels. With custom notification levels you will also receive notifications for select events. To find out more, check out %{notification_link}.":["Los niveles de notificación personalizados son los mismos que los niveles participantes. Con los niveles de notificación personalizados, también recibirá notificaciones para eventos seleccionados. Para obtener más información, consulte %{notification_link}."],"Cycle Analytics":["Cycle Analytics"],"Cycle Analytics gives an overview of how much time it takes to go from idea to production in your project.":["Cycle Analytics ofrece una visión general de cuánto tiempo tarda en pasar de idea a producción en su proyecto."],"CycleAnalyticsStage|Code":["Código"],"CycleAnalyticsStage|Issue":["Incidencia"],"CycleAnalyticsStage|Plan":["Planificación"],"CycleAnalyticsStage|Production":["Producción"],"CycleAnalyticsStage|Review":["Revisión"],"CycleAnalyticsStage|Staging":["Puesta en escena"],"CycleAnalyticsStage|Test":["Pruebas"],"Define a custom pattern with cron syntax":["Definir un patrón personalizado con la sintaxis de cron"],"Delete":["Eliminar"],"Deploy":["Despliegue","Despliegues"],"Description":["Descripción"],"Directory name":["Nombre del directorio"],"Don't show again":["No mostrar de nuevo"],"Download":["Descargar"],"Download tar":["Descargar tar"],"Download tar.bz2":["Descargar tar.bz2"],"Download tar.gz":["Descargar tar.gz"],"Download zip":["Descargar zip"],"DownloadArtifacts|Download":["Descargar"],"DownloadCommit|Email Patches":["Parches por correo electrónico"],"DownloadCommit|Plain Diff":["Diferencias en texto plano"],"DownloadSource|Download":["Descargar"],"Edit":["Editar"],"Edit Pipeline Schedule %{id}":["Editar Programación del Pipeline %{id}"],"Every day (at 4:00am)":["Todos los días (a las 4:00 am)"],"Every month (on the 1st at 4:00am)":["Todos los meses (el día 1 a las 4:00 am)"],"Every week (Sundays at 4:00am)":["Todas las semanas (domingos a las 4:00 am)"],"Failed to change the owner":["Error al cambiar el propietario"],"Failed to remove the pipeline schedule":["Error al eliminar la programación del pipeline"],"Files":["Archivos"],"Find by path":["Buscar por ruta"],"Find file":["Buscar archivo"],"FirstPushedBy|First":["Primer"],"FirstPushedBy|pushed by":["enviado por"],"Fork":["Bifurcación","Bifurcaciones"],"ForkedFromProjectPath|Forked from":["Bifurcado de"],"From issue creation until deploy to production":["Desde la creación de la incidencia hasta el despliegue a producción"],"From merge request merge until deploy to production":["Desde la integración de la solicitud de fusión hasta el despliegue a producción"],"Go to your fork":["Ir a tu bifurcación"],"GoToYourFork|Fork":["Bifurcación"],"Home":["Inicio"],"Housekeeping successfully started":["Servicio de limpieza iniciado con éxito"],"Import repository":["Importar repositorio"],"Interval Pattern":["Patrón de intervalo"],"Introducing Cycle Analytics":["Introducción a Cycle Analytics"],"LFSStatus|Disabled":["Deshabilitado"],"LFSStatus|Enabled":["Habilitado"],"Last %d day":["Último %d día","Últimos %d días"],"Last Pipeline":["Último Pipeline"],"Last Update":["Última actualización"],"Last commit":["Último cambio"],"Learn more in the":["Más información en la"],"Learn more in the|pipeline schedules documentation":["documentación sobre la programación de pipelines"],"Leave group":["Abandonar grupo"],"Leave project":["Abandonar proyecto"],"Limited to showing %d event at most":["Limitado a mostrar máximo %d evento","Limitado a mostrar máximo %d eventos"],"Median":["Mediana"],"MissingSSHKeyWarningLink|add an SSH key":["agregar una clave SSH"],"New Issue":["Nueva incidencia","Nuevas incidencias"],"New Pipeline Schedule":["Nueva Programación del Pipeline"],"New branch":["Nueva rama"],"New directory":["Nuevo directorio"],"New file":["Nuevo archivo"],"New issue":["Nueva incidencia"],"New merge request":["Nueva solicitud de fusión"],"New schedule":["Nueva programación"],"New snippet":["Nuevo fragmento de código"],"New tag":["Nueva etiqueta"],"No repository":["No hay repositorio"],"No schedules":["No hay programaciones"],"Not available":["No disponible"],"Not enough data":["No hay suficientes datos"],"Notification events":["Eventos de notificación"],"NotificationEvent|Close issue":["Cerrar incidencia"],"NotificationEvent|Close merge request":["Cerrar solicitud de fusión"],"NotificationEvent|Failed pipeline":["Pipeline fallido"],"NotificationEvent|Merge merge request":["Integrar solicitud de fusión"],"NotificationEvent|New issue":["Nueva incidencia"],"NotificationEvent|New merge request":["Nueva solicitud de fusión"],"NotificationEvent|New note":["Nueva nota"],"NotificationEvent|Reassign issue":["Reasignar incidencia"],"NotificationEvent|Reassign merge request":["Reasignar solicitud de fusión"],"NotificationEvent|Reopen issue":["Reabrir incidencia"],"NotificationEvent|Successful pipeline":["Pipeline exitoso"],"NotificationLevel|Custom":["Personalizado"],"NotificationLevel|Disabled":["Deshabilitado"],"NotificationLevel|Global":["Global"],"NotificationLevel|On mention":["Cuando me mencionan"],"NotificationLevel|Participate":["Participación"],"NotificationLevel|Watch":["Vigilancia"],"OfSearchInADropdown|Filter":["Filtrar"],"OpenedNDaysAgo|Opened":["Abierto"],"Options":["Opciones"],"Owner":["Propietario"],"Pipeline":["Pipeline"],"Pipeline Health":["Estado del Pipeline"],"Pipeline Schedule":["Programación del Pipeline"],"Pipeline Schedules":["Programaciones de los Pipelines"],"PipelineSchedules|Activated":["Activado"],"PipelineSchedules|Active":["Activos"],"PipelineSchedules|All":["Todos"],"PipelineSchedules|Inactive":["Inactivos"],"PipelineSchedules|Next Run":["Próxima Ejecución"],"PipelineSchedules|None":["Ninguno"],"PipelineSchedules|Provide a short description for this pipeline":["Proporcione una breve descripción para este pipeline"],"PipelineSchedules|Take ownership":["Tomar posesión"],"PipelineSchedules|Target":["Destino"],"PipelineSheduleIntervalPattern|Custom":["Personalizado"],"Pipeline|with stage":["con etapa"],"Pipeline|with stages":["con etapas"],"Project '%{project_name}' queued for deletion.":["Proyecto ‘%{project_name}’ en cola para eliminación."],"Project '%{project_name}' was successfully created.":["Proyecto ‘%{project_name}’ fue creado satisfactoriamente."],"Project '%{project_name}' was successfully updated.":["Proyecto ‘%{project_name}’ fue actualizado satisfactoriamente."],"Project '%{project_name}' will be deleted.":["Proyecto ‘%{project_name}’ será eliminado."],"Project access must be granted explicitly to each user.":["El acceso al proyecto debe concederse explícitamente a cada usuario."],"Project export could not be deleted.":["No se pudo eliminar la exportación del proyecto."],"Project export has been deleted.":["La exportación del proyecto ha sido eliminada."],"Project export link has expired. Please generate a new export from your project settings.":["El enlace de exportación del proyecto ha caducado. Por favor, genera una nueva exportación desde la configuración del proyecto."],"Project export started. A download link will be sent by email.":["Se inició la exportación del proyecto. Se enviará un enlace de descarga por correo electrónico."],"Project home":["Inicio del proyecto"],"ProjectFeature|Disabled":["Deshabilitada"],"ProjectFeature|Everyone with access":["Todos con acceso"],"ProjectFeature|Only team members":["Solo miembros del equipo"],"ProjectFileTree|Name":["Nombre"],"ProjectLastActivity|Never":["Nunca"],"ProjectLifecycle|Stage":["Etapa"],"ProjectNetworkGraph|Graph":["Historial gráfico"],"Read more":["Leer más"],"Readme":["Léeme"],"RefSwitcher|Branches":["Ramas"],"RefSwitcher|Tags":["Etiquetas"],"Related Commits":["Cambios Relacionados"],"Related Deployed Jobs":["Trabajos Desplegados Relacionados"],"Related Issues":["Incidencias Relacionadas"],"Related Jobs":["Trabajos Relacionados"],"Related Merge Requests":["Solicitudes de fusión Relacionadas"],"Related Merged Requests":["Solicitudes de fusión Relacionadas"],"Remind later":["Recordar después"],"Remove project":["Eliminar proyecto"],"Request Access":["Solicitar acceso"],"Revert this commit":["Revertir este cambio"],"Revert this merge request":["Revertir esta solicitud de fusión"],"Save pipeline schedule":["Guardar programación del pipeline"],"Schedule a new pipeline":["Programar un nuevo pipeline"],"Scheduling Pipelines":["Programación de Pipelines"],"Search branches and tags":["Buscar ramas y etiquetas"],"Select Archive Format":["Seleccionar formato de archivo"],"Select a timezone":["Selecciona una zona horaria"],"Select target branch":["Selecciona una rama de destino"],"Set a password on your account to pull or push via %{protocol}":["Establezca una contraseña en su cuenta para actualizar o enviar a través de %{protocol}"],"Set up CI":["Configurar CI"],"Set up Koding":["Configurar Koding"],"Set up auto deploy":["Configurar auto despliegue"],"SetPasswordToCloneLink|set a password":["establecer una contraseña"],"Showing %d event":["Mostrando %d evento","Mostrando %d eventos"],"Source code":["Código fuente"],"StarProject|Star":["Destacar"],"Start a %{new_merge_request} with these changes":["Iniciar una %{new_merge_request} con estos cambios"],"Switch branch/tag":["Cambiar rama/etiqueta"],"Tag":["Etiqueta","Etiquetas"],"Tags":["Etiquetas"],"Target Branch":["Rama de destino"],"The coding stage shows the time from the first commit to creating the merge request. The data will automatically be added here once you create your first merge request.":["La etapa de desarrollo muestra el tiempo desde el primer cambio hasta la creación de la solicitud de fusión. Los datos serán automáticamente incorporados aquí una vez creada tu primera solicitud de fusión."],"The collection of events added to the data gathered for that stage.":["La colección de eventos agregados a los datos recopilados para esa etapa."],"The fork relationship has been removed.":["La relación con la bifurcación se ha eliminado."],"The issue stage shows the time it takes from creating an issue to assigning the issue to a milestone, or add the issue to a list on your Issue Board. Begin creating issues to see data for this stage.":["La etapa de incidencia muestra el tiempo que toma desde la creación de un tema hasta asignar el tema a un hito, o añadir el tema a una lista en el panel de temas. Empieza a crear temas para ver los datos de esta etapa."],"The phase of the development lifecycle.":["La etapa del ciclo de vida de desarrollo."],"The pipelines schedule runs pipelines in the future, repeatedly, for specific branches or tags. Those scheduled pipelines will inherit limited project access based on their associated user.":["La programación de pipelines ejecuta pipelines en el futuro, repetidamente, para ramas o etiquetas específicas. Los pipelines programados heredarán acceso limitado al proyecto basado en su usuario asociado."],"The planning stage shows the time from the previous step to pushing your first commit. This time will be added automatically once you push your first commit.":["La etapa de planificación muestra el tiempo desde el paso anterior hasta el envío de tu primera confirmación. Este tiempo se añadirá automáticamente una vez que usted envíe el primer cambio."],"The production stage shows the total time it takes between creating an issue and deploying the code to production. The data will be automatically added once you have completed the full idea to production cycle.":["La etapa de producción muestra el tiempo total que tarda entre la creación de una incidencia y el despliegue del código a producción. Los datos se añadirán automáticamente una vez haya finalizado por completo el ciclo de idea a producción."],"The project can be accessed by any logged in user.":["El proyecto puede ser accedido por cualquier usuario conectado."],"The project can be accessed without any authentication.":["El proyecto puede accederse sin ninguna autenticación."],"The repository for this project does not exist.":["El repositorio para este proyecto no existe."],"The review stage shows the time from creating the merge request to merging it. The data will automatically be added after you merge your first merge request.":["La etapa de revisión muestra el tiempo desde la creación de la solicitud de fusión hasta que los cambios se fusionaron. Los datos se añadirán automáticamente después de fusionar su primera solicitud de fusión."],"The staging stage shows the time between merging the MR and deploying code to the production environment. The data will be automatically added once you deploy to production for the first time.":["La etapa de puesta en escena muestra el tiempo entre la fusión y el despliegue de código en el entorno de producción. Los datos se añadirán automáticamente una vez que se despliega a producción por primera vez."],"The testing stage shows the time GitLab CI takes to run every pipeline for the related merge request. The data will automatically be added after your first pipeline finishes running.":["La etapa de pruebas muestra el tiempo que GitLab CI toma para ejecutar cada pipeline para la solicitud de fusión relacionada. Los datos se añadirán automáticamente luego de que el primer pipeline termine de ejecutarse."],"The time taken by each data entry gathered by that stage.":["El tiempo utilizado por cada entrada de datos obtenido por esa etapa."],"The value lying at the midpoint of a series of observed values. E.g., between 3, 5, 9, the median is 5. Between 3, 5, 7, 8, the median is (5+7)/2 = 6.":["El valor en el punto medio de una serie de valores observados. Por ejemplo, entre 3, 5, 9, la mediana es 5. Entre 3, 5, 7, 8, la mediana es (5 + 7) / 2 = 6."],"This means you can not push code until you create an empty repository or import existing one.":["Esto significa que no puede enviar código hasta que cree un repositorio vacío o importe uno existente."],"Time before an issue gets scheduled":["Tiempo antes de que una incidencia sea programada"],"Time before an issue starts implementation":["Tiempo antes de que empieze la implementación de una incidencia"],"Time between merge request creation and merge/close":["Tiempo entre la creación de la solicitud de fusión y la integración o cierre de ésta"],"Time until first merge request":["Tiempo hasta la primera solicitud de fusión"],"Timeago|%s days ago":["hace %s días"],"Timeago|%s days remaining":["%s días restantes"],"Timeago|%s hours remaining":["%s horas restantes"],"Timeago|%s minutes ago":["hace %s minutos"],"Timeago|%s minutes remaining":["%s minutos restantes"],"Timeago|%s months ago":["hace %s meses"],"Timeago|%s months remaining":["%s meses restantes"],"Timeago|%s seconds remaining":["%s segundos restantes"],"Timeago|%s weeks ago":["hace %s semanas"],"Timeago|%s weeks remaining":["%s semanas restantes"],"Timeago|%s years ago":["hace %s años"],"Timeago|%s years remaining":["%s años restantes"],"Timeago|1 day remaining":["1 día restante"],"Timeago|1 hour remaining":["1 hora restante"],"Timeago|1 minute remaining":["1 minuto restante"],"Timeago|1 month remaining":["1 mes restante"],"Timeago|1 week remaining":["1 semana restante"],"Timeago|1 year remaining":["1 año restante"],"Timeago|Past due":["Atrasado"],"Timeago|a day ago":["hace un día"],"Timeago|a month ago":["hace un mes"],"Timeago|a week ago":["hace una semana"],"Timeago|a while":["hace un momento"],"Timeago|a year ago":["hace un año"],"Timeago|about %s hours ago":["hace alrededor de %s horas"],"Timeago|about a minute ago":["hace alrededor de 1 minuto"],"Timeago|about an hour ago":["hace alrededor de 1 hora"],"Timeago|in %s days":["en %s días"],"Timeago|in %s hours":["en %s horas"],"Timeago|in %s minutes":["en %s minutos"],"Timeago|in %s months":["en %s meses"],"Timeago|in %s seconds":["en %s segundos"],"Timeago|in %s weeks":["en %s semanas"],"Timeago|in %s years":["en %s años"],"Timeago|in 1 day":["en 1 día"],"Timeago|in 1 hour":["en 1 hora"],"Timeago|in 1 minute":["en 1 minuto"],"Timeago|in 1 month":["en 1 mes"],"Timeago|in 1 week":["en 1 semana"],"Timeago|in 1 year":["en 1 año"],"Timeago|less than a minute ago":["hace menos de 1 minuto"],"Time|hr":["hr","hrs"],"Time|min":["min","mins"],"Time|s":["s"],"Total Time":["Tiempo Total"],"Total test time for all commits/merges":["Tiempo total de pruebas para todos los cambios o integraciones"],"Unstar":["No Destacar"],"Upload New File":["Subir nuevo archivo"],"Upload file":["Subir archivo"],"Use your global notification setting":["Utiliza tu configuración de notificación global"],"VisibilityLevel|Internal":["Interno"],"VisibilityLevel|Private":["Privado"],"VisibilityLevel|Public":["Público"],"Want to see the data? Please ask an administrator for access.":["¿Quieres ver los datos? Por favor pide acceso al administrador."],"We don't have enough data to show this stage.":["No hay suficientes datos para mostrar en esta etapa."],"Withdraw Access Request":["Retirar Solicitud de Acceso"],"You are going to remove %{project_name_with_namespace}.\\nRemoved project CANNOT be restored!\\nAre you ABSOLUTELY sure?":["Va a eliminar %{project_name_with_namespace}.\\n¡El proyecto eliminado NO puede ser restaurado!\\n¿Estás TOTALMENTE seguro?"],"You are going to remove the fork relationship to source project %{forked_from_project}. Are you ABSOLUTELY sure?":["Vas a eliminar el enlace de la bifurcación con el proyecto original %{forked_from_project}. ¿Estás TOTALMENTE seguro?"],"You are going to transfer %{project_name_with_namespace} to another owner. Are you ABSOLUTELY sure?":["Vas a transferir %{project_name_with_namespace} a otro propietario. ¿Estás TOTALMENTE seguro?"],"You can only add files when you are on a branch":["Solo puedes agregar archivos cuando estás en una rama"],"You must sign in to star a project":["Debes iniciar sesión para destacar un proyecto"],"You need permission.":["Necesitas permisos."],"You will not get any notifications via email":["No recibirás ninguna notificación por correo electrónico"],"You will only receive notifications for the events you choose":["Solo recibirás notificaciones de los eventos que elijas"],"You will only receive notifications for threads you have participated in":["Solo recibirás notificaciones de los temas en los que has participado"],"You will receive notifications for any activity":["Recibirás notificaciones por cualquier actividad"],"You will receive notifications only for comments in which you were @mentioned":["Recibirás notificaciones solo para los comentarios en los que se te mencionó"],"You won't be able to pull or push project code via %{protocol} until you %{set_password_link} on your account":["No podrás actualizar o enviar código al proyecto a través de %{protocol} hasta que %{set_password_link} en tu cuenta"],"You won't be able to pull or push project code via SSH until you %{add_ssh_key_link} to your profile":["No podrás actualizar o enviar código al proyecto a través de SSH hasta que %{add_ssh_key_link} en su perfil"],"Your name":["Tu nombre"],"day":["día","días"],"new merge request":["nueva solicitud de fusión"],"notification emails":["correos electrónicos de notificación"],"parent":["padre","padres"]}}}; \ No newline at end of file
+var locales = locales || {}; locales['es'] = {"domain":"app","locale_data":{"app":{"":{"Project-Id-Version":"gitlab 1.0.0","Report-Msgid-Bugs-To":"","PO-Revision-Date":"2017-06-21 12:09-0500","Language-Team":"Spanish","Language":"es","MIME-Version":"1.0","Content-Type":"text/plain; charset=UTF-8","Content-Transfer-Encoding":"8bit","Plural-Forms":"nplurals=2; plural=n != 1;","Last-Translator":"Bob Van Landuyt <bob@gitlab.com>","X-Generator":"Poedit 2.0.2","lang":"es","domain":"app","plural_forms":"nplurals=2; plural=n != 1;"},"%d additional commit has been omitted to prevent performance issues.":["%d cambio adicional ha sido omitido para evitar problemas de rendimiento.","%d cambios adicionales han sido omitidos para evitar problemas de rendimiento."],"%d commit":["%d cambio","%d cambios"],"%{commit_author_link} committed %{commit_timeago}":["%{commit_author_link} cambió %{commit_timeago}"],"About auto deploy":["Acerca del auto despliegue"],"Active":["Activo"],"Activity":["Actividad"],"Add Changelog":["Agregar Changelog"],"Add Contribution guide":["Agregar guía de contribución"],"Add License":["Agregar Licencia"],"Add an SSH key to your profile to pull or push via SSH.":["Agregar una clave SSH a tu perfil para actualizar o enviar a través de SSH."],"Add new directory":["Agregar nuevo directorio"],"Archived project! Repository is read-only":["¡Proyecto archivado! El repositorio es de solo lectura"],"Are you sure you want to delete this pipeline schedule?":["¿Estás seguro que deseas eliminar esta programación del pipeline?"],"Attach a file by drag &amp; drop or %{upload_link}":["Adjunte un archivo arrastrando &amp; soltando o %{upload_link}"],"Branch":["Rama","Ramas"],"Branch <strong>%{branch_name}</strong> was created. To set up auto deploy, choose a GitLab CI Yaml template and commit your changes. %{link_to_autodeploy_doc}":["La rama <strong>%{branch_name}</strong> fue creada. Para configurar el auto despliegue, escoge una plantilla Yaml para GitLab CI y envía tus cambios. %{link_to_autodeploy_doc}"],"BranchSwitcherPlaceholder|Search branches":["Buscar ramas"],"BranchSwitcherTitle|Switch branch":["Cambiar rama"],"Branches":["Ramas"],"Browse Directory":["Examinar directorio"],"Browse File":["Examinar archivo"],"Browse Files":["Examinar archivos"],"Browse files":["Examinar archivos"],"ByAuthor|by":["por"],"CI configuration":["Configuración de CI"],"Cancel":["Cancelar"],"ChangeTypeActionLabel|Pick into branch":["Escoger en la rama"],"ChangeTypeActionLabel|Revert in branch":["Revertir en la rama"],"ChangeTypeAction|Cherry-pick":["Cherry-pick"],"ChangeTypeAction|Revert":["Revertir"],"Changelog":["Changelog"],"Charts":["Gráficos"],"Cherry-pick this commit":["Escoger este cambio"],"Cherry-pick this merge request":["Escoger esta solicitud de fusión"],"CiStatusLabel|canceled":["cancelado"],"CiStatusLabel|created":["creado"],"CiStatusLabel|failed":["fallido"],"CiStatusLabel|manual action":["acción manual"],"CiStatusLabel|passed":["pasó"],"CiStatusLabel|passed with warnings":["pasó con advertencias"],"CiStatusLabel|pending":["pendiente"],"CiStatusLabel|skipped":["omitido"],"CiStatusLabel|waiting for manual action":["esperando acción manual"],"CiStatusText|blocked":["bloqueado"],"CiStatusText|canceled":["cancelado"],"CiStatusText|created":["creado"],"CiStatusText|failed":["fallado"],"CiStatusText|manual":["manual"],"CiStatusText|passed":["pasó"],"CiStatusText|pending":["pendiente"],"CiStatusText|skipped":["omitido"],"CiStatus|running":["en ejecución"],"Commit":["Cambio","Cambios"],"Commit message":["Mensaje del cambio"],"CommitBoxTitle|Commit":["Cambio"],"CommitMessage|Add %{file_name}":["Agregar %{file_name}"],"Commits":["Cambios"],"Commits feed":["Feed de cambios"],"Commits|History":["Historial"],"Committed by":["Enviado por"],"Compare":["Comparar"],"Contribution guide":["Guía de contribución"],"Contributors":["Contribuidores"],"Copy URL to clipboard":["Copiar URL al portapapeles"],"Copy commit SHA to clipboard":["Copiar SHA del cambio al portapapeles"],"Create New Directory":["Crear Nuevo Directorio"],"Create directory":["Crear directorio"],"Create empty bare repository":["Crear repositorio vacío"],"Create merge request":["Crear solicitud de fusión"],"Create new...":["Crear nuevo..."],"CreateNewFork|Fork":["Bifurcar"],"CreateTag|Tag":["Etiqueta"],"Cron Timezone":["Zona horaria del Cron"],"Cron syntax":["Sintaxis de Cron"],"Custom notification events":["Eventos de notificaciones personalizadas"],"Custom notification levels are the same as participating levels. With custom notification levels you will also receive notifications for select events. To find out more, check out %{notification_link}.":["Los niveles de notificación personalizados son los mismos que los niveles participantes. Con los niveles de notificación personalizados, también recibirá notificaciones para eventos seleccionados. Para obtener más información, consulte %{notification_link}."],"Cycle Analytics":["Cycle Analytics"],"Cycle Analytics gives an overview of how much time it takes to go from idea to production in your project.":["Cycle Analytics ofrece una visión general de cuánto tiempo tarda en pasar de idea a producción en su proyecto."],"CycleAnalyticsStage|Code":["Código"],"CycleAnalyticsStage|Issue":["Incidencia"],"CycleAnalyticsStage|Plan":["Planificación"],"CycleAnalyticsStage|Production":["Producción"],"CycleAnalyticsStage|Review":["Revisión"],"CycleAnalyticsStage|Staging":["Puesta en escena"],"CycleAnalyticsStage|Test":["Pruebas"],"Define a custom pattern with cron syntax":["Definir un patrón personalizado con la sintaxis de cron"],"Delete":["Eliminar"],"Deploy":["Despliegue","Despliegues"],"Description":["Descripción"],"Directory name":["Nombre del directorio"],"Don't show again":["No mostrar de nuevo"],"Download":["Descargar"],"Download tar":["Descargar tar"],"Download tar.bz2":["Descargar tar.bz2"],"Download tar.gz":["Descargar tar.gz"],"Download zip":["Descargar zip"],"DownloadArtifacts|Download":["Descargar"],"DownloadCommit|Email Patches":["Parches por correo electrónico"],"DownloadCommit|Plain Diff":["Diferencias en texto plano"],"DownloadSource|Download":["Descargar"],"Edit":["Editar"],"Edit Pipeline Schedule %{id}":["Editar Programación del Pipeline %{id}"],"Every day (at 4:00am)":["Todos los días (a las 4:00 am)"],"Every month (on the 1st at 4:00am)":["Todos los meses (el día 1 a las 4:00 am)"],"Every week (Sundays at 4:00am)":["Todas las semanas (domingos a las 4:00 am)"],"Failed to change the owner":["Error al cambiar el propietario"],"Failed to remove the pipeline schedule":["Error al eliminar la programación del pipeline"],"Files":["Archivos"],"Filter by commit message":["Filtrar por mensaje del cambio"],"Find by path":["Buscar por ruta"],"Find file":["Buscar archivo"],"FirstPushedBy|First":["Primer"],"FirstPushedBy|pushed by":["enviado por"],"Fork":["Bifurcación","Bifurcaciones"],"ForkedFromProjectPath|Forked from":["Bifurcado de"],"From issue creation until deploy to production":["Desde la creación de la incidencia hasta el despliegue a producción"],"From merge request merge until deploy to production":["Desde la integración de la solicitud de fusión hasta el despliegue a producción"],"Go to your fork":["Ir a tu bifurcación"],"GoToYourFork|Fork":["Bifurcación"],"Home":["Inicio"],"Housekeeping successfully started":["Servicio de limpieza iniciado con éxito"],"Import repository":["Importar repositorio"],"Interval Pattern":["Patrón de intervalo"],"Introducing Cycle Analytics":["Introducción a Cycle Analytics"],"LFSStatus|Disabled":["Deshabilitado"],"LFSStatus|Enabled":["Habilitado"],"Last %d day":["Último %d día","Últimos %d días"],"Last Pipeline":["Último Pipeline"],"Last Update":["Última actualización"],"Last commit":["Último cambio"],"Learn more in the":["Más información en la"],"Learn more in the|pipeline schedules documentation":["documentación sobre la programación de pipelines"],"Leave group":["Abandonar grupo"],"Leave project":["Abandonar proyecto"],"Limited to showing %d event at most":["Limitado a mostrar máximo %d evento","Limitado a mostrar máximo %d eventos"],"Median":["Mediana"],"MissingSSHKeyWarningLink|add an SSH key":["agregar una clave SSH"],"New Issue":["Nueva incidencia","Nuevas incidencias"],"New Pipeline Schedule":["Nueva Programación del Pipeline"],"New branch":["Nueva rama"],"New directory":["Nuevo directorio"],"New file":["Nuevo archivo"],"New issue":["Nueva incidencia"],"New merge request":["Nueva solicitud de fusión"],"New schedule":["Nueva programación"],"New snippet":["Nuevo fragmento de código"],"New tag":["Nueva etiqueta"],"No repository":["No hay repositorio"],"No schedules":["No hay programaciones"],"Not available":["No disponible"],"Not enough data":["No hay suficientes datos"],"Notification events":["Eventos de notificación"],"NotificationEvent|Close issue":["Cerrar incidencia"],"NotificationEvent|Close merge request":["Cerrar solicitud de fusión"],"NotificationEvent|Failed pipeline":["Pipeline fallido"],"NotificationEvent|Merge merge request":["Integrar solicitud de fusión"],"NotificationEvent|New issue":["Nueva incidencia"],"NotificationEvent|New merge request":["Nueva solicitud de fusión"],"NotificationEvent|New note":["Nueva nota"],"NotificationEvent|Reassign issue":["Reasignar incidencia"],"NotificationEvent|Reassign merge request":["Reasignar solicitud de fusión"],"NotificationEvent|Reopen issue":["Reabrir incidencia"],"NotificationEvent|Successful pipeline":["Pipeline exitoso"],"NotificationLevel|Custom":["Personalizado"],"NotificationLevel|Disabled":["Deshabilitado"],"NotificationLevel|Global":["Global"],"NotificationLevel|On mention":["Cuando me mencionan"],"NotificationLevel|Participate":["Participación"],"NotificationLevel|Watch":["Vigilancia"],"OfSearchInADropdown|Filter":["Filtrar"],"OpenedNDaysAgo|Opened":["Abierto"],"Options":["Opciones"],"Owner":["Propietario"],"Pipeline":["Pipeline"],"Pipeline Health":["Estado del Pipeline"],"Pipeline Schedule":["Programación del Pipeline"],"Pipeline Schedules":["Programaciones de los Pipelines"],"PipelineSchedules|Activated":["Activado"],"PipelineSchedules|Active":["Activos"],"PipelineSchedules|All":["Todos"],"PipelineSchedules|Inactive":["Inactivos"],"PipelineSchedules|Next Run":["Próxima Ejecución"],"PipelineSchedules|None":["Ninguno"],"PipelineSchedules|Provide a short description for this pipeline":["Proporcione una breve descripción para este pipeline"],"PipelineSchedules|Take ownership":["Tomar posesión"],"PipelineSchedules|Target":["Destino"],"PipelineSheduleIntervalPattern|Custom":["Personalizado"],"Pipeline|with stage":["con etapa"],"Pipeline|with stages":["con etapas"],"Project '%{project_name}' queued for deletion.":["Proyecto ‘%{project_name}’ en cola para eliminación."],"Project '%{project_name}' was successfully created.":["Proyecto ‘%{project_name}’ fue creado satisfactoriamente."],"Project '%{project_name}' was successfully updated.":["Proyecto ‘%{project_name}’ fue actualizado satisfactoriamente."],"Project '%{project_name}' will be deleted.":["Proyecto ‘%{project_name}’ será eliminado."],"Project access must be granted explicitly to each user.":["El acceso al proyecto debe concederse explícitamente a cada usuario."],"Project export could not be deleted.":["No se pudo eliminar la exportación del proyecto."],"Project export has been deleted.":["La exportación del proyecto ha sido eliminada."],"Project export link has expired. Please generate a new export from your project settings.":["El enlace de exportación del proyecto ha caducado. Por favor, genera una nueva exportación desde la configuración del proyecto."],"Project export started. A download link will be sent by email.":["Se inició la exportación del proyecto. Se enviará un enlace de descarga por correo electrónico."],"Project home":["Inicio del proyecto"],"ProjectFeature|Disabled":["Deshabilitada"],"ProjectFeature|Everyone with access":["Todos con acceso"],"ProjectFeature|Only team members":["Solo miembros del equipo"],"ProjectFileTree|Name":["Nombre"],"ProjectLastActivity|Never":["Nunca"],"ProjectLifecycle|Stage":["Etapa"],"ProjectNetworkGraph|Graph":["Historial gráfico"],"Read more":["Leer más"],"Readme":["Léeme"],"RefSwitcher|Branches":["Ramas"],"RefSwitcher|Tags":["Etiquetas"],"Related Commits":["Cambios Relacionados"],"Related Deployed Jobs":["Trabajos Desplegados Relacionados"],"Related Issues":["Incidencias Relacionadas"],"Related Jobs":["Trabajos Relacionados"],"Related Merge Requests":["Solicitudes de fusión Relacionadas"],"Related Merged Requests":["Solicitudes de fusión Relacionadas"],"Remind later":["Recordar después"],"Remove project":["Eliminar proyecto"],"Request Access":["Solicitar acceso"],"Revert this commit":["Revertir este cambio"],"Revert this merge request":["Revertir esta solicitud de fusión"],"Save pipeline schedule":["Guardar programación del pipeline"],"Schedule a new pipeline":["Programar un nuevo pipeline"],"Scheduling Pipelines":["Programación de Pipelines"],"Search branches and tags":["Buscar ramas y etiquetas"],"Select Archive Format":["Seleccionar formato de archivo"],"Select a timezone":["Selecciona una zona horaria"],"Select target branch":["Selecciona una rama de destino"],"Set a password on your account to pull or push via %{protocol}":["Establezca una contraseña en su cuenta para actualizar o enviar a través de %{protocol}"],"Set up CI":["Configurar CI"],"Set up Koding":["Configurar Koding"],"Set up auto deploy":["Configurar auto despliegue"],"SetPasswordToCloneLink|set a password":["establecer una contraseña"],"Showing %d event":["Mostrando %d evento","Mostrando %d eventos"],"Source code":["Código fuente"],"StarProject|Star":["Destacar"],"Start a %{new_merge_request} with these changes":["Iniciar una %{new_merge_request} con estos cambios"],"Switch branch/tag":["Cambiar rama/etiqueta"],"Tag":["Etiqueta","Etiquetas"],"Tags":["Etiquetas"],"Target Branch":["Rama de destino"],"The coding stage shows the time from the first commit to creating the merge request. The data will automatically be added here once you create your first merge request.":["La etapa de desarrollo muestra el tiempo desde el primer cambio hasta la creación de la solicitud de fusión. Los datos serán automáticamente incorporados aquí una vez creada tu primera solicitud de fusión."],"The collection of events added to the data gathered for that stage.":["La colección de eventos agregados a los datos recopilados para esa etapa."],"The fork relationship has been removed.":["La relación con la bifurcación se ha eliminado."],"The issue stage shows the time it takes from creating an issue to assigning the issue to a milestone, or add the issue to a list on your Issue Board. Begin creating issues to see data for this stage.":["La etapa de incidencia muestra el tiempo que toma desde la creación de un tema hasta asignar el tema a un hito, o añadir el tema a una lista en el panel de temas. Empieza a crear temas para ver los datos de esta etapa."],"The phase of the development lifecycle.":["La etapa del ciclo de vida de desarrollo."],"The pipelines schedule runs pipelines in the future, repeatedly, for specific branches or tags. Those scheduled pipelines will inherit limited project access based on their associated user.":["La programación de pipelines ejecuta pipelines en el futuro, repetidamente, para ramas o etiquetas específicas. Los pipelines programados heredarán acceso limitado al proyecto basado en su usuario asociado."],"The planning stage shows the time from the previous step to pushing your first commit. This time will be added automatically once you push your first commit.":["La etapa de planificación muestra el tiempo desde el paso anterior hasta el envío de tu primera confirmación. Este tiempo se añadirá automáticamente una vez que usted envíe el primer cambio."],"The production stage shows the total time it takes between creating an issue and deploying the code to production. The data will be automatically added once you have completed the full idea to production cycle.":["La etapa de producción muestra el tiempo total que tarda entre la creación de una incidencia y el despliegue del código a producción. Los datos se añadirán automáticamente una vez haya finalizado por completo el ciclo de idea a producción."],"The project can be accessed by any logged in user.":["El proyecto puede ser accedido por cualquier usuario conectado."],"The project can be accessed without any authentication.":["El proyecto puede accederse sin ninguna autenticación."],"The repository for this project does not exist.":["El repositorio para este proyecto no existe."],"The review stage shows the time from creating the merge request to merging it. The data will automatically be added after you merge your first merge request.":["La etapa de revisión muestra el tiempo desde la creación de la solicitud de fusión hasta que los cambios se fusionaron. Los datos se añadirán automáticamente después de fusionar su primera solicitud de fusión."],"The staging stage shows the time between merging the MR and deploying code to the production environment. The data will be automatically added once you deploy to production for the first time.":["La etapa de puesta en escena muestra el tiempo entre la fusión y el despliegue de código en el entorno de producción. Los datos se añadirán automáticamente una vez que se despliega a producción por primera vez."],"The testing stage shows the time GitLab CI takes to run every pipeline for the related merge request. The data will automatically be added after your first pipeline finishes running.":["La etapa de pruebas muestra el tiempo que GitLab CI toma para ejecutar cada pipeline para la solicitud de fusión relacionada. Los datos se añadirán automáticamente luego de que el primer pipeline termine de ejecutarse."],"The time taken by each data entry gathered by that stage.":["El tiempo utilizado por cada entrada de datos obtenido por esa etapa."],"The value lying at the midpoint of a series of observed values. E.g., between 3, 5, 9, the median is 5. Between 3, 5, 7, 8, the median is (5+7)/2 = 6.":["El valor en el punto medio de una serie de valores observados. Por ejemplo, entre 3, 5, 9, la mediana es 5. Entre 3, 5, 7, 8, la mediana es (5 + 7) / 2 = 6."],"This means you can not push code until you create an empty repository or import existing one.":["Esto significa que no puede enviar código hasta que cree un repositorio vacío o importe uno existente."],"Time before an issue gets scheduled":["Tiempo antes de que una incidencia sea programada"],"Time before an issue starts implementation":["Tiempo antes de que empieze la implementación de una incidencia"],"Time between merge request creation and merge/close":["Tiempo entre la creación de la solicitud de fusión y la integración o cierre de ésta"],"Time until first merge request":["Tiempo hasta la primera solicitud de fusión"],"Timeago|%s days ago":["hace %s días"],"Timeago|%s days remaining":["%s días restantes"],"Timeago|%s hours remaining":["%s horas restantes"],"Timeago|%s minutes ago":["hace %s minutos"],"Timeago|%s minutes remaining":["%s minutos restantes"],"Timeago|%s months ago":["hace %s meses"],"Timeago|%s months remaining":["%s meses restantes"],"Timeago|%s seconds remaining":["%s segundos restantes"],"Timeago|%s weeks ago":["hace %s semanas"],"Timeago|%s weeks remaining":["%s semanas restantes"],"Timeago|%s years ago":["hace %s años"],"Timeago|%s years remaining":["%s años restantes"],"Timeago|1 day remaining":["1 día restante"],"Timeago|1 hour remaining":["1 hora restante"],"Timeago|1 minute remaining":["1 minuto restante"],"Timeago|1 month remaining":["1 mes restante"],"Timeago|1 week remaining":["1 semana restante"],"Timeago|1 year remaining":["1 año restante"],"Timeago|Past due":["Atrasado"],"Timeago|a day ago":["hace un día"],"Timeago|a month ago":["hace un mes"],"Timeago|a week ago":["hace una semana"],"Timeago|a while":["hace un momento"],"Timeago|a year ago":["hace un año"],"Timeago|about %s hours ago":["hace alrededor de %s horas"],"Timeago|about a minute ago":["hace alrededor de 1 minuto"],"Timeago|about an hour ago":["hace alrededor de 1 hora"],"Timeago|in %s days":["en %s días"],"Timeago|in %s hours":["en %s horas"],"Timeago|in %s minutes":["en %s minutos"],"Timeago|in %s months":["en %s meses"],"Timeago|in %s seconds":["en %s segundos"],"Timeago|in %s weeks":["en %s semanas"],"Timeago|in %s years":["en %s años"],"Timeago|in 1 day":["en 1 día"],"Timeago|in 1 hour":["en 1 hora"],"Timeago|in 1 minute":["en 1 minuto"],"Timeago|in 1 month":["en 1 mes"],"Timeago|in 1 week":["en 1 semana"],"Timeago|in 1 year":["en 1 año"],"Timeago|less than a minute ago":["hace menos de 1 minuto"],"Time|hr":["hr","hrs"],"Time|min":["min","mins"],"Time|s":["s"],"Total Time":["Tiempo Total"],"Total test time for all commits/merges":["Tiempo total de pruebas para todos los cambios o integraciones"],"Unstar":["No Destacar"],"Upload New File":["Subir nuevo archivo"],"Upload file":["Subir archivo"],"UploadLink|click to upload":["Hacer clic para subir"],"Use your global notification setting":["Utiliza tu configuración de notificación global"],"View open merge request":["Ver solicitud de fusión abierta"],"VisibilityLevel|Internal":["Interno"],"VisibilityLevel|Private":["Privado"],"VisibilityLevel|Public":["Público"],"Want to see the data? Please ask an administrator for access.":["¿Quieres ver los datos? Por favor pide acceso al administrador."],"We don't have enough data to show this stage.":["No hay suficientes datos para mostrar en esta etapa."],"Withdraw Access Request":["Retirar Solicitud de Acceso"],"You are going to remove %{project_name_with_namespace}.\\nRemoved project CANNOT be restored!\\nAre you ABSOLUTELY sure?":["Va a eliminar %{project_name_with_namespace}.\\n¡El proyecto eliminado NO puede ser restaurado!\\n¿Estás TOTALMENTE seguro?"],"You are going to remove the fork relationship to source project %{forked_from_project}. Are you ABSOLUTELY sure?":["Vas a eliminar el enlace de la bifurcación con el proyecto original %{forked_from_project}. ¿Estás TOTALMENTE seguro?"],"You are going to transfer %{project_name_with_namespace} to another owner. Are you ABSOLUTELY sure?":["Vas a transferir %{project_name_with_namespace} a otro propietario. ¿Estás TOTALMENTE seguro?"],"You can only add files when you are on a branch":["Solo puedes agregar archivos cuando estás en una rama"],"You have reached your project limit":["Has alcanzado el límite de tu proyecto"],"You must sign in to star a project":["Debes iniciar sesión para destacar un proyecto"],"You need permission.":["Necesitas permisos."],"You will not get any notifications via email":["No recibirás ninguna notificación por correo electrónico"],"You will only receive notifications for the events you choose":["Solo recibirás notificaciones de los eventos que elijas"],"You will only receive notifications for threads you have participated in":["Solo recibirás notificaciones de los temas en los que has participado"],"You will receive notifications for any activity":["Recibirás notificaciones por cualquier actividad"],"You will receive notifications only for comments in which you were @mentioned":["Recibirás notificaciones solo para los comentarios en los que se te mencionó"],"You won't be able to pull or push project code via %{protocol} until you %{set_password_link} on your account":["No podrás actualizar o enviar código al proyecto a través de %{protocol} hasta que %{set_password_link} en tu cuenta"],"You won't be able to pull or push project code via SSH until you %{add_ssh_key_link} to your profile":["No podrás actualizar o enviar código al proyecto a través de SSH hasta que %{add_ssh_key_link} en su perfil"],"Your name":["Tu nombre"],"day":["día","días"],"new merge request":["nueva solicitud de fusión"],"notification emails":["correos electrónicos de notificación"],"parent":["padre","padres"]}}}; \ No newline at end of file
diff --git a/app/assets/javascripts/locale/zh_CN/app.js b/app/assets/javascripts/locale/zh_CN/app.js
index d1335cfbc0f..9c28e4e4627 100644
--- a/app/assets/javascripts/locale/zh_CN/app.js
+++ b/app/assets/javascripts/locale/zh_CN/app.js
@@ -1 +1 @@
-var locales = locales || {}; locales['zh_CN'] = {"domain":"app","locale_data":{"app":{"":{"Project-Id-Version":"gitlab 1.0.0","Report-Msgid-Bugs-To":"","PO-Revision-Date":"2017-05-04 19:24-0500","Last-Translator":"HuangTao <htve@outlook.com>, 2017","Language-Team":"Chinese (China) (https://www.transifex.com/gitlab-zh/teams/75177/zh_CN/)","MIME-Version":"1.0","Content-Type":"text/plain; charset=UTF-8","Content-Transfer-Encoding":"8bit","Language":"zh_CN","Plural-Forms":"nplurals=1; plural=0;","lang":"zh_CN","domain":"app","plural_forms":"nplurals=1; plural=0;"},"Are you sure you want to delete this pipeline schedule?":[""],"ByAuthor|by":["作者:"],"Cancel":[""],"Commit":["提交"],"Cron Timezone":[""],"Cycle Analytics gives an overview of how much time it takes to go from idea to production in your project.":["周期分析概述了项目从想法到产品实现的各阶段所需的时间。"],"CycleAnalyticsStage|Code":["编码"],"CycleAnalyticsStage|Issue":["议题"],"CycleAnalyticsStage|Plan":["计划"],"CycleAnalyticsStage|Production":["生产"],"CycleAnalyticsStage|Review":["评审"],"CycleAnalyticsStage|Staging":["预发布"],"CycleAnalyticsStage|Test":["测试"],"Delete":[""],"Deploy":["部署"],"Description":[""],"Edit":[""],"Edit Pipeline Schedule %{id}":[""],"Failed to change the owner":[""],"Failed to remove the pipeline schedule":[""],"Filter":[""],"FirstPushedBy|First":["首次推送"],"FirstPushedBy|pushed by":["推送者:"],"From issue creation until deploy to production":["从创建议题到部署至生产环境"],"From merge request merge until deploy to production":["从合并请求被合并后到部署至生产环境"],"Interval Pattern":[""],"Introducing Cycle Analytics":["周期分析简介"],"Last %d day":["最后 %d 天"],"Last Pipeline":[""],"Limited to showing %d event at most":["最多显示 %d 个事件"],"Median":["中位数"],"New Issue":["新议题"],"New Pipeline Schedule":[""],"No schedules":[""],"Not available":["数据不足"],"Not enough data":["数据不足"],"OpenedNDaysAgo|Opened":["开始于"],"Owner":[""],"Pipeline Health":["流水线健康指标"],"Pipeline Schedule":[""],"Pipeline Schedules":[""],"PipelineSchedules|Activated":[""],"PipelineSchedules|Active":[""],"PipelineSchedules|All":[""],"PipelineSchedules|Inactive":[""],"PipelineSchedules|Next Run":[""],"PipelineSchedules|None":[""],"PipelineSchedules|Provide a short description for this pipeline":[""],"PipelineSchedules|Take ownership":[""],"PipelineSchedules|Target":[""],"ProjectLifecycle|Stage":["项目生命周期"],"Read more":["了解更多"],"Related Commits":["相关的提交"],"Related Deployed Jobs":["相关的部署作业"],"Related Issues":["相关的议题"],"Related Jobs":["相关的作业"],"Related Merge Requests":["相关的合并请求"],"Related Merged Requests":["相关已合并的合并请求"],"Save pipeline schedule":[""],"Schedule a new pipeline":[""],"Select a timezone":[""],"Select target branch":[""],"Showing %d event":["显示 %d 个事件"],"Target Branch":[""],"The coding stage shows the time from the first commit to creating the merge request. The data will automatically be added here once you create your first merge request.":["编码阶段概述了从第一次提交到创建合并请求的时间。创建第一个合并请求后,数据将自动添加到此处。"],"The collection of events added to the data gathered for that stage.":["与该阶段相关的事件。"],"The issue stage shows the time it takes from creating an issue to assigning the issue to a milestone, or add the issue to a list on your Issue Board. Begin creating issues to see data for this stage.":["议题阶段概述了从创建议题到将议题设置里程碑或将议题添加到议题看板的时间。开始创建议题以查看此阶段的数据。"],"The phase of the development lifecycle.":["项目生命周期中的各个阶段。"],"The planning stage shows the time from the previous step to pushing your first commit. This time will be added automatically once you push your first commit.":["计划阶段概述了从议题添加到日程后到推送首次提交的时间。当首次推送提交后,数据将自动添加到此处。"],"The production stage shows the total time it takes between creating an issue and deploying the code to production. The data will be automatically added once you have completed the full idea to production cycle.":["生产阶段概述了从创建一个议题到将代码部署到生产环境的总时间。当完成想法到部署生产的循环,数据将自动添加到此处。"],"The review stage shows the time from creating the merge request to merging it. The data will automatically be added after you merge your first merge request.":["评审阶段概述了从创建合并请求到被合并的时间。当创建第一个合并请求后,数据将自动添加到此处。"],"The staging stage shows the time between merging the MR and deploying code to the production environment. The data will be automatically added once you deploy to production for the first time.":["预发布阶段概述了从合并请求被合并到部署至生产环境的总时间。首次部署到生产环境后,数据将自动添加到此处。"],"The testing stage shows the time GitLab CI takes to run every pipeline for the related merge request. The data will automatically be added after your first pipeline finishes running.":["测试阶段概述了GitLab CI为相关合并请求运行每个流水线所需的时间。当第一个流水线运行完成后,数据将自动添加到此处。"],"The time taken by each data entry gathered by that stage.":["该阶段每条数据所花的时间"],"The value lying at the midpoint of a series of observed values. E.g., between 3, 5, 9, the median is 5. Between 3, 5, 7, 8, the median is (5+7)/2 = 6.":["中位数是一个数列中最中间的值。例如在 3、5、9 之间,中位数是 5。在 3、5、7、8 之间,中位数是 (5 + 7)/ 2 = 6。"],"Time before an issue gets scheduled":["议题被列入日程表的时间"],"Time before an issue starts implementation":["开始进行编码前的时间"],"Time between merge request creation and merge/close":["从创建合并请求到被合并或关闭的时间"],"Time until first merge request":["创建第一个合并请求之前的时间"],"Time|hr":["小时"],"Time|min":["分钟"],"Time|s":["秒"],"Total Time":["总时间"],"Total test time for all commits/merges":["所有提交和合并的总测试时间"],"Want to see the data? Please ask an administrator for access.":["权限不足。如需查看相关数据,请向管理员申请权限。"],"We don't have enough data to show this stage.":["该阶段的数据不足,无法显示。"],"You need permission.":["您需要相关的权限。"],"day":["天"]}}}; \ No newline at end of file
+var locales = locales || {}; locales['zh_CN'] = {"domain":"app","locale_data":{"app":{"":{"Project-Id-Version":"gitlab 1.0.0","Report-Msgid-Bugs-To":"","POT-Creation-Date":"2017-06-15 21:59-0500","MIME-Version":"1.0","Content-Type":"text/plain; charset=UTF-8","Content-Transfer-Encoding":"8bit","PO-Revision-Date":"2017-06-19 09:57-0400","Last-Translator":"Huang Tao <htve@outlook.com>","Language-Team":"Chinese (China) (https://translate.zanata.org/project/view/GitLab)","Language":"zh-CN","X-Generator":"Zanata 3.9.6","Plural-Forms":"nplurals=1; plural=0","lang":"zh_CN","domain":"app","plural_forms":"nplurals=1; plural=0"},"%{commit_author_link} committed %{commit_timeago}":["由 %{commit_author_link} 提交于 %{commit_timeago}"],"About auto deploy":["关于自动部署"],"Active":["启用"],"Activity":["活动"],"Add Changelog":["添加更新日志"],"Add Contribution guide":["添加贡献指南"],"Add License":["添加许可证"],"Add an SSH key to your profile to pull or push via SSH.":["新建一个用于推送或拉取的 SSH 秘钥到账号中。"],"Add new directory":["添加目录"],"Archived project! Repository is read-only":["项目已归档!存储库为只读状态"],"Are you sure you want to delete this pipeline schedule?":["确定要删除此流水线计划吗?"],"Attach a file by drag &amp; drop or %{upload_link}":["拖放文件到此处或者 %{upload_link}"],"Branch":["分支"],"Branch <strong>%{branch_name}</strong> was created. To set up auto deploy, choose a GitLab CI Yaml template and commit your changes. %{link_to_autodeploy_doc}":["已创建分支 <strong>%{branch_name}</strong> 。如需设置自动部署, 请选择合适的 GitLab CI Yaml 模板并提交更改。%{link_to_autodeploy_doc}"],"Branches":["分支"],"Browse files":["浏览文件"],"ByAuthor|by":["作者:"],"CI configuration":["CI 配置"],"Cancel":["取消"],"ChangeTypeActionLabel|Pick into branch":["选择分支"],"ChangeTypeActionLabel|Revert in branch":["还原分支"],"ChangeTypeAction|Cherry-pick":["优选"],"ChangeTypeAction|Revert":["还原"],"Changelog":["更新日志"],"Charts":["统计图"],"Cherry-pick this commit":["优选此提交"],"Cherry-pick this merge request":["优选此合并请求"],"CiStatusLabel|canceled":["已取消"],"CiStatusLabel|created":["已创建"],"CiStatusLabel|failed":["已失败"],"CiStatusLabel|manual action":["手动操作"],"CiStatusLabel|passed":["已通过"],"CiStatusLabel|passed with warnings":["已通过但有警告"],"CiStatusLabel|pending":["等待中"],"CiStatusLabel|skipped":["已跳过"],"CiStatusLabel|waiting for manual action":["等待手动操作"],"CiStatusText|blocked":["已阻塞"],"CiStatusText|canceled":["已取消"],"CiStatusText|created":["已创建"],"CiStatusText|failed":["已失败"],"CiStatusText|manual":["手动操作"],"CiStatusText|passed":["已通过"],"CiStatusText|pending":["等待中"],"CiStatusText|skipped":["已跳过"],"CiStatus|running":["运行中"],"Commit":["提交"],"Commit message":["提交信息"],"CommitBoxTitle|Commit":["提交"],"CommitMessage|Add %{file_name}":["添加 %{file_name}"],"Commits":["提交"],"Commits|History":["历史"],"Committed by":["提交者:"],"Compare":["比较"],"Contribution guide":["贡献指南"],"Contributors":["贡献者"],"Copy URL to clipboard":["复制 URL 到剪贴板"],"Copy commit SHA to clipboard":["复制提交 SHA 的值到剪贴板"],"Create New Directory":["创建新目录"],"Create directory":["创建目录"],"Create empty bare repository":["创建空的存储库"],"Create merge request":["创建合并请求"],"Create new...":["创建..."],"CreateNewFork|Fork":["派生"],"CreateTag|Tag":["标签"],"Cron Timezone":["Cron 时区"],"Cron syntax":["Cron 语法"],"Custom notification events":["自定义通知事件"],"Custom notification levels are the same as participating levels. With custom notification levels you will also receive notifications for select events. To find out more, check out %{notification_link}.":["自定义通知级别继承自参与级别。使用自定义通知级别,您会收到参与级别及选定事件的通知。想了解更多信息,请查看 %{notification_link}."],"Cycle Analytics":["周期分析"],"Cycle Analytics gives an overview of how much time it takes to go from idea to production in your project.":["周期分析概述了项目从想法到产品实现的各阶段所需的时间。"],"CycleAnalyticsStage|Code":["编码"],"CycleAnalyticsStage|Issue":["议题"],"CycleAnalyticsStage|Plan":["计划"],"CycleAnalyticsStage|Production":["生产"],"CycleAnalyticsStage|Review":["评审"],"CycleAnalyticsStage|Staging":["预发布"],"CycleAnalyticsStage|Test":["测试"],"Define a custom pattern with cron syntax":["使用 Cron 语法定义自定义模式"],"Delete":["删除"],"Deploy":["部署"],"Description":["描述"],"Directory name":["目录名称"],"Don't show again":["不再显示"],"Download":["下载"],"Download tar":["下载 tar"],"Download tar.bz2":["下载 tar.bz2"],"Download tar.gz":["下载 tar.gz"],"Download zip":["下载 zip"],"DownloadArtifacts|Download":["下载"],"DownloadCommit|Email Patches":["电子邮件补丁"],"DownloadCommit|Plain Diff":["差异文件"],"DownloadSource|Download":["下载"],"Edit":["编辑"],"Edit Pipeline Schedule %{id}":["编辑 %{id} 流水线计划"],"Every day (at 4:00am)":["每日执行(凌晨 4 点)"],"Every month (on the 1st at 4:00am)":["每月执行(每月 1 日凌晨 4 点)"],"Every week (Sundays at 4:00am)":["每周执行(周日凌晨 4 点)"],"Failed to change the owner":["无法变更所有者"],"Failed to remove the pipeline schedule":["无法删除流水线计划"],"Files":["文件"],"Find by path":["按路径查找"],"Find file":["查找文件"],"FirstPushedBy|First":["首次推送"],"FirstPushedBy|pushed by":["推送者:"],"Fork":["派生"],"ForkedFromProjectPath|Forked from":["派生自"],"From issue creation until deploy to production":["从创建议题到部署至生产环境"],"From merge request merge until deploy to production":["从合并请求被合并后到部署至生产环境"],"Go to your fork":["跳转到派生项目"],"GoToYourFork|Fork":["跳转到派生项目"],"Home":["首页"],"Housekeeping successfully started":["已开始维护"],"Import repository":["导入存储库"],"Interval Pattern":["循环周期"],"Introducing Cycle Analytics":["周期分析简介"],"LFSStatus|Disabled":["停用"],"LFSStatus|Enabled":["启用"],"Last %d day":["最近 %d 天"],"Last Pipeline":["最新流水线"],"Last Update":["最后更新"],"Last commit":["最后提交"],"Learn more in the":["了解更多"],"Learn more in the|pipeline schedules documentation":["流水线计划文档"],"Leave group":["退出群组"],"Leave project":["退出项目"],"Limited to showing %d event at most":["最多显示 %d 个事件"],"Median":["中位数"],"MissingSSHKeyWarningLink|add an SSH key":["新建 SSH 公钥"],"New Issue":["新建议题"],"New Pipeline Schedule":["创建流水线计划"],"New branch":["新建分支"],"New directory":["新建目录"],"New file":["新建文件"],"New issue":["新建议题"],"New merge request":["新建合并请求"],"New schedule":["新建计划"],"New snippet":["新建代码片段"],"New tag":["新建标签"],"No repository":["没有存储库"],"No schedules":["没有计划"],"Not available":["数据不足"],"Not enough data":["数据不足"],"Notification events":["通知事件"],"NotificationEvent|Close issue":["关闭议题"],"NotificationEvent|Close merge request":["关闭合并请求"],"NotificationEvent|Failed pipeline":["流水线失败"],"NotificationEvent|Merge merge request":["合并请求被合并"],"NotificationEvent|New issue":["新建议题"],"NotificationEvent|New merge request":["新建合并请求"],"NotificationEvent|New note":["新建评论"],"NotificationEvent|Reassign issue":["重新指派议题"],"NotificationEvent|Reassign merge request":["重新指派合并请求"],"NotificationEvent|Reopen issue":["重启议题"],"NotificationEvent|Successful pipeline":["流水线成功完成"],"NotificationLevel|Custom":["自定义"],"NotificationLevel|Disabled":["停用"],"NotificationLevel|Global":["全局"],"NotificationLevel|On mention":["提及"],"NotificationLevel|Participate":["参与"],"NotificationLevel|Watch":["关注"],"OfSearchInADropdown|Filter":["筛选"],"OpenedNDaysAgo|Opened":["开始于"],"Options":["操作"],"Owner":["所有者"],"Pipeline":["流水线"],"Pipeline Health":["流水线健康指标"],"Pipeline Schedule":["流水线计划"],"Pipeline Schedules":["流水线计划"],"PipelineSchedules|Activated":["是否启用"],"PipelineSchedules|Active":["已启用"],"PipelineSchedules|All":["所有"],"PipelineSchedules|Inactive":["未启用"],"PipelineSchedules|Next Run":["下次运行时间"],"PipelineSchedules|None":["无"],"PipelineSchedules|Provide a short description for this pipeline":["为此流水线提供简短描述"],"PipelineSchedules|Take ownership":["取得所有者"],"PipelineSchedules|Target":["目标"],"PipelineSheduleIntervalPattern|Custom":["自定义"],"Pipeline|with stage":["于阶段"],"Pipeline|with stages":["于阶段"],"Project '%{project_name}' queued for deletion.":["项目 '%{project_name}' 已进入删除队列。"],"Project '%{project_name}' was successfully created.":["项目 '%{project_name}' 已创建成功。"],"Project '%{project_name}' was successfully updated.":["项目 '%{project_name}' 已更新完成。"],"Project '%{project_name}' will be deleted.":["项目 '%{project_name}' 将被删除。"],"Project access must be granted explicitly to each user.":["项目访问权限必须明确授权给每个用户。"],"Project export could not be deleted.":["无法删除项目导出。"],"Project export has been deleted.":["项目导出已被删除。"],"Project export link has expired. Please generate a new export from your project settings.":["项目导出链接已过期。请从项目设置中重新生成项目导出。"],"Project export started. A download link will be sent by email.":["项目导出已开始。下载链接将通过电子邮件发送。"],"Project home":["项目首页"],"ProjectFeature|Disabled":["停用"],"ProjectFeature|Everyone with access":["任何对项目有访问权的人"],"ProjectFeature|Only team members":["只限团队成员"],"ProjectFileTree|Name":["名称"],"ProjectLastActivity|Never":["从未"],"ProjectLifecycle|Stage":["阶段"],"ProjectNetworkGraph|Graph":["分支图"],"Read more":["了解更多"],"Readme":["自述文件"],"RefSwitcher|Branches":["分支"],"RefSwitcher|Tags":["标签"],"Related Commits":["相关的提交"],"Related Deployed Jobs":["相关的部署作业"],"Related Issues":["相关的议题"],"Related Jobs":["相关的作业"],"Related Merge Requests":["相关的合并请求"],"Related Merged Requests":["相关已合并的合并请求"],"Remind later":["稍后提醒"],"Remove project":["删除项目"],"Request Access":["申请权限"],"Revert this commit":["还原此提交"],"Revert this merge request":["还原此合并请求"],"Save pipeline schedule":["保存流水线计划"],"Schedule a new pipeline":["新建流水线计划"],"Scheduling Pipelines":["流水线计划"],"Search branches and tags":["搜索分支和标签"],"Select Archive Format":["选择下载格式"],"Select a timezone":["选择时区"],"Select target branch":["选择目标分支"],"Set a password on your account to pull or push via %{protocol}":["为账号创建一个用于推送或拉取的 %{protocol} 密码。"],"Set up CI":["设置 CI"],"Set up Koding":["设置 Koding"],"Set up auto deploy":["设置自动部署"],"SetPasswordToCloneLink|set a password":["设置密码"],"Showing %d event":["显示 %d 个事件"],"Source code":["源代码"],"StarProject|Star":["星标"],"Start a %{new_merge_request} with these changes":["由此更改 %{new_merge_request}"],"Switch branch/tag":["切换分支/标签"],"Tag":["标签"],"Tags":["标签"],"Target Branch":["目标分支"],"The coding stage shows the time from the first commit to creating the merge request. The data will automatically be added here once you create your first merge request.":["编码阶段概述了从第一次提交到创建合并请求的时间。创建第一个合并请求后,数据将自动添加到此处。"],"The collection of events added to the data gathered for that stage.":["与该阶段相关的事件集合。"],"The fork relationship has been removed.":["派生关系已被删除。"],"The issue stage shows the time it takes from creating an issue to assigning the issue to a milestone, or add the issue to a list on your Issue Board. Begin creating issues to see data for this stage.":["议题阶段概述了从创建议题到将议题添加到里程碑或议题看板所花费的时间。创建第一个议题后,数据将自动添加到此处.。"],"The phase of the development lifecycle.":["项目生命周期中的各个阶段。"],"The pipelines schedule runs pipelines in the future, repeatedly, for specific branches or tags. Those scheduled pipelines will inherit limited project access based on their associated user.":["流水线计划会周期性重复运行指定分支或标签的流水线。这些流水线将根据其关联用户继承有限的项目访问权限。"],"The planning stage shows the time from the previous step to pushing your first commit. This time will be added automatically once you push your first commit.":["计划阶段概述了从议题添加到日程到推送首次提交的时间。当首次推送提交后,数据将自动添加到此处。"],"The production stage shows the total time it takes between creating an issue and deploying the code to production. The data will be automatically added once you have completed the full idea to production cycle.":["生产阶段概述了从创建一个议题到将代码部署到生产环境的总时间。当完成想法到部署生产的循环,数据将自动添加到此处。"],"The project can be accessed by any logged in user.":["该项目允许已登录的用户访问。"],"The project can be accessed without any authentication.":["该项目允许任何人访问。"],"The repository for this project does not exist.":["此项目的存储库不存在。"],"The review stage shows the time from creating the merge request to merging it. The data will automatically be added after you merge your first merge request.":["评审阶段概述了从创建合并请求到被合并的时间。当创建第一个合并请求后,数据将自动添加到此处。"],"The staging stage shows the time between merging the MR and deploying code to the production environment. The data will be automatically added once you deploy to production for the first time.":["预发布阶段概述了从合并请求被合并到部署至生产环境的总时间。首次部署到生产环境后,数据将自动添加到此处。"],"The testing stage shows the time GitLab CI takes to run every pipeline for the related merge request. The data will automatically be added after your first pipeline finishes running.":["测试阶段概述了 GitLab CI 为相关合并请求运行每个流水线所需的时间。当第一个流水线运行完成后,数据将自动添加到此处。"],"The time taken by each data entry gathered by that stage.":["该阶段每条数据所花的时间"],"The value lying at the midpoint of a series of observed values. E.g., between 3, 5, 9, the median is 5. Between 3, 5, 7, 8, the median is (5+7)/2 = 6.":["中位数是一个数列中最中间的值。例如在 3、5、9 之间,中位数是 5。在 3、5、7、8 之间,中位数是 (5 + 7)/ 2 = 6。"],"This means you can not push code until you create an empty repository or import existing one.":["在创建一个空的存储库或导入现有存储库之前,将无法推送代码。"],"Time before an issue gets scheduled":["议题被列入日程表的时间"],"Time before an issue starts implementation":["开始进行编码前的时间"],"Time between merge request creation and merge/close":["从创建合并请求到被合并或关闭的时间"],"Time until first merge request":["创建第一个合并请求之前的时间"],"Timeago|%s days ago":[" %s 天前"],"Timeago|%s days remaining":["剩余 %s 天"],"Timeago|%s hours remaining":["剩余 %s 小时"],"Timeago|%s minutes ago":[" %s 分钟前"],"Timeago|%s minutes remaining":["剩余 %s 分钟"],"Timeago|%s months ago":[" %s 个月前"],"Timeago|%s months remaining":["剩余 %s 月"],"Timeago|%s seconds remaining":["剩余 %s 秒"],"Timeago|%s weeks ago":[" %s 星期前"],"Timeago|%s weeks remaining":["剩余 %s 星期"],"Timeago|%s years ago":[" %s 年前"],"Timeago|%s years remaining":["剩余 %s 年"],"Timeago|1 day remaining":["剩余 1 天"],"Timeago|1 hour remaining":["剩余 1 小时"],"Timeago|1 minute remaining":["剩余 1 分钟"],"Timeago|1 month remaining":["剩余 1 个月"],"Timeago|1 week remaining":["剩余 1 星期"],"Timeago|1 year remaining":["剩余 1 年"],"Timeago|Past due":["逾期"],"Timeago|a day ago":[" 1 天前"],"Timeago|a month ago":[" 1 个月前"],"Timeago|a week ago":[" 1 星期前"],"Timeago|a while":["刚刚"],"Timeago|a year ago":[" 1 年前"],"Timeago|about %s hours ago":["约 %s 小时前"],"Timeago|about a minute ago":["约 1 分钟前"],"Timeago|about an hour ago":["约 1 小时前"],"Timeago|in %s days":[" %s 天后"],"Timeago|in %s hours":[" %s 小时后"],"Timeago|in %s minutes":[" %s 分钟后"],"Timeago|in %s months":[" %s 个月后"],"Timeago|in %s seconds":[" %s 秒后"],"Timeago|in %s weeks":[" %s 星期后"],"Timeago|in %s years":[" %s 年后"],"Timeago|in 1 day":[" 1 天后"],"Timeago|in 1 hour":[" 1 小时后"],"Timeago|in 1 minute":[" 1 分钟后"],"Timeago|in 1 month":[" 1 月后"],"Timeago|in 1 week":[" 1 星期后"],"Timeago|in 1 year":[" 1 年后"],"Timeago|less than a minute ago":["不到 1 分钟前"],"Time|hr":["小时"],"Time|min":["分钟"],"Time|s":["秒"],"Total Time":["总时间"],"Total test time for all commits/merges":["所有提交和合并的总测试时间"],"Unstar":["取消星标"],"Upload New File":["上传新文件"],"Upload file":["上传文件"],"Use your global notification setting":["使用全局通知设置"],"VisibilityLevel|Internal":["内部"],"VisibilityLevel|Private":["私有"],"VisibilityLevel|Public":["公开"],"Want to see the data? Please ask an administrator for access.":["权限不足。如需查看相关数据,请向管理员申请权限。"],"We don't have enough data to show this stage.":["该阶段的数据不足,无法显示。"],"Withdraw Access Request":["取消权限申请"],"You are going to remove %{project_name_with_namespace}.\\nRemoved project CANNOT be restored!\\nAre you ABSOLUTELY sure?":["即将要删除 %{project_name_with_namespace}。\\n已删除的项目无法恢复!\\n确定继续吗?"],"You are going to remove the fork relationship to source project %{forked_from_project}. Are you ABSOLUTELY sure?":["即将删除与源项目 %{forked_from_project} 的派生关系。确定继续吗?"],"You are going to transfer %{project_name_with_namespace} to another owner. Are you ABSOLUTELY sure?":["即将 %{project_name_with_namespace} 转移给另一个所有者。确定继续吗?"],"You can only add files when you are on a branch":["只能在分支上添加文件"],"You have reached your project limit":["您已达到项目数量限制"],"You must sign in to star a project":["必须登录才能对项目加星标"],"You need permission.":["需要相关的权限。"],"You will not get any notifications via email":["不会收到任何通知邮件"],"You will only receive notifications for the events you choose":["只接收选择的事件通知"],"You will only receive notifications for threads you have participated in":["只接收参与的主题的通知"],"You will receive notifications for any activity":["接收所有活动的通知"],"You will receive notifications only for comments in which you were @mentioned":["只接收评论中提及(@)您的通知"],"You won't be able to pull or push project code via %{protocol} until you %{set_password_link} on your account":["在账号中 %{set_password_link} 之前将无法通过 %{protocol} 拉取或推送代码。"],"You won't be able to pull or push project code via SSH until you %{add_ssh_key_link} to your profile":["在账号中 %{add_ssh_key_link} 之前将无法通过 SSH 拉取或推送代码。"],"Your name":["您的名字"],"day":["天"],"new merge request":["新建合并请求"],"notification emails":["通知邮件"],"parent":["父级"]}}}; \ No newline at end of file
diff --git a/app/assets/javascripts/merge_request_tabs.js b/app/assets/javascripts/merge_request_tabs.js
index 7bb2236017e..786b6014dc6 100644
--- a/app/assets/javascripts/merge_request_tabs.js
+++ b/app/assets/javascripts/merge_request_tabs.js
@@ -155,7 +155,10 @@ import BlobForkSuggestion from './blob/blob_fork_suggestion';
scrollToElement(container) {
if (location.hash) {
- const offset = -$('.js-tabs-affix').outerHeight();
+ const offset = 0 - (
+ $('.navbar-gitlab').outerHeight() +
+ $('.js-tabs-affix').outerHeight()
+ );
const $el = $(`${container} ${location.hash}:not(.match)`);
if ($el.length) {
$.scrollTo($el[0], { offset });
@@ -291,7 +294,7 @@ import BlobForkSuggestion from './blob/blob_fork_suggestion';
// Scroll any linked note into view
// Similar to `toggler_behavior` in the discussion tab
const hash = window.gl.utils.getLocationHash();
- const anchor = hash && $container.find(`[id="${hash}"]`);
+ const anchor = hash && $container.find(`.note[id="${hash}"]`);
if (anchor && anchor.length > 0) {
const notesContent = anchor.closest('.notes_content');
const lineType = notesContent.hasClass('new') ? 'new' : 'old';
@@ -301,6 +304,7 @@ import BlobForkSuggestion from './blob/blob_fork_suggestion';
forceShow: true,
});
anchor[0].scrollIntoView();
+ window.gl.utils.handleLocationHash();
// We have multiple elements on the page with `#note_xxx`
// (discussion and diff tabs) and `:target` only applies to the first
anchor.addClass('target');
diff --git a/app/assets/javascripts/notes.js b/app/assets/javascripts/notes.js
index 624dd336786..b21d7774920 100644
--- a/app/assets/javascripts/notes.js
+++ b/app/assets/javascripts/notes.js
@@ -4,7 +4,7 @@ no-unused-expressions, quotes, max-len, one-var, one-var-declaration-per-line,
default-case, prefer-template, consistent-return, no-alert, no-return-assign,
no-param-reassign, prefer-arrow-callback, no-else-return, comma-dangle, no-new,
brace-style, no-lonely-if, vars-on-top, no-unused-vars, no-sequences, no-shadow,
-newline-per-chained-call, no-useless-escape */
+newline-per-chained-call, no-useless-escape, class-methods-use-this */
/* global Flash */
/* global Autosave */
/* global ResolveService */
@@ -25,1507 +25,1489 @@ import './task_list';
window.autosize = autosize;
window.Dropzone = Dropzone;
-const normalizeNewlines = function(str) {
+function normalizeNewlines(str) {
return str.replace(/\r\n/g, '\n');
-};
-
-(function() {
- this.Notes = (function() {
- const MAX_VISIBLE_COMMIT_LIST_COUNT = 3;
- const REGEX_QUICK_ACTIONS = /^\/\w+.*$/gm;
-
- Notes.interval = null;
-
- function Notes(notes_url, note_ids, last_fetched_at, view, enableGFM = true) {
- this.updateTargetButtons = this.updateTargetButtons.bind(this);
- this.updateComment = this.updateComment.bind(this);
- this.visibilityChange = this.visibilityChange.bind(this);
- this.cancelDiscussionForm = this.cancelDiscussionForm.bind(this);
- this.onAddDiffNote = this.onAddDiffNote.bind(this);
- this.setupDiscussionNoteForm = this.setupDiscussionNoteForm.bind(this);
- this.onReplyToDiscussionNote = this.onReplyToDiscussionNote.bind(this);
- this.removeNote = this.removeNote.bind(this);
- this.cancelEdit = this.cancelEdit.bind(this);
- this.updateNote = this.updateNote.bind(this);
- this.addDiscussionNote = this.addDiscussionNote.bind(this);
- this.addNoteError = this.addNoteError.bind(this);
- this.addNote = this.addNote.bind(this);
- this.resetMainTargetForm = this.resetMainTargetForm.bind(this);
- this.refresh = this.refresh.bind(this);
- this.keydownNoteText = this.keydownNoteText.bind(this);
- this.toggleCommitList = this.toggleCommitList.bind(this);
- this.postComment = this.postComment.bind(this);
- this.clearFlashWrapper = this.clearFlash.bind(this);
- this.onHashChange = this.onHashChange.bind(this);
-
- this.notes_url = notes_url;
- this.note_ids = note_ids;
- this.enableGFM = enableGFM;
- // Used to keep track of updated notes while people are editing things
- this.updatedNotesTrackingMap = {};
- this.last_fetched_at = last_fetched_at;
- this.noteable_url = document.URL;
- this.notesCountBadge || (this.notesCountBadge = $('.issuable-details').find('.notes-tab .badge'));
- this.basePollingInterval = 15000;
- this.maxPollingSteps = 4;
-
- this.cleanBinding();
- this.addBinding();
- this.setPollingInterval();
- this.setupMainTargetNoteForm();
- this.taskList = new gl.TaskList({
- dataType: 'note',
- fieldName: 'note',
- selector: '.notes'
- });
- this.collapseLongCommitList();
- this.setViewType(view);
-
- // We are in the Merge Requests page so we need another edit form for Changes tab
- if (gl.utils.getPagePath(1) === 'merge_requests') {
- $('.note-edit-form').clone()
- .addClass('mr-note-edit-form').insertAfter('.note-edit-form');
- }
+}
+
+const MAX_VISIBLE_COMMIT_LIST_COUNT = 3;
+const REGEX_QUICK_ACTIONS = /^\/\w+.*$/gm;
+
+export default class Notes {
+ constructor(notes_url, note_ids, last_fetched_at, view, enableGFM = true) {
+ this.updateTargetButtons = this.updateTargetButtons.bind(this);
+ this.updateComment = this.updateComment.bind(this);
+ this.visibilityChange = this.visibilityChange.bind(this);
+ this.cancelDiscussionForm = this.cancelDiscussionForm.bind(this);
+ this.onAddDiffNote = this.onAddDiffNote.bind(this);
+ this.setupDiscussionNoteForm = this.setupDiscussionNoteForm.bind(this);
+ this.onReplyToDiscussionNote = this.onReplyToDiscussionNote.bind(this);
+ this.removeNote = this.removeNote.bind(this);
+ this.cancelEdit = this.cancelEdit.bind(this);
+ this.updateNote = this.updateNote.bind(this);
+ this.addDiscussionNote = this.addDiscussionNote.bind(this);
+ this.addNoteError = this.addNoteError.bind(this);
+ this.addNote = this.addNote.bind(this);
+ this.resetMainTargetForm = this.resetMainTargetForm.bind(this);
+ this.refresh = this.refresh.bind(this);
+ this.keydownNoteText = this.keydownNoteText.bind(this);
+ this.toggleCommitList = this.toggleCommitList.bind(this);
+ this.postComment = this.postComment.bind(this);
+ this.clearFlashWrapper = this.clearFlash.bind(this);
+ this.onHashChange = this.onHashChange.bind(this);
+
+ this.notes_url = notes_url;
+ this.note_ids = note_ids;
+ this.enableGFM = enableGFM;
+ // Used to keep track of updated notes while people are editing things
+ this.updatedNotesTrackingMap = {};
+ this.last_fetched_at = last_fetched_at;
+ this.noteable_url = document.URL;
+ this.notesCountBadge || (this.notesCountBadge = $('.issuable-details').find('.notes-tab .badge'));
+ this.basePollingInterval = 15000;
+ this.maxPollingSteps = 4;
+
+ this.cleanBinding();
+ this.addBinding();
+ this.setPollingInterval();
+ this.setupMainTargetNoteForm();
+ this.taskList = new gl.TaskList({
+ dataType: 'note',
+ fieldName: 'note',
+ selector: '.notes'
+ });
+ this.collapseLongCommitList();
+ this.setViewType(view);
+
+ // We are in the Merge Requests page so we need another edit form for Changes tab
+ if (gl.utils.getPagePath(1) === 'merge_requests') {
+ $('.note-edit-form').clone()
+ .addClass('mr-note-edit-form').insertAfter('.note-edit-form');
+ }
+ }
+
+ setViewType(view) {
+ this.view = Cookies.get('diff_view') || view;
+ }
+
+ addBinding() {
+ // Edit note link
+ $(document).on('click', '.js-note-edit', this.showEditForm.bind(this));
+ $(document).on('click', '.note-edit-cancel', this.cancelEdit);
+ // Reopen and close actions for Issue/MR combined with note form submit
+ $(document).on('click', '.js-comment-submit-button', this.postComment);
+ $(document).on('click', '.js-comment-save-button', this.updateComment);
+ $(document).on('keyup input', '.js-note-text', this.updateTargetButtons);
+ // resolve a discussion
+ $(document).on('click', '.js-comment-resolve-button', this.postComment);
+ // remove a note (in general)
+ $(document).on('click', '.js-note-delete', this.removeNote);
+ // delete note attachment
+ $(document).on('click', '.js-note-attachment-delete', this.removeAttachment);
+ // reset main target form when clicking discard
+ $(document).on('click', '.js-note-discard', this.resetMainTargetForm);
+ // update the file name when an attachment is selected
+ $(document).on('change', '.js-note-attachment-input', this.updateFormAttachment);
+ // reply to diff/discussion notes
+ $(document).on('click', '.js-discussion-reply-button', this.onReplyToDiscussionNote);
+ // add diff note
+ $(document).on('click', '.js-add-diff-note-button', this.onAddDiffNote);
+ // hide diff note form
+ $(document).on('click', '.js-close-discussion-note-form', this.cancelDiscussionForm);
+ // toggle commit list
+ $(document).on('click', '.system-note-commit-list-toggler', this.toggleCommitList);
+ // fetch notes when tab becomes visible
+ $(document).on('visibilitychange', this.visibilityChange);
+ // when issue status changes, we need to refresh data
+ $(document).on('issuable:change', this.refresh);
+ // ajax:events that happen on Form when actions like Reopen, Close are performed on Issues and MRs.
+ $(document).on('ajax:success', '.js-main-target-form', this.addNote);
+ $(document).on('ajax:success', '.js-discussion-note-form', this.addDiscussionNote);
+ $(document).on('ajax:success', '.js-main-target-form', this.resetMainTargetForm);
+ $(document).on('ajax:complete', '.js-main-target-form', this.reenableTargetFormSubmitButton);
+ // when a key is clicked on the notes
+ $(document).on('keydown', '.js-note-text', this.keydownNoteText);
+ // When the URL fragment/hash has changed, `#note_xxx`
+ return $(window).on('hashchange', this.onHashChange);
+ }
+
+ cleanBinding() {
+ $(document).off('click', '.js-note-edit');
+ $(document).off('click', '.note-edit-cancel');
+ $(document).off('click', '.js-note-delete');
+ $(document).off('click', '.js-note-attachment-delete');
+ $(document).off('click', '.js-discussion-reply-button');
+ $(document).off('click', '.js-add-diff-note-button');
+ $(document).off('visibilitychange');
+ $(document).off('keyup input', '.js-note-text');
+ $(document).off('click', '.js-note-target-reopen');
+ $(document).off('click', '.js-note-target-close');
+ $(document).off('click', '.js-note-discard');
+ $(document).off('keydown', '.js-note-text');
+ $(document).off('click', '.js-comment-resolve-button');
+ $(document).off('click', '.system-note-commit-list-toggler');
+ $(document).off('ajax:success', '.js-main-target-form');
+ $(document).off('ajax:success', '.js-discussion-note-form');
+ $(document).off('ajax:complete', '.js-main-target-form');
+ $(window).off('hashchange', this.onHashChange);
+ }
+
+ static initCommentTypeToggle(form) {
+ const dropdownTrigger = form.querySelector('.js-comment-type-dropdown .dropdown-toggle');
+ const dropdownList = form.querySelector('.js-comment-type-dropdown .dropdown-menu');
+ const noteTypeInput = form.querySelector('#note_type');
+ const submitButton = form.querySelector('.js-comment-type-dropdown .js-comment-submit-button');
+ const closeButton = form.querySelector('.js-note-target-close');
+ const reopenButton = form.querySelector('.js-note-target-reopen');
+
+ const commentTypeToggle = new CommentTypeToggle({
+ dropdownTrigger,
+ dropdownList,
+ noteTypeInput,
+ submitButton,
+ closeButton,
+ reopenButton,
+ });
+
+ commentTypeToggle.initDroplab();
+ }
+
+ keydownNoteText(e) {
+ var $textarea, discussionNoteForm, editNote, myLastNote, myLastNoteEditBtn, newText, originalText;
+ if (gl.utils.isMetaKey(e)) {
+ return;
}
- Notes.prototype.setViewType = function(view) {
- this.view = Cookies.get('diff_view') || view;
- };
-
- Notes.prototype.addBinding = function() {
- // Edit note link
- $(document).on('click', '.js-note-edit', this.showEditForm.bind(this));
- $(document).on('click', '.note-edit-cancel', this.cancelEdit);
- // Reopen and close actions for Issue/MR combined with note form submit
- $(document).on('click', '.js-comment-submit-button', this.postComment);
- $(document).on('click', '.js-comment-save-button', this.updateComment);
- $(document).on('keyup input', '.js-note-text', this.updateTargetButtons);
- // resolve a discussion
- $(document).on('click', '.js-comment-resolve-button', this.postComment);
- // remove a note (in general)
- $(document).on('click', '.js-note-delete', this.removeNote);
- // delete note attachment
- $(document).on('click', '.js-note-attachment-delete', this.removeAttachment);
- // reset main target form when clicking discard
- $(document).on('click', '.js-note-discard', this.resetMainTargetForm);
- // update the file name when an attachment is selected
- $(document).on('change', '.js-note-attachment-input', this.updateFormAttachment);
- // reply to diff/discussion notes
- $(document).on('click', '.js-discussion-reply-button', this.onReplyToDiscussionNote);
- // add diff note
- $(document).on('click', '.js-add-diff-note-button', this.onAddDiffNote);
- // hide diff note form
- $(document).on('click', '.js-close-discussion-note-form', this.cancelDiscussionForm);
- // toggle commit list
- $(document).on('click', '.system-note-commit-list-toggler', this.toggleCommitList);
- // fetch notes when tab becomes visible
- $(document).on('visibilitychange', this.visibilityChange);
- // when issue status changes, we need to refresh data
- $(document).on('issuable:change', this.refresh);
- // ajax:events that happen on Form when actions like Reopen, Close are performed on Issues and MRs.
- $(document).on('ajax:success', '.js-main-target-form', this.addNote);
- $(document).on('ajax:success', '.js-discussion-note-form', this.addDiscussionNote);
- $(document).on('ajax:success', '.js-main-target-form', this.resetMainTargetForm);
- $(document).on('ajax:complete', '.js-main-target-form', this.reenableTargetFormSubmitButton);
- // when a key is clicked on the notes
- $(document).on('keydown', '.js-note-text', this.keydownNoteText);
- // When the URL fragment/hash has changed, `#note_xxx`
- return $(window).on('hashchange', this.onHashChange);
- };
-
- Notes.prototype.cleanBinding = function() {
- $(document).off('click', '.js-note-edit');
- $(document).off('click', '.note-edit-cancel');
- $(document).off('click', '.js-note-delete');
- $(document).off('click', '.js-note-attachment-delete');
- $(document).off('click', '.js-discussion-reply-button');
- $(document).off('click', '.js-add-diff-note-button');
- $(document).off('visibilitychange');
- $(document).off('keyup input', '.js-note-text');
- $(document).off('click', '.js-note-target-reopen');
- $(document).off('click', '.js-note-target-close');
- $(document).off('click', '.js-note-discard');
- $(document).off('keydown', '.js-note-text');
- $(document).off('click', '.js-comment-resolve-button');
- $(document).off('click', '.system-note-commit-list-toggler');
- $(document).off('ajax:success', '.js-main-target-form');
- $(document).off('ajax:success', '.js-discussion-note-form');
- $(document).off('ajax:complete', '.js-main-target-form');
- $(window).off('hashchange', this.onHashChange);
- };
-
- Notes.initCommentTypeToggle = function (form) {
- const dropdownTrigger = form.querySelector('.js-comment-type-dropdown .dropdown-toggle');
- const dropdownList = form.querySelector('.js-comment-type-dropdown .dropdown-menu');
- const noteTypeInput = form.querySelector('#note_type');
- const submitButton = form.querySelector('.js-comment-type-dropdown .js-comment-submit-button');
- const closeButton = form.querySelector('.js-note-target-close');
- const reopenButton = form.querySelector('.js-note-target-reopen');
-
- const commentTypeToggle = new CommentTypeToggle({
- dropdownTrigger,
- dropdownList,
- noteTypeInput,
- submitButton,
- closeButton,
- reopenButton,
- });
-
- commentTypeToggle.initDroplab();
- };
-
- Notes.prototype.keydownNoteText = function(e) {
- var $textarea, discussionNoteForm, editNote, myLastNote, myLastNoteEditBtn, newText, originalText;
- if (gl.utils.isMetaKey(e)) {
- return;
- }
-
- $textarea = $(e.target);
- // Edit previous note when UP arrow is hit
- switch (e.which) {
- case 38:
+ $textarea = $(e.target);
+ // Edit previous note when UP arrow is hit
+ switch (e.which) {
+ case 38:
+ if ($textarea.val() !== '') {
+ return;
+ }
+ myLastNote = $(`li.note[data-author-id='${gon.current_user_id}'][data-editable]:last`, $textarea.closest('.note, .notes_holder, #notes'));
+ if (myLastNote.length) {
+ myLastNoteEditBtn = myLastNote.find('.js-note-edit');
+ return myLastNoteEditBtn.trigger('click', [true, myLastNote]);
+ }
+ break;
+ // Cancel creating diff note or editing any note when ESCAPE is hit
+ case 27:
+ discussionNoteForm = $textarea.closest('.js-discussion-note-form');
+ if (discussionNoteForm.length) {
if ($textarea.val() !== '') {
- return;
- }
- myLastNote = $(`li.note[data-author-id='${gon.current_user_id}'][data-editable]:last`, $textarea.closest('.note, .notes_holder, #notes'));
- if (myLastNote.length) {
- myLastNoteEditBtn = myLastNote.find('.js-note-edit');
- return myLastNoteEditBtn.trigger('click', [true, myLastNote]);
- }
- break;
- // Cancel creating diff note or editing any note when ESCAPE is hit
- case 27:
- discussionNoteForm = $textarea.closest('.js-discussion-note-form');
- if (discussionNoteForm.length) {
- if ($textarea.val() !== '') {
- if (!confirm('Are you sure you want to cancel creating this comment?')) {
- return;
- }
+ if (!confirm('Are you sure you want to cancel creating this comment?')) {
+ return;
}
- this.removeDiscussionNoteForm(discussionNoteForm);
- return;
}
- editNote = $textarea.closest('.note');
- if (editNote.length) {
- originalText = $textarea.closest('form').data('original-note');
- newText = $textarea.val();
- if (originalText !== newText) {
- if (!confirm('Are you sure you want to cancel editing this comment?')) {
- return;
- }
+ this.removeDiscussionNoteForm(discussionNoteForm);
+ return;
+ }
+ editNote = $textarea.closest('.note');
+ if (editNote.length) {
+ originalText = $textarea.closest('form').data('original-note');
+ newText = $textarea.val();
+ if (originalText !== newText) {
+ if (!confirm('Are you sure you want to cancel editing this comment?')) {
+ return;
}
- return this.removeNoteEditForm(editNote);
}
- }
- };
+ return this.removeNoteEditForm(editNote);
+ }
+ }
+ }
- Notes.prototype.initRefresh = function() {
+ initRefresh() {
+ if (Notes.interval) {
clearInterval(Notes.interval);
- return Notes.interval = setInterval((function(_this) {
- return function() {
- return _this.refresh();
- };
- })(this), this.pollingInterval);
- };
+ }
+ return Notes.interval = setInterval((function(_this) {
+ return function() {
+ return _this.refresh();
+ };
+ })(this), this.pollingInterval);
+ }
- Notes.prototype.refresh = function() {
- if (!document.hidden) {
- return this.getContent();
- }
- };
+ refresh() {
+ if (!document.hidden) {
+ return this.getContent();
+ }
+ }
- Notes.prototype.getContent = function() {
- if (this.refreshing) {
- return;
- }
- this.refreshing = true;
- return $.ajax({
- url: this.notes_url,
- headers: { 'X-Last-Fetched-At': this.last_fetched_at },
- dataType: 'json',
- success: (function(_this) {
- return function(data) {
- var notes;
- notes = data.notes;
- _this.last_fetched_at = data.last_fetched_at;
- _this.setPollingInterval(data.notes.length);
- return $.each(notes, function(i, note) {
- _this.renderNote(note);
- });
- };
- })(this)
- }).always((function(_this) {
- return function() {
- return _this.refreshing = false;
+ getContent() {
+ if (this.refreshing) {
+ return;
+ }
+ this.refreshing = true;
+ return $.ajax({
+ url: this.notes_url,
+ headers: { 'X-Last-Fetched-At': this.last_fetched_at },
+ dataType: 'json',
+ success: (function(_this) {
+ return function(data) {
+ var notes;
+ notes = data.notes;
+ _this.last_fetched_at = data.last_fetched_at;
+ _this.setPollingInterval(data.notes.length);
+ return $.each(notes, function(i, note) {
+ _this.renderNote(note);
+ });
};
- })(this));
- };
-
- /*
- Increase @pollingInterval up to 120 seconds on every function call,
- if `shouldReset` has a truthy value, 'null' or 'undefined' the variable
- will reset to @basePollingInterval.
-
- Note: this function is used to gradually increase the polling interval
- if there aren't new notes coming from the server
- */
-
- Notes.prototype.setPollingInterval = function(shouldReset) {
- var nthInterval;
- if (shouldReset == null) {
- shouldReset = true;
- }
- nthInterval = this.basePollingInterval * Math.pow(2, this.maxPollingSteps - 1);
- if (shouldReset) {
- this.pollingInterval = this.basePollingInterval;
- } else if (this.pollingInterval < nthInterval) {
- this.pollingInterval *= 2;
+ })(this)
+ }).always((function(_this) {
+ return function() {
+ return _this.refreshing = false;
+ };
+ })(this));
+ }
+
+ /**
+ * Increase @pollingInterval up to 120 seconds on every function call,
+ * if `shouldReset` has a truthy value, 'null' or 'undefined' the variable
+ * will reset to @basePollingInterval.
+ *
+ * Note: this function is used to gradually increase the polling interval
+ * if there aren't new notes coming from the server
+ */
+ setPollingInterval(shouldReset) {
+ var nthInterval;
+ if (shouldReset == null) {
+ shouldReset = true;
+ }
+ nthInterval = this.basePollingInterval * Math.pow(2, this.maxPollingSteps - 1);
+ if (shouldReset) {
+ this.pollingInterval = this.basePollingInterval;
+ } else if (this.pollingInterval < nthInterval) {
+ this.pollingInterval *= 2;
+ }
+ return this.initRefresh();
+ }
+
+ handleQuickActions(noteEntity) {
+ var votesBlock;
+ if (noteEntity.commands_changes) {
+ if ('merge' in noteEntity.commands_changes) {
+ Notes.checkMergeRequestStatus();
}
- return this.initRefresh();
- };
-
- Notes.prototype.handleQuickActions = function(noteEntity) {
- var votesBlock;
- if (noteEntity.commands_changes) {
- if ('merge' in noteEntity.commands_changes) {
- Notes.checkMergeRequestStatus();
- }
- if ('emoji_award' in noteEntity.commands_changes) {
- votesBlock = $('.js-awards-block').eq(0);
- gl.awardsHandler.addAwardToEmojiBar(votesBlock, noteEntity.commands_changes.emoji_award);
- return gl.awardsHandler.scrollToAwards();
- }
+ if ('emoji_award' in noteEntity.commands_changes) {
+ votesBlock = $('.js-awards-block').eq(0);
+ gl.awardsHandler.addAwardToEmojiBar(votesBlock, noteEntity.commands_changes.emoji_award);
+ return gl.awardsHandler.scrollToAwards();
}
- };
-
- Notes.prototype.setupNewNote = function($note) {
- // Update datetime format on the recent note
- gl.utils.localTimeAgo($note.find('.js-timeago'), false);
-
- this.collapseLongCommitList();
- this.taskList.init();
-
- // This stops the note highlight, #note_xxx`, from being removed after real time update
- // The `:target` selector does not re-evaluate after we replace element in the DOM
- Notes.updateNoteTargetSelector($note);
- this.$noteToCleanHighlight = $note;
- };
-
- Notes.prototype.onHashChange = function() {
- if (this.$noteToCleanHighlight) {
- Notes.updateNoteTargetSelector(this.$noteToCleanHighlight);
- }
-
- this.$noteToCleanHighlight = null;
- };
-
- Notes.updateNoteTargetSelector = function($note) {
- const hash = gl.utils.getLocationHash();
- // Needs to be an explicit true/false for the jQuery `toggleClass(force)`
- const addTargetClass = Boolean(hash && $note.filter(`#${hash}`).length > 0);
- $note.toggleClass('target', addTargetClass);
- };
-
- /*
- Render note in main comments area.
+ }
+ }
- Note: for rendering inline notes use renderDiscussionNote
- */
+ setupNewNote($note) {
+ // Update datetime format on the recent note
+ gl.utils.localTimeAgo($note.find('.js-timeago'), false);
- Notes.prototype.renderNote = function(noteEntity, $form, $notesList = $('.main-notes-list')) {
- if (noteEntity.discussion_html) {
- return this.renderDiscussionNote(noteEntity, $form);
- }
+ this.collapseLongCommitList();
+ this.taskList.init();
- if (!noteEntity.valid) {
- if (noteEntity.errors.commands_only) {
- this.addFlash(noteEntity.errors.commands_only, 'notice', this.parentTimeline);
- this.refresh();
- }
- return;
- }
+ // This stops the note highlight, #note_xxx`, from being removed after real time update
+ // The `:target` selector does not re-evaluate after we replace element in the DOM
+ Notes.updateNoteTargetSelector($note);
+ this.$noteToCleanHighlight = $note;
+ }
- const $note = $notesList.find(`#note_${noteEntity.id}`);
- if (Notes.isNewNote(noteEntity, this.note_ids)) {
- this.note_ids.push(noteEntity.id);
+ onHashChange() {
+ if (this.$noteToCleanHighlight) {
+ Notes.updateNoteTargetSelector(this.$noteToCleanHighlight);
+ }
- if ($notesList.length) {
- $notesList.find('.system-note.being-posted').remove();
- }
- const $newNote = Notes.animateAppendNote(noteEntity.html, $notesList);
+ this.$noteToCleanHighlight = null;
+ }
+
+ static updateNoteTargetSelector($note) {
+ const hash = gl.utils.getLocationHash();
+ // Needs to be an explicit true/false for the jQuery `toggleClass(force)`
+ const addTargetClass = Boolean(hash && $note.filter(`#${hash}`).length > 0);
+ $note.toggleClass('target', addTargetClass);
+ }
+
+ /**
+ * Render note in main comments area.
+ *
+ * Note: for rendering inline notes use renderDiscussionNote
+ */
+ renderNote(noteEntity, $form, $notesList = $('.main-notes-list')) {
+ if (noteEntity.discussion_html) {
+ return this.renderDiscussionNote(noteEntity, $form);
+ }
- this.setupNewNote($newNote);
+ if (!noteEntity.valid) {
+ if (noteEntity.errors.commands_only) {
+ this.addFlash(noteEntity.errors.commands_only, 'notice', this.parentTimeline);
this.refresh();
- return this.updateNotesCount(1);
}
- // The server can send the same update multiple times so we need to make sure to only update once per actual update.
- else if (Notes.isUpdatedNote(noteEntity, $note)) {
- const isEditing = $note.hasClass('is-editing');
- const initialContent = normalizeNewlines(
- $note.find('.original-note-content').text().trim()
- );
- const $textarea = $note.find('.js-note-text');
- const currentContent = $textarea.val();
- // There can be CRLF vs LF mismatches if we don't sanitize and compare the same way
- const sanitizedNoteNote = normalizeNewlines(noteEntity.note);
- const isTextareaUntouched = currentContent === initialContent || currentContent === sanitizedNoteNote;
-
- if (isEditing && isTextareaUntouched) {
- $textarea.val(noteEntity.note);
- this.updatedNotesTrackingMap[noteEntity.id] = noteEntity;
- }
- else if (isEditing && !isTextareaUntouched) {
- this.putConflictEditWarningInPlace(noteEntity, $note);
- this.updatedNotesTrackingMap[noteEntity.id] = noteEntity;
- }
- else {
- const $updatedNote = Notes.animateUpdateNote(noteEntity.html, $note);
- this.setupNewNote($updatedNote);
- }
- }
- };
-
- Notes.prototype.isParallelView = function() {
- return Cookies.get('diff_view') === 'parallel';
- };
-
- /*
- Render note in discussion area.
-
- Note: for rendering inline notes use renderDiscussionNote
- */
+ return;
+ }
- Notes.prototype.renderDiscussionNote = function(noteEntity, $form) {
- var discussionContainer, form, row, lineType, diffAvatarContainer;
- if (!Notes.isNewNote(noteEntity, this.note_ids)) {
- return;
- }
+ const $note = $notesList.find(`#note_${noteEntity.id}`);
+ if (Notes.isNewNote(noteEntity, this.note_ids)) {
this.note_ids.push(noteEntity.id);
- form = $form || $(`.js-discussion-note-form[data-discussion-id="${noteEntity.discussion_id}"]`);
- row = form.closest('tr');
- lineType = this.isParallelView() ? form.find('#line_type').val() : 'old';
- diffAvatarContainer = row.prevAll('.line_holder').first().find('.js-avatar-container.' + lineType + '_line');
- // is this the first note of discussion?
- discussionContainer = $(`.notes[data-discussion-id="${noteEntity.discussion_id}"]`);
- if (!discussionContainer.length) {
- discussionContainer = form.closest('.discussion').find('.notes');
- }
- if (discussionContainer.length === 0) {
- if (noteEntity.diff_discussion_html) {
- var $discussion = $(noteEntity.diff_discussion_html).renderGFM();
-
- if (!this.isParallelView() || row.hasClass('js-temp-notes-holder')) {
- // insert the note and the reply button after the temp row
- row.after($discussion);
- } else {
- // Merge new discussion HTML in
- var $notes = $discussion.find(`.notes[data-discussion-id="${noteEntity.discussion_id}"]`);
- var contentContainerClass = '.' + $notes.closest('.notes_content')
- .attr('class')
- .split(' ')
- .join('.');
-
- row.find(contentContainerClass + ' .content').append($notes.closest('.content').children());
- }
- }
- // Init discussion on 'Discussion' page if it is merge request page
- const page = $('body').attr('data-page');
- if ((page && page.indexOf('projects:merge_request') !== -1) || !noteEntity.diff_discussion_html) {
- Notes.animateAppendNote(noteEntity.discussion_html, $('.main-notes-list'));
- }
- } else {
- // append new note to all matching discussions
- Notes.animateAppendNote(noteEntity.html, discussionContainer);
- }
- if (typeof gl.diffNotesCompileComponents !== 'undefined' && noteEntity.discussion_resolvable) {
- gl.diffNotesCompileComponents();
- this.renderDiscussionAvatar(diffAvatarContainer, noteEntity);
+ if ($notesList.length) {
+ $notesList.find('.system-note.being-posted').remove();
}
+ const $newNote = Notes.animateAppendNote(noteEntity.html, $notesList);
- gl.utils.localTimeAgo($('.js-timeago'), false);
- Notes.checkMergeRequestStatus();
+ this.setupNewNote($newNote);
+ this.refresh();
return this.updateNotesCount(1);
- };
-
- Notes.prototype.getLineHolder = function(changesDiscussionContainer) {
- return $(changesDiscussionContainer).closest('.notes_holder')
- .prevAll('.line_holder')
- .first()
- .get(0);
- };
-
- Notes.prototype.renderDiscussionAvatar = function(diffAvatarContainer, noteEntity) {
- var commentButton = diffAvatarContainer.find('.js-add-diff-note-button');
- var avatarHolder = diffAvatarContainer.find('.diff-comment-avatar-holders');
-
- if (!avatarHolder.length) {
- avatarHolder = document.createElement('diff-note-avatars');
- avatarHolder.setAttribute('discussion-id', noteEntity.discussion_id);
-
- diffAvatarContainer.append(avatarHolder);
+ }
+ // The server can send the same update multiple times so we need to make sure to only update once per actual update.
+ else if (Notes.isUpdatedNote(noteEntity, $note)) {
+ const isEditing = $note.hasClass('is-editing');
+ const initialContent = normalizeNewlines(
+ $note.find('.original-note-content').text().trim()
+ );
+ const $textarea = $note.find('.js-note-text');
+ const currentContent = $textarea.val();
+ // There can be CRLF vs LF mismatches if we don't sanitize and compare the same way
+ const sanitizedNoteNote = normalizeNewlines(noteEntity.note);
+ const isTextareaUntouched = currentContent === initialContent || currentContent === sanitizedNoteNote;
- gl.diffNotesCompileComponents();
+ if (isEditing && isTextareaUntouched) {
+ $textarea.val(noteEntity.note);
+ this.updatedNotesTrackingMap[noteEntity.id] = noteEntity;
}
-
- if (commentButton.length) {
- commentButton.remove();
+ else if (isEditing && !isTextareaUntouched) {
+ this.putConflictEditWarningInPlace(noteEntity, $note);
+ this.updatedNotesTrackingMap[noteEntity.id] = noteEntity;
}
- };
-
- /*
- Called in response the main target form has been successfully submitted.
-
- Removes any errors.
- Resets text and preview.
- Resets buttons.
- */
-
- Notes.prototype.resetMainTargetForm = function(e) {
- var form;
- form = $('.js-main-target-form');
- // remove validation errors
- form.find('.js-errors').remove();
- // reset text and preview
- form.find('.js-md-write-button').click();
- form.find('.js-note-text').val('').trigger('input');
- form.find('.js-note-text').data('autosave').reset();
-
- var event = document.createEvent('Event');
- event.initEvent('autosize:update', true, false);
- form.find('.js-autosize')[0].dispatchEvent(event);
-
- this.updateTargetButtons(e);
- };
-
- Notes.prototype.reenableTargetFormSubmitButton = function() {
- var form;
- form = $('.js-main-target-form');
- return form.find('.js-note-text').trigger('input');
- };
-
- /*
- Shows the main form and does some setup on it.
-
- Sets some hidden fields in the form.
- */
-
- Notes.prototype.setupMainTargetNoteForm = function() {
- var form;
- // find the form
- form = $('.js-new-note-form');
- // Set a global clone of the form for later cloning
- this.formClone = form.clone();
- // show the form
- this.setupNoteForm(form);
- // fix classes
- form.removeClass('js-new-note-form');
- form.addClass('js-main-target-form');
- form.find('#note_line_code').remove();
- form.find('#note_position').remove();
- form.find('#note_type').val('');
- form.find('#in_reply_to_discussion_id').remove();
- form.find('.js-comment-resolve-button').closest('comment-and-resolve-btn').remove();
- this.parentTimeline = form.parents('.timeline');
-
- if (form.length) {
- Notes.initCommentTypeToggle(form.get(0));
- }
- };
-
- /*
- General note form setup.
-
- deactivates the submit button when text is empty
- hides the preview button when text is empty
- setup GFM auto complete
- show the form
- */
-
- Notes.prototype.setupNoteForm = function(form) {
- var textarea, key;
- new gl.GLForm(form, this.enableGFM);
- textarea = form.find('.js-note-text');
- key = [
- 'Note',
- form.find('#note_noteable_type').val(),
- form.find('#note_noteable_id').val(),
- form.find('#note_commit_id').val(),
- form.find('#note_type').val(),
- form.find('#in_reply_to_discussion_id').val(),
-
- // LegacyDiffNote
- form.find('#note_line_code').val(),
-
- // DiffNote
- form.find('#note_position').val()
- ];
- return new Autosave(textarea, key);
- };
-
- /*
- Called in response to the new note form being submitted
-
- Adds new note to list.
- */
-
- Notes.prototype.addNote = function($form, note) {
- return this.renderNote(note);
- };
-
- Notes.prototype.addNoteError = function($form) {
- let formParentTimeline;
- if ($form.hasClass('js-main-target-form')) {
- formParentTimeline = $form.parents('.timeline');
- } else if ($form.hasClass('js-discussion-note-form')) {
- formParentTimeline = $form.closest('.discussion-notes').find('.notes');
+ else {
+ const $updatedNote = Notes.animateUpdateNote(noteEntity.html, $note);
+ this.setupNewNote($updatedNote);
}
- return this.addFlash('Your comment could not be submitted! Please check your network connection and try again.', 'alert', formParentTimeline);
- };
-
- Notes.prototype.updateNoteError = $parentTimeline => new Flash('Your comment could not be updated! Please check your network connection and try again.');
-
- /*
- Called in response to the new note form being submitted
-
- Adds new note to list.
- */
-
- Notes.prototype.addDiscussionNote = function($form, note, isNewDiffComment) {
- if ($form.attr('data-resolve-all') != null) {
- var projectPath = $form.data('project-path');
- var discussionId = $form.data('discussion-id');
- var mergeRequestId = $form.data('noteable-iid');
+ }
+ }
+
+ isParallelView() {
+ return Cookies.get('diff_view') === 'parallel';
+ }
+
+ /**
+ * Render note in discussion area.
+ *
+ * Note: for rendering inline notes use renderDiscussionNote
+ */
+ renderDiscussionNote(noteEntity, $form) {
+ var discussionContainer, form, row, lineType, diffAvatarContainer;
+ if (!Notes.isNewNote(noteEntity, this.note_ids)) {
+ return;
+ }
+ this.note_ids.push(noteEntity.id);
+ form = $form || $(`.js-discussion-note-form[data-discussion-id="${noteEntity.discussion_id}"]`);
+ row = form.closest('tr');
+ lineType = this.isParallelView() ? form.find('#line_type').val() : 'old';
+ diffAvatarContainer = row.prevAll('.line_holder').first().find('.js-avatar-container.' + lineType + '_line');
+ // is this the first note of discussion?
+ discussionContainer = $(`.notes[data-discussion-id="${noteEntity.discussion_id}"]`);
+ if (!discussionContainer.length) {
+ discussionContainer = form.closest('.discussion').find('.notes');
+ }
+ if (discussionContainer.length === 0) {
+ if (noteEntity.diff_discussion_html) {
+ var $discussion = $(noteEntity.diff_discussion_html).renderGFM();
- if (ResolveService != null) {
- ResolveService.toggleResolveForDiscussion(mergeRequestId, discussionId);
+ if (!this.isParallelView() || row.hasClass('js-temp-notes-holder')) {
+ // insert the note and the reply button after the temp row
+ row.after($discussion);
+ } else {
+ // Merge new discussion HTML in
+ var $notes = $discussion.find(`.notes[data-discussion-id="${noteEntity.discussion_id}"]`);
+ var contentContainerClass = '.' + $notes.closest('.notes_content')
+ .attr('class')
+ .split(' ')
+ .join('.');
+
+ row.find(contentContainerClass + ' .content').append($notes.closest('.content').children());
}
}
-
- this.renderNote(note, $form);
- // cleanup after successfully creating a diff/discussion note
- if (isNewDiffComment) {
- this.removeDiscussionNoteForm($form);
+ // Init discussion on 'Discussion' page if it is merge request page
+ const page = $('body').attr('data-page');
+ if ((page && page.indexOf('projects:merge_request') !== -1) || !noteEntity.diff_discussion_html) {
+ Notes.animateAppendNote(noteEntity.discussion_html, $('.main-notes-list'));
}
- };
+ } else {
+ // append new note to all matching discussions
+ Notes.animateAppendNote(noteEntity.html, discussionContainer);
+ }
- /*
- Called in response to the edit note form being submitted
+ if (typeof gl.diffNotesCompileComponents !== 'undefined' && noteEntity.discussion_resolvable) {
+ gl.diffNotesCompileComponents();
+ this.renderDiscussionAvatar(diffAvatarContainer, noteEntity);
+ }
- Updates the current note field.
- */
+ gl.utils.localTimeAgo($('.js-timeago'), false);
+ Notes.checkMergeRequestStatus();
+ return this.updateNotesCount(1);
+ }
- Notes.prototype.updateNote = function(noteEntity, $targetNote) {
- var $noteEntityEl, $note_li;
- // Convert returned HTML to a jQuery object so we can modify it further
- $noteEntityEl = $(noteEntity.html);
- $noteEntityEl.addClass('fade-in-full');
- this.revertNoteEditForm($targetNote);
- $noteEntityEl.renderGFM();
- // Find the note's `li` element by ID and replace it with the updated HTML
- $note_li = $('.note-row-' + noteEntity.id);
+ getLineHolder(changesDiscussionContainer) {
+ return $(changesDiscussionContainer).closest('.notes_holder')
+ .prevAll('.line_holder')
+ .first()
+ .get(0);
+ }
- $note_li.replaceWith($noteEntityEl);
- this.setupNewNote($noteEntityEl);
+ renderDiscussionAvatar(diffAvatarContainer, noteEntity) {
+ var commentButton = diffAvatarContainer.find('.js-add-diff-note-button');
+ var avatarHolder = diffAvatarContainer.find('.diff-comment-avatar-holders');
- if (typeof gl.diffNotesCompileComponents !== 'undefined') {
- gl.diffNotesCompileComponents();
- }
- };
+ if (!avatarHolder.length) {
+ avatarHolder = document.createElement('diff-note-avatars');
+ avatarHolder.setAttribute('discussion-id', noteEntity.discussion_id);
- Notes.prototype.checkContentToAllowEditing = function($el) {
- var initialContent = $el.find('.original-note-content').text().trim();
- var currentContent = $el.find('.js-note-text').val();
- var isAllowed = true;
+ diffAvatarContainer.append(avatarHolder);
- if (currentContent === initialContent) {
- this.removeNoteEditForm($el);
- }
- else {
- var $buttons = $el.find('.note-form-actions');
- var isWidgetVisible = gl.utils.isInViewport($el.get(0));
-
- if (!isWidgetVisible) {
- gl.utils.scrollToElement($el);
- }
+ gl.diffNotesCompileComponents();
+ }
- $el.find('.js-finish-edit-warning').show();
- isAllowed = false;
- }
+ if (commentButton.length) {
+ commentButton.remove();
+ }
+ }
+
+ /**
+ * Called in response the main target form has been successfully submitted.
+ *
+ * Removes any errors.
+ * Resets text and preview.
+ * Resets buttons.
+ */
+ resetMainTargetForm(e) {
+ var form;
+ form = $('.js-main-target-form');
+ // remove validation errors
+ form.find('.js-errors').remove();
+ // reset text and preview
+ form.find('.js-md-write-button').click();
+ form.find('.js-note-text').val('').trigger('input');
+ form.find('.js-note-text').data('autosave').reset();
+
+ var event = document.createEvent('Event');
+ event.initEvent('autosize:update', true, false);
+ form.find('.js-autosize')[0].dispatchEvent(event);
+
+ this.updateTargetButtons(e);
+ }
+
+ reenableTargetFormSubmitButton() {
+ var form;
+ form = $('.js-main-target-form');
+ return form.find('.js-note-text').trigger('input');
+ }
+
+ /**
+ * Shows the main form and does some setup on it.
+ *
+ * Sets some hidden fields in the form.
+ */
+ setupMainTargetNoteForm() {
+ var form;
+ // find the form
+ form = $('.js-new-note-form');
+ // Set a global clone of the form for later cloning
+ this.formClone = form.clone();
+ // show the form
+ this.setupNoteForm(form);
+ // fix classes
+ form.removeClass('js-new-note-form');
+ form.addClass('js-main-target-form');
+ form.find('#note_line_code').remove();
+ form.find('#note_position').remove();
+ form.find('#note_type').val('');
+ form.find('#in_reply_to_discussion_id').remove();
+ form.find('.js-comment-resolve-button').closest('comment-and-resolve-btn').remove();
+ this.parentTimeline = form.parents('.timeline');
+
+ if (form.length) {
+ Notes.initCommentTypeToggle(form.get(0));
+ }
+ }
+
+ /**
+ * General note form setup.
+ *
+ * deactivates the submit button when text is empty
+ * hides the preview button when text is empty
+ * setup GFM auto complete
+ * show the form
+ */
+ setupNoteForm(form) {
+ var textarea, key;
+ new gl.GLForm(form, this.enableGFM);
+ textarea = form.find('.js-note-text');
+ key = [
+ 'Note',
+ form.find('#note_noteable_type').val(),
+ form.find('#note_noteable_id').val(),
+ form.find('#note_commit_id').val(),
+ form.find('#note_type').val(),
+ form.find('#in_reply_to_discussion_id').val(),
- return isAllowed;
- };
+ // LegacyDiffNote
+ form.find('#note_line_code').val(),
- /*
- Called in response to clicking the edit note link
+ // DiffNote
+ form.find('#note_position').val()
+ ];
+ return new Autosave(textarea, key);
+ }
+
+ /**
+ * Called in response to the new note form being submitted
+ *
+ * Adds new note to list.
+ */
+ addNote($form, note) {
+ return this.renderNote(note);
+ }
+
+ addNoteError($form) {
+ let formParentTimeline;
+ if ($form.hasClass('js-main-target-form')) {
+ formParentTimeline = $form.parents('.timeline');
+ } else if ($form.hasClass('js-discussion-note-form')) {
+ formParentTimeline = $form.closest('.discussion-notes').find('.notes');
+ }
+ return this.addFlash('Your comment could not be submitted! Please check your network connection and try again.', 'alert', formParentTimeline);
+ }
+
+ updateNoteError($parentTimeline) {
+ new Flash('Your comment could not be updated! Please check your network connection and try again.');
+ }
+
+ /**
+ * Called in response to the new note form being submitted
+ *
+ * Adds new note to list.
+ */
+ addDiscussionNote($form, note, isNewDiffComment) {
+ if ($form.attr('data-resolve-all') != null) {
+ var projectPath = $form.data('project-path');
+ var discussionId = $form.data('discussion-id');
+ var mergeRequestId = $form.data('noteable-iid');
+
+ if (ResolveService != null) {
+ ResolveService.toggleResolveForDiscussion(mergeRequestId, discussionId);
+ }
+ }
- Replaces the note text with the note edit form
- Adds a data attribute to the form with the original content of the note for cancellations
- */
- Notes.prototype.showEditForm = function(e, scrollTo, myLastNote) {
- e.preventDefault();
+ this.renderNote(note, $form);
+ // cleanup after successfully creating a diff/discussion note
+ if (isNewDiffComment) {
+ this.removeDiscussionNoteForm($form);
+ }
+ }
+
+ /**
+ * Called in response to the edit note form being submitted
+ *
+ * Updates the current note field.
+ */
+ updateNote(noteEntity, $targetNote) {
+ var $noteEntityEl, $note_li;
+ // Convert returned HTML to a jQuery object so we can modify it further
+ $noteEntityEl = $(noteEntity.html);
+ $noteEntityEl.addClass('fade-in-full');
+ this.revertNoteEditForm($targetNote);
+ $noteEntityEl.renderGFM();
+ // Find the note's `li` element by ID and replace it with the updated HTML
+ $note_li = $('.note-row-' + noteEntity.id);
+
+ $note_li.replaceWith($noteEntityEl);
+ this.setupNewNote($noteEntityEl);
+
+ if (typeof gl.diffNotesCompileComponents !== 'undefined') {
+ gl.diffNotesCompileComponents();
+ }
+ }
- var $target = $(e.target);
- var $editForm = $(this.getEditFormSelector($target));
- var $note = $target.closest('.note');
- var $currentlyEditing = $('.note.is-editing:visible');
+ checkContentToAllowEditing($el) {
+ var initialContent = $el.find('.original-note-content').text().trim();
+ var currentContent = $el.find('.js-note-text').val();
+ var isAllowed = true;
- if ($currentlyEditing.length) {
- var isEditAllowed = this.checkContentToAllowEditing($currentlyEditing);
+ if (currentContent === initialContent) {
+ this.removeNoteEditForm($el);
+ }
+ else {
+ var $buttons = $el.find('.note-form-actions');
+ var isWidgetVisible = gl.utils.isInViewport($el.get(0));
- if (!isEditAllowed) {
- return;
- }
+ if (!isWidgetVisible) {
+ gl.utils.scrollToElement($el);
}
- $note.find('.js-note-attachment-delete').show();
- $editForm.addClass('current-note-edit-form');
- $note.addClass('is-editing');
- this.putEditFormInPlace($target);
- };
+ $el.find('.js-finish-edit-warning').show();
+ isAllowed = false;
+ }
- /*
- Called in response to clicking the edit note link
+ return isAllowed;
+ }
- Hides edit form and restores the original note text to the editor textarea.
- */
+ /**
+ * Called in response to clicking the edit note link
+ *
+ * Replaces the note text with the note edit form
+ * Adds a data attribute to the form with the original content of the note for cancellations
+ */
+ showEditForm(e, scrollTo, myLastNote) {
+ e.preventDefault();
- Notes.prototype.cancelEdit = function(e) {
- e.preventDefault();
- const $target = $(e.target);
- const $note = $target.closest('.note');
- const noteId = $note.attr('data-note-id');
+ var $target = $(e.target);
+ var $editForm = $(this.getEditFormSelector($target));
+ var $note = $target.closest('.note');
+ var $currentlyEditing = $('.note.is-editing:visible');
- this.revertNoteEditForm($target);
+ if ($currentlyEditing.length) {
+ var isEditAllowed = this.checkContentToAllowEditing($currentlyEditing);
- if (this.updatedNotesTrackingMap[noteId]) {
- const $newNote = $(this.updatedNotesTrackingMap[noteId].html);
- $note.replaceWith($newNote);
- this.setupNewNote($newNote);
- // Now that we have taken care of the update, clear it out
- delete this.updatedNotesTrackingMap[noteId];
- }
- else {
- $note.find('.js-finish-edit-warning').hide();
- this.removeNoteEditForm($note);
+ if (!isEditAllowed) {
+ return;
}
- };
-
- Notes.prototype.revertNoteEditForm = function($target) {
- $target = $target || $('.note.is-editing:visible');
- var selector = this.getEditFormSelector($target);
- var $editForm = $(selector);
+ }
- $editForm.insertBefore('.notes-form');
- $editForm.find('.js-comment-save-button').enable();
- $editForm.find('.js-finish-edit-warning').hide();
- };
+ $note.find('.js-note-attachment-delete').show();
+ $editForm.addClass('current-note-edit-form');
+ $note.addClass('is-editing');
+ this.putEditFormInPlace($target);
+ }
+
+ /**
+ * Called in response to clicking the edit note link
+ *
+ * Hides edit form and restores the original note text to the editor textarea.
+ */
+ cancelEdit(e) {
+ e.preventDefault();
+ const $target = $(e.target);
+ const $note = $target.closest('.note');
+ const noteId = $note.attr('data-note-id');
+
+ this.revertNoteEditForm($target);
+
+ if (this.updatedNotesTrackingMap[noteId]) {
+ const $newNote = $(this.updatedNotesTrackingMap[noteId].html);
+ $note.replaceWith($newNote);
+ this.setupNewNote($newNote);
+ // Now that we have taken care of the update, clear it out
+ delete this.updatedNotesTrackingMap[noteId];
+ }
+ else {
+ $note.find('.js-finish-edit-warning').hide();
+ this.removeNoteEditForm($note);
+ }
+ }
- Notes.prototype.getEditFormSelector = function($el) {
- var selector = '.note-edit-form:not(.mr-note-edit-form)';
+ revertNoteEditForm($target) {
+ $target = $target || $('.note.is-editing:visible');
+ var selector = this.getEditFormSelector($target);
+ var $editForm = $(selector);
- if ($el.parents('#diffs').length) {
- selector = '.note-edit-form.mr-note-edit-form';
- }
+ $editForm.insertBefore('.notes-form');
+ $editForm.find('.js-comment-save-button').enable();
+ $editForm.find('.js-finish-edit-warning').hide();
+ }
- return selector;
- };
+ getEditFormSelector($el) {
+ var selector = '.note-edit-form:not(.mr-note-edit-form)';
- Notes.prototype.removeNoteEditForm = function($note) {
- var form = $note.find('.current-note-edit-form');
- $note.removeClass('is-editing');
- form.removeClass('current-note-edit-form');
- form.find('.js-finish-edit-warning').hide();
- // Replace markdown textarea text with original note text.
- return form.find('.js-note-text').val(form.find('form.edit-note').data('original-note'));
- };
+ if ($el.parents('#diffs').length) {
+ selector = '.note-edit-form.mr-note-edit-form';
+ }
- /*
- Called in response to deleting a note of any kind.
-
- Removes the actual note from view.
- Removes the whole discussion if the last note is being removed.
- */
-
- Notes.prototype.removeNote = function(e) {
- var noteElId, noteId, dataNoteId, $note, lineHolder;
- $note = $(e.currentTarget).closest('.note');
- noteElId = $note.attr('id');
- noteId = $note.attr('data-note-id');
- lineHolder = $(e.currentTarget).closest('.notes[data-discussion-id]')
- .closest('.notes_holder')
- .prev('.line_holder');
- $(`.note[id="${noteElId}"]`).each((function(_this) {
- // A same note appears in the "Discussion" and in the "Changes" tab, we have
- // to remove all. Using $('.note[id='noteId']') ensure we get all the notes,
- // where $('#noteId') would return only one.
- return function(i, el) {
- var $note, $notes;
- $note = $(el);
- $notes = $note.closest('.discussion-notes');
-
- if (typeof gl.diffNotesCompileComponents !== 'undefined') {
- if (gl.diffNoteApps[noteElId]) {
- gl.diffNoteApps[noteElId].$destroy();
- }
+ return selector;
+ }
+
+ removeNoteEditForm($note) {
+ var form = $note.find('.current-note-edit-form');
+ $note.removeClass('is-editing');
+ form.removeClass('current-note-edit-form');
+ form.find('.js-finish-edit-warning').hide();
+ // Replace markdown textarea text with original note text.
+ return form.find('.js-note-text').val(form.find('form.edit-note').data('original-note'));
+ }
+
+ /**
+ * Called in response to deleting a note of any kind.
+ *
+ * Removes the actual note from view.
+ * Removes the whole discussion if the last note is being removed.
+ */
+ removeNote(e) {
+ var noteElId, noteId, dataNoteId, $note, lineHolder;
+ $note = $(e.currentTarget).closest('.note');
+ noteElId = $note.attr('id');
+ noteId = $note.attr('data-note-id');
+ lineHolder = $(e.currentTarget).closest('.notes[data-discussion-id]')
+ .closest('.notes_holder')
+ .prev('.line_holder');
+ $(`.note[id="${noteElId}"]`).each((function(_this) {
+ // A same note appears in the "Discussion" and in the "Changes" tab, we have
+ // to remove all. Using $('.note[id='noteId']') ensure we get all the notes,
+ // where $('#noteId') would return only one.
+ return function(i, el) {
+ var $note, $notes;
+ $note = $(el);
+ $notes = $note.closest('.discussion-notes');
+
+ if (typeof gl.diffNotesCompileComponents !== 'undefined') {
+ if (gl.diffNoteApps[noteElId]) {
+ gl.diffNoteApps[noteElId].$destroy();
}
+ }
- $note.remove();
+ $note.remove();
- // check if this is the last note for this line
- if ($notes.find('.note').length === 0) {
- var notesTr = $notes.closest('tr');
+ // check if this is the last note for this line
+ if ($notes.find('.note').length === 0) {
+ var notesTr = $notes.closest('tr');
- // "Discussions" tab
- $notes.closest('.timeline-entry').remove();
+ // "Discussions" tab
+ $notes.closest('.timeline-entry').remove();
- // The notes tr can contain multiple lists of notes, like on the parallel diff
- if (notesTr.find('.discussion-notes').length > 1) {
- $notes.remove();
- } else {
- notesTr.remove();
- }
+ // The notes tr can contain multiple lists of notes, like on the parallel diff
+ if (notesTr.find('.discussion-notes').length > 1) {
+ $notes.remove();
+ } else {
+ notesTr.remove();
}
- };
- })(this));
-
- Notes.checkMergeRequestStatus();
- return this.updateNotesCount(-1);
- };
-
- /*
- Called in response to clicking the delete attachment link
-
- Removes the attachment wrapper view, including image tag if it exists
- Resets the note editing form
- */
+ }
+ };
+ })(this));
+
+ Notes.checkMergeRequestStatus();
+ return this.updateNotesCount(-1);
+ }
+
+ /**
+ * Called in response to clicking the delete attachment link
+ *
+ * Removes the attachment wrapper view, including image tag if it exists
+ * Resets the note editing form
+ */
+ removeAttachment() {
+ const $note = $(this).closest('.note');
+ $note.find('.note-attachment').remove();
+ $note.find('.note-body > .note-text').show();
+ $note.find('.note-header').show();
+ return $note.find('.current-note-edit-form').remove();
+ }
+
+ /**
+ * Called when clicking on the "reply" button for a diff line.
+ *
+ * Shows the note form below the notes.
+ */
+ onReplyToDiscussionNote(e) {
+ this.replyToDiscussionNote(e.target);
+ }
+
+ replyToDiscussionNote(target) {
+ var form, replyLink;
+ form = this.cleanForm(this.formClone.clone());
+ replyLink = $(target).closest('.js-discussion-reply-button');
+ // insert the form after the button
+ replyLink
+ .closest('.discussion-reply-holder')
+ .hide()
+ .after(form);
+ // show the form
+ return this.setupDiscussionNoteForm(replyLink, form);
+ }
+
+ /**
+ * Shows the diff or discussion form and does some setup on it.
+ *
+ * Sets some hidden fields in the form.
+ *
+ * Note: dataHolder must have the "discussionId" and "lineCode" data attributes set.
+ */
+ setupDiscussionNoteForm(dataHolder, form) {
+ // setup note target
+ var discussionID = dataHolder.data('discussionId');
+
+ if (discussionID) {
+ form.attr('data-discussion-id', discussionID);
+ form.find('#in_reply_to_discussion_id').val(discussionID);
+ }
- Notes.prototype.removeAttachment = function() {
- const $note = $(this).closest('.note');
- $note.find('.note-attachment').remove();
- $note.find('.note-body > .note-text').show();
- $note.find('.note-header').show();
- return $note.find('.current-note-edit-form').remove();
- };
+ form.attr('data-line-code', dataHolder.data('lineCode'));
+ form.find('#line_type').val(dataHolder.data('lineType'));
- /*
- Called when clicking on the "reply" button for a diff line.
+ form.find('#note_noteable_type').val(dataHolder.data('noteableType'));
+ form.find('#note_noteable_id').val(dataHolder.data('noteableId'));
+ form.find('#note_commit_id').val(dataHolder.data('commitId'));
+ form.find('#note_type').val(dataHolder.data('noteType'));
- Shows the note form below the notes.
- */
+ // LegacyDiffNote
+ form.find('#note_line_code').val(dataHolder.data('lineCode'));
- Notes.prototype.onReplyToDiscussionNote = function(e) {
- this.replyToDiscussionNote(e.target);
- };
+ // DiffNote
+ form.find('#note_position').val(dataHolder.attr('data-position'));
- Notes.prototype.replyToDiscussionNote = function(target) {
- var form, replyLink;
- form = this.cleanForm(this.formClone.clone());
- replyLink = $(target).closest('.js-discussion-reply-button');
- // insert the form after the button
- replyLink
- .closest('.discussion-reply-holder')
- .hide()
- .after(form);
- // show the form
- return this.setupDiscussionNoteForm(replyLink, form);
- };
+ form.find('.js-note-discard').show().removeClass('js-note-discard').addClass('js-close-discussion-note-form').text(form.find('.js-close-discussion-note-form').data('cancel-text'));
+ form.find('.js-note-target-close').remove();
+ form.find('.js-note-new-discussion').remove();
+ this.setupNoteForm(form);
- /*
- Shows the diff or discussion form and does some setup on it.
+ form
+ .removeClass('js-main-target-form')
+ .addClass('discussion-form js-discussion-note-form');
- Sets some hidden fields in the form.
+ if (typeof gl.diffNotesCompileComponents !== 'undefined') {
+ var $commentBtn = form.find('comment-and-resolve-btn');
+ $commentBtn.attr(':discussion-id', `'${discussionID}'`);
- Note: dataHolder must have the "discussionId" and "lineCode" data attributes set.
- */
+ gl.diffNotesCompileComponents();
+ }
- Notes.prototype.setupDiscussionNoteForm = function(dataHolder, form) {
- // setup note target
- var discussionID = dataHolder.data('discussionId');
+ form.find('.js-note-text').focus();
+ form
+ .find('.js-comment-resolve-button')
+ .attr('data-discussion-id', discussionID);
+ }
+
+ /**
+ * Called when clicking on the "add a comment" button on the side of a diff line.
+ *
+ * Inserts a temporary row for the form below the line.
+ * Sets up the form and shows it.
+ */
+ onAddDiffNote(e) {
+ e.preventDefault();
+ const link = e.currentTarget || e.target;
+ const $link = $(link);
+ const showReplyInput = !$link.hasClass('js-diff-comment-avatar');
+ this.toggleDiffNote({
+ target: $link,
+ lineType: link.dataset.lineType,
+ showReplyInput
+ });
+ }
+
+ toggleDiffNote({
+ target,
+ lineType,
+ forceShow,
+ showReplyInput = false,
+ }) {
+ var $link, addForm, hasNotes, newForm, noteForm, replyButton, row, rowCssToAdd, targetContent, isDiffCommentAvatar;
+ $link = $(target);
+ row = $link.closest('tr');
+ const nextRow = row.next();
+ let targetRow = row;
+ if (nextRow.is('.notes_holder')) {
+ targetRow = nextRow;
+ }
- if (discussionID) {
- form.attr('data-discussion-id', discussionID);
- form.find('#in_reply_to_discussion_id').val(discussionID);
+ hasNotes = nextRow.is('.notes_holder');
+ addForm = false;
+ let lineTypeSelector = '';
+ rowCssToAdd = '<tr class="notes_holder js-temp-notes-holder"><td class="notes_line" colspan="2"></td><td class="notes_content"><div class="content"></div></td></tr>';
+ // In parallel view, look inside the correct left/right pane
+ if (this.isParallelView()) {
+ lineTypeSelector = `.${lineType}`;
+ rowCssToAdd = '<tr class="notes_holder js-temp-notes-holder"><td class="notes_line old"></td><td class="notes_content parallel old"><div class="content"></div></td><td class="notes_line new"></td><td class="notes_content parallel new"><div class="content"></div></td></tr>';
+ }
+ const notesContentSelector = `.notes_content${lineTypeSelector} .content`;
+ let notesContent = targetRow.find(notesContentSelector);
+
+ if (hasNotes && showReplyInput) {
+ targetRow.show();
+ notesContent = targetRow.find(notesContentSelector);
+ if (notesContent.length) {
+ notesContent.show();
+ replyButton = notesContent.find('.js-discussion-reply-button:visible');
+ if (replyButton.length) {
+ this.replyToDiscussionNote(replyButton[0]);
+ } else {
+ // In parallel view, the form may not be present in one of the panes
+ noteForm = notesContent.find('.js-discussion-note-form');
+ if (noteForm.length === 0) {
+ addForm = true;
+ }
+ }
}
+ } else if (showReplyInput) {
+ // add a notes row and insert the form
+ row.after(rowCssToAdd);
+ targetRow = row.next();
+ notesContent = targetRow.find(notesContentSelector);
+ addForm = true;
+ } else {
+ const isCurrentlyShown = targetRow.find('.content:not(:empty)').is(':visible');
+ const isForced = forceShow === true || forceShow === false;
+ const showNow = forceShow === true || (!isCurrentlyShown && !isForced);
+
+ targetRow.toggle(showNow);
+ notesContent.toggle(showNow);
+ }
- form.attr('data-line-code', dataHolder.data('lineCode'));
- form.find('#line_type').val(dataHolder.data('lineType'));
-
- form.find('#note_noteable_type').val(dataHolder.data('noteableType'));
- form.find('#note_noteable_id').val(dataHolder.data('noteableId'));
- form.find('#note_commit_id').val(dataHolder.data('commitId'));
- form.find('#note_type').val(dataHolder.data('noteType'));
-
- // LegacyDiffNote
- form.find('#note_line_code').val(dataHolder.data('lineCode'));
-
- // DiffNote
- form.find('#note_position').val(dataHolder.attr('data-position'));
-
- form.find('.js-note-discard').show().removeClass('js-note-discard').addClass('js-close-discussion-note-form').text(form.find('.js-close-discussion-note-form').data('cancel-text'));
- form.find('.js-note-target-close').remove();
- form.find('.js-note-new-discussion').remove();
- this.setupNoteForm(form);
-
- form
- .removeClass('js-main-target-form')
- .addClass('discussion-form js-discussion-note-form');
-
- if (typeof gl.diffNotesCompileComponents !== 'undefined') {
- var $commentBtn = form.find('comment-and-resolve-btn');
- $commentBtn.attr(':discussion-id', `'${discussionID}'`);
-
- gl.diffNotesCompileComponents();
+ if (addForm) {
+ newForm = this.cleanForm(this.formClone.clone());
+ newForm.appendTo(notesContent);
+ // show the form
+ return this.setupDiscussionNoteForm($link, newForm);
+ }
+ }
+
+ /**
+ * Called in response to "cancel" on a diff note form.
+ *
+ * Shows the reply button again.
+ * Removes the form and if necessary it's temporary row.
+ */
+ removeDiscussionNoteForm(form) {
+ var glForm, row;
+ row = form.closest('tr');
+ glForm = form.data('gl-form');
+ glForm.destroy();
+ form.find('.js-note-text').data('autosave').reset();
+ // show the reply button (will only work for replies)
+ form
+ .prev('.discussion-reply-holder')
+ .show();
+ if (row.is('.js-temp-notes-holder')) {
+ // remove temporary row for diff lines
+ return row.remove();
+ } else {
+ // only remove the form
+ return form.remove();
+ }
+ }
+
+ cancelDiscussionForm(e) {
+ var form;
+ e.preventDefault();
+ form = $(e.target).closest('.js-discussion-note-form');
+ return this.removeDiscussionNoteForm(form);
+ }
+
+ /**
+ * Called after an attachment file has been selected.
+ *
+ * Updates the file name for the selected attachment.
+ */
+ updateFormAttachment() {
+ var filename, form;
+ form = $(this).closest('form');
+ // get only the basename
+ filename = $(this).val().replace(/^.*[\\\/]/, '');
+ return form.find('.js-attachment-filename').text(filename);
+ }
+
+ /**
+ * Called when the tab visibility changes
+ */
+ visibilityChange() {
+ return this.refresh();
+ }
+
+ updateTargetButtons(e) {
+ var closebtn, closetext, discardbtn, form, reopenbtn, reopentext, textarea;
+ textarea = $(e.target);
+ form = textarea.parents('form');
+ reopenbtn = form.find('.js-note-target-reopen');
+ closebtn = form.find('.js-note-target-close');
+ discardbtn = form.find('.js-note-discard');
+
+ if (textarea.val().trim().length > 0) {
+ reopentext = reopenbtn.attr('data-alternative-text');
+ closetext = closebtn.attr('data-alternative-text');
+ if (reopenbtn.text() !== reopentext) {
+ reopenbtn.text(reopentext);
}
-
- form.find('.js-note-text').focus();
- form
- .find('.js-comment-resolve-button')
- .attr('data-discussion-id', discussionID);
- };
-
- /*
- Called when clicking on the "add a comment" button on the side of a diff line.
-
- Inserts a temporary row for the form below the line.
- Sets up the form and shows it.
- */
-
- Notes.prototype.onAddDiffNote = function(e) {
- e.preventDefault();
- const link = e.currentTarget || e.target;
- const $link = $(link);
- const showReplyInput = !$link.hasClass('js-diff-comment-avatar');
- this.toggleDiffNote({
- target: $link,
- lineType: link.dataset.lineType,
- showReplyInput
- });
- };
-
- Notes.prototype.toggleDiffNote = function({
- target,
- lineType,
- forceShow,
- showReplyInput = false,
- }) {
- var $link, addForm, hasNotes, newForm, noteForm, replyButton, row, rowCssToAdd, targetContent, isDiffCommentAvatar;
- $link = $(target);
- row = $link.closest('tr');
- const nextRow = row.next();
- let targetRow = row;
- if (nextRow.is('.notes_holder')) {
- targetRow = nextRow;
+ if (closebtn.text() !== closetext) {
+ closebtn.text(closetext);
}
-
- hasNotes = nextRow.is('.notes_holder');
- addForm = false;
- let lineTypeSelector = '';
- rowCssToAdd = '<tr class="notes_holder js-temp-notes-holder"><td class="notes_line" colspan="2"></td><td class="notes_content"><div class="content"></div></td></tr>';
- // In parallel view, look inside the correct left/right pane
- if (this.isParallelView()) {
- lineTypeSelector = `.${lineType}`;
- rowCssToAdd = '<tr class="notes_holder js-temp-notes-holder"><td class="notes_line old"></td><td class="notes_content parallel old"><div class="content"></div></td><td class="notes_line new"></td><td class="notes_content parallel new"><div class="content"></div></td></tr>';
+ if (reopenbtn.is(':not(.btn-comment-and-reopen)')) {
+ reopenbtn.addClass('btn-comment-and-reopen');
}
- const notesContentSelector = `.notes_content${lineTypeSelector} .content`;
- let notesContent = targetRow.find(notesContentSelector);
-
- if (hasNotes && showReplyInput) {
- targetRow.show();
- notesContent = targetRow.find(notesContentSelector);
- if (notesContent.length) {
- notesContent.show();
- replyButton = notesContent.find('.js-discussion-reply-button:visible');
- if (replyButton.length) {
- this.replyToDiscussionNote(replyButton[0]);
- } else {
- // In parallel view, the form may not be present in one of the panes
- noteForm = notesContent.find('.js-discussion-note-form');
- if (noteForm.length === 0) {
- addForm = true;
- }
- }
- }
- } else if (showReplyInput) {
- // add a notes row and insert the form
- row.after(rowCssToAdd);
- targetRow = row.next();
- notesContent = targetRow.find(notesContentSelector);
- addForm = true;
- } else {
- const isCurrentlyShown = targetRow.find('.content:not(:empty)').is(':visible');
- const isForced = forceShow === true || forceShow === false;
- const showNow = forceShow === true || (!isCurrentlyShown && !isForced);
-
- targetRow.toggle(showNow);
- notesContent.toggle(showNow);
+ if (closebtn.is(':not(.btn-comment-and-close)')) {
+ closebtn.addClass('btn-comment-and-close');
}
-
- if (addForm) {
- newForm = this.cleanForm(this.formClone.clone());
- newForm.appendTo(notesContent);
- // show the form
- return this.setupDiscussionNoteForm($link, newForm);
+ if (discardbtn.is(':hidden')) {
+ return discardbtn.show();
}
- };
-
- /*
- Called in response to "cancel" on a diff note form.
-
- Shows the reply button again.
- Removes the form and if necessary it's temporary row.
- */
-
- Notes.prototype.removeDiscussionNoteForm = function(form) {
- var glForm, row;
- row = form.closest('tr');
- glForm = form.data('gl-form');
- glForm.destroy();
- form.find('.js-note-text').data('autosave').reset();
- // show the reply button (will only work for replies)
- form
- .prev('.discussion-reply-holder')
- .show();
- if (row.is('.js-temp-notes-holder')) {
- // remove temporary row for diff lines
- return row.remove();
- } else {
- // only remove the form
- return form.remove();
+ } else {
+ reopentext = reopenbtn.data('original-text');
+ closetext = closebtn.data('original-text');
+ if (reopenbtn.text() !== reopentext) {
+ reopenbtn.text(reopentext);
}
- };
-
- Notes.prototype.cancelDiscussionForm = function(e) {
- var form;
- e.preventDefault();
- form = $(e.target).closest('.js-discussion-note-form');
- return this.removeDiscussionNoteForm(form);
- };
-
- /*
- Called after an attachment file has been selected.
-
- Updates the file name for the selected attachment.
- */
-
- Notes.prototype.updateFormAttachment = function() {
- var filename, form;
- form = $(this).closest('form');
- // get only the basename
- filename = $(this).val().replace(/^.*[\\\/]/, '');
- return form.find('.js-attachment-filename').text(filename);
- };
-
- /*
- Called when the tab visibility changes
- */
-
- Notes.prototype.visibilityChange = function() {
- return this.refresh();
- };
-
- Notes.prototype.updateTargetButtons = function(e) {
- var closebtn, closetext, discardbtn, form, reopenbtn, reopentext, textarea;
- textarea = $(e.target);
- form = textarea.parents('form');
- reopenbtn = form.find('.js-note-target-reopen');
- closebtn = form.find('.js-note-target-close');
- discardbtn = form.find('.js-note-discard');
-
- if (textarea.val().trim().length > 0) {
- reopentext = reopenbtn.attr('data-alternative-text');
- closetext = closebtn.attr('data-alternative-text');
- if (reopenbtn.text() !== reopentext) {
- reopenbtn.text(reopentext);
- }
- if (closebtn.text() !== closetext) {
- closebtn.text(closetext);
- }
- if (reopenbtn.is(':not(.btn-comment-and-reopen)')) {
- reopenbtn.addClass('btn-comment-and-reopen');
- }
- if (closebtn.is(':not(.btn-comment-and-close)')) {
- closebtn.addClass('btn-comment-and-close');
- }
- if (discardbtn.is(':hidden')) {
- return discardbtn.show();
- }
- } else {
- reopentext = reopenbtn.data('original-text');
- closetext = closebtn.data('original-text');
- if (reopenbtn.text() !== reopentext) {
- reopenbtn.text(reopentext);
- }
- if (closebtn.text() !== closetext) {
- closebtn.text(closetext);
- }
- if (reopenbtn.is('.btn-comment-and-reopen')) {
- reopenbtn.removeClass('btn-comment-and-reopen');
- }
- if (closebtn.is('.btn-comment-and-close')) {
- closebtn.removeClass('btn-comment-and-close');
- }
- if (discardbtn.is(':visible')) {
- return discardbtn.hide();
- }
+ if (closebtn.text() !== closetext) {
+ closebtn.text(closetext);
}
- };
-
- Notes.prototype.putEditFormInPlace = function($el) {
- var $editForm = $(this.getEditFormSelector($el));
- var $note = $el.closest('.note');
-
- $editForm.insertAfter($note.find('.note-text'));
-
- var $originalContentEl = $note.find('.original-note-content');
- var originalContent = $originalContentEl.text().trim();
- var postUrl = $originalContentEl.data('post-url');
- var targetId = $originalContentEl.data('target-id');
- var targetType = $originalContentEl.data('target-type');
-
- new gl.GLForm($editForm.find('form'), this.enableGFM);
-
- $editForm.find('form')
- .attr('action', postUrl)
- .attr('data-remote', 'true');
- $editForm.find('.js-form-target-id').val(targetId);
- $editForm.find('.js-form-target-type').val(targetType);
- $editForm.find('.js-note-text').focus().val(originalContent);
- $editForm.find('.js-md-write-button').trigger('click');
- $editForm.find('.referenced-users').hide();
- };
-
- Notes.prototype.putConflictEditWarningInPlace = function(noteEntity, $note) {
- if ($note.find('.js-conflict-edit-warning').length === 0) {
- const $alert = $(`<div class="js-conflict-edit-warning alert alert-danger">
- This comment has changed since you started editing, please review the
- <a href="#note_${noteEntity.id}" target="_blank" rel="noopener noreferrer">
- updated comment
- </a>
- to ensure information is not lost
- </div>`);
- $alert.insertAfter($note.find('.note-text'));
+ if (reopenbtn.is('.btn-comment-and-reopen')) {
+ reopenbtn.removeClass('btn-comment-and-reopen');
}
- };
-
- Notes.prototype.updateNotesCount = function(updateCount) {
- return this.notesCountBadge.text(parseInt(this.notesCountBadge.text(), 10) + updateCount);
- };
-
- Notes.prototype.toggleCommitList = function(e) {
- const $element = $(e.currentTarget);
- const $closestSystemCommitList = $element.siblings('.system-note-commit-list');
-
- $element.find('.fa').toggleClass('fa-angle-down').toggleClass('fa-angle-up');
- $closestSystemCommitList.toggleClass('hide-shade');
- };
-
- /**
- Scans system notes with `ul` elements in system note body
- then collapse long commit list pushed by user to make it less
- intrusive.
- */
- Notes.prototype.collapseLongCommitList = function() {
- const systemNotes = $('#notes-list').find('li.system-note').has('ul');
-
- $.each(systemNotes, function(index, systemNote) {
- const $systemNote = $(systemNote);
- const headerMessage = $systemNote.find('.note-text').find('p:first').text().replace(':', '');
-
- $systemNote.find('.note-header .system-note-message').html(headerMessage);
-
- if ($systemNote.find('li').length > MAX_VISIBLE_COMMIT_LIST_COUNT) {
- $systemNote.find('.note-text').addClass('system-note-commit-list');
- $systemNote.find('.system-note-commit-list-toggler').show();
- } else {
- $systemNote.find('.note-text').addClass('system-note-commit-list hide-shade');
- }
- });
- };
-
- Notes.prototype.addFlash = function(...flashParams) {
- this.flashInstance = new Flash(...flashParams);
- };
-
- Notes.prototype.clearFlash = function() {
- if (this.flashInstance && this.flashInstance.flashContainer) {
- this.flashInstance.flashContainer.hide();
- this.flashInstance = null;
+ if (closebtn.is('.btn-comment-and-close')) {
+ closebtn.removeClass('btn-comment-and-close');
}
- };
-
- Notes.prototype.cleanForm = function($form) {
- // Remove JS classes that are not needed here
- $form
- .find('.js-comment-type-dropdown')
- .removeClass('btn-group');
-
- // Remove dropdown
- $form
- .find('.dropdown-menu')
- .remove();
-
- return $form;
- };
-
- /**
- * Check if note does not exists on page
- */
- Notes.isNewNote = function(noteEntity, noteIds) {
- return $.inArray(noteEntity.id, noteIds) === -1;
- };
-
- /**
- * Check if $note already contains the `noteEntity` content
- */
- Notes.isUpdatedNote = function(noteEntity, $note) {
- // There can be CRLF vs LF mismatches if we don't sanitize and compare the same way
- const sanitizedNoteEntityText = normalizeNewlines(noteEntity.note.trim());
- const currentNoteText = normalizeNewlines(
- $note.find('.original-note-content').first().text().trim()
- );
- return sanitizedNoteEntityText !== currentNoteText;
- };
-
- Notes.checkMergeRequestStatus = function() {
- if (gl.utils.getPagePath(1) === 'merge_requests') {
- gl.mrWidget.checkStatus();
+ if (discardbtn.is(':visible')) {
+ return discardbtn.hide();
}
- };
+ }
+ }
+
+ putEditFormInPlace($el) {
+ var $editForm = $(this.getEditFormSelector($el));
+ var $note = $el.closest('.note');
+
+ $editForm.insertAfter($note.find('.note-text'));
+
+ var $originalContentEl = $note.find('.original-note-content');
+ var originalContent = $originalContentEl.text().trim();
+ var postUrl = $originalContentEl.data('post-url');
+ var targetId = $originalContentEl.data('target-id');
+ var targetType = $originalContentEl.data('target-type');
+
+ new gl.GLForm($editForm.find('form'), this.enableGFM);
+
+ $editForm.find('form')
+ .attr('action', postUrl)
+ .attr('data-remote', 'true');
+ $editForm.find('.js-form-target-id').val(targetId);
+ $editForm.find('.js-form-target-type').val(targetType);
+ $editForm.find('.js-note-text').focus().val(originalContent);
+ $editForm.find('.js-md-write-button').trigger('click');
+ $editForm.find('.referenced-users').hide();
+ }
+
+ putConflictEditWarningInPlace(noteEntity, $note) {
+ if ($note.find('.js-conflict-edit-warning').length === 0) {
+ const $alert = $(`<div class="js-conflict-edit-warning alert alert-danger">
+ This comment has changed since you started editing, please review the
+ <a href="#note_${noteEntity.id}" target="_blank" rel="noopener noreferrer">
+ updated comment
+ </a>
+ to ensure information is not lost
+ </div>`);
+ $alert.insertAfter($note.find('.note-text'));
+ }
+ }
- Notes.animateAppendNote = function(noteHtml, $notesList) {
- const $note = $(noteHtml);
+ updateNotesCount(updateCount) {
+ return this.notesCountBadge.text(parseInt(this.notesCountBadge.text(), 10) + updateCount);
+ }
- $note.addClass('fade-in-full').renderGFM();
- $notesList.append($note);
- return $note;
- };
+ toggleCommitList(e) {
+ const $element = $(e.currentTarget);
+ const $closestSystemCommitList = $element.siblings('.system-note-commit-list');
- Notes.animateUpdateNote = function(noteHtml, $note) {
- const $updatedNote = $(noteHtml);
+ $element.find('.fa').toggleClass('fa-angle-down').toggleClass('fa-angle-up');
+ $closestSystemCommitList.toggleClass('hide-shade');
+ }
- $updatedNote.addClass('fade-in').renderGFM();
- $note.replaceWith($updatedNote);
- return $updatedNote;
- };
+ /**
+ * Scans system notes with `ul` elements in system note body
+ * then collapse long commit list pushed by user to make it less
+ * intrusive.
+ */
+ collapseLongCommitList() {
+ const systemNotes = $('#notes-list').find('li.system-note').has('ul');
- /**
- * Get data from Form attributes to use for saving/submitting comment.
- */
- Notes.prototype.getFormData = function($form) {
- return {
- formData: $form.serialize(),
- formContent: _.escape($form.find('.js-note-text').val()),
- formAction: $form.attr('action'),
- };
- };
+ $.each(systemNotes, function(index, systemNote) {
+ const $systemNote = $(systemNote);
+ const headerMessage = $systemNote.find('.note-text').find('p:first').text().replace(':', '');
- /**
- * Identify if comment has any quick actions
- */
- Notes.prototype.hasQuickActions = function(formContent) {
- return REGEX_QUICK_ACTIONS.test(formContent);
- };
-
- /**
- * Remove quick actions and leave comment with pure message
- */
- Notes.prototype.stripQuickActions = function(formContent) {
- return formContent.replace(REGEX_QUICK_ACTIONS, '').trim();
- };
+ $systemNote.find('.note-header .system-note-message').html(headerMessage);
- /**
- * Gets appropriate description from quick actions found in provided `formContent`
- */
- Notes.prototype.getQuickActionDescription = function (formContent, availableQuickActions = []) {
- let tempFormContent;
+ if ($systemNote.find('li').length > MAX_VISIBLE_COMMIT_LIST_COUNT) {
+ $systemNote.find('.note-text').addClass('system-note-commit-list');
+ $systemNote.find('.system-note-commit-list-toggler').show();
+ } else {
+ $systemNote.find('.note-text').addClass('system-note-commit-list hide-shade');
+ }
+ });
+ }
- // Identify executed quick actions from `formContent`
- const executedCommands = availableQuickActions.filter((command, index) => {
- const commandRegex = new RegExp(`/${command.name}`);
- return commandRegex.test(formContent);
- });
+ addFlash(...flashParams) {
+ this.flashInstance = new Flash(...flashParams);
+ }
- if (executedCommands && executedCommands.length) {
- if (executedCommands.length > 1) {
- tempFormContent = 'Applying multiple commands';
- } else {
- const commandDescription = executedCommands[0].description.toLowerCase();
- tempFormContent = `Applying command to ${commandDescription}`;
- }
+ clearFlash() {
+ if (this.flashInstance && this.flashInstance.flashContainer) {
+ this.flashInstance.flashContainer.hide();
+ this.flashInstance = null;
+ }
+ }
+
+ cleanForm($form) {
+ // Remove JS classes that are not needed here
+ $form
+ .find('.js-comment-type-dropdown')
+ .removeClass('btn-group');
+
+ // Remove dropdown
+ $form
+ .find('.dropdown-menu')
+ .remove();
+
+ return $form;
+ }
+
+ /**
+ * Check if note does not exists on page
+ */
+ static isNewNote(noteEntity, noteIds) {
+ return $.inArray(noteEntity.id, noteIds) === -1;
+ }
+
+ /**
+ * Check if $note already contains the `noteEntity` content
+ */
+ static isUpdatedNote(noteEntity, $note) {
+ // There can be CRLF vs LF mismatches if we don't sanitize and compare the same way
+ const sanitizedNoteEntityText = normalizeNewlines(noteEntity.note.trim());
+ const currentNoteText = normalizeNewlines(
+ $note.find('.original-note-content').first().text().trim()
+ );
+ return sanitizedNoteEntityText !== currentNoteText;
+ }
+
+ static checkMergeRequestStatus() {
+ if (gl.utils.getPagePath(1) === 'merge_requests') {
+ gl.mrWidget.checkStatus();
+ }
+ }
+
+ static animateAppendNote(noteHtml, $notesList) {
+ const $note = $(noteHtml);
+
+ $note.addClass('fade-in-full').renderGFM();
+ $notesList.append($note);
+ return $note;
+ }
+
+ static animateUpdateNote(noteHtml, $note) {
+ const $updatedNote = $(noteHtml);
+
+ $updatedNote.addClass('fade-in').renderGFM();
+ $note.replaceWith($updatedNote);
+ return $updatedNote;
+ }
+
+ /**
+ * Get data from Form attributes to use for saving/submitting comment.
+ */
+ getFormData($form) {
+ return {
+ formData: $form.serialize(),
+ formContent: _.escape($form.find('.js-note-text').val()),
+ formAction: $form.attr('action'),
+ };
+ }
+
+ /**
+ * Identify if comment has any quick actions
+ */
+ hasQuickActions(formContent) {
+ return REGEX_QUICK_ACTIONS.test(formContent);
+ }
+
+ /**
+ * Remove quick actions and leave comment with pure message
+ */
+ stripQuickActions(formContent) {
+ return formContent.replace(REGEX_QUICK_ACTIONS, '').trim();
+ }
+
+ /**
+ * Gets appropriate description from quick actions found in provided `formContent`
+ */
+ getQuickActionDescription(formContent, availableQuickActions = []) {
+ let tempFormContent;
+
+ // Identify executed quick actions from `formContent`
+ const executedCommands = availableQuickActions.filter((command, index) => {
+ const commandRegex = new RegExp(`/${command.name}`);
+ return commandRegex.test(formContent);
+ });
+
+ if (executedCommands && executedCommands.length) {
+ if (executedCommands.length > 1) {
+ tempFormContent = 'Applying multiple commands';
} else {
- tempFormContent = 'Applying command';
+ const commandDescription = executedCommands[0].description.toLowerCase();
+ tempFormContent = `Applying command to ${commandDescription}`;
}
+ } else {
+ tempFormContent = 'Applying command';
+ }
- return tempFormContent;
- };
-
- /**
- * Create placeholder note DOM element populated with comment body
- * that we will show while comment is being posted.
- * Once comment is _actually_ posted on server, we will have final element
- * in response that we will show in place of this temporary element.
- */
- Notes.prototype.createPlaceholderNote = function ({ formContent, uniqueId, isDiscussionNote, currentUsername, currentUserFullname, currentUserAvatar }) {
- const discussionClass = isDiscussionNote ? 'discussion' : '';
- const $tempNote = $(
- `<li id="${uniqueId}" class="note being-posted fade-in-half timeline-entry">
- <div class="timeline-entry-inner">
- <div class="timeline-icon">
- <a href="/${currentUsername}">
- <img class="avatar s40" src="${currentUserAvatar}">
- </a>
- </div>
- <div class="timeline-content ${discussionClass}">
- <div class="note-header">
- <div class="note-header-info">
- <a href="/${currentUsername}">
- <span class="hidden-xs">${currentUserFullname}</span>
- <span class="note-headline-light">@${currentUsername}</span>
- </a>
- </div>
+ return tempFormContent;
+ }
+
+ /**
+ * Create placeholder note DOM element populated with comment body
+ * that we will show while comment is being posted.
+ * Once comment is _actually_ posted on server, we will have final element
+ * in response that we will show in place of this temporary element.
+ */
+ createPlaceholderNote({ formContent, uniqueId, isDiscussionNote, currentUsername, currentUserFullname, currentUserAvatar }) {
+ const discussionClass = isDiscussionNote ? 'discussion' : '';
+ const $tempNote = $(
+ `<li id="${uniqueId}" class="note being-posted fade-in-half timeline-entry">
+ <div class="timeline-entry-inner">
+ <div class="timeline-icon">
+ <a href="/${currentUsername}">
+ <img class="avatar s40" src="${currentUserAvatar}">
+ </a>
+ </div>
+ <div class="timeline-content ${discussionClass}">
+ <div class="note-header">
+ <div class="note-header-info">
+ <a href="/${currentUsername}">
+ <span class="hidden-xs">${currentUserFullname}</span>
+ <span class="note-headline-light">@${currentUsername}</span>
+ </a>
+ </div>
+ </div>
+ <div class="note-body">
+ <div class="note-text">
+ <p>${formContent}</p>
</div>
- <div class="note-body">
- <div class="note-text">
- <p>${formContent}</p>
- </div>
- </div>
- </div>
+ </div>
+ </div>
+ </div>
+ </li>`
+ );
+
+ return $tempNote;
+ }
+
+ /**
+ * Create Placeholder System Note DOM element populated with quick action description
+ */
+ createPlaceholderSystemNote({ formContent, uniqueId }) {
+ const $tempNote = $(
+ `<li id="${uniqueId}" class="note system-note timeline-entry being-posted fade-in-half">
+ <div class="timeline-entry-inner">
+ <div class="timeline-content">
+ <i>${formContent}</i>
</div>
- </li>`
- );
-
- return $tempNote;
- };
-
- /**
- * Create Placeholder System Note DOM element populated with quick action description
- */
- Notes.prototype.createPlaceholderSystemNote = function ({ formContent, uniqueId }) {
- const $tempNote = $(
- `<li id="${uniqueId}" class="note system-note timeline-entry being-posted fade-in-half">
- <div class="timeline-entry-inner">
- <div class="timeline-content">
- <i>${formContent}</i>
- </div>
- </div>
- </li>`
- );
+ </div>
+ </li>`
+ );
+
+ return $tempNote;
+ }
+
+ /**
+ * This method does following tasks step-by-step whenever a new comment
+ * is submitted by user (both main thread comments as well as discussion comments).
+ *
+ * 1) Get Form metadata
+ * 2) Identify comment type; a) Main thread b) Discussion thread c) Discussion resolve
+ * 3) Build temporary placeholder element (using `createPlaceholderNote`)
+ * 4) Show placeholder note on UI
+ * 5) Perform network request to submit the note using `gl.utils.ajaxPost`
+ * a) If request is successfully completed
+ * 1. Remove placeholder element
+ * 2. Show submitted Note element
+ * 3. Perform post-submit errands
+ * a. Mark discussion as resolved if comment submission was for resolve.
+ * b. Reset comment form to original state.
+ * b) If request failed
+ * 1. Remove placeholder element
+ * 2. Show error Flash message about failure
+ */
+ postComment(e) {
+ e.preventDefault();
+
+ // Get Form metadata
+ const $submitBtn = $(e.target);
+ let $form = $submitBtn.parents('form');
+ const $closeBtn = $form.find('.js-note-target-close');
+ const isDiscussionNote = $submitBtn.parent().find('li.droplab-item-selected').attr('id') === 'discussion';
+ const isMainForm = $form.hasClass('js-main-target-form');
+ const isDiscussionForm = $form.hasClass('js-discussion-note-form');
+ const isDiscussionResolve = $submitBtn.hasClass('js-comment-resolve-button');
+ const { formData, formContent, formAction } = this.getFormData($form);
+ let noteUniqueId;
+ let systemNoteUniqueId;
+ let hasQuickActions = false;
+ let $notesContainer;
+ let tempFormContent;
+
+ // Get reference to notes container based on type of comment
+ if (isDiscussionForm) {
+ $notesContainer = $form.parent('.discussion-notes').find('.notes');
+ } else if (isMainForm) {
+ $notesContainer = $('ul.main-notes-list');
+ }
- return $tempNote;
- };
+ // If comment is to resolve discussion, disable submit buttons while
+ // comment posting is finished.
+ if (isDiscussionResolve) {
+ $submitBtn.disable();
+ $form.find('.js-comment-submit-button').disable();
+ }
- /**
- * This method does following tasks step-by-step whenever a new comment
- * is submitted by user (both main thread comments as well as discussion comments).
- *
- * 1) Get Form metadata
- * 2) Identify comment type; a) Main thread b) Discussion thread c) Discussion resolve
- * 3) Build temporary placeholder element (using `createPlaceholderNote`)
- * 4) Show placeholder note on UI
- * 5) Perform network request to submit the note using `gl.utils.ajaxPost`
- * a) If request is successfully completed
- * 1. Remove placeholder element
- * 2. Show submitted Note element
- * 3. Perform post-submit errands
- * a. Mark discussion as resolved if comment submission was for resolve.
- * b. Reset comment form to original state.
- * b) If request failed
- * 1. Remove placeholder element
- * 2. Show error Flash message about failure
- */
- Notes.prototype.postComment = function(e) {
- e.preventDefault();
-
- // Get Form metadata
- const $submitBtn = $(e.target);
- let $form = $submitBtn.parents('form');
- const $closeBtn = $form.find('.js-note-target-close');
- const isDiscussionNote = $submitBtn.parent().find('li.droplab-item-selected').attr('id') === 'discussion';
- const isMainForm = $form.hasClass('js-main-target-form');
- const isDiscussionForm = $form.hasClass('js-discussion-note-form');
- const isDiscussionResolve = $submitBtn.hasClass('js-comment-resolve-button');
- const { formData, formContent, formAction } = this.getFormData($form);
- let noteUniqueId;
- let systemNoteUniqueId;
- let hasQuickActions = false;
- let $notesContainer;
- let tempFormContent;
-
- // Get reference to notes container based on type of comment
- if (isDiscussionForm) {
- $notesContainer = $form.parent('.discussion-notes').find('.notes');
- } else if (isMainForm) {
- $notesContainer = $('ul.main-notes-list');
- }
+ tempFormContent = formContent;
+ if (this.hasQuickActions(formContent)) {
+ tempFormContent = this.stripQuickActions(formContent);
+ hasQuickActions = true;
+ }
- // If comment is to resolve discussion, disable submit buttons while
- // comment posting is finished.
- if (isDiscussionResolve) {
- $submitBtn.disable();
- $form.find('.js-comment-submit-button').disable();
- }
+ // Show placeholder note
+ if (tempFormContent) {
+ noteUniqueId = _.uniqueId('tempNote_');
+ $notesContainer.append(this.createPlaceholderNote({
+ formContent: tempFormContent,
+ uniqueId: noteUniqueId,
+ isDiscussionNote,
+ currentUsername: gon.current_username,
+ currentUserFullname: gon.current_user_fullname,
+ currentUserAvatar: gon.current_user_avatar_url,
+ }));
+ }
- tempFormContent = formContent;
- if (this.hasQuickActions(formContent)) {
- tempFormContent = this.stripQuickActions(formContent);
- hasQuickActions = true;
- }
+ // Show placeholder system note
+ if (hasQuickActions) {
+ systemNoteUniqueId = _.uniqueId('tempSystemNote_');
+ $notesContainer.append(this.createPlaceholderSystemNote({
+ formContent: this.getQuickActionDescription(formContent, AjaxCache.get(gl.GfmAutoComplete.dataSources.commands)),
+ uniqueId: systemNoteUniqueId,
+ }));
+ }
- // Show placeholder note
- if (tempFormContent) {
- noteUniqueId = _.uniqueId('tempNote_');
- $notesContainer.append(this.createPlaceholderNote({
- formContent: tempFormContent,
- uniqueId: noteUniqueId,
- isDiscussionNote,
- currentUsername: gon.current_username,
- currentUserFullname: gon.current_user_fullname,
- currentUserAvatar: gon.current_user_avatar_url,
- }));
+ // Clear the form textarea
+ if ($notesContainer.length) {
+ if (isMainForm) {
+ this.resetMainTargetForm(e);
+ } else if (isDiscussionForm) {
+ this.removeDiscussionNoteForm($form);
}
+ }
- // Show placeholder system note
- if (hasQuickActions) {
- systemNoteUniqueId = _.uniqueId('tempSystemNote_');
- $notesContainer.append(this.createPlaceholderSystemNote({
- formContent: this.getQuickActionDescription(formContent, AjaxCache.get(gl.GfmAutoComplete.dataSources.commands)),
- uniqueId: systemNoteUniqueId,
- }));
- }
+ /* eslint-disable promise/catch-or-return */
+ // Make request to submit comment on server
+ gl.utils.ajaxPost(formAction, formData)
+ .then((note) => {
+ // Submission successful! remove placeholder
+ $notesContainer.find(`#${noteUniqueId}`).remove();
- // Clear the form textarea
- if ($notesContainer.length) {
- if (isMainForm) {
- this.resetMainTargetForm(e);
- } else if (isDiscussionForm) {
- this.removeDiscussionNoteForm($form);
+ // Reset cached commands list when command is applied
+ if (hasQuickActions) {
+ $form.find('textarea.js-note-text').trigger('clear-commands-cache.atwho');
}
- }
-
- /* eslint-disable promise/catch-or-return */
- // Make request to submit comment on server
- gl.utils.ajaxPost(formAction, formData)
- .then((note) => {
- // Submission successful! remove placeholder
- $notesContainer.find(`#${noteUniqueId}`).remove();
- // Reset cached commands list when command is applied
- if (hasQuickActions) {
- $form.find('textarea.js-note-text').trigger('clear-commands-cache.atwho');
- }
-
- // Clear previous form errors
- this.clearFlashWrapper();
+ // Clear previous form errors
+ this.clearFlashWrapper();
- // Check if this was discussion comment
- if (isDiscussionForm) {
- // Remove flash-container
- $notesContainer.find('.flash-container').remove();
+ // Check if this was discussion comment
+ if (isDiscussionForm) {
+ // Remove flash-container
+ $notesContainer.find('.flash-container').remove();
- // If comment intends to resolve discussion, do the same.
- if (isDiscussionResolve) {
- $form
- .attr('data-discussion-id', $submitBtn.data('discussion-id'))
- .attr('data-resolve-all', 'true')
- .attr('data-project-path', $submitBtn.data('project-path'));
- }
+ // If comment intends to resolve discussion, do the same.
+ if (isDiscussionResolve) {
+ $form
+ .attr('data-discussion-id', $submitBtn.data('discussion-id'))
+ .attr('data-resolve-all', 'true')
+ .attr('data-project-path', $submitBtn.data('project-path'));
+ }
- // Show final note element on UI
- this.addDiscussionNote($form, note, $notesContainer.length === 0);
+ // Show final note element on UI
+ this.addDiscussionNote($form, note, $notesContainer.length === 0);
- // append flash-container to the Notes list
- if ($notesContainer.length) {
- $notesContainer.append('<div class="flash-container" style="display: none;"></div>');
- }
- } else if (isMainForm) { // Check if this was main thread comment
- // Show final note element on UI and perform form and action buttons cleanup
- this.addNote($form, note);
- this.reenableTargetFormSubmitButton(e);
+ // append flash-container to the Notes list
+ if ($notesContainer.length) {
+ $notesContainer.append('<div class="flash-container" style="display: none;"></div>');
}
+ } else if (isMainForm) { // Check if this was main thread comment
+ // Show final note element on UI and perform form and action buttons cleanup
+ this.addNote($form, note);
+ this.reenableTargetFormSubmitButton(e);
+ }
- if (note.commands_changes) {
- this.handleQuickActions(note);
- }
+ if (note.commands_changes) {
+ this.handleQuickActions(note);
+ }
- $form.trigger('ajax:success', [note]);
- }).fail(() => {
- // Submission failed, remove placeholder note and show Flash error message
- $notesContainer.find(`#${noteUniqueId}`).remove();
+ $form.trigger('ajax:success', [note]);
+ }).fail(() => {
+ // Submission failed, remove placeholder note and show Flash error message
+ $notesContainer.find(`#${noteUniqueId}`).remove();
- if (hasQuickActions) {
- $notesContainer.find(`#${systemNoteUniqueId}`).remove();
- }
+ if (hasQuickActions) {
+ $notesContainer.find(`#${systemNoteUniqueId}`).remove();
+ }
- // Show form again on UI on failure
- if (isDiscussionForm && $notesContainer.length) {
- const replyButton = $notesContainer.parent().find('.js-discussion-reply-button');
- this.replyToDiscussionNote(replyButton[0]);
- $form = $notesContainer.parent().find('form');
- }
+ // Show form again on UI on failure
+ if (isDiscussionForm && $notesContainer.length) {
+ const replyButton = $notesContainer.parent().find('.js-discussion-reply-button');
+ this.replyToDiscussionNote(replyButton[0]);
+ $form = $notesContainer.parent().find('form');
+ }
- $form.find('.js-note-text').val(formContent);
- this.reenableTargetFormSubmitButton(e);
- this.addNoteError($form);
- });
+ $form.find('.js-note-text').val(formContent);
+ this.reenableTargetFormSubmitButton(e);
+ this.addNoteError($form);
+ });
- return $closeBtn.text($closeBtn.data('original-text'));
- };
+ return $closeBtn.text($closeBtn.data('original-text'));
+ }
+
+ /**
+ * This method does following tasks step-by-step whenever an existing comment
+ * is updated by user (both main thread comments as well as discussion comments).
+ *
+ * 1) Get Form metadata
+ * 2) Update note element with new content
+ * 3) Perform network request to submit the updated note using `gl.utils.ajaxPost`
+ * a) If request is successfully completed
+ * 1. Show submitted Note element
+ * b) If request failed
+ * 1. Revert Note element to original content
+ * 2. Show error Flash message about failure
+ */
+ updateComment(e) {
+ e.preventDefault();
+
+ // Get Form metadata
+ const $submitBtn = $(e.target);
+ const $form = $submitBtn.parents('form');
+ const $closeBtn = $form.find('.js-note-target-close');
+ const $editingNote = $form.parents('.note.is-editing');
+ const $noteBody = $editingNote.find('.js-task-list-container');
+ const $noteBodyText = $noteBody.find('.note-text');
+ const { formData, formContent, formAction } = this.getFormData($form);
+
+ // Cache original comment content
+ const cachedNoteBodyText = $noteBodyText.html();
+
+ // Show updated comment content temporarily
+ $noteBodyText.html(_.escape(formContent));
+ $editingNote.removeClass('is-editing fade-in-full').addClass('being-posted fade-in-half');
+ $editingNote.find('.note-headline-meta a').html('<i class="fa fa-spinner fa-spin" aria-label="Comment is being updated" aria-hidden="true"></i>');
+
+ /* eslint-disable promise/catch-or-return */
+ // Make request to update comment on server
+ gl.utils.ajaxPost(formAction, formData)
+ .then((note) => {
+ // Submission successful! render final note element
+ this.updateNote(note, $editingNote);
+ })
+ .fail(() => {
+ // Submission failed, revert back to original note
+ $noteBodyText.html(_.escape(cachedNoteBodyText));
+ $editingNote.removeClass('being-posted fade-in');
+ $editingNote.find('.fa.fa-spinner').remove();
+
+ // Show Flash message about failure
+ this.updateNoteError();
+ });
- /**
- * This method does following tasks step-by-step whenever an existing comment
- * is updated by user (both main thread comments as well as discussion comments).
- *
- * 1) Get Form metadata
- * 2) Update note element with new content
- * 3) Perform network request to submit the updated note using `gl.utils.ajaxPost`
- * a) If request is successfully completed
- * 1. Show submitted Note element
- * b) If request failed
- * 1. Revert Note element to original content
- * 2. Show error Flash message about failure
- */
- Notes.prototype.updateComment = function(e) {
- e.preventDefault();
-
- // Get Form metadata
- const $submitBtn = $(e.target);
- const $form = $submitBtn.parents('form');
- const $closeBtn = $form.find('.js-note-target-close');
- const $editingNote = $form.parents('.note.is-editing');
- const $noteBody = $editingNote.find('.js-task-list-container');
- const $noteBodyText = $noteBody.find('.note-text');
- const { formData, formContent, formAction } = this.getFormData($form);
-
- // Cache original comment content
- const cachedNoteBodyText = $noteBodyText.html();
-
- // Show updated comment content temporarily
- $noteBodyText.html(_.escape(formContent));
- $editingNote.removeClass('is-editing fade-in-full').addClass('being-posted fade-in-half');
- $editingNote.find('.note-headline-meta a').html('<i class="fa fa-spinner fa-spin" aria-label="Comment is being updated" aria-hidden="true"></i>');
-
- /* eslint-disable promise/catch-or-return */
- // Make request to update comment on server
- gl.utils.ajaxPost(formAction, formData)
- .then((note) => {
- // Submission successful! render final note element
- this.updateNote(note, $editingNote);
- })
- .fail(() => {
- // Submission failed, revert back to original note
- $noteBodyText.html(_.escape(cachedNoteBodyText));
- $editingNote.removeClass('being-posted fade-in');
- $editingNote.find('.fa.fa-spinner').remove();
-
- // Show Flash message about failure
- this.updateNoteError();
- });
-
- return $closeBtn.text($closeBtn.data('original-text'));
- };
+ return $closeBtn.text($closeBtn.data('original-text'));
+ }
+}
- return Notes;
- })();
-}).call(window);
+window.Notes = Notes;
diff --git a/app/assets/javascripts/prometheus_metrics/constants.js b/app/assets/javascripts/prometheus_metrics/constants.js
new file mode 100644
index 00000000000..50f1248456e
--- /dev/null
+++ b/app/assets/javascripts/prometheus_metrics/constants.js
@@ -0,0 +1,5 @@
+export default {
+ EMPTY: 'empty',
+ LOADING: 'loading',
+ LIST: 'list',
+};
diff --git a/app/assets/javascripts/prometheus_metrics/index.js b/app/assets/javascripts/prometheus_metrics/index.js
new file mode 100644
index 00000000000..a0c43c5abe1
--- /dev/null
+++ b/app/assets/javascripts/prometheus_metrics/index.js
@@ -0,0 +1,6 @@
+import PrometheusMetrics from './prometheus_metrics';
+
+$(() => {
+ const prometheusMetrics = new PrometheusMetrics('.js-prometheus-metrics-monitoring');
+ prometheusMetrics.loadActiveMetrics();
+});
diff --git a/app/assets/javascripts/prometheus_metrics/prometheus_metrics.js b/app/assets/javascripts/prometheus_metrics/prometheus_metrics.js
new file mode 100644
index 00000000000..ef4d6df5138
--- /dev/null
+++ b/app/assets/javascripts/prometheus_metrics/prometheus_metrics.js
@@ -0,0 +1,109 @@
+import PANEL_STATE from './constants';
+
+export default class PrometheusMetrics {
+ constructor(wrapperSelector) {
+ this.backOffRequestCounter = 0;
+
+ this.$wrapper = $(wrapperSelector);
+
+ this.$monitoredMetricsPanel = this.$wrapper.find('.js-panel-monitored-metrics');
+ this.$monitoredMetricsCount = this.$monitoredMetricsPanel.find('.js-monitored-count');
+ this.$monitoredMetricsLoading = this.$monitoredMetricsPanel.find('.js-loading-metrics');
+ this.$monitoredMetricsEmpty = this.$monitoredMetricsPanel.find('.js-empty-metrics');
+ this.$monitoredMetricsList = this.$monitoredMetricsPanel.find('.js-metrics-list');
+
+ this.$missingEnvVarPanel = this.$wrapper.find('.js-panel-missing-env-vars');
+ this.$panelToggle = this.$missingEnvVarPanel.find('.js-panel-toggle');
+ this.$missingEnvVarMetricCount = this.$missingEnvVarPanel.find('.js-env-var-count');
+ this.$missingEnvVarMetricsList = this.$missingEnvVarPanel.find('.js-missing-var-metrics-list');
+
+ this.activeMetricsEndpoint = this.$monitoredMetricsPanel.data('active-metrics');
+
+ this.$panelToggle.on('click', e => this.handlePanelToggle(e));
+ }
+
+ /* eslint-disable class-methods-use-this */
+ handlePanelToggle(e) {
+ const $toggleBtn = $(e.currentTarget);
+ const $currentPanelBody = $toggleBtn.closest('.panel').find('.panel-body');
+ $currentPanelBody.toggleClass('hidden');
+ if ($toggleBtn.hasClass('fa-caret-down')) {
+ $toggleBtn.removeClass('fa-caret-down').addClass('fa-caret-right');
+ } else {
+ $toggleBtn.removeClass('fa-caret-right').addClass('fa-caret-down');
+ }
+ }
+
+ showMonitoringMetricsPanelState(stateName) {
+ switch (stateName) {
+ case PANEL_STATE.LOADING:
+ this.$monitoredMetricsLoading.removeClass('hidden');
+ this.$monitoredMetricsEmpty.addClass('hidden');
+ this.$monitoredMetricsList.addClass('hidden');
+ break;
+ case PANEL_STATE.LIST:
+ this.$monitoredMetricsLoading.addClass('hidden');
+ this.$monitoredMetricsEmpty.addClass('hidden');
+ this.$monitoredMetricsList.removeClass('hidden');
+ break;
+ default:
+ this.$monitoredMetricsLoading.addClass('hidden');
+ this.$monitoredMetricsEmpty.removeClass('hidden');
+ this.$monitoredMetricsList.addClass('hidden');
+ break;
+ }
+ }
+
+ populateActiveMetrics(metrics) {
+ let totalMonitoredMetrics = 0;
+ let totalMissingEnvVarMetrics = 0;
+
+ metrics.forEach((metric) => {
+ this.$monitoredMetricsList.append(`<li>${metric.group}<span class="badge">${metric.active_metrics}</span></li>`);
+ totalMonitoredMetrics += metric.active_metrics;
+ if (metric.metrics_missing_requirements > 0) {
+ this.$missingEnvVarMetricsList.append(`<li>${metric.group}</li>`);
+ totalMissingEnvVarMetrics += 1;
+ }
+ });
+
+ this.$monitoredMetricsCount.text(totalMonitoredMetrics);
+ this.showMonitoringMetricsPanelState(PANEL_STATE.LIST);
+
+ if (totalMissingEnvVarMetrics > 0) {
+ this.$missingEnvVarPanel.removeClass('hidden');
+ this.$missingEnvVarPanel.find('.flash-container').off('click');
+ this.$missingEnvVarMetricCount.text(totalMissingEnvVarMetrics);
+ }
+ }
+
+ loadActiveMetrics() {
+ this.showMonitoringMetricsPanelState(PANEL_STATE.LOADING);
+ gl.utils.backOff((next, stop) => {
+ $.getJSON(this.activeMetricsEndpoint)
+ .done((res) => {
+ if (res && res.success) {
+ stop(res);
+ } else {
+ this.backOffRequestCounter = this.backOffRequestCounter += 1;
+ if (this.backOffRequestCounter < 3) {
+ next();
+ } else {
+ stop(res);
+ }
+ }
+ })
+ .fail(stop);
+ })
+ .then((res) => {
+ if (res && res.data && res.data.length) {
+ this.populateActiveMetrics(res.data);
+ } else {
+ this.showMonitoringMetricsPanelState(PANEL_STATE.EMPTY);
+ }
+ })
+ .catch(() => {
+ this.showMonitoringMetricsPanelState(PANEL_STATE.EMPTY);
+ });
+ }
+}
diff --git a/app/assets/javascripts/right_sidebar.js b/app/assets/javascripts/right_sidebar.js
index da7c0c5a36c..322162afdb8 100644
--- a/app/assets/javascripts/right_sidebar.js
+++ b/app/assets/javascripts/right_sidebar.js
@@ -10,8 +10,6 @@ import Cookies from 'js-cookie';
this.$sidebarInner = this.sidebar.find('.issuable-sidebar');
this.$navGitlab = $('.navbar-gitlab');
- this.$layoutNav = $('.layout-nav');
- this.$subScroll = $('.sub-nav-scroll');
this.$rightSidebar = $('.js-right-sidebar');
this.removeListeners();
@@ -215,7 +213,7 @@ import Cookies from 'js-cookie';
};
Sidebar.prototype.setSidebarHeight = function() {
- const $navHeight = this.$navGitlab.outerHeight() + this.$layoutNav.outerHeight() + (this.$subScroll ? this.$subScroll.outerHeight() : 0);
+ const $navHeight = this.$navGitlab.outerHeight();
const diff = $navHeight - $(window).scrollTop();
if (diff > 0) {
this.$rightSidebar.outerHeight($(window).height() - diff);
diff --git a/app/assets/javascripts/settings_panels.js b/app/assets/javascripts/settings_panels.js
index 59ff2a86293..7fa5996d600 100644
--- a/app/assets/javascripts/settings_panels.js
+++ b/app/assets/javascripts/settings_panels.js
@@ -4,7 +4,7 @@ function expandSectionParent($section, $content) {
}
function expandSection($section) {
- $section.find('.js-settings-toggle').text('Close');
+ $section.find('.js-settings-toggle').text('Collapse');
const $content = $section.find('.settings-content');
$content.addClass('expanded').off('scroll.expandSection').scrollTop(0);
diff --git a/app/assets/javascripts/users_select.js b/app/assets/javascripts/users_select.js
index ec45253e50b..46efdcf4202 100644
--- a/app/assets/javascripts/users_select.js
+++ b/app/assets/javascripts/users_select.js
@@ -643,7 +643,7 @@ UsersSelect.prototype.formatResult = function(user) {
} else {
avatar = gon.default_avatar_url;
}
- return "<div class='user-result " + (!user.username ? 'no-username' : void 0) + "'> <div class='user-image'><img class='avatar s24' src='" + avatar + "'></div> <div class='user-name'>" + user.name + "</div> <div class='user-username'>" + (user.username || "") + "</div> </div>";
+ return "<div class='user-result " + (!user.username ? 'no-username' : void 0) + "'> <div class='user-image'><img class='avatar avatar-inline s32' src='" + avatar + "'></div> <div class='user-name dropdown-menu-user-full-name'>" + user.name + "</div> <div class='user-username dropdown-menu-user-username'>" + ("@" + user.username || "") + "</div> </div>";
};
UsersSelect.prototype.formatSelection = function(user) {
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_related_links.js b/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_related_links.js
index 686cb38cbb1..205804670fa 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_related_links.js
+++ b/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_related_links.js
@@ -1,63 +1,42 @@
export default {
name: 'MRWidgetRelatedLinks',
props: {
- isMerged: { type: Boolean, required: true },
relatedLinks: { type: Object, required: true },
},
computed: {
- // TODO: the following should be handled by i18n
- closingText() {
- if (this.isMerged) {
- return `Closed ${this.issueLabel('closing')}`;
- }
-
- return `Closes ${this.issueLabel('closing')}`;
- },
hasLinks() {
const { closing, mentioned, assignToMe } = this.relatedLinks;
return closing || mentioned || assignToMe;
},
- // TODO: the following should be handled by i18n
- mentionedText() {
- if (this.isMerged) {
- if (this.hasMultipleIssues(this.relatedLinks.mentioned)) {
- return 'are mentioned but were not closed';
- }
-
- return 'is mentioned but was not closed';
- }
-
- if (this.hasMultipleIssues(this.relatedLinks.mentioned)) {
- return 'are mentioned but will not be closed';
- }
-
- return 'is mentioned but will not be closed';
- },
},
methods: {
hasMultipleIssues(text) {
- return /<\/a>,? and <a/.test(text);
+ return !text ? false : text.match(/<\/a> and <a/);
},
- // TODO: the following should be handled by i18n
issueLabel(field) {
return this.hasMultipleIssues(this.relatedLinks[field]) ? 'issues' : 'issue';
},
+ verbLabel(field) {
+ return this.hasMultipleIssues(this.relatedLinks[field]) ? 'are' : 'is';
+ },
},
template: `
- <div v-if="hasLinks">
+ <section
+ v-if="hasLinks"
+ class="mr-info-list mr-links">
<div class="legend"></div>
<p v-if="relatedLinks.closing">
- {{closingText}}
+ Closes {{issueLabel('closing')}}
<span v-html="relatedLinks.closing"></span>.
</p>
<p v-if="relatedLinks.mentioned">
<span class="capitalize">{{issueLabel('mentioned')}}</span>
<span v-html="relatedLinks.mentioned"></span>
- {{mentionedText}}
+ {{verbLabel('mentioned')}} mentioned but will not be closed.
</p>
<p v-if="relatedLinks.assignToMe">
<span v-html="relatedLinks.assignToMe"></span>
</p>
- </div>
+ </section>
`,
};
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_merged.js b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_merged.js
index 9b8eed9016d..c7d32d18141 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_merged.js
+++ b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_merged.js
@@ -1,9 +1,7 @@
/* global Flash */
import mrWidgetAuthorTime from '../../components/mr_widget_author_time';
-import mrWidgetRelatedLinks from '../../components/mr_widget_related_links';
import eventHub from '../../event_hub';
-import '../../../flash';
export default {
name: 'MRWidgetMerged',
@@ -13,7 +11,6 @@ export default {
},
components: {
'mr-widget-author-and-time': mrWidgetAuthorTime,
- 'mr-widget-related-links': mrWidgetRelatedLinks,
},
data() {
return {
@@ -21,9 +18,6 @@ export default {
};
},
computed: {
- shouldRenderRelatedLinks() {
- return this.mr.relatedLinks && this.mr.isMerged;
- },
shouldShowRemoveSourceBranch() {
const { sourceBranchRemoved, isRemovingSourceBranch, canRemoveSourceBranch } = this.mr;
@@ -92,10 +86,6 @@ export default {
aria-hidden="true" />
The source branch is being removed.
</p>
- <mr-widget-related-links
- v-if="shouldRenderRelatedLinks"
- :is-merged="mr.isMerged()"
- :related-links="mr.relatedLinks" />
</section>
<div
v-if="shouldShowMergedButtons"
diff --git a/app/assets/javascripts/vue_merge_request_widget/mr_widget_options.js b/app/assets/javascripts/vue_merge_request_widget/mr_widget_options.js
index 222d0b7f79e..2339a00ddd0 100644
--- a/app/assets/javascripts/vue_merge_request_widget/mr_widget_options.js
+++ b/app/assets/javascripts/vue_merge_request_widget/mr_widget_options.js
@@ -48,7 +48,7 @@ export default {
return stateMaps.stateToComponentMap[this.mr.state];
},
shouldRenderMergeHelp() {
- return !this.mr.isMerged;
+ return stateMaps.statesToShowHelpWidget.indexOf(this.mr.state) > -1;
},
shouldRenderPipelines() {
return Object.keys(this.mr.pipeline).length || this.mr.hasCI;
@@ -238,14 +238,9 @@ export default {
:is="componentName"
:mr="mr"
:service="service" />
- <section
+ <mr-widget-related-links
v-if="shouldRenderRelatedLinks"
- class="mr-info-list mr-links">
- <div class="legend"></div>
- <mr-widget-related-links
- :is-merged="mr.isMerged"
- :related-links="mr.relatedLinks" />
- </section>
+ :related-links="mr.relatedLinks" />
<mr-widget-merge-help v-if="shouldRenderMergeHelp" />
</div>
`,
diff --git a/app/assets/javascripts/vue_merge_request_widget/stores/mr_widget_store.js b/app/assets/javascripts/vue_merge_request_widget/stores/mr_widget_store.js
index ad73efb37e1..69bc1436284 100644
--- a/app/assets/javascripts/vue_merge_request_widget/stores/mr_widget_store.js
+++ b/app/assets/javascripts/vue_merge_request_widget/stores/mr_widget_store.js
@@ -1,18 +1,6 @@
import Timeago from 'timeago.js';
import { getStateKey } from '../dependencies';
-const unmergedStates = [
- 'locked',
- 'conflicts',
- 'workInProgress',
- 'readyToMerge',
- 'checking',
- 'unresolvedDiscussions',
- 'pipelineFailed',
- 'pipelineBlocked',
- 'autoMergeFailed',
-];
-
export default class MergeRequestStore {
constructor(data) {
@@ -77,7 +65,6 @@ export default class MergeRequestStore {
this.mergeActionsContentPath = data.commit_change_content_path;
this.isRemovingSourceBranch = this.isRemovingSourceBranch || false;
this.isOpen = data.state === 'opened' || data.state === 'reopened' || false;
- this.isMerged = unmergedStates.indexOf(data.state) === -1;
this.hasMergeableDiscussionsState = data.mergeable_discussions_state === false;
this.canRemoveSourceBranch = currentUser.can_remove_source_branch || false;
this.canMerge = !!data.merge_path;
diff --git a/app/assets/javascripts/vue_merge_request_widget/stores/state_maps.js b/app/assets/javascripts/vue_merge_request_widget/stores/state_maps.js
index dd939d98d0f..605dd3a1ff4 100644
--- a/app/assets/javascripts/vue_merge_request_widget/stores/state_maps.js
+++ b/app/assets/javascripts/vue_merge_request_widget/stores/state_maps.js
@@ -19,6 +19,19 @@ const stateToComponentMap = {
shaMismatch: 'mr-widget-sha-mismatch',
};
+const statesToShowHelpWidget = [
+ 'locked',
+ 'conflicts',
+ 'workInProgress',
+ 'readyToMerge',
+ 'checking',
+ 'unresolvedDiscussions',
+ 'pipelineFailed',
+ 'pipelineBlocked',
+ 'autoMergeFailed',
+];
+
export default {
stateToComponentMap,
+ statesToShowHelpWidget,
};
diff --git a/app/assets/stylesheets/framework/dropdowns.scss b/app/assets/stylesheets/framework/dropdowns.scss
index cba890ce831..4f54ca24940 100644
--- a/app/assets/stylesheets/framework/dropdowns.scss
+++ b/app/assets/stylesheets/framework/dropdowns.scss
@@ -395,6 +395,11 @@
.dropdown-menu-align-right {
left: auto;
right: 0;
+ margin-top: -5px;
+
+ @media (max-width: $screen-xs-max) {
+ left: 0;
+ }
}
.dropdown-menu-selectable {
diff --git a/app/assets/stylesheets/framework/header.scss b/app/assets/stylesheets/framework/header.scss
index c2a17c22524..0f60a071d53 100644
--- a/app/assets/stylesheets/framework/header.scss
+++ b/app/assets/stylesheets/framework/header.scss
@@ -349,6 +349,7 @@ header {
min-width: 140px;
margin-top: -5px;
color: $gl-text-color;
+ left: auto;
.current-user {
padding: 5px 18px;
diff --git a/app/assets/stylesheets/framework/nav.scss b/app/assets/stylesheets/framework/nav.scss
index 3787ef370b2..28b2a7cfacd 100644
--- a/app/assets/stylesheets/framework/nav.scss
+++ b/app/assets/stylesheets/framework/nav.scss
@@ -45,8 +45,7 @@
li {
display: flex;
- a,
- .btn-link {
+ a {
padding: $gl-btn-padding;
padding-bottom: 11px;
font-size: 14px;
@@ -68,29 +67,7 @@
}
}
- .btn-link {
- padding-top: 16px;
- padding-left: 15px;
- padding-right: 15px;
- border-left: none;
- border-right: none;
- border-top: none;
- border-radius: 0;
-
- &:hover,
- &:active,
- &:focus {
- background-color: transparent;
- }
-
- &:active {
- outline: 0;
- box-shadow: none;
- }
- }
-
- &.active a,
- &.active .btn-link {
+ &.active a {
border-bottom: 2px solid $link-underline-blue;
color: $black;
font-weight: 600;
diff --git a/app/assets/stylesheets/framework/timeline.scss b/app/assets/stylesheets/framework/timeline.scss
index 10881987038..3d68a50f91f 100644
--- a/app/assets/stylesheets/framework/timeline.scss
+++ b/app/assets/stylesheets/framework/timeline.scss
@@ -44,6 +44,10 @@
&:target,
&.target {
background: $line-target-blue;
+
+ &.system-note .note-body .note-text.system-note-commit-list::after {
+ background: linear-gradient(rgba($line-target-blue, 0.1) -100px, $line-target-blue 100%);
+ }
}
.avatar {
diff --git a/app/assets/stylesheets/framework/variables.scss b/app/assets/stylesheets/framework/variables.scss
index 1fcd8f39922..988463990e3 100644
--- a/app/assets/stylesheets/framework/variables.scss
+++ b/app/assets/stylesheets/framework/variables.scss
@@ -327,6 +327,7 @@ $note-disabled-comment-color: #b2b2b2;
$note-targe3-outside: #fffff0;
$note-targe3-inside: #ffffd3;
$note-line2-border: #ddd;
+$note-icon-gutter-width: 55px;
/*
diff --git a/app/assets/stylesheets/pages/environments.scss b/app/assets/stylesheets/pages/environments.scss
index 89bd437b362..1046ebfa2e2 100644
--- a/app/assets/stylesheets/pages/environments.scss
+++ b/app/assets/stylesheets/pages/environments.scss
@@ -83,6 +83,7 @@
.avatar {
float: none;
+ margin-right: 0;
}
}
diff --git a/app/assets/stylesheets/pages/members.scss b/app/assets/stylesheets/pages/members.scss
index 4be0e133b69..f21005895e4 100644
--- a/app/assets/stylesheets/pages/members.scss
+++ b/app/assets/stylesheets/pages/members.scss
@@ -136,10 +136,6 @@
width: 250px;
}
- @media (min-width: $screen-md-min) {
- width: 350px;
- }
-
&.input-short {
@media (min-width: $screen-md-min) {
width: 170px;
diff --git a/app/assets/stylesheets/pages/merge_requests.scss b/app/assets/stylesheets/pages/merge_requests.scss
index c0bd045f1fc..59e0624d94e 100644
--- a/app/assets/stylesheets/pages/merge_requests.scss
+++ b/app/assets/stylesheets/pages/merge_requests.scss
@@ -372,10 +372,6 @@
margin-left: 12px;
}
- &.mr-state-locked + .mr-info-list.mr-links {
- margin-top: -16px;
- }
-
&.empty-state {
.artwork {
margin-bottom: $gl-padding;
@@ -423,7 +419,7 @@
.commit {
margin: 0;
- padding: 10px 0;
+ padding: 10px;
list-style: none;
&:hover {
diff --git a/app/assets/stylesheets/pages/note_form.scss b/app/assets/stylesheets/pages/note_form.scss
index 69fed4e6bf7..9877ed2cfd6 100644
--- a/app/assets/stylesheets/pages/note_form.scss
+++ b/app/assets/stylesheets/pages/note_form.scss
@@ -148,8 +148,20 @@
padding: 6px 0;
}
-.notes-form > li {
- border: 0;
+.notes.notes-form > li.timeline-entry {
+ @include notes-media('max', $screen-sm-max) {
+ padding: 0;
+ }
+
+ .timeline-content {
+ @include notes-media('max', $screen-sm-max) {
+ margin: 0;
+ }
+ }
+
+ .timeline-entry-inner {
+ border: 0;
+ }
}
.note-edit-form {
diff --git a/app/assets/stylesheets/pages/notes.scss b/app/assets/stylesheets/pages/notes.scss
index 8b7df4dd72b..f0dbe4249c5 100644
--- a/app/assets/stylesheets/pages/notes.scss
+++ b/app/assets/stylesheets/pages/notes.scss
@@ -14,16 +14,6 @@ ul.notes {
margin: 0;
padding: 0;
- .timeline-content {
- margin-left: 55px;
-
- &.timeline-content-form {
- @include notes-media('max', $screen-sm-max) {
- margin-left: 0;
- }
- }
- }
-
.note-created-ago,
.note-updated-at {
white-space: nowrap;
@@ -46,17 +36,49 @@ ul.notes {
}
}
- > li {
- padding: $gl-padding $gl-btn-padding;
+ > li { // .timeline-entry
+ padding: 0;
display: block;
position: relative;
- border-bottom: 1px solid $white-normal;
+ border-bottom: 0;
+
+ @include notes-media('min', $screen-sm-min) {
+ padding-left: $note-icon-gutter-width;
+ }
- &:last-child {
- // Override `.timeline > li:last-child { border-bottom: none; }`
+ .timeline-entry-inner {
+ padding: $gl-padding $gl-btn-padding;
border-bottom: 1px solid $white-normal;
}
+ &:target,
+ &.target {
+ border-bottom: 1px solid $white-normal;
+
+ &:not(:first-child) {
+ border-top: 1px solid $white-normal;
+ margin-top: -1px;
+ }
+
+ .timeline-entry-inner {
+ border-bottom: 0;
+ }
+ }
+
+ .timeline-icon {
+ @include notes-media('min', $screen-sm-min) {
+ margin-left: -$note-icon-gutter-width;
+ }
+ }
+
+ .timeline-content {
+ margin-left: $note-icon-gutter-width;
+
+ @include notes-media('min', $screen-sm-min) {
+ margin-left: 0;
+ }
+ }
+
&.being-posted {
pointer-events: none;
opacity: 0.5;
@@ -73,7 +95,7 @@ ul.notes {
}
&.note-discussion {
- &.timeline-entry {
+ .timeline-entry-inner {
padding: $gl-padding 10px;
}
}
@@ -152,13 +174,8 @@ ul.notes {
.system-note {
font-size: 14px;
- padding-left: 0;
clear: both;
- @include notes-media('min', $screen-sm-min) {
- margin-left: 65px;
- }
-
.note-header-info {
padding-bottom: 0;
}
@@ -192,13 +209,16 @@ ul.notes {
.timeline-icon {
float: left;
+ @include notes-media('min', $screen-sm-min) {
+ margin-left: 0;
+ width: auto;
+ }
+
svg {
width: 16px;
height: 16px;
fill: $gray-darkest;
- position: absolute;
- left: 0;
- top: 2px;
+ margin-top: 2px;
}
}
@@ -250,7 +270,7 @@ ul.notes {
&::after {
content: '';
width: 100%;
- height: 67px;
+ height: 70px;
position: absolute;
left: 0;
bottom: 0;
@@ -639,15 +659,12 @@ ul.notes {
.discussion-body,
.diff-file {
.notes .note {
- padding-left: $gl-padding;
- padding-right: $gl-padding;
-
- &.system-note {
- padding-left: 0;
+ border-bottom: 1px solid $white-normal;
- @media (min-width: $screen-sm-min) {
- margin-left: 70px;
- }
+ .timeline-entry-inner {
+ padding-left: $gl-padding;
+ padding-right: $gl-padding;
+ border-bottom: none;
}
}
}
diff --git a/app/assets/stylesheets/pages/pipelines.scss b/app/assets/stylesheets/pages/pipelines.scss
index a85ba3a5955..9637d26e56d 100644
--- a/app/assets/stylesheets/pages/pipelines.scss
+++ b/app/assets/stylesheets/pages/pipelines.scss
@@ -133,7 +133,7 @@
overflow: hidden;
display: inline-block;
white-space: nowrap;
- vertical-align: top;
+ vertical-align: middle;
text-overflow: ellipsis;
}
diff --git a/app/assets/stylesheets/pages/projects.scss b/app/assets/stylesheets/pages/projects.scss
index 062665bc634..562ecbc6986 100644
--- a/app/assets/stylesheets/pages/projects.scss
+++ b/app/assets/stylesheets/pages/projects.scss
@@ -380,7 +380,7 @@ a.deploy-project-label {
padding: 0;
background: transparent;
border: none;
- line-height: 36px;
+ line-height: 34px;
margin: 0;
> li + li::before {
diff --git a/app/assets/stylesheets/pages/settings.scss b/app/assets/stylesheets/pages/settings.scss
index 7697a1b1c58..d69a8e0995c 100644
--- a/app/assets/stylesheets/pages/settings.scss
+++ b/app/assets/stylesheets/pages/settings.scss
@@ -126,3 +126,66 @@
margin-left: 5px;
}
}
+
+.prometheus-metrics-monitoring {
+ .panel {
+ .panel-toggle {
+ width: 14px;
+ }
+
+ .badge {
+ font-size: inherit;
+ }
+
+ .panel-heading .badge-count {
+ color: $white-light;
+ background: $common-gray-dark;
+ }
+
+ .panel-body {
+ padding: 0;
+ }
+
+ .flash-container {
+ margin-bottom: 0;
+ cursor: default;
+
+ .flash-notice {
+ border-radius: 0;
+ }
+ }
+ }
+
+ .loading-metrics,
+ .empty-metrics {
+ padding: 30px 10px;
+
+ p,
+ .btn {
+ margin-top: 10px;
+ margin-bottom: 0;
+ }
+ }
+
+ .loading-metrics .metrics-load-spinner {
+ color: $loading-color;
+ }
+
+ .metrics-list {
+ margin-bottom: 0;
+
+ li {
+ padding: $gl-padding;
+
+ .badge {
+ margin-left: 5px;
+ background: $badge-bg;
+ }
+ }
+
+ /* Ensure we don't add border if there's only single li */
+ li + li {
+ border-top: 1px solid $border-color;
+ }
+ }
+}
diff --git a/app/assets/stylesheets/pages/tree.scss b/app/assets/stylesheets/pages/tree.scss
index ab63225147f..ce1a13c6afa 100644
--- a/app/assets/stylesheets/pages/tree.scss
+++ b/app/assets/stylesheets/pages/tree.scss
@@ -1,6 +1,72 @@
.tree-holder {
- > .nav-block {
- margin: 11px 0;
+ .nav-block {
+ margin: 10px 0;
+
+ @media (min-width: $screen-sm-min) {
+ display: flex;
+
+ .tree-ref-container {
+ flex: 1;
+ }
+
+ .tree-controls {
+ text-align: right;
+
+ .btn-group {
+ margin-left: 10px;
+ }
+ }
+
+ .tree-ref-holder {
+ float: left;
+ margin-right: 15px;
+ }
+
+ .repo-breadcrumb {
+ li:last-of-type {
+ position: relative;
+ }
+ }
+
+ .add-to-tree-dropdown {
+ position: absolute;
+ left: 18px;
+ }
+ }
+ }
+
+ @media (max-width: $screen-xs-max) {
+ .repo-breadcrumb {
+ margin-top: 10px;
+ position: relative;
+
+ .dropdown-menu {
+ min-width: 100%;
+ width: 100%;
+ left: inherit;
+ right: 0;
+ }
+ }
+
+ .add-to-tree-dropdown {
+ position: absolute;
+ left: 0;
+ right: 0;
+ }
+
+ .tree-controls {
+ margin-bottom: 10px;
+
+ .btn,
+ .dropdown,
+ .btn-group {
+ width: 100%;
+ }
+
+ .btn {
+ margin: 10px 0 0;
+ }
+ }
}
.file-finder {
@@ -131,11 +197,6 @@
}
}
-.tree-ref-holder {
- float: left;
- margin-right: 15px;
-}
-
.blob-commit-info {
list-style: none;
margin: 0;
@@ -159,16 +220,6 @@
color: $md-link-color;
}
-.tree-controls {
- float: right;
- position: relative;
- z-index: 2;
-
- .project-action-button {
- margin-left: $btn-side-margin;
- }
-}
-
.repo-charts {
.sub-header {
margin: 20px 0;
diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb
index 91694ebcd1d..824ce845706 100644
--- a/app/controllers/application_controller.rb
+++ b/app/controllers/application_controller.rb
@@ -40,6 +40,10 @@ class ApplicationController < ActionController::Base
render_404
end
+ rescue_from(ActionController::UnknownFormat) do
+ render_404
+ end
+
rescue_from Gitlab::Access::AccessDeniedError do |exception|
render_403
end
diff --git a/app/controllers/concerns/creates_commit.rb b/app/controllers/concerns/creates_commit.rb
index 36ad307a93b..1a9904bbe57 100644
--- a/app/controllers/concerns/creates_commit.rb
+++ b/app/controllers/concerns/creates_commit.rb
@@ -97,8 +97,8 @@ module CreatesCommit
def merge_request_exists?
return @merge_request if defined?(@merge_request)
- @merge_request = MergeRequestsFinder.new(current_user, project_id: @project.id).execute.opened.
- find_by(source_project_id: @project_to_commit_into, source_branch: @branch_name, target_branch: @start_branch)
+ @merge_request = MergeRequestsFinder.new(current_user, project_id: @project.id).execute.opened
+ .find_by(source_project_id: @project_to_commit_into, source_branch: @branch_name, target_branch: @start_branch)
end
def different_project?
diff --git a/app/controllers/concerns/membership_actions.rb b/app/controllers/concerns/membership_actions.rb
index 8d07780f6c2..47d9ae350ae 100644
--- a/app/controllers/concerns/membership_actions.rb
+++ b/app/controllers/concerns/membership_actions.rb
@@ -15,8 +15,8 @@ module MembershipActions
end
def destroy
- Members::DestroyService.new(membershipable, current_user, params).
- execute(:all)
+ Members::DestroyService.new(membershipable, current_user, params)
+ .execute(:all)
respond_to do |format|
format.html do
@@ -42,8 +42,8 @@ module MembershipActions
end
def leave
- member = Members::DestroyService.new(membershipable, current_user, user_id: current_user.id).
- execute(:all)
+ member = Members::DestroyService.new(membershipable, current_user, user_id: current_user.id)
+ .execute(:all)
notice =
if member.request?
diff --git a/app/controllers/dashboard/projects_controller.rb b/app/controllers/dashboard/projects_controller.rb
index 641c502dbe4..91c1e4dff79 100644
--- a/app/controllers/dashboard/projects_controller.rb
+++ b/app/controllers/dashboard/projects_controller.rb
@@ -22,8 +22,8 @@ class Dashboard::ProjectsController < Dashboard::ApplicationController
end
def starred
- @projects = load_projects(params.merge(starred: true)).
- includes(:forked_from_project, :tags).page(params[:page])
+ @projects = load_projects(params.merge(starred: true))
+ .includes(:forked_from_project, :tags).page(params[:page])
@groups = []
@@ -45,8 +45,8 @@ class Dashboard::ProjectsController < Dashboard::ApplicationController
end
def load_projects(finder_params)
- ProjectsFinder.new(params: finder_params, current_user: current_user).
- execute.includes(:route, namespace: :route)
+ ProjectsFinder.new(params: finder_params, current_user: current_user)
+ .execute.includes(:route, namespace: :route)
end
def load_events
diff --git a/app/controllers/explore/projects_controller.rb b/app/controllers/explore/projects_controller.rb
index 8f1870759e4..741879dee35 100644
--- a/app/controllers/explore/projects_controller.rb
+++ b/app/controllers/explore/projects_controller.rb
@@ -49,7 +49,7 @@ class Explore::ProjectsController < Explore::ApplicationController
private
def load_projects
- ProjectsFinder.new(current_user: current_user, params: params).
- execute.includes(:route, namespace: :route)
+ ProjectsFinder.new(current_user: current_user, params: params)
+ .execute.includes(:route, namespace: :route)
end
end
diff --git a/app/controllers/jwt_controller.rb b/app/controllers/jwt_controller.rb
index 11db164b3fa..4bceb1d67a3 100644
--- a/app/controllers/jwt_controller.rb
+++ b/app/controllers/jwt_controller.rb
@@ -11,8 +11,8 @@ class JwtController < ApplicationController
service = SERVICES[params[:service]]
return head :not_found unless service
- result = service.new(@authentication_result.project, @authentication_result.actor, auth_params).
- execute(authentication_abilities: @authentication_result.authentication_abilities)
+ result = service.new(@authentication_result.project, @authentication_result.actor, auth_params)
+ .execute(authentication_abilities: @authentication_result.authentication_abilities)
render json: result, status: result[:http_status]
end
diff --git a/app/controllers/omniauth_callbacks_controller.rb b/app/controllers/omniauth_callbacks_controller.rb
index 2a8c8ca4bad..b82681b197e 100644
--- a/app/controllers/omniauth_callbacks_controller.rb
+++ b/app/controllers/omniauth_callbacks_controller.rb
@@ -144,7 +144,7 @@ class OmniauthCallbacksController < Devise::OmniauthCallbacksController
end
def log_audit_event(user, options = {})
- AuditEventService.new(user, user, options).
- for_authentication.security_event
+ AuditEventService.new(user, user, options)
+ .for_authentication.security_event
end
end
diff --git a/app/controllers/profiles_controller.rb b/app/controllers/profiles_controller.rb
index 72f34930ca8..f98a9e24de1 100644
--- a/app/controllers/profiles_controller.rb
+++ b/app/controllers/profiles_controller.rb
@@ -49,9 +49,9 @@ class ProfilesController < Profiles::ApplicationController
end
def audit_log
- @events = AuditEvent.where(entity_type: "User", entity_id: current_user.id).
- order("created_at DESC").
- page(params[:page])
+ @events = AuditEvent.where(entity_type: "User", entity_id: current_user.id)
+ .order("created_at DESC")
+ .page(params[:page])
end
def update_username
diff --git a/app/controllers/projects/application_controller.rb b/app/controllers/projects/application_controller.rb
index 603a51266da..3d7ce4f0222 100644
--- a/app/controllers/projects/application_controller.rb
+++ b/app/controllers/projects/application_controller.rb
@@ -53,9 +53,21 @@ class Projects::ApplicationController < ApplicationController
end
end
+ def check_project_feature_available!(feature)
+ render_404 unless project.feature_available?(feature, current_user)
+ end
+
+ def check_issuables_available!
+ render_404 unless project.feature_available?(:issues, current_user) ||
+ project.feature_available?(:merge_requests, current_user)
+ end
+
def method_missing(method_sym, *arguments, &block)
- if method_sym.to_s =~ /\Aauthorize_(.*)!\z/
+ case method_sym.to_s
+ when /\Aauthorize_(.*)!\z/
authorize_action!($1.to_sym)
+ when /\Acheck_(.*)_available!\z/
+ check_project_feature_available!($1.to_sym)
else
super
end
diff --git a/app/controllers/projects/blob_controller.rb b/app/controllers/projects/blob_controller.rb
index 66e6a9a451c..a82d6fd5a4a 100644
--- a/app/controllers/projects/blob_controller.rb
+++ b/app/controllers/projects/blob_controller.rb
@@ -187,7 +187,7 @@ class Projects::BlobController < Projects::ApplicationController
end
def set_last_commit_sha
- @last_commit_sha = Gitlab::Git::Commit.
- last_for_path(@repository, @ref, @path).sha
+ @last_commit_sha = Gitlab::Git::Commit
+ .last_for_path(@repository, @ref, @path).sha
end
end
diff --git a/app/controllers/projects/branches_controller.rb b/app/controllers/projects/branches_controller.rb
index 70b06cfd9b4..94a752c21eb 100644
--- a/app/controllers/projects/branches_controller.rb
+++ b/app/controllers/projects/branches_controller.rb
@@ -37,8 +37,8 @@ class Projects::BranchesController < Projects::ApplicationController
redirect_to_autodeploy = project.empty_repo? && project.deployment_services.present?
- result = CreateBranchService.new(project, current_user).
- execute(branch_name, ref)
+ result = CreateBranchService.new(project, current_user)
+ .execute(branch_name, ref)
if params[:issue_iid]
issue = IssuesFinder.new(current_user, project_id: @project.id).find_by(iid: params[:issue_iid])
diff --git a/app/controllers/projects/commits_controller.rb b/app/controllers/projects/commits_controller.rb
index f33797ca310..37b5a6e9d48 100644
--- a/app/controllers/projects/commits_controller.rb
+++ b/app/controllers/projects/commits_controller.rb
@@ -18,11 +18,11 @@ class Projects::CommitsController < Projects::ApplicationController
@repository.commits(@ref, path: @path, limit: @limit, offset: @offset)
end
- @note_counts = project.notes.where(commit_id: @commits.map(&:id)).
- group(:commit_id).count
+ @note_counts = project.notes.where(commit_id: @commits.map(&:id))
+ .group(:commit_id).count
- @merge_request = MergeRequestsFinder.new(current_user, project_id: @project.id).execute.opened.
- find_by(source_project: @project, source_branch: @ref, target_branch: @repository.root_ref)
+ @merge_request = MergeRequestsFinder.new(current_user, project_id: @project.id).execute.opened
+ .find_by(source_project: @project, source_branch: @ref, target_branch: @repository.root_ref)
respond_to do |format|
format.html
diff --git a/app/controllers/projects/compare_controller.rb b/app/controllers/projects/compare_controller.rb
index 88dd600e5fe..ef400c4d745 100644
--- a/app/controllers/projects/compare_controller.rb
+++ b/app/controllers/projects/compare_controller.rb
@@ -61,7 +61,7 @@ class Projects::CompareController < Projects::ApplicationController
end
def merge_request
- @merge_request ||= MergeRequestsFinder.new(current_user, project_id: @project.id).execute.opened.
- find_by(source_project: @project, source_branch: @head_ref, target_branch: @start_ref)
+ @merge_request ||= MergeRequestsFinder.new(current_user, project_id: @project.id).execute.opened
+ .find_by(source_project: @project, source_branch: @head_ref, target_branch: @start_ref)
end
end
diff --git a/app/controllers/projects/deployments_controller.rb b/app/controllers/projects/deployments_controller.rb
index 6644deb49c9..47c312ffddf 100644
--- a/app/controllers/projects/deployments_controller.rb
+++ b/app/controllers/projects/deployments_controller.rb
@@ -22,6 +22,22 @@ class Projects::DeploymentsController < Projects::ApplicationController
render_404
end
+ def additional_metrics
+ return render_404 unless deployment.has_additional_metrics?
+
+ respond_to do |format|
+ format.json do
+ metrics = deployment.additional_metrics
+
+ if metrics.any?
+ render json: metrics
+ else
+ head :no_content
+ end
+ end
+ end
+ end
+
private
def deployment
diff --git a/app/controllers/projects/discussions_controller.rb b/app/controllers/projects/discussions_controller.rb
index f4a18a5e8f7..2e6ab7903b8 100644
--- a/app/controllers/projects/discussions_controller.rb
+++ b/app/controllers/projects/discussions_controller.rb
@@ -1,5 +1,5 @@
class Projects::DiscussionsController < Projects::ApplicationController
- before_action :module_enabled
+ before_action :check_merge_requests_available!
before_action :merge_request
before_action :discussion
before_action :authorize_resolve_discussion!
@@ -34,8 +34,4 @@ class Projects::DiscussionsController < Projects::ApplicationController
def authorize_resolve_discussion!
access_denied! unless discussion.can_resolve?(current_user)
end
-
- def module_enabled
- render_404 unless @project.feature_available?(:merge_requests, current_user)
- end
end
diff --git a/app/controllers/projects/environments_controller.rb b/app/controllers/projects/environments_controller.rb
index efe83776834..f88a1ffd1e9 100644
--- a/app/controllers/projects/environments_controller.rb
+++ b/app/controllers/projects/environments_controller.rb
@@ -129,6 +129,16 @@ class Projects::EnvironmentsController < Projects::ApplicationController
end
end
+ def additional_metrics
+ respond_to do |format|
+ format.json do
+ additional_metrics = environment.additional_metrics || {}
+
+ render json: additional_metrics, status: additional_metrics.any? ? :ok : :no_content
+ end
+ end
+ end
+
private
def verify_api_request!
diff --git a/app/controllers/projects/issues_controller.rb b/app/controllers/projects/issues_controller.rb
index 56f76e752d0..dfc6baa34a4 100644
--- a/app/controllers/projects/issues_controller.rb
+++ b/app/controllers/projects/issues_controller.rb
@@ -9,7 +9,7 @@ class Projects::IssuesController < Projects::ApplicationController
prepend_before_action :authenticate_user!, only: [:new]
before_action :redirect_to_external_issue_tracker, only: [:index, :new]
- before_action :module_enabled
+ before_action :check_issues_available!
before_action :issue, except: [:index, :new, :create, :bulk_update]
# Allow write(create) issue
@@ -250,7 +250,7 @@ class Projects::IssuesController < Projects::ApplicationController
return render_404 unless can?(current_user, :push_code, @project) && @issue.can_be_worked_on?(current_user)
end
- def module_enabled
+ def check_issues_available!
return render_404 unless @project.feature_available?(:issues, current_user) && @project.default_issues_tracker?
end
diff --git a/app/controllers/projects/labels_controller.rb b/app/controllers/projects/labels_controller.rb
index 1beac202efe..daa973c9281 100644
--- a/app/controllers/projects/labels_controller.rb
+++ b/app/controllers/projects/labels_controller.rb
@@ -1,7 +1,7 @@
class Projects::LabelsController < Projects::ApplicationController
include ToggleSubscriptionAction
- before_action :module_enabled
+ before_action :check_issuables_available!
before_action :label, only: [:edit, :update, :destroy, :promote]
before_action :find_labels, only: [:index, :set_priorities, :remove_priority, :toggle_subscription]
before_action :authorize_read_label!
@@ -135,12 +135,6 @@ class Projects::LabelsController < Projects::ApplicationController
protected
- def module_enabled
- unless @project.feature_available?(:issues, current_user) || @project.feature_available?(:merge_requests, current_user)
- return render_404
- end
- end
-
def label_params
params.require(:label).permit(:title, :description, :color)
end
diff --git a/app/controllers/projects/merge_requests_controller.rb b/app/controllers/projects/merge_requests_controller.rb
index 314906b5f09..879ff6d393e 100644
--- a/app/controllers/projects/merge_requests_controller.rb
+++ b/app/controllers/projects/merge_requests_controller.rb
@@ -7,7 +7,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController
include ToggleAwardEmoji
include IssuableCollections
- before_action :module_enabled
+ before_action :check_merge_requests_available!
before_action :merge_request, only: [
:edit, :update, :show, :diffs, :commits, :conflicts, :conflict_for_path, :pipelines, :merge,
:pipeline_status, :ci_environments_status, :toggle_subscription, :cancel_merge_when_pipeline_succeeds, :remove_wip, :resolve_conflicts, :assign_related_issues, :commit_change_content
@@ -143,8 +143,8 @@ class Projects::MergeRequestsController < Projects::ApplicationController
# Get commits from repository
# or from cache if already merged
@commits = @merge_request.commits
- @note_counts = Note.where(commit_id: @commits.map(&:id)).
- group(:commit_id).count
+ @note_counts = Note.where(commit_id: @commits.map(&:id))
+ .group(:commit_id).count
render json: { html: view_to_html_string('projects/merge_requests/show/_commits') }
end
@@ -192,9 +192,9 @@ class Projects::MergeRequestsController < Projects::ApplicationController
end
begin
- MergeRequests::Conflicts::ResolveService.
- new(merge_request).
- execute(current_user, params)
+ MergeRequests::Conflicts::ResolveService
+ .new(merge_request)
+ .execute(current_user, params)
flash[:notice] = 'All merge conflicts were resolved. The merge request can now be merged.'
@@ -461,10 +461,6 @@ class Projects::MergeRequestsController < Projects::ApplicationController
return render_404 unless @conflicts_list.can_be_resolved_by?(current_user)
end
- def module_enabled
- return render_404 unless @project.feature_available?(:merge_requests, current_user)
- end
-
def validates_merge_request
# Show git not found page
# if there is no saved commits between source & target branch
@@ -562,8 +558,8 @@ class Projects::MergeRequestsController < Projects::ApplicationController
@commits = @merge_request.compare_commits.reverse
@commit = @merge_request.diff_head_commit
- @note_counts = Note.where(commit_id: @commits.map(&:id)).
- group(:commit_id).count
+ @note_counts = Note.where(commit_id: @commits.map(&:id))
+ .group(:commit_id).count
@labels = LabelsFinder.new(current_user, project_id: @project.id).execute
@@ -579,10 +575,10 @@ class Projects::MergeRequestsController < Projects::ApplicationController
def merge_request_params
params.require(:merge_request)
- .permit(merge_request_params_ce)
+ .permit(merge_request_params_attributes)
end
- def merge_request_params_ce
+ def merge_request_params_attributes
[
:assignee_id,
:description,
@@ -602,7 +598,11 @@ class Projects::MergeRequestsController < Projects::ApplicationController
end
def merge_params
- params.permit(:should_remove_source_branch, :commit_message)
+ params.permit(merge_params_attributes)
+ end
+
+ def merge_params_attributes
+ [:should_remove_source_branch, :commit_message]
end
# Make sure merge requests created before 8.0
diff --git a/app/controllers/projects/milestones_controller.rb b/app/controllers/projects/milestones_controller.rb
index 62410d7f57f..953b1e83e49 100644
--- a/app/controllers/projects/milestones_controller.rb
+++ b/app/controllers/projects/milestones_controller.rb
@@ -1,7 +1,7 @@
class Projects::MilestonesController < Projects::ApplicationController
include MilestoneActions
- before_action :module_enabled
+ before_action :check_issuables_available!
before_action :milestone, only: [:edit, :update, :destroy, :show, :merge_requests, :participants, :labels]
# Allow read any milestone
@@ -95,12 +95,6 @@ class Projects::MilestonesController < Projects::ApplicationController
return render_404 unless can?(current_user, :admin_milestone, @project)
end
- def module_enabled
- unless @project.feature_available?(:issues, current_user) || @project.feature_available?(:merge_requests, current_user)
- return render_404
- end
- end
-
def milestone_params
params.require(:milestone).permit(:title, :description, :start_date, :due_date, :state_event)
end
diff --git a/app/controllers/projects/prometheus_controller.rb b/app/controllers/projects/prometheus_controller.rb
new file mode 100644
index 00000000000..507468d7102
--- /dev/null
+++ b/app/controllers/projects/prometheus_controller.rb
@@ -0,0 +1,24 @@
+class Projects::PrometheusController < Projects::ApplicationController
+ before_action :authorize_read_project!
+ before_action :require_prometheus_metrics!
+
+ def active_metrics
+ respond_to do |format|
+ format.json do
+ matched_metrics = project.prometheus_service.matched_metrics || {}
+
+ if matched_metrics.any?
+ render json: matched_metrics
+ else
+ head :no_content
+ end
+ end
+ end
+ end
+
+ private
+
+ def require_prometheus_metrics!
+ render_404 unless project.prometheus_service.present?
+ end
+end
diff --git a/app/controllers/projects/settings/ci_cd_controller.rb b/app/controllers/projects/settings/ci_cd_controller.rb
index 6f009d61950..24fe78bc1bd 100644
--- a/app/controllers/projects/settings/ci_cd_controller.rb
+++ b/app/controllers/projects/settings/ci_cd_controller.rb
@@ -14,8 +14,8 @@ module Projects
def define_runners_variables
@project_runners = @project.runners.ordered
- @assignable_runners = current_user.ci_authorized_runners.
- assignable_for(project).ordered.page(params[:page]).per(20)
+ @assignable_runners = current_user.ci_authorized_runners
+ .assignable_for(project).ordered.page(params[:page]).per(20)
@shared_runners = Ci::Runner.shared.active
@shared_runners_count = @shared_runners.count(:all)
end
diff --git a/app/controllers/projects/snippets_controller.rb b/app/controllers/projects/snippets_controller.rb
index 8a8f8d6a27d..98dd307bd9d 100644
--- a/app/controllers/projects/snippets_controller.rb
+++ b/app/controllers/projects/snippets_controller.rb
@@ -5,7 +5,7 @@ class Projects::SnippetsController < Projects::ApplicationController
include SnippetsActions
include RendersBlob
- before_action :module_enabled
+ before_action :check_snippets_available!
before_action :snippet, only: [:show, :edit, :destroy, :update, :raw, :toggle_award_emoji, :mark_as_spam]
# Allow read any snippet
@@ -102,10 +102,6 @@ class Projects::SnippetsController < Projects::ApplicationController
return render_404 unless can?(current_user, :admin_project_snippet, @snippet)
end
- def module_enabled
- return render_404 unless @project.feature_available?(:snippets, current_user)
- end
-
def snippet_params
params.require(:project_snippet).permit(:title, :content, :file_name, :private, :visibility_level, :description)
end
diff --git a/app/controllers/projects/tags_controller.rb b/app/controllers/projects/tags_controller.rb
index afbea3e2b40..ebc9f4edab4 100644
--- a/app/controllers/projects/tags_controller.rb
+++ b/app/controllers/projects/tags_controller.rb
@@ -29,8 +29,8 @@ class Projects::TagsController < Projects::ApplicationController
end
def create
- result = Tags::CreateService.new(@project, current_user).
- execute(params[:tag_name], params[:ref], params[:message], params[:release_description])
+ result = Tags::CreateService.new(@project, current_user)
+ .execute(params[:tag_name], params[:ref], params[:message], params[:release_description])
if result[:status] == :success
@tag = result[:tag]
diff --git a/app/controllers/sessions_controller.rb b/app/controllers/sessions_controller.rb
index d7c702b94f8..0d8186dce02 100644
--- a/app/controllers/sessions_controller.rb
+++ b/app/controllers/sessions_controller.rb
@@ -128,8 +128,8 @@ class SessionsController < Devise::SessionsController
end
def log_audit_event(user, options = {})
- AuditEventService.new(user, user, options).
- for_authentication.security_event
+ AuditEventService.new(user, user, options)
+ .for_authentication.security_event
end
def log_user_activity(user)
diff --git a/app/controllers/sherlock/application_controller.rb b/app/controllers/sherlock/application_controller.rb
index 682ca5e3821..6bdd3568a78 100644
--- a/app/controllers/sherlock/application_controller.rb
+++ b/app/controllers/sherlock/application_controller.rb
@@ -4,8 +4,8 @@ module Sherlock
def find_transaction
if params[:transaction_id]
- @transaction = Gitlab::Sherlock.collection.
- find_transaction(params[:transaction_id])
+ @transaction = Gitlab::Sherlock.collection
+ .find_transaction(params[:transaction_id])
end
end
end
diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb
index c211106fbaa..8131eba6a2f 100644
--- a/app/controllers/users_controller.rb
+++ b/app/controllers/users_controller.rb
@@ -106,11 +106,11 @@ class UsersController < ApplicationController
def load_events
# Get user activity feed for projects common for both users
- @events = user.recent_events.
- merge(projects_for_current_user).
- references(:project).
- with_associations.
- limit_recent(20, params[:offset])
+ @events = user.recent_events
+ .merge(projects_for_current_user)
+ .references(:project)
+ .with_associations
+ .limit_recent(20, params[:offset])
end
def load_projects
diff --git a/app/finders/events_finder.rb b/app/finders/events_finder.rb
index 29beb6cb224..46ecbaba73a 100644
--- a/app/finders/events_finder.rb
+++ b/app/finders/events_finder.rb
@@ -33,8 +33,8 @@ class EventsFinder
private
def by_current_user_access(events)
- events.merge(ProjectsFinder.new(current_user: current_user).execute).
- joins(:project)
+ events.merge(ProjectsFinder.new(current_user: current_user).execute)
+ .joins(:project)
end
def by_action(events)
diff --git a/app/finders/group_members_finder.rb b/app/finders/group_members_finder.rb
index fce3775f40e..067aff408df 100644
--- a/app/finders/group_members_finder.rb
+++ b/app/finders/group_members_finder.rb
@@ -8,9 +8,9 @@ class GroupMembersFinder
return group_members unless @group.parent
- parents_members = GroupMember.non_request.
- where(source_id: @group.ancestors.select(:id)).
- where.not(user_id: @group.users.select(:id))
+ parents_members = GroupMember.non_request
+ .where(source_id: @group.ancestors.select(:id))
+ .where.not(user_id: @group.users.select(:id))
wheres = ["members.id IN (#{group_members.select(:id).to_sql})"]
wheres << "members.id IN (#{parents_members.select(:id).to_sql})"
diff --git a/app/helpers/commits_helper.rb b/app/helpers/commits_helper.rb
index 5b5cdebe919..0accd1f8d77 100644
--- a/app/helpers/commits_helper.rb
+++ b/app/helpers/commits_helper.rb
@@ -85,20 +85,20 @@ module CommitsHelper
if @path.blank?
return link_to(
- "Browse Files",
+ _("Browse Files"),
namespace_project_tree_path(project.namespace, project, commit),
class: "btn btn-default"
)
elsif @repo.blob_at(commit.id, @path)
return link_to(
- "Browse File",
+ _("Browse File"),
namespace_project_blob_path(project.namespace, project,
tree_join(commit.id, @path)),
class: "btn btn-default"
)
elsif @path.present?
return link_to(
- "Browse Directory",
+ _("Browse Directory"),
namespace_project_tree_path(project.namespace, project,
tree_join(commit.id, @path)),
class: "btn btn-default"
diff --git a/app/helpers/form_helper.rb b/app/helpers/form_helper.rb
index 014fc46b130..8ceb5c36bda 100644
--- a/app/helpers/form_helper.rb
+++ b/app/helpers/form_helper.rb
@@ -8,10 +8,10 @@ module FormHelper
content_tag(:div, class: 'alert alert-danger', id: 'error_explanation') do
content_tag(:h4, headline) <<
content_tag(:ul) do
- model.errors.full_messages.
- map { |msg| content_tag(:li, msg) }.
- join.
- html_safe
+ model.errors.full_messages
+ .map { |msg| content_tag(:li, msg) }
+ .join
+ .html_safe
end
end
end
diff --git a/app/helpers/projects_helper.rb b/app/helpers/projects_helper.rb
index 0d0459f5a70..d10e0bd45b0 100644
--- a/app/helpers/projects_helper.rb
+++ b/app/helpers/projects_helper.rb
@@ -194,8 +194,8 @@ module ProjectsHelper
end
def load_pipeline_status(projects)
- Gitlab::Cache::Ci::ProjectPipelineStatus.
- load_in_batch_for_projects(projects)
+ Gitlab::Cache::Ci::ProjectPipelineStatus
+ .load_in_batch_for_projects(projects)
end
private
diff --git a/app/helpers/search_helper.rb b/app/helpers/search_helper.rb
index 9c46035057f..8f15904f068 100644
--- a/app/helpers/search_helper.rb
+++ b/app/helpers/search_helper.rb
@@ -97,8 +97,8 @@ module SearchHelper
# Autocomplete results for the current user's projects
def projects_autocomplete(term, limit = 5)
- current_user.authorized_projects.search_by_title(term).
- sorted_by_stars.non_archived.limit(limit).map do |p|
+ current_user.authorized_projects.search_by_title(term)
+ .sorted_by_stars.non_archived.limit(limit).map do |p|
{
category: "Projects",
id: p.id,
diff --git a/app/helpers/users_helper.rb b/app/helpers/users_helper.rb
index 9c623c9ba7c..b5f54d3e154 100644
--- a/app/helpers/users_helper.rb
+++ b/app/helpers/users_helper.rb
@@ -4,4 +4,14 @@ module UsersHelper
title: user.email,
class: 'has-tooltip commit-committer-link')
end
+
+ def user_email_help_text(user)
+ return 'We also use email for avatar detection if no avatar is uploaded.' unless user.unconfirmed_email.present?
+
+ confirmation_link = link_to 'Resend confirmation e-mail', user_confirmation_path(user: { email: @user.unconfirmed_email }), method: :post
+
+ h('Please click the link in the confirmation email before continuing. It was sent to ') +
+ content_tag(:strong) { user.unconfirmed_email } + h('.') +
+ content_tag(:p) { confirmation_link }
+ end
end
diff --git a/app/helpers/wiki_helper.rb b/app/helpers/wiki_helper.rb
index 3e3f6246fc5..99212a3438f 100644
--- a/app/helpers/wiki_helper.rb
+++ b/app/helpers/wiki_helper.rb
@@ -6,8 +6,8 @@ module WikiHelper
# Returns a String composed of the capitalized name of each directory and the
# capitalized name of the page itself.
def breadcrumb(page_slug)
- page_slug.split('/').
- map { |dir_or_page| WikiPage.unhyphenize(dir_or_page).capitalize }.
- join(' / ')
+ page_slug.split('/')
+ .map { |dir_or_page| WikiPage.unhyphenize(dir_or_page).capitalize }
+ .join(' / ')
end
end
diff --git a/app/models/award_emoji.rb b/app/models/award_emoji.rb
index ebe60441603..91b62dabbcd 100644
--- a/app/models/award_emoji.rb
+++ b/app/models/award_emoji.rb
@@ -19,9 +19,9 @@ class AwardEmoji < ActiveRecord::Base
class << self
def votes_for_collection(ids, type)
- select('name', 'awardable_id', 'COUNT(*) as count').
- where('name IN (?) AND awardable_type = ? AND awardable_id IN (?)', [DOWNVOTE_NAME, UPVOTE_NAME], type, ids).
- group('name', 'awardable_id')
+ select('name', 'awardable_id', 'COUNT(*) as count')
+ .where('name IN (?) AND awardable_type = ? AND awardable_id IN (?)', [DOWNVOTE_NAME, UPVOTE_NAME], type, ids)
+ .group('name', 'awardable_id')
end
end
diff --git a/app/models/ci/build.rb b/app/models/ci/build.rb
index 58758f7ca8a..a300536532b 100644
--- a/app/models/ci/build.rb
+++ b/app/models/ci/build.rb
@@ -138,17 +138,6 @@ module Ci
ExpandVariables.expand(environment, simple_variables) if environment
end
- def environment_url
- return @environment_url if defined?(@environment_url)
-
- @environment_url =
- if unexpanded_url = options&.dig(:environment, :url)
- ExpandVariables.expand(unexpanded_url, simple_variables)
- else
- persisted_environment&.external_url
- end
- end
-
def has_environment?
environment.present?
end
@@ -192,7 +181,7 @@ module Ci
slugified.gsub(/[^a-z0-9]/, '-')[0..62]
end
- # Variables whose value does not depend on other variables
+ # Variables whose value does not depend on environment
def simple_variables
variables = predefined_variables
variables += project.predefined_variables
@@ -207,7 +196,8 @@ module Ci
variables
end
- # All variables, including those dependent on other variables
+ # All variables, including those dependent on environment, which could
+ # contain unexpanded variables.
def variables
simple_variables.concat(persisted_environment_variables)
end
@@ -481,9 +471,10 @@ module Ci
variables = persisted_environment.predefined_variables
- if url = environment_url
- variables << { key: 'CI_ENVIRONMENT_URL', value: url, public: true }
- end
+ # Here we're passing unexpanded environment_url for runner to expand,
+ # and we need to make sure that CI_ENVIRONMENT_NAME and
+ # CI_ENVIRONMENT_SLUG so on are available for the URL be expanded.
+ variables << { key: 'CI_ENVIRONMENT_URL', value: environment_url, public: true } if environment_url
variables
end
@@ -506,6 +497,10 @@ module Ci
variables
end
+ def environment_url
+ options&.dig(:environment, :url) || persisted_environment&.external_url
+ end
+
def build_attributes_from_config
return {} unless pipeline.config_processor
diff --git a/app/models/ci/pipeline.rb b/app/models/ci/pipeline.rb
index 9ddecba5183..1b3e5a25ac2 100644
--- a/app/models/ci/pipeline.rb
+++ b/app/models/ci/pipeline.rb
@@ -168,8 +168,8 @@ module Ci
end
def stages_names
- statuses.order(:stage_idx).distinct.
- pluck(:stage, :stage_idx).map(&:first)
+ statuses.order(:stage_idx).distinct
+ .pluck(:stage, :stage_idx).map(&:first)
end
def legacy_stage(name)
diff --git a/app/models/ci/runner.rb b/app/models/ci/runner.rb
index 487ba61bc9c..d12f96f3d0b 100644
--- a/app/models/ci/runner.rb
+++ b/app/models/ci/runner.rb
@@ -30,8 +30,8 @@ module Ci
scope :assignable_for, ->(project) do
# FIXME: That `to_sql` is needed to workaround a weird Rails bug.
# Without that, placeholders would miss one and couldn't match.
- where(locked: false).
- where.not("id IN (#{project.runners.select(:id).to_sql})").specific
+ where(locked: false)
+ .where.not("id IN (#{project.runners.select(:id).to_sql})").specific
end
validate :tag_constraints
diff --git a/app/models/concerns/issuable.rb b/app/models/concerns/issuable.rb
index 8e367576c9d..d178ee4422b 100644
--- a/app/models/concerns/issuable.rb
+++ b/app/models/concerns/issuable.rb
@@ -161,9 +161,9 @@ module Issuable
#
milestones_due_date = 'MIN(milestones.due_date)'
- order_milestone_due_asc.
- order_labels_priority(excluded_labels: excluded_labels, extra_select_columns: [milestones_due_date]).
- reorder(Gitlab::Database.nulls_last_order(milestones_due_date, 'ASC'),
+ order_milestone_due_asc
+ .order_labels_priority(excluded_labels: excluded_labels, extra_select_columns: [milestones_due_date])
+ .reorder(Gitlab::Database.nulls_last_order(milestones_due_date, 'ASC'),
Gitlab::Database.nulls_last_order('highest_priority', 'ASC'))
end
@@ -182,9 +182,9 @@ module Issuable
"(#{highest_priority}) AS highest_priority"
] + extra_select_columns
- select(select_columns.join(', ')).
- group(arel_table[:id]).
- reorder(Gitlab::Database.nulls_last_order('highest_priority', 'ASC'))
+ select(select_columns.join(', '))
+ .group(arel_table[:id])
+ .reorder(Gitlab::Database.nulls_last_order('highest_priority', 'ASC'))
end
def with_label(title, sort = nil)
diff --git a/app/models/concerns/relative_positioning.rb b/app/models/concerns/relative_positioning.rb
index f1d8532a6d6..7cb9a28a284 100644
--- a/app/models/concerns/relative_positioning.rb
+++ b/app/models/concerns/relative_positioning.rb
@@ -18,10 +18,10 @@ module RelativePositioning
prev_pos = nil
if self.relative_position
- prev_pos = self.class.
- in_projects(project.id).
- where('relative_position < ?', self.relative_position).
- maximum(:relative_position)
+ prev_pos = self.class
+ .in_projects(project.id)
+ .where('relative_position < ?', self.relative_position)
+ .maximum(:relative_position)
end
prev_pos
@@ -31,10 +31,10 @@ module RelativePositioning
next_pos = nil
if self.relative_position
- next_pos = self.class.
- in_projects(project.id).
- where('relative_position > ?', self.relative_position).
- minimum(:relative_position)
+ next_pos = self.class
+ .in_projects(project.id)
+ .where('relative_position > ?', self.relative_position)
+ .minimum(:relative_position)
end
next_pos
diff --git a/app/models/concerns/routable.rb b/app/models/concerns/routable.rb
index 63d02b76f6b..ec7796a9dbb 100644
--- a/app/models/concerns/routable.rb
+++ b/app/models/concerns/routable.rb
@@ -107,6 +107,14 @@ module Routable
RequestStore[key] ||= uncached_full_path
end
+ def build_full_path
+ if parent && path
+ parent.full_path + '/' + path
+ else
+ path
+ end
+ end
+
private
def uncached_full_path
@@ -135,14 +143,6 @@ module Routable
end
end
- def build_full_path
- if parent && path
- parent.full_path + '/' + path
- else
- path
- end
- end
-
def update_route
prepare_route
route.save
diff --git a/app/models/concerns/sortable.rb b/app/models/concerns/sortable.rb
index b9a2d812edd..a155a064032 100644
--- a/app/models/concerns/sortable.rb
+++ b/app/models/concerns/sortable.rb
@@ -39,12 +39,12 @@ module Sortable
private
def highest_label_priority(target_type_column: nil, target_type: nil, target_column:, project_column:, excluded_labels: [])
- query = Label.select(LabelPriority.arel_table[:priority].minimum).
- left_join_priorities.
- joins(:label_links).
- where("label_priorities.project_id = #{project_column}").
- where("label_links.target_id = #{target_column}").
- reorder(nil)
+ query = Label.select(LabelPriority.arel_table[:priority].minimum)
+ .left_join_priorities
+ .joins(:label_links)
+ .where("label_priorities.project_id = #{project_column}")
+ .where("label_links.target_id = #{target_column}")
+ .reorder(nil)
query =
if target_type_column
diff --git a/app/models/concerns/subscribable.rb b/app/models/concerns/subscribable.rb
index 83daa9b1a64..f60a0f8f438 100644
--- a/app/models/concerns/subscribable.rb
+++ b/app/models/concerns/subscribable.rb
@@ -27,16 +27,16 @@ module Subscribable
end
def subscribers(project)
- subscriptions_available(project).
- where(subscribed: true).
- map(&:user)
+ subscriptions_available(project)
+ .where(subscribed: true)
+ .map(&:user)
end
def toggle_subscription(user, project = nil)
unsubscribe_from_other_levels(user, project)
- find_or_initialize_subscription(user, project).
- update(subscribed: !subscribed?(user, project))
+ find_or_initialize_subscription(user, project)
+ .update(subscribed: !subscribed?(user, project))
end
def subscribe(user, project = nil)
@@ -69,14 +69,14 @@ module Subscribable
end
def find_or_initialize_subscription(user, project)
- subscriptions.
- find_or_initialize_by(user_id: user.id, project_id: project.try(:id))
+ subscriptions
+ .find_or_initialize_by(user_id: user.id, project_id: project.try(:id))
end
def subscriptions_available(project)
t = Subscription.arel_table
- subscriptions.
- where(t[:project_id].eq(nil).or(t[:project_id].eq(project.try(:id))))
+ subscriptions
+ .where(t[:project_id].eq(nil).or(t[:project_id].eq(project.try(:id))))
end
end
diff --git a/app/models/deployment.rb b/app/models/deployment.rb
index 85e7901dfee..056c49e7162 100644
--- a/app/models/deployment.rb
+++ b/app/models/deployment.rb
@@ -58,10 +58,10 @@ class Deployment < ActiveRecord::Base
def update_merge_request_metrics!
return unless environment.update_merge_request_metrics?
- merge_requests = project.merge_requests.
- joins(:metrics).
- where(target_branch: self.ref, merge_request_metrics: { first_deployed_to_production_at: nil }).
- where("merge_request_metrics.merged_at <= ?", self.created_at)
+ merge_requests = project.merge_requests
+ .joins(:metrics)
+ .where(target_branch: self.ref, merge_request_metrics: { first_deployed_to_production_at: nil })
+ .where("merge_request_metrics.merged_at <= ?", self.created_at)
if previous_deployment
merge_requests = merge_requests.where("merge_request_metrics.merged_at >= ?", previous_deployment.created_at)
@@ -76,17 +76,17 @@ class Deployment < ActiveRecord::Base
merge_requests.map(&:id)
end
- MergeRequest::Metrics.
- where(merge_request_id: merge_request_ids, first_deployed_to_production_at: nil).
- update_all(first_deployed_to_production_at: self.created_at)
+ MergeRequest::Metrics
+ .where(merge_request_id: merge_request_ids, first_deployed_to_production_at: nil)
+ .update_all(first_deployed_to_production_at: self.created_at)
end
def previous_deployment
@previous_deployment ||=
- project.deployments.joins(:environment).
- where(environments: { name: self.environment.name }, ref: self.ref).
- where.not(id: self.id).
- take
+ project.deployments.joins(:environment)
+ .where(environments: { name: self.environment.name }, ref: self.ref)
+ .where.not(id: self.id)
+ .take
end
def stop_action
@@ -114,6 +114,17 @@ class Deployment < ActiveRecord::Base
project.monitoring_service.deployment_metrics(self)
end
+ def has_additional_metrics?
+ project.prometheus_service.present?
+ end
+
+ def additional_metrics
+ return {} unless project.prometheus_service.present?
+
+ metrics = project.prometheus_service.additional_deployment_metrics(self)
+ metrics&.merge(deployment_time: created_at.to_i) || {}
+ end
+
private
def ref_path
diff --git a/app/models/environment.rb b/app/models/environment.rb
index d5b974b2d31..7ad36f1d80c 100644
--- a/app/models/environment.rb
+++ b/app/models/environment.rb
@@ -40,9 +40,9 @@ class Environment < ActiveRecord::Base
scope :stopped, -> { with_state(:stopped) }
scope :order_by_last_deployed_at, -> do
max_deployment_id_sql =
- Deployment.select(Deployment.arel_table[:id].maximum).
- where(Deployment.arel_table[:environment_id].eq(arel_table[:id])).
- to_sql
+ Deployment.select(Deployment.arel_table[:id].maximum)
+ .where(Deployment.arel_table[:environment_id].eq(arel_table[:id]))
+ .to_sql
order(Gitlab::Database.nulls_first_order("(#{max_deployment_id_sql})", 'ASC'))
end
@@ -157,6 +157,16 @@ class Environment < ActiveRecord::Base
project.monitoring_service.environment_metrics(self) if has_metrics?
end
+ def has_additional_metrics?
+ project.prometheus_service.present? && available? && last_deployment.present?
+ end
+
+ def additional_metrics
+ if has_additional_metrics?
+ project.prometheus_service.additional_environment_metrics(self)
+ end
+ end
+
# An environment name is not necessarily suitable for use in URLs, DNS
# or other third-party contexts, so provide a slugified version. A slug has
# the following properties:
diff --git a/app/models/event.rb b/app/models/event.rb
index fad6ff03927..29bc141c5cd 100644
--- a/app/models/event.rb
+++ b/app/models/event.rb
@@ -376,9 +376,9 @@ class Event < ActiveRecord::Base
# At this point it's possible for multiple threads/processes to try to
# update the project. Only one query should actually perform the update,
# hence we add the extra WHERE clause for last_activity_at.
- Project.unscoped.where(id: project_id).
- where('last_activity_at <= ?', RESET_PROJECT_ACTIVITY_INTERVAL.ago).
- update_all(last_activity_at: created_at)
+ Project.unscoped.where(id: project_id)
+ .where('last_activity_at <= ?', RESET_PROJECT_ACTIVITY_INTERVAL.ago)
+ .update_all(last_activity_at: created_at)
end
def authored_by?(user)
@@ -392,7 +392,7 @@ class Event < ActiveRecord::Base
end
def set_last_repository_updated_at
- Project.unscoped.where(id: project_id).
- update_all(last_repository_updated_at: created_at)
+ Project.unscoped.where(id: project_id)
+ .update_all(last_repository_updated_at: created_at)
end
end
diff --git a/app/models/group.rb b/app/models/group.rb
index 5bb2cdc5eff..0b93460d473 100644
--- a/app/models/group.rb
+++ b/app/models/group.rb
@@ -206,8 +206,8 @@ class Group < Namespace
end
def refresh_members_authorized_projects
- UserProjectAccessChangedService.new(user_ids_for_project_authorizations).
- execute
+ UserProjectAccessChangedService.new(user_ids_for_project_authorizations)
+ .execute
end
def user_ids_for_project_authorizations
@@ -225,10 +225,10 @@ class Group < Namespace
def max_member_access_for_user(user)
return GroupMember::OWNER if user.admin?
- members_with_parents.
- where(user_id: user).
- reorder(access_level: :desc).
- first&.
+ members_with_parents
+ .where(user_id: user)
+ .reorder(access_level: :desc)
+ .first&.
access_level || GroupMember::NO_ACCESS
end
diff --git a/app/models/issue.rb b/app/models/issue.rb
index f0f525aea21..3a9a6dba601 100644
--- a/app/models/issue.rb
+++ b/app/models/issue.rb
@@ -124,8 +124,8 @@ class Issue < ActiveRecord::Base
end
def self.order_by_position_and_priority
- order_labels_priority.
- reorder(Gitlab::Database.nulls_last_order('relative_position', 'ASC'),
+ order_labels_priority
+ .reorder(Gitlab::Database.nulls_last_order('relative_position', 'ASC'),
Gitlab::Database.nulls_last_order('highest_priority', 'ASC'),
"id DESC")
end
diff --git a/app/models/issue_collection.rb b/app/models/issue_collection.rb
index f0b7d9914c8..49f011c113f 100644
--- a/app/models/issue_collection.rb
+++ b/app/models/issue_collection.rb
@@ -17,9 +17,9 @@ class IssueCollection
# Given all the issue projects we get a list of projects that the current
# user has at least reporter access to.
- projects_with_reporter_access = user.
- projects_with_reporter_access_limited_to(project_ids).
- pluck(:id)
+ projects_with_reporter_access = user
+ .projects_with_reporter_access_limited_to(project_ids)
+ .pluck(:id)
collection.select do |issue|
if projects_with_reporter_access.include?(issue.project_id)
diff --git a/app/models/label.rb b/app/models/label.rb
index 955d6b4079b..ed6a8411da9 100644
--- a/app/models/label.rb
+++ b/app/models/label.rb
@@ -46,9 +46,9 @@ class Label < ActiveRecord::Base
labels = Label.arel_table
priorities = LabelPriority.arel_table
- label_priorities = labels.join(priorities, Arel::Nodes::OuterJoin).
- on(labels[:id].eq(priorities[:label_id]).and(priorities[:project_id].eq(project.id))).
- join_sources
+ label_priorities = labels.join(priorities, Arel::Nodes::OuterJoin)
+ .on(labels[:id].eq(priorities[:label_id]).and(priorities[:project_id].eq(project.id)))
+ .join_sources
joins(label_priorities).where(priorities[:priority].eq(nil))
end
@@ -57,9 +57,9 @@ class Label < ActiveRecord::Base
labels = Label.arel_table
priorities = LabelPriority.arel_table
- label_priorities = labels.join(priorities, Arel::Nodes::OuterJoin).
- on(labels[:id].eq(priorities[:label_id])).
- join_sources
+ label_priorities = labels.join(priorities, Arel::Nodes::OuterJoin)
+ .on(labels[:id].eq(priorities[:label_id]))
+ .join_sources
joins(label_priorities)
end
diff --git a/app/models/member.rb b/app/models/member.rb
index 788a32dd8e3..dc9247bc9a0 100644
--- a/app/models/member.rb
+++ b/app/models/member.rb
@@ -99,9 +99,9 @@ class Member < ActiveRecord::Base
users = User.arel_table
members = Member.arel_table
- member_users = members.join(users, Arel::Nodes::OuterJoin).
- on(members[:user_id].eq(users[:id])).
- join_sources
+ member_users = members.join(users, Arel::Nodes::OuterJoin)
+ .on(members[:user_id].eq(users[:id]))
+ .join_sources
joins(member_users)
end
diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb
index ea22ab53587..f581a25f093 100644
--- a/app/models/merge_request.rb
+++ b/app/models/merge_request.rb
@@ -577,8 +577,8 @@ class MergeRequest < ActiveRecord::Base
messages = [title, description]
messages.concat(commits.map(&:safe_message)) if merge_request_diff
- Gitlab::ClosingIssueExtractor.new(project, current_user).
- closed_by_message(messages.join("\n"))
+ Gitlab::ClosingIssueExtractor.new(project, current_user)
+ .closed_by_message(messages.join("\n"))
else
[]
end
diff --git a/app/models/merge_requests_closing_issues.rb b/app/models/merge_requests_closing_issues.rb
index daafb137be4..7f7c114803d 100644
--- a/app/models/merge_requests_closing_issues.rb
+++ b/app/models/merge_requests_closing_issues.rb
@@ -7,9 +7,9 @@ class MergeRequestsClosingIssues < ActiveRecord::Base
class << self
def count_for_collection(ids)
- group(:issue_id).
- where(issue_id: ids).
- pluck('issue_id', 'COUNT(*) as count')
+ group(:issue_id)
+ .where(issue_id: ids)
+ .pluck('issue_id', 'COUNT(*) as count')
end
end
end
diff --git a/app/models/milestone.rb b/app/models/milestone.rb
index 0a6fc064aec..d2e2749f70d 100644
--- a/app/models/milestone.rb
+++ b/app/models/milestone.rb
@@ -98,11 +98,11 @@ class Milestone < ActiveRecord::Base
if Gitlab::Database.postgresql?
rel.order(:project_id, :due_date).select('DISTINCT ON (project_id) id')
else
- rel.
- group(:project_id).
- having('due_date = MIN(due_date)').
- pluck(:id, :project_id, :due_date).
- map(&:first)
+ rel
+ .group(:project_id)
+ .having('due_date = MIN(due_date)')
+ .pluck(:id, :project_id, :due_date)
+ .map(&:first)
end
end
diff --git a/app/models/namespace.rb b/app/models/namespace.rb
index b48d73dcae7..583d4fb5244 100644
--- a/app/models/namespace.rb
+++ b/app/models/namespace.rb
@@ -181,16 +181,16 @@ class Namespace < ActiveRecord::Base
def ancestors
return self.class.none unless parent_id
- Gitlab::GroupHierarchy.
- new(self.class.where(id: parent_id)).
- base_and_ancestors
+ Gitlab::GroupHierarchy
+ .new(self.class.where(id: parent_id))
+ .base_and_ancestors
end
# Returns all the descendants of the current namespace.
def descendants
- Gitlab::GroupHierarchy.
- new(self.class.where(parent_id: id)).
- base_and_descendants
+ Gitlab::GroupHierarchy
+ .new(self.class.where(parent_id: id))
+ .base_and_descendants
end
def user_ids_for_project_authorizations
@@ -253,10 +253,10 @@ class Namespace < ActiveRecord::Base
end
def refresh_access_of_projects_invited_groups
- Group.
- joins(project_group_links: :project).
- where(projects: { namespace_id: id }).
- find_each(&:refresh_members_authorized_projects)
+ Group
+ .joins(project_group_links: :project)
+ .where(projects: { namespace_id: id })
+ .find_each(&:refresh_members_authorized_projects)
end
def remove_exports!
diff --git a/app/models/note.rb b/app/models/note.rb
index 3221e653e30..ca6999427c0 100644
--- a/app/models/note.rb
+++ b/app/models/note.rb
@@ -137,9 +137,9 @@ class Note < ActiveRecord::Base
end
def count_for_collection(ids, type)
- user.select('noteable_id', 'COUNT(*) as count').
- group(:noteable_id).
- where(noteable_type: type, noteable_id: ids)
+ user.select('noteable_id', 'COUNT(*) as count')
+ .group(:noteable_id)
+ .where(noteable_type: type, noteable_id: ids)
end
end
diff --git a/app/models/project.rb b/app/models/project.rb
index 36ec4f398ca..2c2685875f8 100644
--- a/app/models/project.rb
+++ b/app/models/project.rb
@@ -244,8 +244,8 @@ class Project < ActiveRecord::Base
scope :inside_path, ->(path) do
# We need routes alias rs for JOIN so it does not conflict with
# includes(:route) which we use in ProjectsFinder.
- joins("INNER JOIN routes rs ON rs.source_id = projects.id AND rs.source_type = 'Project'").
- where('rs.path LIKE ?', "#{sanitize_sql_like(path)}/%")
+ joins("INNER JOIN routes rs ON rs.source_id = projects.id AND rs.source_type = 'Project'")
+ .where('rs.path LIKE ?', "#{sanitize_sql_like(path)}/%")
end
# "enabled" here means "not disabled". It includes private features!
@@ -270,10 +270,10 @@ class Project < ActiveRecord::Base
# logged in user.
def self.public_or_visible_to_user(user = nil)
if user
- authorized = user.
- project_authorizations.
- select(1).
- where('project_authorizations.project_id = projects.id')
+ authorized = user
+ .project_authorizations
+ .select(1)
+ .where('project_authorizations.project_id = projects.id')
levels = Gitlab::VisibilityLevel.levels_for_user(user)
@@ -298,11 +298,11 @@ class Project < ActiveRecord::Base
elsif user
column = ProjectFeature.quoted_access_level_column(feature)
- authorized = user.project_authorizations.select(1).
- where('project_authorizations.project_id = projects.id')
+ authorized = user.project_authorizations.select(1)
+ .where('project_authorizations.project_id = projects.id')
- with_project_feature.
- where("#{column} IN (?) OR (#{column} = ? AND EXISTS (?))",
+ with_project_feature
+ .where("#{column} IN (?) OR (#{column} = ? AND EXISTS (?))",
visible,
ProjectFeature::PRIVATE,
authorized)
@@ -369,14 +369,14 @@ class Project < ActiveRecord::Base
# unscoping unnecessary conditions that'll be applied
# when executing `where("projects.id IN (#{union.to_sql})")`
projects = unscoped.select(:id).where(
- ptable[:path].matches(pattern).
- or(ptable[:name].matches(pattern)).
- or(ptable[:description].matches(pattern))
+ ptable[:path].matches(pattern)
+ .or(ptable[:name].matches(pattern))
+ .or(ptable[:description].matches(pattern))
)
- namespaces = unscoped.select(:id).
- joins(:namespace).
- where(ntable[:name].matches(pattern))
+ namespaces = unscoped.select(:id)
+ .joins(:namespace)
+ .where(ntable[:name].matches(pattern))
union = Gitlab::SQL::Union.new([projects, namespaces])
@@ -417,8 +417,8 @@ class Project < ActiveRecord::Base
end
def trending
- joins('INNER JOIN trending_projects ON projects.id = trending_projects.project_id').
- reorder('trending_projects.id ASC')
+ joins('INNER JOIN trending_projects ON projects.id = trending_projects.project_id')
+ .reorder('trending_projects.id ASC')
end
def cached_count
diff --git a/app/models/project_authorization.rb b/app/models/project_authorization.rb
index def09675253..73302207e6b 100644
--- a/app/models/project_authorization.rb
+++ b/app/models/project_authorization.rb
@@ -7,9 +7,9 @@ class ProjectAuthorization < ActiveRecord::Base
validates :user, uniqueness: { scope: [:project, :access_level] }, presence: true
def self.select_from_union(union)
- select(['project_id', 'MAX(access_level) AS access_level']).
- from("(#{union.to_sql}) #{ProjectAuthorization.table_name}").
- group(:project_id)
+ select(['project_id', 'MAX(access_level) AS access_level'])
+ .from("(#{union.to_sql}) #{ProjectAuthorization.table_name}")
+ .group(:project_id)
end
def self.insert_authorizations(rows, per_batch = 1000)
diff --git a/app/models/project_services/mattermost_slash_commands_service.rb b/app/models/project_services/mattermost_slash_commands_service.rb
index c2f887e24a0..4d2037286a2 100644
--- a/app/models/project_services/mattermost_slash_commands_service.rb
+++ b/app/models/project_services/mattermost_slash_commands_service.rb
@@ -20,8 +20,8 @@ class MattermostSlashCommandsService < SlashCommandsService
end
def configure(user, params)
- token = Mattermost::Command.new(user).
- create(command(params))
+ token = Mattermost::Command.new(user)
+ .create(command(params))
update(active: true, token: token) if token
rescue Mattermost::Error => e
diff --git a/app/models/project_services/prometheus_service.rb b/app/models/project_services/prometheus_service.rb
index 110b8bc209b..c9df678f97e 100644
--- a/app/models/project_services/prometheus_service.rb
+++ b/app/models/project_services/prometheus_service.rb
@@ -28,17 +28,6 @@ class PrometheusService < MonitoringService
'Prometheus monitoring'
end
- def help
- <<-MD.strip_heredoc
- Retrieves the Kubernetes node metrics `container_cpu_usage_seconds_total`
- and `container_memory_usage_bytes` from the configured Prometheus server.
-
- If you are not using [Auto-Deploy](https://docs.gitlab.com/ee/ci/autodeploy/index.html)
- or have set up your own Prometheus server, an `environment` label is required on each metric to
- [identify the Environment](https://docs.gitlab.com/ce/user/project/integrations/prometheus.html#metrics-and-labels).
- MD
- end
-
def self.to_param
'prometheus'
end
@@ -50,6 +39,7 @@ class PrometheusService < MonitoringService
name: 'api_url',
title: 'API URL',
placeholder: 'Prometheus API Base URL, like http://prometheus.example.com/',
+ help: 'By default, Prometheus listens on ‘http://localhost:9090’. It’s not recommended to change the default address and port as this might affect or conflict with other services running on the GitLab server.',
required: true
}
]
@@ -65,23 +55,34 @@ class PrometheusService < MonitoringService
end
def environment_metrics(environment)
- with_reactive_cache(Gitlab::Prometheus::Queries::EnvironmentQuery.name, environment.id, &:itself)
+ with_reactive_cache(Gitlab::Prometheus::Queries::EnvironmentQuery.name, environment.id, &method(:rename_data_to_metrics))
end
def deployment_metrics(deployment)
- metrics = with_reactive_cache(Gitlab::Prometheus::Queries::DeploymentQuery.name, deployment.id, &:itself)
+ metrics = with_reactive_cache(Gitlab::Prometheus::Queries::DeploymentQuery.name, deployment.id, &method(:rename_data_to_metrics))
metrics&.merge(deployment_time: created_at.to_i) || {}
end
+ def additional_environment_metrics(environment)
+ with_reactive_cache(Gitlab::Prometheus::Queries::AdditionalMetricsEnvironmentQuery.name, environment.id, &:itself)
+ end
+
+ def additional_deployment_metrics(deployment)
+ with_reactive_cache(Gitlab::Prometheus::Queries::AdditionalMetricsDeploymentQuery.name, deployment.id, &:itself)
+ end
+
+ def matched_metrics
+ with_reactive_cache(Gitlab::Prometheus::Queries::MatchedMetricsQuery.name, &:itself)
+ end
+
# Cache metrics for specific environment
def calculate_reactive_cache(query_class_name, *args)
return unless active? && project && !project.pending_delete?
- metrics = Kernel.const_get(query_class_name).new(client).query(*args)
-
+ data = Kernel.const_get(query_class_name).new(client).query(*args)
{
success: true,
- metrics: metrics,
+ data: data,
last_update: Time.now.utc
}
rescue Gitlab::PrometheusError => err
@@ -91,4 +92,11 @@ class PrometheusService < MonitoringService
def client
@prometheus ||= Gitlab::PrometheusClient.new(api_url: api_url)
end
+
+ private
+
+ def rename_data_to_metrics(metrics)
+ metrics[:metrics] = metrics.delete :data
+ metrics
+ end
end
diff --git a/app/models/project_team.rb b/app/models/project_team.rb
index e1cc56551ba..674eacd28e8 100644
--- a/app/models/project_team.rb
+++ b/app/models/project_team.rb
@@ -172,10 +172,10 @@ class ProjectTeam
return access if user_ids.empty?
- users_access = project.project_authorizations.
- where(user: user_ids).
- group(:user_id).
- maximum(:access_level)
+ users_access = project.project_authorizations
+ .where(user: user_ids)
+ .group(:user_id)
+ .maximum(:access_level)
access.merge!(users_access)
diff --git a/app/models/repository.rb b/app/models/repository.rb
index 7460515fea8..c67475357d9 100644
--- a/app/models/repository.rb
+++ b/app/models/repository.rb
@@ -241,11 +241,11 @@ class Repository
cache.fetch(:"diverging_commit_counts_#{branch.name}") do
# Rugged seems to throw a `ReferenceError` when given branch_names rather
# than SHA-1 hashes
- number_commits_behind = raw_repository.
- count_commits_between(branch.dereferenced_target.sha, root_ref_hash)
+ number_commits_behind = raw_repository
+ .count_commits_between(branch.dereferenced_target.sha, root_ref_hash)
- number_commits_ahead = raw_repository.
- count_commits_between(root_ref_hash, branch.dereferenced_target.sha)
+ number_commits_ahead = raw_repository
+ .count_commits_between(root_ref_hash, branch.dereferenced_target.sha)
{ behind: number_commits_behind, ahead: number_commits_ahead }
end
diff --git a/app/models/todo.rb b/app/models/todo.rb
index 696d139af74..7af54b2beb2 100644
--- a/app/models/todo.rb
+++ b/app/models/todo.rb
@@ -70,9 +70,9 @@ class Todo < ActiveRecord::Base
highest_priority = highest_label_priority(params).to_sql
- select("#{table_name}.*, (#{highest_priority}) AS highest_priority").
- order(Gitlab::Database.nulls_last_order('highest_priority', 'ASC')).
- order('todos.created_at')
+ select("#{table_name}.*, (#{highest_priority}) AS highest_priority")
+ .order(Gitlab::Database.nulls_last_order('highest_priority', 'ASC'))
+ .order('todos.created_at')
end
end
diff --git a/app/models/user.rb b/app/models/user.rb
index 782c162e1f3..954a30155f7 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -223,13 +223,13 @@ class User < ActiveRecord::Base
scope :order_oldest_sign_in, -> { reorder(Gitlab::Database.nulls_last_order('last_sign_in_at', 'ASC')) }
def self.with_two_factor
- joins("LEFT OUTER JOIN u2f_registrations AS u2f ON u2f.user_id = users.id").
- where("u2f.id IS NOT NULL OR otp_required_for_login = ?", true).distinct(arel_table[:id])
+ joins("LEFT OUTER JOIN u2f_registrations AS u2f ON u2f.user_id = users.id")
+ .where("u2f.id IS NOT NULL OR otp_required_for_login = ?", true).distinct(arel_table[:id])
end
def self.without_two_factor
- joins("LEFT OUTER JOIN u2f_registrations AS u2f ON u2f.user_id = users.id").
- where("u2f.id IS NULL AND otp_required_for_login = ?", false)
+ joins("LEFT OUTER JOIN u2f_registrations AS u2f ON u2f.user_id = users.id")
+ .where("u2f.id IS NULL AND otp_required_for_login = ?", false)
end
#
@@ -300,9 +300,9 @@ class User < ActiveRecord::Base
pattern = "%#{query}%"
where(
- table[:name].matches(pattern).
- or(table[:email].matches(pattern)).
- or(table[:username].matches(pattern))
+ table[:name].matches(pattern)
+ .or(table[:email].matches(pattern))
+ .or(table[:username].matches(pattern))
)
end
@@ -317,10 +317,10 @@ class User < ActiveRecord::Base
matched_by_emails_user_ids = email_table.project(email_table[:user_id]).where(email_table[:email].matches(pattern))
where(
- table[:name].matches(pattern).
- or(table[:email].matches(pattern)).
- or(table[:username].matches(pattern)).
- or(table[:id].in(matched_by_emails_user_ids))
+ table[:name].matches(pattern)
+ .or(table[:email].matches(pattern))
+ .or(table[:username].matches(pattern))
+ .or(table[:id].in(matched_by_emails_user_ids))
)
end
@@ -503,8 +503,8 @@ class User < ActiveRecord::Base
# Returns the groups a user has access to
def authorized_groups
- union = Gitlab::SQL::Union.
- new([groups.select(:id), authorized_projects.select(:namespace_id)])
+ union = Gitlab::SQL::Union
+ .new([groups.select(:id), authorized_projects.select(:namespace_id)])
Group.where("namespaces.id IN (#{union.to_sql})")
end
@@ -533,8 +533,8 @@ class User < ActiveRecord::Base
projects = super()
if min_access_level
- projects = projects.
- where('project_authorizations.access_level >= ?', min_access_level)
+ projects = projects
+ .where('project_authorizations.access_level >= ?', min_access_level)
end
projects
@@ -619,9 +619,9 @@ class User < ActiveRecord::Base
next unless project
if project.repository.branch_exists?(event.branch_name)
- merge_requests = MergeRequest.where("created_at >= ?", event.created_at).
- where(source_project_id: project.id,
- source_branch: event.branch_name)
+ merge_requests = MergeRequest.where("created_at >= ?", event.created_at)
+ .where(source_project_id: project.id,
+ source_branch: event.branch_name)
merge_requests.empty?
end
end
@@ -832,8 +832,8 @@ class User < ActiveRecord::Base
def toggle_star(project)
UsersStarProject.transaction do
- user_star_project = users_star_projects.
- where(project: project, user: self).lock(true).first
+ user_star_project = users_star_projects
+ .where(project: project, user: self).lock(true).first
if user_star_project
user_star_project.destroy
@@ -869,11 +869,11 @@ class User < ActiveRecord::Base
# ms on a database with a similar size to GitLab.com's database. On the other
# hand, using a subquery means we can get the exact same data in about 40 ms.
def contributed_projects
- events = Event.select(:project_id).
- contributions.where(author_id: self).
- where("created_at > ?", Time.now - 1.year).
- uniq.
- reorder(nil)
+ events = Event.select(:project_id)
+ .contributions.where(author_id: self)
+ .where("created_at > ?", Time.now - 1.year)
+ .uniq
+ .reorder(nil)
Project.where(id: events)
end
@@ -884,9 +884,9 @@ class User < ActiveRecord::Base
def ci_authorized_runners
@ci_authorized_runners ||= begin
- runner_ids = Ci::RunnerProject.
- where("ci_runner_projects.project_id IN (#{ci_projects_union.to_sql})").
- select(:runner_id)
+ runner_ids = Ci::RunnerProject
+ .where("ci_runner_projects.project_id IN (#{ci_projects_union.to_sql})")
+ .select(:runner_id)
Ci::Runner.specific.where(id: runner_ids)
end
end
diff --git a/app/models/wiki_page.rb b/app/models/wiki_page.rb
index c771c22f46a..224eb3cd4d0 100644
--- a/app/models/wiki_page.rb
+++ b/app/models/wiki_page.rb
@@ -22,16 +22,16 @@ class WikiPage
def self.group_by_directory(pages)
return [] if pages.blank?
- pages.sort_by { |page| [page.directory, page.slug] }.
- group_by(&:directory).
- map do |dir, pages|
+ pages.sort_by { |page| [page.directory, page.slug] }
+ .group_by(&:directory)
+ .map do |dir, pages|
if dir.present?
WikiDirectory.new(dir, pages)
else
pages
end
- end.
- flatten
+ end
+ .flatten
end
def self.unhyphenize(name)
diff --git a/app/services/ci/create_pipeline_service.rb b/app/services/ci/create_pipeline_service.rb
index 769749c9925..942145c4a8c 100644
--- a/app/services/ci/create_pipeline_service.rb
+++ b/app/services/ci/create_pipeline_service.rb
@@ -67,8 +67,8 @@ module Ci
def update_merge_requests_head_pipeline
return unless pipeline.latest?
- MergeRequest.where(source_project: @pipeline.project, source_branch: @pipeline.ref).
- update_all(head_pipeline_id: @pipeline.id)
+ MergeRequest.where(source_project: @pipeline.project, source_branch: @pipeline.ref)
+ .update_all(head_pipeline_id: @pipeline.id)
end
def skip_ci?
diff --git a/app/services/ci/create_trigger_request_service.rb b/app/services/ci/create_trigger_request_service.rb
index beb27a5a597..cf3d4aee2bc 100644
--- a/app/services/ci/create_trigger_request_service.rb
+++ b/app/services/ci/create_trigger_request_service.rb
@@ -3,8 +3,8 @@ module Ci
def execute(project, trigger, ref, variables = nil)
trigger_request = trigger.trigger_requests.create(variables: variables)
- pipeline = Ci::CreatePipelineService.new(project, trigger.owner, ref: ref).
- execute(:trigger, ignore_skip_ci: true, trigger_request: trigger_request)
+ pipeline = Ci::CreatePipelineService.new(project, trigger.owner, ref: ref)
+ .execute(:trigger, ignore_skip_ci: true, trigger_request: trigger_request)
trigger_request if pipeline.persisted?
end
diff --git a/app/services/ci/register_job_service.rb b/app/services/ci/register_job_service.rb
index d6a4280ce4c..af84d4c7427 100644
--- a/app/services/ci/register_job_service.rb
+++ b/app/services/ci/register_job_service.rb
@@ -54,15 +54,15 @@ module Ci
def builds_for_shared_runner
new_builds.
# don't run projects which have not enabled shared runners and builds
- joins(:project).where(projects: { shared_runners_enabled: true }).
- joins('LEFT JOIN project_features ON ci_builds.project_id = project_features.project_id').
- where('project_features.builds_access_level IS NULL or project_features.builds_access_level > 0').
+ joins(:project).where(projects: { shared_runners_enabled: true })
+ .joins('LEFT JOIN project_features ON ci_builds.project_id = project_features.project_id')
+ .where('project_features.builds_access_level IS NULL or project_features.builds_access_level > 0').
# Implement fair scheduling
# this returns builds that are ordered by number of running builds
# we prefer projects that don't use shared runners at all
- joins("LEFT JOIN (#{running_builds_for_shared_runners.to_sql}) AS project_builds ON ci_builds.project_id=project_builds.project_id").
- order('COALESCE(project_builds.running_builds, 0) ASC', 'ci_builds.id ASC')
+ joins("LEFT JOIN (#{running_builds_for_shared_runners.to_sql}) AS project_builds ON ci_builds.project_id=project_builds.project_id")
+ .order('COALESCE(project_builds.running_builds, 0) ASC', 'ci_builds.id ASC')
end
def builds_for_specific_runner
@@ -70,8 +70,8 @@ module Ci
end
def running_builds_for_shared_runners
- Ci::Build.running.where(runner: Ci::Runner.shared).
- group(:project_id).select(:project_id, 'count(*) AS running_builds')
+ Ci::Build.running.where(runner: Ci::Runner.shared)
+ .group(:project_id).select(:project_id, 'count(*) AS running_builds')
end
def new_builds
diff --git a/app/services/concerns/issues/resolve_discussions.rb b/app/services/concerns/issues/resolve_discussions.rb
index 910a2a15e5d..7d45b4aa26a 100644
--- a/app/services/concerns/issues/resolve_discussions.rb
+++ b/app/services/concerns/issues/resolve_discussions.rb
@@ -10,9 +10,9 @@ module Issues
def merge_request_to_resolve_discussions_of
return @merge_request_to_resolve_discussions_of if defined?(@merge_request_to_resolve_discussions_of)
- @merge_request_to_resolve_discussions_of = MergeRequestsFinder.new(current_user, project_id: project.id).
- execute.
- find_by(iid: merge_request_to_resolve_discussions_of_iid)
+ @merge_request_to_resolve_discussions_of = MergeRequestsFinder.new(current_user, project_id: project.id)
+ .execute
+ .find_by(iid: merge_request_to_resolve_discussions_of_iid)
end
def discussions_to_resolve
diff --git a/app/services/create_deployment_service.rb b/app/services/create_deployment_service.rb
index 46823418bb0..63b85c3de7d 100644
--- a/app/services/create_deployment_service.rb
+++ b/app/services/create_deployment_service.rb
@@ -2,7 +2,7 @@ class CreateDeploymentService
attr_reader :job
delegate :expanded_environment_name,
- :environment_url,
+ :variables,
:project,
to: :job
@@ -14,7 +14,8 @@ class CreateDeploymentService
return unless executable?
ActiveRecord::Base.transaction do
- environment.external_url = environment_url if environment_url
+ environment.external_url = expanded_environment_url if
+ expanded_environment_url
environment.fire_state_event(action)
return unless environment.save
@@ -49,6 +50,17 @@ class CreateDeploymentService
@environment_options ||= job.options&.dig(:environment) || {}
end
+ def expanded_environment_url
+ return @expanded_environment_url if defined?(@expanded_environment_url)
+
+ @expanded_environment_url =
+ ExpandVariables.expand(environment_url, variables) if environment_url
+ end
+
+ def environment_url
+ environment_options[:url]
+ end
+
def on_stop
environment_options[:on_stop]
end
diff --git a/app/services/files/update_service.rb b/app/services/files/update_service.rb
index f23a9f6d57c..bcca1386bed 100644
--- a/app/services/files/update_service.rb
+++ b/app/services/files/update_service.rb
@@ -28,8 +28,8 @@ module Files
end
def last_commit
- @last_commit ||= Gitlab::Git::Commit.
- last_for_path(@start_project.repository, @start_branch, @file_path)
+ @last_commit ||= Gitlab::Git::Commit
+ .last_for_path(@start_project.repository, @start_branch, @file_path)
end
def validate!
diff --git a/app/services/git_push_service.rb b/app/services/git_push_service.rb
index fb1d4aed58b..20d1fb29289 100644
--- a/app/services/git_push_service.rb
+++ b/app/services/git_push_service.rb
@@ -86,8 +86,8 @@ class GitPushService < BaseService
push_commits.last(PROCESS_COMMIT_LIMIT).each do |commit|
if commit.matches_cross_reference_regex?
- ProcessCommitWorker.
- perform_async(project.id, current_user.id, commit.to_hash, default)
+ ProcessCommitWorker
+ .perform_async(project.id, current_user.id, commit.to_hash, default)
end
end
end
diff --git a/app/services/issuable_base_service.rb b/app/services/issuable_base_service.rb
index c8db4728aea..8dd0846f3bc 100644
--- a/app/services/issuable_base_service.rb
+++ b/app/services/issuable_base_service.rb
@@ -144,8 +144,8 @@ class IssuableBaseService < BaseService
def merge_quick_actions_into_params!(issuable)
description, command_params =
- QuickActions::InterpretService.new(project, current_user).
- execute(params[:description], issuable)
+ QuickActions::InterpretService.new(project, current_user)
+ .execute(params[:description], issuable)
# Avoid a description already set on an issuable to be overwritten by a nil
params[:description] = description if params.key?(:description)
diff --git a/app/services/issues/create_service.rb b/app/services/issues/create_service.rb
index 3cf4b82b9f2..718a7ac1f22 100644
--- a/app/services/issues/create_service.rb
+++ b/app/services/issues/create_service.rb
@@ -30,8 +30,8 @@ module Issues
Discussions::ResolveService.new(project, current_user,
merge_request: merge_request_to_resolve_discussions_of,
- follow_up_issue: issue).
- execute(discussions_to_resolve)
+ follow_up_issue: issue)
+ .execute(discussions_to_resolve)
end
private
diff --git a/app/services/labels/promote_service.rb b/app/services/labels/promote_service.rb
index 76d0ba67b07..43b539ded53 100644
--- a/app/services/labels/promote_service.rb
+++ b/app/services/labels/promote_service.rb
@@ -26,29 +26,29 @@ module Labels
private
def label_ids_for_merge(new_label)
- LabelsFinder.
- new(current_user, title: new_label.title, group_id: project.group.id).
- execute(skip_authorization: true).
- where.not(id: new_label).
- select(:id) # Can't use pluck() to avoid object-creation because of the batching
+ LabelsFinder
+ .new(current_user, title: new_label.title, group_id: project.group.id)
+ .execute(skip_authorization: true)
+ .where.not(id: new_label)
+ .select(:id) # Can't use pluck() to avoid object-creation because of the batching
end
def update_issuables(new_label, label_ids)
- LabelLink.
- where(label: label_ids).
- update_all(label_id: new_label)
+ LabelLink
+ .where(label: label_ids)
+ .update_all(label_id: new_label)
end
def update_issue_board_lists(new_label, label_ids)
- List.
- where(label: label_ids).
- update_all(label_id: new_label)
+ List
+ .where(label: label_ids)
+ .update_all(label_id: new_label)
end
def update_priorities(new_label, label_ids)
- LabelPriority.
- where(label: label_ids).
- update_all(label_id: new_label)
+ LabelPriority
+ .where(label: label_ids)
+ .update_all(label_id: new_label)
end
def update_project_labels(label_ids)
diff --git a/app/services/labels/transfer_service.rb b/app/services/labels/transfer_service.rb
index 514679ed29d..d2ece354efc 100644
--- a/app/services/labels/transfer_service.rb
+++ b/app/services/labels/transfer_service.rb
@@ -41,16 +41,16 @@ module Labels
end
def group_labels_applied_to_issues
- Label.joins(:issues).
- where(
+ Label.joins(:issues)
+ .where(
issues: { project_id: project.id },
labels: { type: 'GroupLabel', group_id: old_group.id }
)
end
def group_labels_applied_to_merge_requests
- Label.joins(:merge_requests).
- where(
+ Label.joins(:merge_requests)
+ .where(
merge_requests: { target_project_id: project.id },
labels: { type: 'GroupLabel', group_id: old_group.id }
)
@@ -64,15 +64,15 @@ module Labels
end
def update_label_links(labels, old_label_id:, new_label_id:)
- LabelLink.joins(:label).
- merge(labels).
- where(label_id: old_label_id).
- update_all(label_id: new_label_id)
+ LabelLink.joins(:label)
+ .merge(labels)
+ .where(label_id: old_label_id)
+ .update_all(label_id: new_label_id)
end
def update_label_priorities(old_label_id:, new_label_id:)
- LabelPriority.where(project_id: project.id, label_id: old_label_id).
- update_all(label_id: new_label_id)
+ LabelPriority.where(project_id: project.id, label_id: old_label_id)
+ .update_all(label_id: new_label_id)
end
end
end
diff --git a/app/services/members/authorized_destroy_service.rb b/app/services/members/authorized_destroy_service.rb
index f846d72498f..de3a252d6c6 100644
--- a/app/services/members/authorized_destroy_service.rb
+++ b/app/services/members/authorized_destroy_service.rb
@@ -26,30 +26,30 @@ module Members
def unassign_issues_and_merge_requests(member)
if member.is_a?(GroupMember)
- issues = Issue.unscoped.select(1).
- joins(:project).
- where('issues.id = issue_assignees.issue_id AND projects.namespace_id = ?', member.source_id)
+ issues = Issue.unscoped.select(1)
+ .joins(:project)
+ .where('issues.id = issue_assignees.issue_id AND projects.namespace_id = ?', member.source_id)
# DELETE FROM issue_assignees WHERE user_id = X AND EXISTS (...)
- IssueAssignee.unscoped.
- where('user_id = :user_id AND EXISTS (:sub)', user_id: member.user_id, sub: issues).
- delete_all
+ IssueAssignee.unscoped
+ .where('user_id = :user_id AND EXISTS (:sub)', user_id: member.user_id, sub: issues)
+ .delete_all
- MergeRequestsFinder.new(user, group_id: member.source_id, assignee_id: member.user_id).
- execute.
- update_all(assignee_id: nil)
+ MergeRequestsFinder.new(user, group_id: member.source_id, assignee_id: member.user_id)
+ .execute
+ .update_all(assignee_id: nil)
else
project = member.source
# SELECT 1 FROM issues WHERE issues.id = issue_assignees.issue_id AND issues.project_id = X
- issues = Issue.unscoped.select(1).
- where('issues.id = issue_assignees.issue_id').
- where(project_id: project.id)
+ issues = Issue.unscoped.select(1)
+ .where('issues.id = issue_assignees.issue_id')
+ .where(project_id: project.id)
# DELETE FROM issue_assignees WHERE user_id = X AND EXISTS (...)
- IssueAssignee.unscoped.
- where('user_id = :user_id AND EXISTS (:sub)', user_id: member.user_id, sub: issues).
- delete_all
+ IssueAssignee.unscoped
+ .where('user_id = :user_id AND EXISTS (:sub)', user_id: member.user_id, sub: issues)
+ .delete_all
project.merge_requests.opened.assigned_to(member.user).update_all(assignee_id: nil)
end
diff --git a/app/services/merge_requests/conflicts/resolve_service.rb b/app/services/merge_requests/conflicts/resolve_service.rb
index c2c335b8461..6b6e231f4f9 100644
--- a/app/services/merge_requests/conflicts/resolve_service.rb
+++ b/app/services/merge_requests/conflicts/resolve_service.rb
@@ -27,10 +27,10 @@ module MergeRequests
tree: merge_index.write_tree(rugged)
}
- conflicts_for_resolution.
- project.
- repository.
- resolve_conflicts(current_user, merge_request.source_branch, commit_params)
+ conflicts_for_resolution
+ .project
+ .repository
+ .resolve_conflicts(current_user, merge_request.source_branch, commit_params)
end
end
diff --git a/app/services/merge_requests/merge_service.rb b/app/services/merge_requests/merge_service.rb
index fac3ac7a4c7..b247cb89e5e 100644
--- a/app/services/merge_requests/merge_service.rb
+++ b/app/services/merge_requests/merge_service.rb
@@ -61,8 +61,8 @@ module MergeRequests
MergeRequests::PostMergeService.new(project, current_user).execute(merge_request)
if params[:should_remove_source_branch].present? || @merge_request.force_remove_source_branch?
- DeleteBranchService.new(@merge_request.source_project, branch_deletion_user).
- execute(merge_request.source_branch)
+ DeleteBranchService.new(@merge_request.source_project, branch_deletion_user)
+ .execute(merge_request.source_branch)
end
end
diff --git a/app/services/merge_requests/refresh_service.rb b/app/services/merge_requests/refresh_service.rb
index 81d217929d5..e0e7c43f802 100644
--- a/app/services/merge_requests/refresh_service.rb
+++ b/app/services/merge_requests/refresh_service.rb
@@ -43,9 +43,9 @@ module MergeRequests
end
filter_merge_requests(merge_requests).each do |merge_request|
- MergeRequests::PostMergeService.
- new(merge_request.target_project, @current_user).
- execute(merge_request)
+ MergeRequests::PostMergeService
+ .new(merge_request.target_project, @current_user)
+ .execute(merge_request)
end
end
@@ -56,8 +56,8 @@ module MergeRequests
# Refresh merge request diff if we push to source or target branch of merge request
# Note: we should update merge requests from forks too
def reload_merge_requests
- merge_requests = @project.merge_requests.opened.
- by_source_or_target_branch(@branch_name).to_a
+ merge_requests = @project.merge_requests.opened
+ .by_source_or_target_branch(@branch_name).to_a
# Fork merge requests
merge_requests += MergeRequest.opened
diff --git a/app/services/notes/quick_actions_service.rb b/app/services/notes/quick_actions_service.rb
index 8f81b54164a..a8d0cc15527 100644
--- a/app/services/notes/quick_actions_service.rb
+++ b/app/services/notes/quick_actions_service.rb
@@ -22,8 +22,8 @@ module Notes
def extract_commands(note, options = {})
return [note.note, {}] unless supported?(note)
- QuickActions::InterpretService.new(project, current_user, options).
- execute(note.note, note.noteable)
+ QuickActions::InterpretService.new(project, current_user, options)
+ .execute(note.note, note.noteable)
end
def execute(command_params, note)
diff --git a/app/services/tags/create_service.rb b/app/services/tags/create_service.rb
index 1756da9e519..674792f6138 100644
--- a/app/services/tags/create_service.rb
+++ b/app/services/tags/create_service.rb
@@ -19,8 +19,8 @@ module Tags
if new_tag
if release_description
- CreateReleaseService.new(@project, @current_user).
- execute(tag_name, release_description)
+ CreateReleaseService.new(@project, @current_user)
+ .execute(tag_name, release_description)
end
success.merge(tag: new_tag)
diff --git a/app/validators/dynamic_path_validator.rb b/app/validators/dynamic_path_validator.rb
index 27ac60637fd..4688aabc2a8 100644
--- a/app/validators/dynamic_path_validator.rb
+++ b/app/validators/dynamic_path_validator.rb
@@ -26,7 +26,7 @@ class DynamicPathValidator < ActiveModel::EachValidator
end
def path_valid_for_record?(record, value)
- full_path = record.respond_to?(:full_path) ? record.full_path : value
+ full_path = record.respond_to?(:build_full_path) ? record.build_full_path : value
return true unless full_path
diff --git a/app/views/groups/merge_requests.html.haml b/app/views/groups/merge_requests.html.haml
index 8fe0bd149f3..45e39252e16 100644
--- a/app/views/groups/merge_requests.html.haml
+++ b/app/views/groups/merge_requests.html.haml
@@ -18,5 +18,4 @@
- if current_user
To see all merge requests you should visit #{link_to 'dashboard', merge_requests_dashboard_path} page.
- .prepend-top-default
- = render 'shared/merge_requests'
+ = render 'shared/merge_requests'
diff --git a/app/views/profiles/show.html.haml b/app/views/profiles/show.html.haml
index 15672289c65..087ae778b0f 100644
--- a/app/views/profiles/show.html.haml
+++ b/app/views/profiles/show.html.haml
@@ -1,6 +1,6 @@
= render 'profiles/head'
-= form_for @user, url: profile_path, method: :put, html: { multipart: true, class: "edit-user prepend-top-default" }, authenticity_token: true do |f|
+= bootstrap_form_for @user, url: profile_path, method: :put, html: { multipart: true, class: 'edit-user prepend-top-default' }, authenticity_token: true do |f|
= form_errors(@user)
.row
@@ -11,11 +11,11 @@
- if @user.avatar?
You can change your avatar here
- if gravatar_enabled?
- or remove the current avatar to revert to #{link_to Gitlab.config.gravatar.host, "http://" + Gitlab.config.gravatar.host}
+ or remove the current avatar to revert to #{link_to Gitlab.config.gravatar.host, 'http://' + Gitlab.config.gravatar.host}
- else
You can upload an avatar here
- if gravatar_enabled?
- or change it at #{link_to Gitlab.config.gravatar.host, "http://" + Gitlab.config.gravatar.host}
+ or change it at #{link_to Gitlab.config.gravatar.host, 'http://' + Gitlab.config.gravatar.host}
.col-lg-9
.clearfix.avatar-image.append-bottom-default
= link_to avatar_icon(@user, 400), target: '_blank', rel: 'noopener noreferrer' do
@@ -26,12 +26,12 @@
%a.btn.js-choose-user-avatar-button
Browse file...
%span.avatar-file-name.prepend-left-default.js-avatar-filename No file chosen
- = f.file_field :avatar, class: "js-user-avatar-input hidden", accept: "image/*"
+ = f.file_field_without_bootstrap :avatar, class: 'js-user-avatar-input hidden', accept: 'image/*'
.help-block
The maximum file size allowed is 200KB.
- if @user.avatar?
%hr
- = link_to 'Remove avatar', profile_avatar_path, data: { confirm: "Avatar will be removed. Are you sure?" }, method: :delete, class: "btn btn-gray"
+ = link_to 'Remove avatar', profile_avatar_path, data: { confirm: 'Avatar will be removed. Are you sure?' }, method: :delete, class: 'btn btn-gray'
%hr
.row
.col-lg-3.profile-settings-sidebar
@@ -43,91 +43,50 @@
Some options are unavailable for LDAP accounts
.col-lg-9
.row
- .form-group.col-md-9
- = f.label :name, class: "label-light"
- = f.text_field :name, class: "form-control", required: true
- %span.help-block Enter your name, so people you know can recognize you.
+ = f.text_field :name, required: true, wrapper: { class: 'col-md-9' },
+ help: 'Enter your name, so people you know can recognize you.'
+ = f.text_field :id, readonly: true, label: 'User ID', wrapper: { class: 'col-md-3' }
- .form-group.col-md-3
- = f.label :id, class: 'label-light' do
- User ID
- = f.text_field :id, class: 'form-control', readonly: true
-
-
- .form-group
- = f.label :email, class: "label-light"
- - if @user.external_email?
- = f.text_field :email, class: "form-control", required: true, readonly: true
- %span.help-block.light
- Your email address was automatically set based on your #{email_provider_label} account.
- - else
- - if @user.temp_oauth_email?
- = f.text_field :email, class: "form-control", required: true, value: nil
- - else
- = f.text_field :email, class: "form-control", required: true
- - if @user.unconfirmed_email.present?
- %span.help-block
- Please click the link in the confirmation email before continuing. It was sent to
- = succeed "." do
- %strong= @user.unconfirmed_email
- %p
- = link_to "Resend confirmation e-mail", user_confirmation_path(user: { email: @user.unconfirmed_email }), method: :post
-
- - else
- %span.help-block We also use email for avatar detection if no avatar is uploaded.
- .form-group
- = f.label :public_email, class: "label-light"
- = f.select :public_email, options_for_select(@user.all_emails, selected: @user.public_email), { include_blank: 'Do not show on profile' }, class: "select2"
- %span.help-block This email will be displayed on your public profile.
- .form-group
- = f.label :preferred_language, class: "label-light"
- = f.select :preferred_language, Gitlab::I18n::AVAILABLE_LANGUAGES.map { |value, label| [label, value] },
- {}, class: "select2"
- %span.help-block This feature is experimental and translations are not complete yet.
- .form-group
- = f.label :skype, class: "label-light"
- = f.text_field :skype, class: "form-control"
- .form-group
- = f.label :linkedin, class: "label-light"
- = f.text_field :linkedin, class: "form-control"
- .form-group
- = f.label :twitter, class: "label-light"
- = f.text_field :twitter, class: "form-control"
- .form-group
- = f.label :website_url, 'Website', class: "label-light"
- = f.text_field :website_url, class: "form-control"
- .form-group
- = f.label :location, 'Location', class: "label-light"
- = f.text_field :location, class: "form-control"
- .form-group
- = f.label :organization, 'Organization', class: "label-light"
- = f.text_field :organization, class: "form-control"
- .form-group
- = f.label :bio, class: "label-light"
- = f.text_area :bio, rows: 4, class: "form-control", maxlength: 250
- %span.help-block Tell us about yourself in fewer than 250 characters.
+ - if @user.external_email?
+ = f.text_field :email, required: true, readonly: true, help: 'Your email address was automatically set based on your #{email_provider_label} account.'
+ - else
+ = f.text_field :email, required: true, value: (@user.email unless @user.temp_oauth_email?),
+ help: user_email_help_text(@user)
+ = f.select :public_email, options_for_select(@user.all_emails, selected: @user.public_email),
+ { help: 'This email will be displayed on your public profile.', include_blank: 'Do not show on profile' },
+ control_class: 'select2'
+ = f.select :preferred_language, Gitlab::I18n::AVAILABLE_LANGUAGES.map { |value, label| [label, value] },
+ { help: 'This feature is experimental and translations are not complete yet.' },
+ control_class: 'select2'
+ = f.text_field :skype
+ = f.text_field :linkedin
+ = f.text_field :twitter
+ = f.text_field :website_url, label: 'Website'
+ = f.text_field :location
+ = f.text_field :organization
+ = f.text_area :bio, rows: 4, maxlength: 250, help: 'Tell us about yourself in fewer than 250 characters.'
.prepend-top-default.append-bottom-default
- = f.submit 'Update profile settings', class: "btn btn-success"
- = link_to "Cancel", user_path(current_user), class: "btn btn-cancel"
+ = f.submit 'Update profile settings', class: 'btn btn-success'
+ = link_to 'Cancel', user_path(current_user), class: 'btn btn-cancel'
.modal.modal-profile-crop
.modal-dialog
.modal-content
.modal-header
- %button.close{ :type => "button", :'data-dismiss' => "modal" }
+ %button.close{ type: 'button', 'data-dismiss': 'modal' }
%span
&times;
%h4.modal-title
Position and size your new avatar
.modal-body
.profile-crop-image-container
- %img.modal-profile-crop-image{ alt: "Avatar cropper" }
+ %img.modal-profile-crop-image{ alt: 'Avatar cropper' }
.crop-controls
.btn-group
- %button.btn.btn-primary{ data: { method: "zoom", option: "0.1" } }
+ %button.btn.btn-primary{ data: { method: 'zoom', option: '0.1' } }
%span.fa.fa-search-plus
- %button.btn.btn-primary{ data: { method: "zoom", option: "-0.1" } }
+ %button.btn.btn-primary{ data: { method: 'zoom', option: '-0.1' } }
%span.fa.fa-search-minus
.modal-footer
- %button.btn.btn-primary.js-upload-user-avatar{ :type => "button" }
+ %button.btn.btn-primary.js-upload-user-avatar{ type: 'button' }
Set new profile picture
diff --git a/app/views/projects/_find_file_link.html.haml b/app/views/projects/_find_file_link.html.haml
index c748ccf65e6..cb4d2bbacf5 100644
--- a/app/views/projects/_find_file_link.html.haml
+++ b/app/views/projects/_find_file_link.html.haml
@@ -1,3 +1,3 @@
-= link_to namespace_project_find_file_path(@project.namespace, @project, @ref), class: 'btn btn-grouped shortcuts-find-file', rel: 'nofollow' do
+= link_to namespace_project_find_file_path(@project.namespace, @project, @ref), class: 'btn shortcuts-find-file', rel: 'nofollow' do
= icon('search')
%span= _('Find file')
diff --git a/app/views/projects/blob/_breadcrumb.html.haml b/app/views/projects/blob/_breadcrumb.html.haml
index 0ad9f258e48..5840e9863f4 100644
--- a/app/views/projects/blob/_breadcrumb.html.haml
+++ b/app/views/projects/blob/_breadcrumb.html.haml
@@ -1,9 +1,26 @@
- blame = local_assigns.fetch(:blame, false)
.nav-block
+ .tree-ref-container
+ .tree-ref-holder
+ = render 'shared/ref_switcher', destination: 'blob', path: @path
+
+ %ul.breadcrumb.repo-breadcrumb
+ %li
+ = link_to namespace_project_tree_path(@project.namespace, @project, @ref) do
+ = @project.path
+ - path_breadcrumbs do |title, path|
+ - title = truncate(title, length: 40)
+ %li
+ - if path == @path
+ = link_to namespace_project_blob_path(@project.namespace, @project, tree_join(@ref, path)) do
+ %strong= title
+ - else
+ = link_to title, namespace_project_tree_path(@project.namespace, @project, tree_join(@ref, path))
+
.tree-controls
= render 'projects/find_file_link'
- .btn-group.prepend-left-10{ role: "group" }<
+ .btn-group{ role: "group" }<
-# only show normal/blame view links for text files
- if blob.readable_text?
- if blame
@@ -18,19 +35,3 @@
= link_to 'Permalink', namespace_project_blob_path(@project.namespace, @project,
tree_join(@commit.sha, @path)), class: 'btn js-data-file-blob-permalink-url'
-
- .tree-ref-holder
- = render 'shared/ref_switcher', destination: 'blob', path: @path
-
- %ul.breadcrumb.repo-breadcrumb
- %li
- = link_to namespace_project_tree_path(@project.namespace, @project, @ref) do
- = @project.path
- - path_breadcrumbs do |title, path|
- - title = truncate(title, length: 40)
- %li
- - if path == @path
- = link_to namespace_project_blob_path(@project.namespace, @project, tree_join(@ref, path)) do
- %strong= title
- - else
- = link_to title, namespace_project_tree_path(@project.namespace, @project, tree_join(@ref, path))
diff --git a/app/views/projects/commits/_commits.html.haml b/app/views/projects/commits/_commits.html.haml
index d3380c917e4..93fd0789c11 100644
--- a/app/views/projects/commits/_commits.html.haml
+++ b/app/views/projects/commits/_commits.html.haml
@@ -3,8 +3,8 @@
- commits.chunk { |c| c.committed_date.in_time_zone.to_date }.each do |day, commits|
%li.commit-header.js-commit-header{ data: { day: day } }
- %span.day= day.strftime('%d %b, %Y')
- %span.commits-count= pluralize(commits.count, 'commit')
+ %span.day= l(day, format: '%d %b, %Y')
+ %span.commits-count= n_("%d commit", "%d commits", commits.count) % commits.count
%li.commits-row{ data: { day: day } }
%ul.content-list.commit-list
@@ -12,4 +12,4 @@
- if hidden > 0
%li.alert.alert-warning
- #{number_with_delimiter(hidden)} additional commits have been omitted to prevent performance issues.
+ = n_('%d additional commit has been omitted to prevent performance issues.', '%d additional commits have been omitted to prevent performance issues.', hidden) % number_with_delimiter(hidden)
diff --git a/app/views/projects/commits/show.html.haml b/app/views/projects/commits/show.html.haml
index c1c2fb3d299..fabd825aec8 100644
--- a/app/views/projects/commits/show.html.haml
+++ b/app/views/projects/commits/show.html.haml
@@ -1,6 +1,6 @@
- @no_container = true
-- page_title "Commits", @ref
+- page_title _("Commits"), @ref
= content_for :meta_tags do
= auto_discovery_link_tag(:atom, namespace_project_commits_url(@project.namespace, @project, @ref, rss_url_options), title: "#{@project.name}:#{@ref} commits")
@@ -18,16 +18,16 @@
.block-controls.hidden-xs.hidden-sm
- if @merge_request.present?
.control
- = link_to "View open merge request", namespace_project_merge_request_path(@project.namespace, @project, @merge_request), class: 'btn'
+ = link_to _("View open merge request"), namespace_project_merge_request_path(@project.namespace, @project, @merge_request), class: 'btn'
- elsif create_mr_button?(@repository.root_ref, @ref)
.control
- = link_to "Create merge request", create_mr_path(@repository.root_ref, @ref), class: 'btn btn-success'
+ = link_to _("Create merge request"), create_mr_path(@repository.root_ref, @ref), class: 'btn btn-success'
.control
= form_tag(namespace_project_commits_path(@project.namespace, @project, @id), method: :get, class: 'commits-search-form') do
- = search_field_tag :search, params[:search], { placeholder: 'Filter by commit message', id: 'commits-search', class: 'form-control search-text-input input-short', spellcheck: false }
+ = search_field_tag :search, params[:search], { placeholder: _('Filter by commit message'), id: 'commits-search', class: 'form-control search-text-input input-short', spellcheck: false }
.control
- = link_to namespace_project_commits_path(@project.namespace, @project, @ref, rss_url_options), title: "Commits feed", class: 'btn' do
+ = link_to namespace_project_commits_path(@project.namespace, @project, @ref, rss_url_options), title: _("Commits feed"), class: 'btn' do
= icon("rss")
%div{ id: dom_id(@project) }
diff --git a/app/views/projects/deploy_keys/_index.html.haml b/app/views/projects/deploy_keys/_index.html.haml
index 6e038ffd9c0..cb98ce04430 100644
--- a/app/views/projects/deploy_keys/_index.html.haml
+++ b/app/views/projects/deploy_keys/_index.html.haml
@@ -4,7 +4,7 @@
%h4
Deploy Keys
%button.btn.js-settings-toggle
- = expanded ? 'Close' : 'Expand'
+ = expanded ? 'Collapse' : 'Expand'
%p
Deploy keys allow read-only or read-write (if enabled) access to your repository. Deploy keys can be used for CI, staging or production servers. You can create a deploy key or add an existing one.
.settings-content.no-animate{ class: ('expanded' if expanded) }
diff --git a/app/views/projects/deployments/_deployment.html.haml b/app/views/projects/deployments/_deployment.html.haml
index 9b2ec9ae41c..26f3c297260 100644
--- a/app/views/projects/deployments/_deployment.html.haml
+++ b/app/views/projects/deployments/_deployment.html.haml
@@ -3,24 +3,25 @@
.table-mobile-header{ role: 'rowheader' } ID
%strong.table-mobile-content ##{deployment.iid}
- .table-section.section-40{ role: 'gridcell' }
+ .table-section.section-30{ role: 'gridcell' }
.table-mobile-header{ role: 'rowheader' } Commit
= render 'projects/deployments/commit', deployment: deployment
- .table-section.section-15.build-column{ role: 'gridcell' }
+ .table-section.section-25.build-column{ role: 'gridcell' }
.table-mobile-header{ role: 'rowheader' } Job
- if deployment.deployable
- = link_to [@project.namespace.becomes(Namespace), @project, deployment.deployable], class: 'build-link table-mobile-content' do
- #{deployment.deployable.name} (##{deployment.deployable.id})
- - if deployment.user
- by
- = user_avatar(user: deployment.user, size: 20)
+ .table-mobile-content
+ = link_to [@project.namespace.becomes(Namespace), @project, deployment.deployable], class: 'build-link' do
+ #{deployment.deployable.name} (##{deployment.deployable.id})
+ - if deployment.user
+ by
+ = user_avatar(user: deployment.user, size: 20)
.table-section.section-15{ role: 'gridcell' }
.table-mobile-header{ role: 'rowheader' } Created
%span.table-mobile-content= time_ago_with_tooltip(deployment.created_at)
.table-section.section-20.table-button-footer{ role: 'gridcell' }
- .btn-group.table-action-button
+ .btn-group.table-action-buttons
= render 'projects/deployments/actions', deployment: deployment
= render 'projects/deployments/rollback', deployment: deployment
diff --git a/app/views/projects/edit.html.haml b/app/views/projects/edit.html.haml
index 296e37e20e6..78057facde7 100644
--- a/app/views/projects/edit.html.haml
+++ b/app/views/projects/edit.html.haml
@@ -1,10 +1,12 @@
+- @content_class = "limit-container-width" unless fluid_layout
+
= render "projects/settings/head"
.project-edit-container
.row.prepend-top-default
- .col-lg-3.profile-settings-sidebar
+ .col-lg-4.profile-settings-sidebar
%h4.prepend-top-0
Project settings
- .col-lg-9
+ .col-lg-8
.project-edit-errors
= form_for [@project.namespace.becomes(Namespace), @project], remote: true, html: { multipart: true, class: "edit-project" }, authenticity_token: true do |f|
%fieldset
@@ -39,66 +41,66 @@
Sharing &amp; Permissions
.form_group.prepend-top-20.sharing-and-permissions
.row.js-visibility-select
- .col-md-9
+ .col-md-8
.label-light
= label_tag :project_visibility, 'Project Visibility', class: 'label-light', for: :project_visibility_level
= link_to icon('question-circle'), help_page_path("public_access/public_access")
%span.help-block
- .col-md-3.visibility-select-container
+ .col-md-4.visibility-select-container
= render('projects/visibility_select', model_method: :visibility_level, form: f, selected_level: @project.visibility_level)
= f.fields_for :project_feature do |feature_fields|
%fieldset.features
.row
- .col-md-9.project-feature
+ .col-md-8.project-feature
= feature_fields.label :repository_access_level, "Repository", class: 'label-light'
%span.help-block View and edit files in this project
- .col-md-3.js-repo-access-level
+ .col-md-4.js-repo-access-level
= project_feature_access_select(:repository_access_level)
.row
- .col-md-9.project-feature.nested
+ .col-md-8.project-feature.nested
= feature_fields.label :merge_requests_access_level, "Merge requests", class: 'label-light'
%span.help-block Submit changes to be merged upstream
- .col-md-3
+ .col-md-4
= project_feature_access_select(:merge_requests_access_level)
.row
- .col-md-9.project-feature.nested
+ .col-md-8.project-feature.nested
= feature_fields.label :builds_access_level, "Pipelines", class: 'label-light'
%span.help-block Build, test, and deploy your changes
- .col-md-3
+ .col-md-4
= project_feature_access_select(:builds_access_level)
.row
- .col-md-9.project-feature
+ .col-md-8.project-feature
= feature_fields.label :snippets_access_level, "Snippets", class: 'label-light'
%span.help-block Share code pastes with others out of Git repository
- .col-md-3
+ .col-md-4
= project_feature_access_select(:snippets_access_level)
.row
- .col-md-9.project-feature
+ .col-md-8.project-feature
= feature_fields.label :issues_access_level, "Issues", class: 'label-light'
%span.help-block Lightweight issue tracking system for this project
- .col-md-3
+ .col-md-4
= project_feature_access_select(:issues_access_level)
.row
- .col-md-9.project-feature
+ .col-md-8.project-feature
= feature_fields.label :wiki_access_level, "Wiki", class: 'label-light'
%span.help-block Pages for project documentation
- .col-md-3
+ .col-md-4
= project_feature_access_select(:wiki_access_level)
.form-group
= render 'shared/allow_request_access', form: f
- if Gitlab.config.lfs.enabled && current_user.admin?
.row.js-lfs-enabled
- .col-md-9
+ .col-md-8
= f.label :lfs_enabled, 'LFS', class: 'label-light'
%span.help-block
Git Large File Storage
= link_to icon('question-circle'), help_page_path('workflow/lfs/manage_large_binaries_with_git_lfs')
- .col-md-3
+ .col-md-4
.select-wrapper
= f.select :lfs_enabled, [%w(Enabled true), %w(Disabled false)], {}, selected: @project.lfs_enabled?, class: 'pull-right form-control project-repo-select select-control', data: { field: 'lfs_enabled' }
= icon('chevron-down')
@@ -138,19 +140,19 @@
.row.prepend-top-default
%hr
.row.prepend-top-default
- .col-lg-3
+ .col-lg-4
%h4.prepend-top-0
Housekeeping
%p.append-bottom-0
%p
Runs a number of housekeeping tasks within the current repository,
such as compressing file revisions and removing unreachable objects.
- .col-lg-9
+ .col-lg-8
= link_to 'Housekeeping', housekeeping_namespace_project_path(@project.namespace, @project),
method: :post, class: "btn btn-default"
%hr
.row.prepend-top-default
- .col-lg-3
+ .col-lg-4
%h4.prepend-top-0
Export project
%p.append-bottom-0
@@ -159,7 +161,7 @@
%p
Once the exported file is ready, you will receive a notification email with a download link.
- .col-lg-9
+ .col-lg-8
- if @project.export_project_path
= link_to 'Download export', download_export_namespace_project_path(@project.namespace, @project),
@@ -190,7 +192,7 @@
- if can? current_user, :archive_project, @project
%hr
.row.prepend-top-default
- .col-lg-3
+ .col-lg-4
%h4.warning-title.prepend-top-0
- if @project.archived?
Unarchive project
@@ -201,7 +203,7 @@
Unarchiving the project will mark its repository as active. The project can be committed to.
- else
Archiving the project will mark its repository as read-only. It is hidden from the dashboard and doesn't show up in searches.
- .col-lg-9
+ .col-lg-8
- if @project.archived?
%p
%strong Once active this project shows up in the search and on the dashboard.
@@ -216,10 +218,10 @@
method: :post, class: "btn btn-warning"
%hr
.row.prepend-top-default
- .col-lg-3
+ .col-lg-4
%h4.prepend-top-0.warning-title
Rename repository
- .col-lg-9
+ .col-lg-8
= render 'projects/errors'
= form_for([@project.namespace.becomes(Namespace), @project]) do |f|
.form-group.project_name_holder
@@ -244,12 +246,12 @@
- if can?(current_user, :change_namespace, @project)
%hr
.row.prepend-top-default
- .col-lg-3
+ .col-lg-4
%h4.prepend-top-0.danger-title
Transfer project to new group
%p.append-bottom-0
Please select the group you want to transfer this project to in the dropdown to the right.
- .col-lg-9
+ .col-lg-8
= form_for([@project.namespace.becomes(Namespace), @project], url: transfer_namespace_project_path(@project.namespace, @project), method: :put, remote: true, html: { class: 'js-project-transfer-form' } ) do |f|
.form-group
= label_tag :new_namespace_id, nil, class: 'label-light' do
@@ -265,7 +267,7 @@
- if @project.forked? && can?(current_user, :remove_fork_project, @project)
%hr
.row.prepend-top-default.append-bottom-default
- .col-lg-3
+ .col-lg-4
%h4.prepend-top-0.danger-title
Remove fork relationship
%p.append-bottom-0
@@ -273,7 +275,7 @@
This will remove the fork relationship to source project
= succeed "." do
= link_to @project.forked_from_project.name_with_namespace, project_path(@project.forked_from_project)
- .col-lg-9
+ .col-lg-8
= form_for([@project.namespace.becomes(Namespace), @project], url: remove_fork_namespace_project_path(@project.namespace, @project), method: :delete, remote: true, html: { class: 'transfer-project' }) do |f|
%p
%strong Once removed, the fork relationship cannot be restored and you will no longer be able to send merge requests to the source.
@@ -281,12 +283,12 @@
- if can?(current_user, :remove_project, @project)
%hr
.row.prepend-top-default.append-bottom-default
- .col-lg-3
+ .col-lg-4
%h4.prepend-top-0.danger-title
Remove project
%p.append-bottom-0
Removing the project will delete its repository and all related resources including issues, merge requests etc.
- .col-lg-9
+ .col-lg-8
= form_tag(namespace_project_path(@project.namespace, @project), method: :delete) do
%p
%strong Removed projects cannot be restored!
diff --git a/app/views/projects/hooks/_index.html.haml b/app/views/projects/hooks/_index.html.haml
index 676b7c345bc..776681ea09a 100644
--- a/app/views/projects/hooks/_index.html.haml
+++ b/app/views/projects/hooks/_index.html.haml
@@ -1,12 +1,12 @@
.row.prepend-top-default
- .col-lg-3
+ .col-lg-4
%h4.prepend-top-0
= page_title
%p
#{link_to 'Webhooks', help_page_path('user/project/integrations/webhooks')} can be
used for binding events when something is happening within the project.
- .col-lg-9.append-bottom-default
+ .col-lg-8.append-bottom-default
= form_for @hook, as: :hook, url: polymorphic_path([@project.namespace.becomes(Namespace), @project, :hooks]) do |f|
= render partial: 'shared/web_hooks/form', locals: { form: f, hook: @hook }
= f.submit 'Add webhook', class: 'btn btn-create'
diff --git a/app/views/projects/pipelines_settings/_badge.html.haml b/app/views/projects/pipelines_settings/_badge.html.haml
index 43bbd735059..3de518c8b9a 100644
--- a/app/views/projects/pipelines_settings/_badge.html.haml
+++ b/app/views/projects/pipelines_settings/_badge.html.haml
@@ -1,8 +1,8 @@
%div{ class: badge.title.gsub(' ', '-') }
- .col-lg-3.profile-settings-sidebar
+ .col-lg-4.profile-settings-sidebar
%h4.prepend-top-0
= badge.title.capitalize
- .col-lg-9
+ .col-lg-8
.prepend-top-10
.panel.panel-default
.panel-heading
diff --git a/app/views/projects/pipelines_settings/_show.html.haml b/app/views/projects/pipelines_settings/_show.html.haml
index 3b17daeb6da..580129ca809 100644
--- a/app/views/projects/pipelines_settings/_show.html.haml
+++ b/app/views/projects/pipelines_settings/_show.html.haml
@@ -1,8 +1,8 @@
.row.prepend-top-default
- .col-lg-3.profile-settings-sidebar
+ .col-lg-4.profile-settings-sidebar
%h4.prepend-top-0
Pipelines
- .col-lg-9
+ .col-lg-8
= form_for @project, url: namespace_project_pipelines_settings_path(@project.namespace.becomes(Namespace), @project) do |f|
%fieldset.builds-feature
- unless @repository.gitlab_ci_yml
diff --git a/app/views/projects/project_members/_index.html.haml b/app/views/projects/project_members/_index.html.haml
index cfae371e169..fa99610c0be 100644
--- a/app/views/projects/project_members/_index.html.haml
+++ b/app/views/projects/project_members/_index.html.haml
@@ -1,5 +1,5 @@
.row.prepend-top-default
- .col-lg-3.settings-sidebar
+ .col-lg-4.settings-sidebar
%h4.prepend-top-0
Project members
- if can?(current_user, :admin_project_member, @project)
@@ -13,7 +13,7 @@
%i Masters
or
%i Owners
- .col-lg-9
+ .col-lg-8
.light
- if can?(current_user, :admin_project_member, @project)
%ul.nav-links.project-member-tabs{ role: 'tablist' }
diff --git a/app/views/projects/protected_branches/_index.html.haml b/app/views/projects/protected_branches/_index.html.haml
index 9af67649741..5d2422bdf54 100644
--- a/app/views/projects/protected_branches/_index.html.haml
+++ b/app/views/projects/protected_branches/_index.html.haml
@@ -7,7 +7,7 @@
%h4
Protected Branches
%button.btn.js-settings-toggle
- = expanded ? 'Close' : 'Expand'
+ = expanded ? 'Collapse' : 'Expand'
%p
Keep stable branches secure and force developers to use merge requests.
.settings-content.no-animate{ class: ('expanded' if expanded) }
diff --git a/app/views/projects/protected_tags/_index.html.haml b/app/views/projects/protected_tags/_index.html.haml
index 976e1d7e93f..8250f692a69 100644
--- a/app/views/projects/protected_tags/_index.html.haml
+++ b/app/views/projects/protected_tags/_index.html.haml
@@ -7,7 +7,7 @@
%h4
Protected Tags
%button.btn.js-settings-toggle
- = expanded ? 'Close' : 'Expand'
+ = expanded ? 'Collapse' : 'Expand'
%p
Limit access to creating and updating tags.
.settings-content.no-animate{ class: ('expanded' if expanded) }
diff --git a/app/views/projects/services/_form.html.haml b/app/views/projects/services/_form.html.haml
index 9167789a69d..6dffc026392 100644
--- a/app/views/projects/services/_form.html.haml
+++ b/app/views/projects/services/_form.html.haml
@@ -23,3 +23,7 @@
- disabled_title = @service.disabled_title
= link_to 'Cancel', namespace_project_settings_integrations_path(@project.namespace, @project), class: 'btn btn-cancel'
+
+- if lookup_context.template_exists?('show', "projects/services/#{@service.to_param}", true)
+ %hr
+ = render "projects/services/#{@service.to_param}/show"
diff --git a/app/views/projects/services/_index.html.haml b/app/views/projects/services/_index.html.haml
index 86d5a0ec7b8..997b702da33 100644
--- a/app/views/projects/services/_index.html.haml
+++ b/app/views/projects/services/_index.html.haml
@@ -1,9 +1,9 @@
.row.prepend-top-default.append-bottom-default
- .col-lg-3
+ .col-lg-4
%h4.prepend-top-0
Project services
%p Project services allow you to integrate GitLab with other applications
- .col-lg-9
+ .col-lg-8
%table.table
%colgroup
%col
diff --git a/app/views/projects/services/prometheus/_show.html.haml b/app/views/projects/services/prometheus/_show.html.haml
new file mode 100644
index 00000000000..c4ac384ca1a
--- /dev/null
+++ b/app/views/projects/services/prometheus/_show.html.haml
@@ -0,0 +1,45 @@
+- content_for :page_specific_javascripts do
+ = webpack_bundle_tag('prometheus_metrics')
+
+.row.prepend-top-default.append-bottom-default.prometheus-metrics-monitoring.js-prometheus-metrics-monitoring
+ .col-lg-3
+ %h4.prepend-top-0
+ Metrics
+ %p
+ Metrics are automatically configured and monitored
+ based on a library of metrics from popular exporters.
+ = link_to 'More information', '#'
+
+ .col-lg-9
+ .panel.panel-default.js-panel-monitored-metrics{ data: { "active-metrics" => "#{namespace_project_prometheus_active_metrics_path(@project.namespace, @project, :json)}" } }
+ .panel-heading
+ %h3.panel-title
+ Monitored
+ %span.badge.js-monitored-count 0
+ .panel-body
+ .loading-metrics.text-center.js-loading-metrics
+ = icon('spinner spin 3x', class: 'metrics-load-spinner')
+ %p Finding and configuring metrics...
+ .empty-metrics.text-center.hidden.js-empty-metrics
+ = custom_icon('icon_empty_metrics')
+ %p No metrics are being monitored. To start monitoring, deploy to an environment.
+ = link_to project_environments_path(@project), title: 'View environments', class: 'btn btn-success' do
+ View environments
+ %ul.list-unstyled.metrics-list.hidden.js-metrics-list
+
+ .panel.panel-default.hidden.js-panel-missing-env-vars
+ .panel-heading
+ %h3.panel-title
+ = icon('caret-right lg fw', class: 'panel-toggle js-panel-toggle', 'aria-label' => 'Toggle panel')
+ Missing environment variable
+ %span.badge.js-env-var-count 0
+ .panel-body.hidden
+ .flash-container
+ .flash-notice
+ .flash-text
+ To set up automatic monitoring, add the environment variable
+ %code
+ $CI_ENVIRONMENT_SLUG
+ to exporter&rsquo;s queries.
+ = link_to 'More information', '#'
+ %ul.list-unstyled.metrics-list.js-missing-var-metrics-list
diff --git a/app/views/projects/settings/ci_cd/show.html.haml b/app/views/projects/settings/ci_cd/show.html.haml
index e8d2e91bd76..00ccc3ec41e 100644
--- a/app/views/projects/settings/ci_cd/show.html.haml
+++ b/app/views/projects/settings/ci_cd/show.html.haml
@@ -1,3 +1,4 @@
+- @content_class = "limit-container-width" unless fluid_layout
- page_title "Pipelines"
= render "projects/settings/head"
diff --git a/app/views/projects/settings/integrations/show.html.haml b/app/views/projects/settings/integrations/show.html.haml
index f69992566b5..1d1d0849289 100644
--- a/app/views/projects/settings/integrations/show.html.haml
+++ b/app/views/projects/settings/integrations/show.html.haml
@@ -1,3 +1,4 @@
+- @content_class = "limit-container-width" unless fluid_layout
- page_title 'Integrations'
= render "projects/settings/head"
= render 'projects/hooks/index'
diff --git a/app/views/projects/settings/members/show.html.haml b/app/views/projects/settings/members/show.html.haml
index 343807b87cd..1e7695ac397 100644
--- a/app/views/projects/settings/members/show.html.haml
+++ b/app/views/projects/settings/members/show.html.haml
@@ -1,3 +1,5 @@
+- @content_class = "limit-container-width" unless fluid_layout
+
- page_title "Members"
= render "projects/settings/head"
diff --git a/app/views/projects/tree/_tree_header.html.haml b/app/views/projects/tree/_tree_header.html.haml
index abde2a48587..00da76349da 100644
--- a/app/views/projects/tree/_tree_header.html.haml
+++ b/app/views/projects/tree/_tree_header.html.haml
@@ -1,79 +1,81 @@
-.tree-controls
- = render 'projects/find_file_link'
-
- = link_to s_('Commits|History'), namespace_project_commits_path(@project.namespace, @project, @id), class: 'btn btn-grouped'
-
- = render 'projects/buttons/download', project: @project, ref: @ref
+.tree-ref-container
+ .tree-ref-holder
+ = render 'shared/ref_switcher', destination: 'tree', path: @path
-.tree-ref-holder
- = render 'shared/ref_switcher', destination: 'tree', path: @path
-
-%ul.breadcrumb.repo-breadcrumb
- %li
- = link_to namespace_project_tree_path(@project.namespace, @project, @ref) do
- = @project.path
- - path_breadcrumbs do |title, path|
+ %ul.breadcrumb.repo-breadcrumb
%li
- = link_to truncate(title, length: 40), namespace_project_tree_path(@project.namespace, @project, tree_join(@ref, path))
+ = link_to namespace_project_tree_path(@project.namespace, @project, @ref) do
+ = @project.path
+ - path_breadcrumbs do |title, path|
+ %li
+ = link_to truncate(title, length: 40), namespace_project_tree_path(@project.namespace, @project, tree_join(@ref, path))
- - if current_user
- %li
- - if !on_top_of_branch?
- %span.btn.add-to-tree.disabled.has-tooltip{ title: _("You can only add files when you are on a branch"), data: { container: 'body' } }
- = icon('plus')
- - else
- %span.dropdown
- %a.dropdown-toggle.btn.add-to-tree{ href: '#', "data-toggle" => "dropdown" }
+ - if current_user
+ %li
+ - if !on_top_of_branch?
+ %span.btn.add-to-tree.disabled.has-tooltip{ title: _("You can only add files when you are on a branch"), data: { container: 'body' } }
= icon('plus')
- %ul.dropdown-menu
- - if can_edit_tree?
- %li
- = link_to namespace_project_new_blob_path(@project.namespace, @project, @id) do
- = icon('pencil fw')
- #{ _('New file') }
- %li
- = link_to '#modal-upload-blob', { 'data-target' => '#modal-upload-blob', 'data-toggle' => 'modal' } do
- = icon('file fw')
- #{ _('Upload file') }
- %li
- = link_to '#modal-create-new-dir', { 'data-target' => '#modal-create-new-dir', 'data-toggle' => 'modal' } do
- = icon('folder fw')
- #{ _('New directory') }
- - elsif can?(current_user, :fork_project, @project)
- %li
- - continue_params = { to: namespace_project_new_blob_path(@project.namespace, @project, @id),
- notice: edit_in_new_fork_notice,
- notice_now: edit_in_new_fork_notice_now }
- - fork_path = namespace_project_forks_path(@project.namespace, @project, namespace_key: current_user.namespace.id,
- continue: continue_params)
- = link_to fork_path, method: :post do
- = icon('pencil fw')
- #{ _('New file') }
+ - else
+ %span.dropdown
+ %a.dropdown-toggle.btn.add-to-tree{ href: '#', "data-toggle" => "dropdown", "data-target" => ".add-to-tree-dropdown" }
+ = icon('plus')
+ .add-to-tree-dropdown
+ %ul.dropdown-menu
+ - if can_edit_tree?
+ %li
+ = link_to namespace_project_new_blob_path(@project.namespace, @project, @id) do
+ = icon('pencil fw')
+ #{ _('New file') }
+ %li
+ = link_to '#modal-upload-blob', { 'data-target' => '#modal-upload-blob', 'data-toggle' => 'modal' } do
+ = icon('file fw')
+ #{ _('Upload file') }
+ %li
+ = link_to '#modal-create-new-dir', { 'data-target' => '#modal-create-new-dir', 'data-toggle' => 'modal' } do
+ = icon('folder fw')
+ #{ _('New directory') }
+ - elsif can?(current_user, :fork_project, @project)
+ %li
+ - continue_params = { to: namespace_project_new_blob_path(@project.namespace, @project, @id),
+ notice: edit_in_new_fork_notice,
+ notice_now: edit_in_new_fork_notice_now }
+ - fork_path = namespace_project_forks_path(@project.namespace, @project, namespace_key: current_user.namespace.id,
+ continue: continue_params)
+ = link_to fork_path, method: :post do
+ = icon('pencil fw')
+ #{ _('New file') }
+ %li
+ - continue_params = { to: request.fullpath,
+ notice: edit_in_new_fork_notice + " Try to upload a file again.",
+ notice_now: edit_in_new_fork_notice_now }
+ - fork_path = namespace_project_forks_path(@project.namespace, @project, namespace_key: current_user.namespace.id,
+ continue: continue_params)
+ = link_to fork_path, method: :post do
+ = icon('file fw')
+ #{ _('Upload file') }
+ %li
+ - continue_params = { to: request.fullpath,
+ notice: edit_in_new_fork_notice + " Try to create a new directory again.",
+ notice_now: edit_in_new_fork_notice_now }
+ - fork_path = namespace_project_forks_path(@project.namespace, @project, namespace_key: current_user.namespace.id,
+ continue: continue_params)
+ = link_to fork_path, method: :post do
+ = icon('folder fw')
+ #{ _('New directory') }
+
+ %li.divider
%li
- - continue_params = { to: request.fullpath,
- notice: edit_in_new_fork_notice + " Try to upload a file again.",
- notice_now: edit_in_new_fork_notice_now }
- - fork_path = namespace_project_forks_path(@project.namespace, @project, namespace_key: current_user.namespace.id,
- continue: continue_params)
- = link_to fork_path, method: :post do
- = icon('file fw')
- #{ _('Upload file') }
+ = link_to new_namespace_project_branch_path(@project.namespace, @project) do
+ = icon('code-fork fw')
+ #{ _('New branch') }
%li
- - continue_params = { to: request.fullpath,
- notice: edit_in_new_fork_notice + " Try to create a new directory again.",
- notice_now: edit_in_new_fork_notice_now }
- - fork_path = namespace_project_forks_path(@project.namespace, @project, namespace_key: current_user.namespace.id,
- continue: continue_params)
- = link_to fork_path, method: :post do
- = icon('folder fw')
- #{ _('New directory') }
+ = link_to new_namespace_project_tag_path(@project.namespace, @project) do
+ = icon('tags fw')
+ #{ _('New tag') }
+
+.tree-controls
+ = render 'projects/find_file_link'
- %li.divider
- %li
- = link_to new_namespace_project_branch_path(@project.namespace, @project) do
- = icon('code-fork fw')
- #{ _('New branch') }
- %li
- = link_to new_namespace_project_tag_path(@project.namespace, @project) do
- = icon('tags fw')
- #{ _('New tag') }
+ = link_to s_('Commits|History'), namespace_project_commits_path(@project.namespace, @project, @id), class: 'btn'
+
+ = render 'projects/buttons/download', project: @project, ref: @ref
diff --git a/app/views/projects/triggers/_index.html.haml b/app/views/projects/triggers/_index.html.haml
index cc74e50a5e3..e9a2f803edd 100644
--- a/app/views/projects/triggers/_index.html.haml
+++ b/app/views/projects/triggers/_index.html.haml
@@ -1,7 +1,7 @@
.row.prepend-top-default.append-bottom-default.triggers-container
- .col-lg-3
+ .col-lg-4
= render "projects/triggers/content"
- .col-lg-9
+ .col-lg-8
.panel.panel-default
.panel-heading
%h4.panel-title
diff --git a/app/views/projects/variables/_index.html.haml b/app/views/projects/variables/_index.html.haml
index 1b852a9c5b3..5e6786f6698 100644
--- a/app/views/projects/variables/_index.html.haml
+++ b/app/views/projects/variables/_index.html.haml
@@ -1,7 +1,7 @@
.row.prepend-top-default.append-bottom-default
- .col-lg-3
+ .col-lg-4
= render "projects/variables/content"
- .col-lg-9
+ .col-lg-8
%h5.prepend-top-0
Add a variable
= render "projects/variables/form", btn_text: "Add new variable"
diff --git a/app/views/shared/icons/_icon_empty_metrics.svg b/app/views/shared/icons/_icon_empty_metrics.svg
new file mode 100644
index 00000000000..24fa353f3ba
--- /dev/null
+++ b/app/views/shared/icons/_icon_empty_metrics.svg
@@ -0,0 +1,5 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="64" height="64" viewBox="0 0 64 64">
+ <g fill="#E5E5E5">
+ <path d="M32 64C30.8954305 64 30 63.1045695 30 62 30 60.8954305 30.8954305 60 32 60 33.8894444 60 35.7536611 59.8131396 37.574335 59.4454933 38.6570511 59.2268618 39.7120017 59.9273408 39.9306331 61.0100569 40.1492646 62.0927729 39.4487856 63.1477235 38.3660695 63.366355 36.285133 63.7865558 34.1557023 64 32 64zM49.2301062 58.9696428C51.0302775 57.8173242 52.7114504 56.4871355 54.247711 55.0008916 55.0415758 54.232873 55.0625283 52.9667164 54.2945097 52.1728516 53.5264912 51.3789869 52.2603346 51.3580344 51.4664698 52.1260529 50.1212672 53.4274592 48.6493395 54.5920875 47.0736141 55.6007347 46.1433158 56.1962335 45.8719072 57.4331365 46.4674061 58.3634348 47.0629049 59.2937331 48.2998079 59.5651416 49.2301062 58.9696428zM61.0426034 45.4531856C61.9412068 43.5163476 62.6441937 41.4911051 63.1388045 39.4034279 63.393449 38.3286117 62.7285685 37.2508708 61.6537523 36.9962262 60.5789361 36.7415816 59.5011952 37.4064621 59.2465506 38.4812784 58.8141946 40.3061875 58.1997219 42.0764286 57.4141077 43.7697311 56.9492346 44.7717126 57.3846469 45.9608331 58.3866284 46.4257062 59.3886098 46.8905793 60.5777303 46.455167 61.0426034 45.4531856zM63.7270657 27.8034151C63.4476841 25.6718707 62.9558906 23.5863203 62.2616468 21.5714028 61.9018246 20.527084 60.7635435 19.9721898 59.7192246 20.3320119 58.6749058 20.6918341 58.1200116 21.8301152 58.4798337 22.874434 59.0867105 24.6357842 59.5166381 26.45898 59.760988 28.3232492 59.9045362 29.4184513 60.9087418 30.1899192 62.0039439 30.046371 63.099146 29.9028228 63.8706139 28.8986173 63.7270657 27.8034151zM56.4699838 11.3781121C55.0919588 9.74451505 53.5537382 8.25140603 51.8798083 6.92273835 51.0146495 6.23602588 49.7566092 6.38068523 49.0698968 7.24584403 48.3831843 8.11100284 48.5278436 9.36904308 49.3930024 10.0557555 50.8587525 11.2191822 52.2058153 12.5267396 53.4125204 13.9572433 54.1247279 14.8015385 55.3865225 14.9086168 56.2308177 14.1964094 57.0751129 13.484202 57.1821912 12.2224073 56.4699838 11.3781121zM41.481294 1.42849704C39.4470333.798260231 37.3474846.371987025 35.2067823.158824109 34.1076485.0493765922 33.1278998.851675811 33.0184523 1.95080957 32.9090048 3.04994333 33.711304 4.02969203 34.8104377 4.13913955 36.6833634 4.32563829 38.5191483 4.69835932 40.297557 5.24933028 41.3526509 5.57621023 42.4729622 4.98587613 42.7998421 3.93078217 43.1267221 2.8756882 42.536388 1.75537699 41.481294 1.42849704zM23.6558195 1.0993008C21.5852929 1.6571259 19.5822296 2.42161363 17.6728876 3.37914679 16.6855233 3.874309 16.2865147 5.07613416 16.7816769 6.06349841 17.2768392 7.05086266 18.4786643 7.44987125 19.4660286 6.95470905 21.1354949 6.11747332 22.8864813 5.44919307 24.6963667 4.96158787 25.7629079 4.67424869 26.3945759 3.57671185 26.1072367 2.51017072 25.8198975 1.44362959 24.7223606.811961615 23.6558195 1.0993008zM8.36290105 10.4291871C6.92120358 12.00815 5.63985273 13.7275139 4.53998784 15.5610549 3.97179016 16.5082746 4.27904822 17.7367631 5.22626792 18.3049608 6.17348763 18.8731585 7.40197615 18.5659004 7.97017383 17.6186807 8.9327668 16.0139803 10.054503 14.5087932 11.3168098 13.126301 12.0615972 12.3106016 12.0041117 11.0455771 11.1884123 10.3007897 10.372713 9.55600224 9.10768848 9.61348772 8.36290105 10.4291871zM.450120287 26.6230259C.151304663 28.3883054 0 30.1850053 0 32 0 32.2974081.00406268322 32.594367.0121750297 32.8908218.0423897377 33.994978.96197903 34.8655796 2.0661352 34.8353649 3.17029137 34.8051502 4.04089294 33.8855609 4.01067824 32.7814047 4.00356366 32.521412 4 32.2609289 4 32 4 30.4089462 4.13249902 28.8355581 4.39401589 27.2906242 4.57836807 26.2015475 3.84494393 25.1692294 2.75586724 24.9848772 1.66679054 24.800525.634472466 25.5339492.450120287 26.6230259zM2.45830096 44.3202494C3.28286321 46.2952494 4.30407075 48.1806071 5.50459135 49.9494734 6.124886 50.8634254 7.36863868 51.1014818 8.28259072 50.4811871 9.19654276 49.8608925 9.43459912 48.6171398 8.81430448 47.7031878 7.76386025 46.1554464 6.87058107 44.5062706 6.14951581 42.7791677 5.72395784 41.7598668 4.55266835 41.2785432 3.53336751 41.7041011 2.51406668 42.1296591 2.03274299 43.3009486 2.45830096 44.3202494zM13.73374 58.2776222C15.4883094 59.4994144 17.3614388 60.5433005 19.3262717 61.39161 20.3403619 61.8294398 21.5173756 61.3622885 21.9552054 60.3481983 22.3930351 59.3341082 21.9258838 58.1570945 20.9117937 57.7192647 19.1934726 56.9773858 17.5548741 56.0642026 16.0195384 54.9950736 15.1130877 54.3638678 13.8665707 54.5869979 13.2353649 55.4934487 12.6041591 56.3998995 12.8272892 57.6464164 13.73374 58.2776222zM30.6955071 63.9738646C29.5918263 63.9295649 28.7330282 62.9989428 28.7773279 61.895262 28.8216276 60.7915812 29.7522497 59.9327832 30.8559305 59.9770829 31.2344492 59.9922759 31.6140624 59.9999282 31.9946308 59.9999995 33.0992003 60.0002065 33.994463 60.8958047 33.994256 62.0003742 33.9940491 63.1049437 33.0984508 64.0002064 31.9938814 63.9999994 31.5600677 63.9999181 31.1272192 63.9911927 30.6955071 63.9738646zM30.1721098 44.2840559C30.7941711 46.023825 33.2407935 46.0619159 33.9167124 44.3423547L38.9452693 31.5495297 41.1315797 35.2685507C41.4908522 35.8796908 42.1468005 36.2549751 42.8557214 36.2549751L51.1106965 36.2549751C52.215266 36.2549751 53.1106965 35.3595446 53.1106965 34.2549751 53.1106965 33.1504056 52.215266 32.2549751 51.1106965 32.2549751L43.9999712 32.2549751 40.3112064 25.9802055C39.465988 24.5424477 37.3358287 24.7099356 36.7257006 26.2621229L32.1439734 37.9181973 26.2115967 21.3266406C25.5807315 19.562249 23.0875908 19.5563214 22.4483429 21.3176933L18.4775633 32.2587065 13 32.2587065C11.8954305 32.2587065 11 33.154137 11 34.2587065 11 35.363276 11.8954305 36.2587065 13 36.2587065L19.8793532 36.2587065C20.720826 36.2587065 21.4722973 35.732004 21.7593685 34.9410132L24.314328 27.9011249 30.1721098 44.2840559z"/>
+ </g>
+</svg>
diff --git a/app/views/shared/issuable/_nav.html.haml b/app/views/shared/issuable/_nav.html.haml
index cf7ba52d840..3f03cc7a275 100644
--- a/app/views/shared/issuable/_nav.html.haml
+++ b/app/views/shared/issuable/_nav.html.haml
@@ -1,24 +1,25 @@
- type = local_assigns.fetch(:type, :issues)
- page_context_word = type.to_s.humanize(capitalize: false)
- issuables = @issues || @merge_requests
-- closed_title = 'Filter by issues that are currently closed.'
%ul.nav-links.issues-state-filters
%li{ class: active_when(params[:state] == 'opened') }>
- %button.btn.btn-link{ id: 'state-opened', title: "Filter by #{page_context_word} that are currently opened.", type: 'button', data: { state: 'opened' } }
+ = link_to page_filter_path(state: 'opened', label: true), id: 'state-opened', title: "Filter by #{page_context_word} that are currently opened.", data: { state: 'opened' } do
#{issuables_state_counter_text(type, :opened)}
- if type == :merge_requests
%li{ class: active_when(params[:state] == 'merged') }>
- %button.btn.btn-link{ id: 'state-merged', title: 'Filter by merge requests that are currently merged.', type: 'button', data: { state: 'merged' } }
+ = link_to page_filter_path(state: 'merged', label: true), id: 'state-merged', title: 'Filter by merge requests that are currently merged.', data: { state: 'merged' } do
#{issuables_state_counter_text(type, :merged)}
- - closed_title = 'Filter by merge requests that are currently closed and unmerged.'
-
- %li{ class: active_when(params[:state] == 'closed') }>
- %button.btn.btn-link{ id: 'state-closed', title: closed_title, type: 'button', data: { state: 'closed' } }
- #{issuables_state_counter_text(type, :closed)}
+ %li{ class: active_when(params[:state] == 'closed') }>
+ = link_to page_filter_path(state: 'closed', label: true), id: 'state-closed', title: 'Filter by merge requests that are currently closed and unmerged.', data: { state: 'closed' } do
+ #{issuables_state_counter_text(type, :closed)}
+ - else
+ %li{ class: active_when(params[:state] == 'closed') }>
+ = link_to page_filter_path(state: 'closed', label: true), id: 'state-closed', title: 'Filter by issues that are currently closed.', data: { state: 'closed' } do
+ #{issuables_state_counter_text(type, :closed)}
%li{ class: active_when(params[:state] == 'all') }>
- %button.btn.btn-link{ id: 'state-all', title: "Show all #{page_context_word}.", type: 'button', data: { state: 'all' } }
+ = link_to page_filter_path(state: 'all', label: true), id: 'state-all', title: "Show all #{page_context_word}.", data: { state: 'all' } do
#{issuables_state_counter_text(type, :all)}
diff --git a/app/views/shared/notes/_notes_with_form.html.haml b/app/views/shared/notes/_notes_with_form.html.haml
index 0cca8d875d2..f0fcc414756 100644
--- a/app/views/shared/notes/_notes_with_form.html.haml
+++ b/app/views/shared/notes/_notes_with_form.html.haml
@@ -6,13 +6,14 @@
- if can_create_note?
%ul.notes.notes-form.timeline
%li.timeline-entry
- .flash-container.timeline-content
+ .timeline-entry-inner
+ .flash-container.timeline-content
- .timeline-icon.hidden-xs.hidden-sm
- %a.author_link{ href: user_path(current_user) }
- = image_tag avatar_icon(current_user), alt: current_user.to_reference, class: 'avatar s40'
- .timeline-content.timeline-content-form
- = render "shared/notes/form", view: diff_view, supports_autocomplete: autocomplete
+ .timeline-icon.hidden-xs.hidden-sm
+ %a.author_link{ href: user_path(current_user) }
+ = image_tag avatar_icon(current_user), alt: current_user.to_reference, class: 'avatar s40'
+ .timeline-content.timeline-content-form
+ = render "shared/notes/form", view: diff_view, supports_autocomplete: autocomplete
- elsif !current_user
.disabled-comment.text-center.prepend-top-default
Please
diff --git a/app/workers/merge_worker.rb b/app/workers/merge_worker.rb
index 79efca4f2f9..48e2da338f6 100644
--- a/app/workers/merge_worker.rb
+++ b/app/workers/merge_worker.rb
@@ -7,7 +7,7 @@ class MergeWorker
current_user = User.find(current_user_id)
merge_request = MergeRequest.find(merge_request_id)
- MergeRequests::MergeService.new(merge_request.target_project, current_user, params).
- execute(merge_request)
+ MergeRequests::MergeService.new(merge_request.target_project, current_user, params)
+ .execute(merge_request)
end
end
diff --git a/app/workers/process_commit_worker.rb b/app/workers/process_commit_worker.rb
index fe6a49976e0..c0c03848a40 100644
--- a/app/workers/process_commit_worker.rb
+++ b/app/workers/process_commit_worker.rb
@@ -47,8 +47,8 @@ class ProcessCommitWorker
# therefor we use IssueCollection here and skip the authorization check in
# Issues::CloseService#execute.
IssueCollection.new(issues).updatable_by_user(user).each do |issue|
- Issues::CloseService.new(project, author).
- close_issue(issue, commit: commit)
+ Issues::CloseService.new(project, author)
+ .close_issue(issue, commit: commit)
end
end
@@ -57,8 +57,8 @@ class ProcessCommitWorker
return if mentioned_issues.empty?
- Issue::Metrics.where(issue_id: mentioned_issues.map(&:id), first_mentioned_in_commit_at: nil).
- update_all(first_mentioned_in_commit_at: commit.committed_date)
+ Issue::Metrics.where(issue_id: mentioned_issues.map(&:id), first_mentioned_in_commit_at: nil)
+ .update_all(first_mentioned_in_commit_at: commit.committed_date)
end
def build_commit(project, hash)
diff --git a/app/workers/project_cache_worker.rb b/app/workers/project_cache_worker.rb
index 8ff9d07860f..505ff9e086e 100644
--- a/app/workers/project_cache_worker.rb
+++ b/app/workers/project_cache_worker.rb
@@ -32,8 +32,8 @@ class ProjectCacheWorker
private
def try_obtain_lease_for(project_id, section)
- Gitlab::ExclusiveLease.
- new("project_cache_worker:#{project_id}:#{section}", timeout: LEASE_TIMEOUT).
- try_obtain
+ Gitlab::ExclusiveLease
+ .new("project_cache_worker:#{project_id}:#{section}", timeout: LEASE_TIMEOUT)
+ .try_obtain
end
end
diff --git a/app/workers/propagate_service_template_worker.rb b/app/workers/propagate_service_template_worker.rb
index 5ce0e0405d0..6b607451c7a 100644
--- a/app/workers/propagate_service_template_worker.rb
+++ b/app/workers/propagate_service_template_worker.rb
@@ -14,8 +14,8 @@ class PropagateServiceTemplateWorker
private
def try_obtain_lease_for(template_id)
- Gitlab::ExclusiveLease.
- new("propagate_service_template_worker:#{template_id}", timeout: LEASE_TIMEOUT).
- try_obtain
+ Gitlab::ExclusiveLease
+ .new("propagate_service_template_worker:#{template_id}", timeout: LEASE_TIMEOUT)
+ .try_obtain
end
end
diff --git a/app/workers/prune_old_events_worker.rb b/app/workers/prune_old_events_worker.rb
index 392abb9c21b..2b43bb19ad1 100644
--- a/app/workers/prune_old_events_worker.rb
+++ b/app/workers/prune_old_events_worker.rb
@@ -10,9 +10,9 @@ class PruneOldEventsWorker
'(id IN (SELECT id FROM (?) ids_to_remove))',
Event.unscoped.where(
'created_at < ?',
- (12.months + 1.day).ago).
- select(:id).
- limit(10_000)).
- delete_all
+ (12.months + 1.day).ago)
+ .select(:id)
+ .limit(10_000))
+ .delete_all
end
end
diff --git a/app/workers/repository_check/batch_worker.rb b/app/workers/repository_check/batch_worker.rb
index c3e7491ec4e..b94d83bd709 100644
--- a/app/workers/repository_check/batch_worker.rb
+++ b/app/workers/repository_check/batch_worker.rb
@@ -32,10 +32,10 @@ module RepositoryCheck
# has to sit and wait for this query to finish.
def project_ids
limit = 10_000
- never_checked_projects = Project.where('last_repository_check_at IS NULL AND created_at < ?', 24.hours.ago).
- limit(limit).pluck(:id)
- old_check_projects = Project.where('last_repository_check_at < ?', 1.month.ago).
- reorder('last_repository_check_at ASC').limit(limit).pluck(:id)
+ never_checked_projects = Project.where('last_repository_check_at IS NULL AND created_at < ?', 24.hours.ago)
+ .limit(limit).pluck(:id)
+ old_check_projects = Project.where('last_repository_check_at < ?', 1.month.ago)
+ .reorder('last_repository_check_at ASC').limit(limit).pluck(:id)
never_checked_projects + old_check_projects
end
diff --git a/app/workers/update_user_activity_worker.rb b/app/workers/update_user_activity_worker.rb
index b3c2f13aa33..31bbdb69edb 100644
--- a/app/workers/update_user_activity_worker.rb
+++ b/app/workers/update_user_activity_worker.rb
@@ -7,8 +7,8 @@ class UpdateUserActivityWorker
ids = pairs.keys
conditions = 'WHEN id = ? THEN ? ' * ids.length
- User.where(id: ids).
- update_all([
+ User.where(id: ids)
+ .update_all([
"last_activity_on = CASE #{conditions} ELSE last_activity_on END",
*pairs.to_a.flatten
])
diff --git a/changelogs/unreleased/25102-files-view-button.yml b/changelogs/unreleased/25102-files-view-button.yml
new file mode 100644
index 00000000000..4ba815d9464
--- /dev/null
+++ b/changelogs/unreleased/25102-files-view-button.yml
@@ -0,0 +1,4 @@
+---
+title: Fix mobile view of files view buttons
+merge_request:
+author:
diff --git a/changelogs/unreleased/28717-support-additional-prometheus-metrics.yml b/changelogs/unreleased/28717-support-additional-prometheus-metrics.yml
new file mode 100644
index 00000000000..720a79b8e1c
--- /dev/null
+++ b/changelogs/unreleased/28717-support-additional-prometheus-metrics.yml
@@ -0,0 +1,4 @@
+---
+title: Additional Prometheus metrics support
+merge_request: 11712
+author:
diff --git a/changelogs/unreleased/32301-filter-archive-project-on-param-present.yml b/changelogs/unreleased/32301-filter-archive-project-on-param-present.yml
new file mode 100644
index 00000000000..d6534ed4e1a
--- /dev/null
+++ b/changelogs/unreleased/32301-filter-archive-project-on-param-present.yml
@@ -0,0 +1,4 @@
+---
+title: Filter archived project in API v3 only if param present
+merge_request: 12245
+author: Ivan Chernov
diff --git a/changelogs/unreleased/33441-supplement_simplified_chinese_translation_of_i18n.yml b/changelogs/unreleased/33441-supplement_simplified_chinese_translation_of_i18n.yml
new file mode 100644
index 00000000000..a7d8ac9054b
--- /dev/null
+++ b/changelogs/unreleased/33441-supplement_simplified_chinese_translation_of_i18n.yml
@@ -0,0 +1,4 @@
+---
+title: Supplement Simplified Chinese translation of Project Page & Repository Page
+merge_request: 11994
+author: Huang Tao
diff --git a/changelogs/unreleased/33917-mr-comment-system-note-highlight-don-t-have-the-same-width.yml b/changelogs/unreleased/33917-mr-comment-system-note-highlight-don-t-have-the-same-width.yml
new file mode 100644
index 00000000000..f3b92878f6d
--- /dev/null
+++ b/changelogs/unreleased/33917-mr-comment-system-note-highlight-don-t-have-the-same-width.yml
@@ -0,0 +1,4 @@
+---
+title: Standardize timeline note margins across different viewport sizes
+merge_request: 12364
+author:
diff --git a/changelogs/unreleased/34008-fix-CI_ENVIRONMENT_URL-2.yml b/changelogs/unreleased/34008-fix-CI_ENVIRONMENT_URL-2.yml
new file mode 100644
index 00000000000..7f4d6e3bc67
--- /dev/null
+++ b/changelogs/unreleased/34008-fix-CI_ENVIRONMENT_URL-2.yml
@@ -0,0 +1,4 @@
+---
+title: Fix passing CI_ENVIRONMENT_NAME and CI_ENVIRONMENT_SLUG for CI_ENVIRONMENT_URL
+merge_request: 12344
+author:
diff --git a/changelogs/unreleased/34010-fix-linking-to-parallel-diff-line-number-creating-gray-box.yml b/changelogs/unreleased/34010-fix-linking-to-parallel-diff-line-number-creating-gray-box.yml
new file mode 100644
index 00000000000..705d44f8b10
--- /dev/null
+++ b/changelogs/unreleased/34010-fix-linking-to-parallel-diff-line-number-creating-gray-box.yml
@@ -0,0 +1,4 @@
+---
+title: Fix linking to line number on side-by-side diff creating empty discussion box
+merge_request:
+author:
diff --git a/changelogs/unreleased/dm-unnecessary-top-padding.yml b/changelogs/unreleased/dm-unnecessary-top-padding.yml
new file mode 100644
index 00000000000..4557c06f8e7
--- /dev/null
+++ b/changelogs/unreleased/dm-unnecessary-top-padding.yml
@@ -0,0 +1,4 @@
+---
+title: Remove unnecessary top padding on group MR index
+merge_request:
+author:
diff --git a/changelogs/unreleased/doc-gitaly-network.yml b/changelogs/unreleased/doc-gitaly-network.yml
new file mode 100644
index 00000000000..5376d8d5096
--- /dev/null
+++ b/changelogs/unreleased/doc-gitaly-network.yml
@@ -0,0 +1,4 @@
+---
+title: Add option to run Gitaly on a remote server
+merge_request: 12381
+author:
diff --git a/changelogs/unreleased/fix-33259.yml b/changelogs/unreleased/fix-33259.yml
new file mode 100644
index 00000000000..c68e42c02cf
--- /dev/null
+++ b/changelogs/unreleased/fix-33259.yml
@@ -0,0 +1,4 @@
+---
+title: Fix GitHub importer performance on branch existence check
+merge_request:
+author:
diff --git a/config/boot.rb b/config/boot.rb
index 16de55d7a86..2d01092acd5 100644
--- a/config/boot.rb
+++ b/config/boot.rb
@@ -9,15 +9,3 @@ require 'bundler/setup' if File.exist?(ENV['BUNDLE_GEMFILE'])
if ENV['RAILS_ENV'] == 'development' || ENV['RAILS_ENV'] == 'test'
ENV['prometheus_multiproc_dir'] ||= 'tmp/prometheus_multiproc_dir'
end
-
-# Default Bootsnap configuration from https://github.com/Shopify/bootsnap#usage
-require 'bootsnap'
-Bootsnap.setup(
- cache_dir: 'tmp/cache',
- development_mode: ENV['RAILS_ENV'] == 'development',
- load_path_cache: true,
- autoload_paths_cache: true,
- disable_trace: false,
- compile_cache_iseq: true,
- compile_cache_yaml: true
-)
diff --git a/config/initializers/8_metrics.rb b/config/initializers/8_metrics.rb
index 508b886d6a0..a0a63ddf8f0 100644
--- a/config/initializers/8_metrics.rb
+++ b/config/initializers/8_metrics.rb
@@ -154,8 +154,8 @@ if Gitlab::Metrics.enabled?
ActiveRecord::Querying.public_instance_methods(false).map(&:to_s)
)
- Gitlab::Metrics::Instrumentation.
- instrument_class_hierarchy(ActiveRecord::Base) do |klass, method|
+ Gitlab::Metrics::Instrumentation
+ .instrument_class_hierarchy(ActiveRecord::Base) do |klass, method|
# Instrumenting the ApplicationSetting class can lead to an infinite
# loop. Since the data is cached any way we don't really need to
# instrument it.
diff --git a/config/initializers/active_record_data_types.rb b/config/initializers/active_record_data_types.rb
index beb97c6fce0..fef591c397d 100644
--- a/config/initializers/active_record_data_types.rb
+++ b/config/initializers/active_record_data_types.rb
@@ -4,21 +4,78 @@
if Gitlab::Database.postgresql?
require 'active_record/connection_adapters/postgresql_adapter'
- module ActiveRecord
- module ConnectionAdapters
- class PostgreSQLAdapter
- NATIVE_DATABASE_TYPES.merge!(datetime_with_timezone: { name: 'timestamptz' })
+ module ActiveRecord::ConnectionAdapters::PostgreSQL::OID
+ # Add the class `DateTimeWithTimeZone` so we can map `timestamptz` to it.
+ class DateTimeWithTimeZone < DateTime
+ def type
+ :datetime_with_timezone
end
end
end
+
+ module RegisterDateTimeWithTimeZone
+ # Run original `initialize_type_map` and then register `timestamptz` as a
+ # `DateTimeWithTimeZone`.
+ #
+ # Apparently it does not matter that the original `initialize_type_map`
+ # aliases `timestamptz` to `timestamp`.
+ #
+ # When schema dumping, `timestamptz` columns will be output as
+ # `t.datetime_with_timezone`.
+ def initialize_type_map(mapping)
+ super mapping
+
+ mapping.register_type 'timestamptz' do |_, _, sql_type|
+ precision = extract_precision(sql_type)
+ ActiveRecord::ConnectionAdapters::PostgreSQLAdapter::OID::DateTimeWithTimeZone.new(precision: precision)
+ end
+ end
+ end
+
+ class ActiveRecord::ConnectionAdapters::PostgreSQLAdapter
+ prepend RegisterDateTimeWithTimeZone
+
+ # Add column type `datetime_with_timezone` so we can do this in
+ # migrations:
+ #
+ # add_column(:users, :datetime_with_timezone)
+ #
+ NATIVE_DATABASE_TYPES[:datetime_with_timezone] = { name: 'timestamptz' }
+ end
elsif Gitlab::Database.mysql?
require 'active_record/connection_adapters/mysql2_adapter'
- module ActiveRecord
- module ConnectionAdapters
- class AbstractMysqlAdapter
- NATIVE_DATABASE_TYPES.merge!(datetime_with_timezone: { name: 'timestamp' })
+ module RegisterDateTimeWithTimeZone
+ # Run original `initialize_type_map` and then register `timestamp` as a
+ # `MysqlDateTimeWithTimeZone`.
+ #
+ # When schema dumping, `timestamp` columns will be output as
+ # `t.datetime_with_timezone`.
+ def initialize_type_map(mapping)
+ super mapping
+
+ mapping.register_type(%r(timestamp)i) do |sql_type|
+ precision = extract_precision(sql_type)
+ ActiveRecord::ConnectionAdapters::AbstractMysqlAdapter::MysqlDateTimeWithTimeZone.new(precision: precision)
end
end
end
+
+ class ActiveRecord::ConnectionAdapters::AbstractMysqlAdapter
+ prepend RegisterDateTimeWithTimeZone
+
+ # Add the class `DateTimeWithTimeZone` so we can map `timestamp` to it.
+ class MysqlDateTimeWithTimeZone < MysqlDateTime
+ def type
+ :datetime_with_timezone
+ end
+ end
+
+ # Add column type `datetime_with_timezone` so we can do this in
+ # migrations:
+ #
+ # add_column(:users, :datetime_with_timezone)
+ #
+ NATIVE_DATABASE_TYPES[:datetime_with_timezone] = { name: 'timestamp' }
+ end
end
diff --git a/config/initializers/active_record_table_definition.rb b/config/initializers/active_record_table_definition.rb
index 4f59e35f4da..8e3a1c7a62f 100644
--- a/config/initializers/active_record_table_definition.rb
+++ b/config/initializers/active_record_table_definition.rb
@@ -3,15 +3,15 @@
require 'active_record/connection_adapters/abstract/schema_definitions'
-# Appends columns `created_at` and `updated_at` to a table.
-#
-# It is used in table creation like:
-# create_table 'users' do |t|
-# t.timestamps_with_timezone
-# end
module ActiveRecord
module ConnectionAdapters
class TableDefinition
+ # Appends columns `created_at` and `updated_at` to a table.
+ #
+ # It is used in table creation like:
+ # create_table 'users' do |t|
+ # t.timestamps_with_timezone
+ # end
def timestamps_with_timezone(**options)
options[:null] = false if options[:null].nil?
@@ -19,6 +19,16 @@ module ActiveRecord
column(column_name, :datetime_with_timezone, options)
end
end
+
+ # Adds specified column with appropriate timestamp type
+ #
+ # It is used in table creation like:
+ # create_table 'users' do |t|
+ # t.datetime_with_timezone :did_something_at
+ # end
+ def datetime_with_timezone(column_name, **options)
+ column(column_name, :datetime_with_timezone, options)
+ end
end
end
end
diff --git a/config/initializers/bootstrap_form.rb b/config/initializers/bootstrap_form.rb
new file mode 100644
index 00000000000..11171b38a85
--- /dev/null
+++ b/config/initializers/bootstrap_form.rb
@@ -0,0 +1,7 @@
+module BootstrapFormBuilderCustomization
+ def label_class
+ "label-light"
+ end
+end
+
+BootstrapForm::FormBuilder.prepend(BootstrapFormBuilderCustomization)
diff --git a/config/initializers/flipper.rb b/config/initializers/flipper.rb
new file mode 100644
index 00000000000..0fee832788d
--- /dev/null
+++ b/config/initializers/flipper.rb
@@ -0,0 +1,4 @@
+require 'flipper/middleware/memoizer'
+
+Rails.application.config.middleware.use Flipper::Middleware::Memoizer,
+ lambda { Feature.flipper }
diff --git a/config/prometheus/additional_metrics.yml b/config/prometheus/additional_metrics.yml
new file mode 100644
index 00000000000..daecde49570
--- /dev/null
+++ b/config/prometheus/additional_metrics.yml
@@ -0,0 +1,32 @@
+- group: Kubernetes
+ priority: 1
+ metrics:
+ - title: "Memory usage"
+ y_label: "Values"
+ required_metrics:
+ - container_memory_usage_bytes
+ weight: 1
+ queries:
+ - query_range: 'avg(container_memory_usage_bytes{%{environment_filter}}) / 2^20'
+ label: Container memory
+ unit: MiB
+ - title: "Current memory usage"
+ required_metrics:
+ - container_memory_usage_bytes
+ weight: 1
+ queries:
+ - query: 'avg(container_memory_usage_bytes{%{environment_filter}}) / 2^20'
+ display_empty: false
+ unit: MiB
+ - title: "CPU usage"
+ required_metrics:
+ - container_cpu_usage_seconds_total
+ weight: 1
+ queries:
+ - query_range: 'avg(rate(container_cpu_usage_seconds_total{%{environment_filter}}[2m])) * 100'
+ - title: "Current CPU usage"
+ required_metrics:
+ - container_cpu_usage_seconds_total
+ weight: 1
+ queries:
+ - query: 'avg(rate(container_cpu_usage_seconds_total{%{environment_filter}}[2m])) * 100'
diff --git a/config/routes/project.rb b/config/routes/project.rb
index f95cc3101d3..19e18c733b1 100644
--- a/config/routes/project.rb
+++ b/config/routes/project.rb
@@ -73,6 +73,10 @@ constraints(ProjectUrlConstrainer.new) do
resource :mattermost, only: [:new, :create]
+ namespace :prometheus do
+ get :active_metrics
+ end
+
resources :deploy_keys, constraints: { id: /\d+/ }, only: [:index, :new, :create, :edit, :update] do
member do
put :enable
@@ -153,6 +157,7 @@ constraints(ProjectUrlConstrainer.new) do
post :stop
get :terminal
get :metrics
+ get :additional_metrics
get '/terminal.ws/authorize', to: 'environments#terminal_websocket_authorize', constraints: { format: nil }
end
@@ -163,6 +168,7 @@ constraints(ProjectUrlConstrainer.new) do
resources :deployments, only: [:index] do
member do
get :metrics
+ get :additional_metrics
end
end
end
diff --git a/config/webpack.config.js b/config/webpack.config.js
index fb91ffef7e7..2e8c94655c1 100644
--- a/config/webpack.config.js
+++ b/config/webpack.config.js
@@ -55,6 +55,7 @@ var config = {
pipelines: './pipelines/pipelines_bundle.js',
pipelines_details: './pipelines/pipeline_details_bundle.js',
profile: './profile/profile_bundle.js',
+ prometheus_metrics: './prometheus_metrics',
protected_branches: './protected_branches/protected_branches_bundle.js',
protected_tags: './protected_tags',
sidebar: './sidebar/sidebar_bundle.js',
@@ -240,6 +241,7 @@ if (IS_DEV_SERVER) {
port: DEV_SERVER_PORT,
headers: { 'Access-Control-Allow-Origin': '*' },
stats: 'errors-only',
+ hot: DEV_SERVER_LIVERELOAD,
inline: DEV_SERVER_LIVERELOAD
};
config.output.publicPath = '//' + DEV_SERVER_HOST + ':' + DEV_SERVER_PORT + config.output.publicPath;
@@ -247,6 +249,9 @@ if (IS_DEV_SERVER) {
// watch node_modules for changes if we encounter a missing module compile error
new WatchMissingNodeModulesPlugin(path.join(ROOT_PATH, 'node_modules'))
);
+ if (DEV_SERVER_LIVERELOAD) {
+ config.plugins.push(new webpack.HotModuleReplacementPlugin());
+ }
}
if (WEBPACK_REPORT) {
diff --git a/db/migrate/20161124141322_migrate_process_commit_worker_jobs.rb b/db/migrate/20161124141322_migrate_process_commit_worker_jobs.rb
index e5292cfba07..c0cb9d78748 100644
--- a/db/migrate/20161124141322_migrate_process_commit_worker_jobs.rb
+++ b/db/migrate/20161124141322_migrate_process_commit_worker_jobs.rb
@@ -6,9 +6,9 @@ class MigrateProcessCommitWorkerJobs < ActiveRecord::Migration
class Project < ActiveRecord::Base
def self.find_including_path(id)
- select("projects.*, CONCAT(namespaces.path, '/', projects.path) AS path_with_namespace").
- joins('INNER JOIN namespaces ON namespaces.id = projects.namespace_id').
- find_by(id: id)
+ select("projects.*, CONCAT(namespaces.path, '/', projects.path) AS path_with_namespace")
+ .joins('INNER JOIN namespaces ON namespaces.id = projects.namespace_id')
+ .find_by(id: id)
end
def repository_storage_path
diff --git a/db/migrate/20161207231620_fixup_environment_name_uniqueness.rb b/db/migrate/20161207231620_fixup_environment_name_uniqueness.rb
index a20a903a752..f73e4f6c99b 100644
--- a/db/migrate/20161207231620_fixup_environment_name_uniqueness.rb
+++ b/db/migrate/20161207231620_fixup_environment_name_uniqueness.rb
@@ -8,11 +8,11 @@ class FixupEnvironmentNameUniqueness < ActiveRecord::Migration
environments = Arel::Table.new(:environments)
# Get all [project_id, name] pairs that occur more than once
- finder_sql = environments.
- group(environments[:project_id], environments[:name]).
- having(Arel.sql("COUNT(1)").gt(1)).
- project(environments[:project_id], environments[:name]).
- to_sql
+ finder_sql = environments
+ .group(environments[:project_id], environments[:name])
+ .having(Arel.sql("COUNT(1)").gt(1))
+ .project(environments[:project_id], environments[:name])
+ .to_sql
conflicting = connection.exec_query(finder_sql)
@@ -28,12 +28,12 @@ class FixupEnvironmentNameUniqueness < ActiveRecord::Migration
# Rename conflicting environments by appending "-#{id}" to all but the first
def fix_duplicates(project_id, name)
environments = Arel::Table.new(:environments)
- finder_sql = environments.
- where(environments[:project_id].eq(project_id)).
- where(environments[:name].eq(name)).
- order(environments[:id].asc).
- project(environments[:id], environments[:name]).
- to_sql
+ finder_sql = environments
+ .where(environments[:project_id].eq(project_id))
+ .where(environments[:name].eq(name))
+ .order(environments[:id].asc)
+ .project(environments[:id], environments[:name])
+ .to_sql
# Now we have the data for all the conflicting rows
conflicts = connection.exec_query(finder_sql).rows
@@ -41,11 +41,11 @@ class FixupEnvironmentNameUniqueness < ActiveRecord::Migration
conflicts.each do |id, name|
update_sql =
- Arel::UpdateManager.new(ActiveRecord::Base).
- table(environments).
- set(environments[:name] => name + "-" + id.to_s).
- where(environments[:id].eq(id)).
- to_sql
+ Arel::UpdateManager.new(ActiveRecord::Base)
+ .table(environments)
+ .set(environments[:name] => name + "-" + id.to_s)
+ .where(environments[:id].eq(id))
+ .to_sql
connection.exec_update(update_sql, self.class.name, [])
end
diff --git a/db/migrate/20161207231626_add_environment_slug.rb b/db/migrate/20161207231626_add_environment_slug.rb
index 8e98ee5b9ba..83cdd484c4c 100644
--- a/db/migrate/20161207231626_add_environment_slug.rb
+++ b/db/migrate/20161207231626_add_environment_slug.rb
@@ -19,10 +19,10 @@ class AddEnvironmentSlug < ActiveRecord::Migration
finder = environments.project(:id, :name)
connection.exec_query(finder.to_sql).rows.each do |id, name|
- updater = Arel::UpdateManager.new(ActiveRecord::Base).
- table(environments).
- set(environments[:slug] => generate_slug(name)).
- where(environments[:id].eq(id))
+ updater = Arel::UpdateManager.new(ActiveRecord::Base)
+ .table(environments)
+ .set(environments[:slug] => generate_slug(name))
+ .where(environments[:id].eq(id))
connection.exec_update(updater.to_sql, self.class.name, [])
end
diff --git a/db/migrate/20170316163800_rename_system_namespaces.rb b/db/migrate/20170316163800_rename_system_namespaces.rb
index b5408fbf112..9e9fb5ac225 100644
--- a/db/migrate/20170316163800_rename_system_namespaces.rb
+++ b/db/migrate/20170316163800_rename_system_namespaces.rb
@@ -159,9 +159,9 @@ class RenameSystemNamespaces < ActiveRecord::Migration
end
def system_namespace
- @system_namespace ||= Namespace.where(parent_id: nil).
- where(arel_table[:path].matches(system_namespace_path)).
- first
+ @system_namespace ||= Namespace.where(parent_id: nil)
+ .where(arel_table[:path].matches(system_namespace_path))
+ .first
end
def system_namespace_path
@@ -209,8 +209,8 @@ class RenameSystemNamespaces < ActiveRecord::Migration
end
def repo_paths_for_namespace(namespace)
- projects_for_namespace(namespace).distinct.
- select(:repository_storage).map(&:repository_storage_path)
+ projects_for_namespace(namespace).distinct
+ .select(:repository_storage).map(&:repository_storage_path)
end
def uploads_dir
diff --git a/db/migrate/20170503140202_turn_nested_groups_into_regular_groups_for_mysql.rb b/db/migrate/20170503140202_turn_nested_groups_into_regular_groups_for_mysql.rb
index c67690642c9..33908ae1156 100644
--- a/db/migrate/20170503140202_turn_nested_groups_into_regular_groups_for_mysql.rb
+++ b/db/migrate/20170503140202_turn_nested_groups_into_regular_groups_for_mysql.rb
@@ -87,8 +87,8 @@ class TurnNestedGroupsIntoRegularGroupsForMysql < ActiveRecord::Migration
while current&.parent_id
# We're using find_by(id: ...) here to deal with cases where the
# parent_id may point to a missing row.
- current = Namespace.unscoped.select([:id, :parent_id]).
- find_by(id: current.parent_id)
+ current = Namespace.unscoped.select([:id, :parent_id])
+ .find_by(id: current.parent_id)
ancestors << current.id if current
end
@@ -99,11 +99,11 @@ class TurnNestedGroupsIntoRegularGroupsForMysql < ActiveRecord::Migration
# Returns a relation containing all the members that have access to any of
# the current namespace's parent namespaces.
def all_members_for(namespace)
- Member.
- unscoped.
- select(['user_id', 'MAX(access_level) AS access_level']).
- where(source_type: 'Namespace', source_id: ancestors_for(namespace)).
- group(:user_id)
+ Member
+ .unscoped
+ .select(['user_id', 'MAX(access_level) AS access_level'])
+ .where(source_type: 'Namespace', source_id: ancestors_for(namespace))
+ .group(:user_id)
end
def bulk_insert_members(rows)
diff --git a/db/migrate/20170526185602_add_stage_id_to_ci_builds.rb b/db/migrate/20170526185602_add_stage_id_to_ci_builds.rb
index d5675d5828b..d27cba76d81 100644
--- a/db/migrate/20170526185602_add_stage_id_to_ci_builds.rb
+++ b/db/migrate/20170526185602_add_stage_id_to_ci_builds.rb
@@ -3,19 +3,11 @@ class AddStageIdToCiBuilds < ActiveRecord::Migration
DOWNTIME = false
- disable_ddl_transaction!
-
def up
add_column :ci_builds, :stage_id, :integer
-
- add_concurrent_foreign_key :ci_builds, :ci_stages, column: :stage_id, on_delete: :cascade
- add_concurrent_index :ci_builds, :stage_id
end
def down
- remove_foreign_key :ci_builds, column: :stage_id
- remove_concurrent_index :ci_builds, :stage_id
-
remove_column :ci_builds, :stage_id, :integer
end
end
diff --git a/db/post_migrate/20161109150329_fix_project_records_with_invalid_visibility.rb b/db/post_migrate/20161109150329_fix_project_records_with_invalid_visibility.rb
index 14b5ef476f0..69007b8e8ed 100644
--- a/db/post_migrate/20161109150329_fix_project_records_with_invalid_visibility.rb
+++ b/db/post_migrate/20161109150329_fix_project_records_with_invalid_visibility.rb
@@ -13,13 +13,13 @@ class FixProjectRecordsWithInvalidVisibility < ActiveRecord::Migration
namespaces = Arel::Table.new(:namespaces)
finder_sql =
- projects.
- join(namespaces, Arel::Nodes::InnerJoin).
- on(projects[:namespace_id].eq(namespaces[:id])).
- where(projects[:visibility_level].gt(namespaces[:visibility_level])).
- project(projects[:id], namespaces[:visibility_level]).
- take(BATCH_SIZE).
- to_sql
+ projects
+ .join(namespaces, Arel::Nodes::InnerJoin)
+ .on(projects[:namespace_id].eq(namespaces[:id]))
+ .where(projects[:visibility_level].gt(namespaces[:visibility_level]))
+ .project(projects[:id], namespaces[:visibility_level])
+ .take(BATCH_SIZE)
+ .to_sql
# Update matching rows in batches. Each batch can cause up to 3 UPDATE
# statements, in addition to the SELECT: one per visibility_level
@@ -33,10 +33,10 @@ class FixProjectRecordsWithInvalidVisibility < ActiveRecord::Migration
end
updates.each do |visibility_level, project_ids|
- updater = Arel::UpdateManager.new(ActiveRecord::Base).
- table(projects).
- set(projects[:visibility_level] => visibility_level).
- where(projects[:id].in(project_ids))
+ updater = Arel::UpdateManager.new(ActiveRecord::Base)
+ .table(projects)
+ .set(projects[:visibility_level] => visibility_level)
+ .where(projects[:id].in(project_ids))
ActiveRecord::Base.connection.exec_update(updater.to_sql, self.class.name, [])
end
diff --git a/db/post_migrate/20161221153951_rename_reserved_project_names.rb b/db/post_migrate/20161221153951_rename_reserved_project_names.rb
index 49a6bc884a8..d322844e2fd 100644
--- a/db/post_migrate/20161221153951_rename_reserved_project_names.rb
+++ b/db/post_migrate/20161221153951_rename_reserved_project_names.rb
@@ -79,17 +79,17 @@ class RenameReservedProjectNames < ActiveRecord::Migration
private
def reserved_projects
- Project.unscoped.
- includes(:namespace).
- where('EXISTS (SELECT 1 FROM namespaces WHERE projects.namespace_id = namespaces.id)').
- where('projects.path' => KNOWN_PATHS)
+ Project.unscoped
+ .includes(:namespace)
+ .where('EXISTS (SELECT 1 FROM namespaces WHERE projects.namespace_id = namespaces.id)')
+ .where('projects.path' => KNOWN_PATHS)
end
def route_exists?(full_path)
quoted_path = ActiveRecord::Base.connection.quote_string(full_path)
- ActiveRecord::Base.connection.
- select_all("SELECT id, path FROM routes WHERE path = '#{quoted_path}'").present?
+ ActiveRecord::Base.connection
+ .select_all("SELECT id, path FROM routes WHERE path = '#{quoted_path}'").present?
end
# Adds number to the end of the path that is not taken by other route
diff --git a/db/post_migrate/20170104150317_requeue_pending_delete_projects.rb b/db/post_migrate/20170104150317_requeue_pending_delete_projects.rb
index f399950bd5e..d7be004d47f 100644
--- a/db/post_migrate/20170104150317_requeue_pending_delete_projects.rb
+++ b/db/post_migrate/20170104150317_requeue_pending_delete_projects.rb
@@ -39,11 +39,11 @@ class RequeuePendingDeleteProjects < ActiveRecord::Migration
def find_batch
projects = Arel::Table.new(:projects)
- projects.project(projects[:id]).
- where(projects[:pending_delete].eq(true)).
- where(projects[:namespace_id].not_eq(nil)).
- skip(@offset * BATCH_SIZE).
- take(BATCH_SIZE).
- to_sql
+ projects.project(projects[:id])
+ .where(projects[:pending_delete].eq(true))
+ .where(projects[:namespace_id].not_eq(nil))
+ .skip(@offset * BATCH_SIZE)
+ .take(BATCH_SIZE)
+ .to_sql
end
end
diff --git a/db/post_migrate/20170106142508_fill_authorized_projects.rb b/db/post_migrate/20170106142508_fill_authorized_projects.rb
index 314c8440c8b..0ca20587981 100644
--- a/db/post_migrate/20170106142508_fill_authorized_projects.rb
+++ b/db/post_migrate/20170106142508_fill_authorized_projects.rb
@@ -15,8 +15,8 @@ class FillAuthorizedProjects < ActiveRecord::Migration
disable_ddl_transaction!
def up
- relation = User.select(:id).
- where('authorized_projects_populated IS NOT TRUE')
+ relation = User.select(:id)
+ .where('authorized_projects_populated IS NOT TRUE')
relation.find_in_batches(batch_size: 1_000) do |rows|
args = rows.map { |row| [row.id] }
diff --git a/db/post_migrate/20170313133418_rename_more_reserved_project_names.rb b/db/post_migrate/20170313133418_rename_more_reserved_project_names.rb
index 44c688fa134..6a49450cc50 100644
--- a/db/post_migrate/20170313133418_rename_more_reserved_project_names.rb
+++ b/db/post_migrate/20170313133418_rename_more_reserved_project_names.rb
@@ -21,17 +21,17 @@ class RenameMoreReservedProjectNames < ActiveRecord::Migration
private
def reserved_projects
- Project.unscoped.
- includes(:namespace).
- where('EXISTS (SELECT 1 FROM namespaces WHERE projects.namespace_id = namespaces.id)').
- where('projects.path' => KNOWN_PATHS)
+ Project.unscoped
+ .includes(:namespace)
+ .where('EXISTS (SELECT 1 FROM namespaces WHERE projects.namespace_id = namespaces.id)')
+ .where('projects.path' => KNOWN_PATHS)
end
def route_exists?(full_path)
quoted_path = ActiveRecord::Base.connection.quote_string(full_path)
- ActiveRecord::Base.connection.
- select_all("SELECT id, path FROM routes WHERE path = '#{quoted_path}'").present?
+ ActiveRecord::Base.connection
+ .select_all("SELECT id, path FROM routes WHERE path = '#{quoted_path}'").present?
end
# Adds number to the end of the path that is not taken by other route
diff --git a/db/post_migrate/20170324160416_migrate_user_activities_to_users_last_activity_on.rb b/db/post_migrate/20170324160416_migrate_user_activities_to_users_last_activity_on.rb
index 9ad36482c8a..397a9a2d28e 100644
--- a/db/post_migrate/20170324160416_migrate_user_activities_to_users_last_activity_on.rb
+++ b/db/post_migrate/20170324160416_migrate_user_activities_to_users_last_activity_on.rb
@@ -38,11 +38,11 @@ class MigrateUserActivitiesToUsersLastActivityOn < ActiveRecord::Migration
activities = activities(day.at_beginning_of_day, day.at_end_of_day, page: page)
update_sql =
- Arel::UpdateManager.new(ActiveRecord::Base).
- table(users_table).
- set(users_table[:last_activity_on] => day.to_date).
- where(users_table[:username].in(activities.map(&:first))).
- to_sql
+ Arel::UpdateManager.new(ActiveRecord::Base)
+ .table(users_table)
+ .set(users_table[:last_activity_on] => day.to_date)
+ .where(users_table[:username].in(activities.map(&:first)))
+ .to_sql
connection.exec_update(update_sql, self.class.name, [])
diff --git a/db/post_migrate/20170502070007_enable_auto_cancel_pending_pipelines_for_all.rb b/db/post_migrate/20170502070007_enable_auto_cancel_pending_pipelines_for_all.rb
index 3c13a3d2518..765daa0a347 100644
--- a/db/post_migrate/20170502070007_enable_auto_cancel_pending_pipelines_for_all.rb
+++ b/db/post_migrate/20170502070007_enable_auto_cancel_pending_pipelines_for_all.rb
@@ -7,6 +7,8 @@ class EnableAutoCancelPendingPipelinesForAll < ActiveRecord::Migration
DOWNTIME = false
def up
+ disable_statement_timeout
+
update_column_in_batches(:projects, :auto_cancel_pending_pipelines, 1)
end
diff --git a/db/post_migrate/20170502101023_cleanup_namespaceless_pending_delete_projects.rb b/db/post_migrate/20170502101023_cleanup_namespaceless_pending_delete_projects.rb
index ce52de91cdd..c1e64f20109 100644
--- a/db/post_migrate/20170502101023_cleanup_namespaceless_pending_delete_projects.rb
+++ b/db/post_migrate/20170502101023_cleanup_namespaceless_pending_delete_projects.rb
@@ -37,11 +37,11 @@ class CleanupNamespacelessPendingDeleteProjects < ActiveRecord::Migration
def find_batch
projects = Arel::Table.new(:projects)
- projects.project(projects[:id]).
- where(projects[:pending_delete].eq(true)).
- where(projects[:namespace_id].eq(nil)).
- skip(@offset * BATCH_SIZE).
- take(BATCH_SIZE).
- to_sql
+ projects.project(projects[:id])
+ .where(projects[:pending_delete].eq(true))
+ .where(projects[:namespace_id].eq(nil))
+ .skip(@offset * BATCH_SIZE)
+ .take(BATCH_SIZE)
+ .to_sql
end
end
diff --git a/db/post_migrate/20170508170547_add_head_pipeline_for_each_merge_request.rb b/db/post_migrate/20170508170547_add_head_pipeline_for_each_merge_request.rb
index bc3850c0c23..0a4a2d3867a 100644
--- a/db/post_migrate/20170508170547_add_head_pipeline_for_each_merge_request.rb
+++ b/db/post_migrate/20170508170547_add_head_pipeline_for_each_merge_request.rb
@@ -9,11 +9,11 @@ class AddHeadPipelineForEachMergeRequest < ActiveRecord::Migration
pipelines = Arel::Table.new(:ci_pipelines)
merge_requests = Arel::Table.new(:merge_requests)
- head_id = pipelines.
- project(Arel::Nodes::NamedFunction.new('max', [pipelines[:id]])).
- from(pipelines).
- where(pipelines[:ref].eq(merge_requests[:source_branch])).
- where(pipelines[:project_id].eq(merge_requests[:source_project_id]))
+ head_id = pipelines
+ .project(Arel::Nodes::NamedFunction.new('max', [pipelines[:id]]))
+ .from(pipelines)
+ .where(pipelines[:ref].eq(merge_requests[:source_branch]))
+ .where(pipelines[:project_id].eq(merge_requests[:source_project_id]))
sub_query = Arel::Nodes::SqlLiteral.new(Arel::Nodes::Grouping.new(head_id).to_sql)
diff --git a/db/post_migrate/20170526185901_remove_stage_id_index_from_builds.rb b/db/post_migrate/20170526185901_remove_stage_id_index_from_builds.rb
new file mode 100644
index 00000000000..3879cf9133b
--- /dev/null
+++ b/db/post_migrate/20170526185901_remove_stage_id_index_from_builds.rb
@@ -0,0 +1,18 @@
+class RemoveStageIdIndexFromBuilds < ActiveRecord::Migration
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+
+ disable_ddl_transaction!
+
+ def up
+ if index_exists?(:ci_builds, :stage_id)
+ remove_foreign_key(:ci_builds, column: :stage_id)
+ remove_concurrent_index(:ci_builds, :stage_id)
+ end
+ end
+
+ def down
+ # noop
+ end
+end
diff --git a/db/post_migrate/20170526185921_migrate_build_stage_reference.rb b/db/post_migrate/20170526185921_migrate_build_stage_reference.rb
index 797e106cae4..98c32d8284c 100644
--- a/db/post_migrate/20170526185921_migrate_build_stage_reference.rb
+++ b/db/post_migrate/20170526185921_migrate_build_stage_reference.rb
@@ -3,23 +3,17 @@ class MigrateBuildStageReference < ActiveRecord::Migration
DOWNTIME = false
- def up
- disable_statement_timeout
-
- stage_id = Arel.sql <<-SQL.strip_heredoc
- (SELECT id FROM ci_stages
- WHERE ci_stages.pipeline_id = ci_builds.commit_id
- AND ci_stages.name = ci_builds.stage)
- SQL
+ ##
+ # This is an empty migration, content has been moved to a new one:
+ # post migrate 20170526190000 MigrateBuildStageReferenceAgain
+ #
+ # See gitlab-org/gitlab-ce!12337 for more details.
- update_column_in_batches(:ci_builds, :stage_id, stage_id) do |table, query|
- query.where(table[:stage_id].eq(nil))
- end
+ def up
+ # noop
end
def down
- disable_statement_timeout
-
- update_column_in_batches(:ci_builds, :stage_id, nil)
+ # noop
end
end
diff --git a/db/post_migrate/20170526190000_migrate_build_stage_reference_again.rb b/db/post_migrate/20170526190000_migrate_build_stage_reference_again.rb
new file mode 100644
index 00000000000..97cb242415d
--- /dev/null
+++ b/db/post_migrate/20170526190000_migrate_build_stage_reference_again.rb
@@ -0,0 +1,27 @@
+class MigrateBuildStageReferenceAgain < ActiveRecord::Migration
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+
+ disable_ddl_transaction!
+
+ def up
+ disable_statement_timeout
+
+ stage_id = Arel.sql <<-SQL.strip_heredoc
+ (SELECT id FROM ci_stages
+ WHERE ci_stages.pipeline_id = ci_builds.commit_id
+ AND ci_stages.name = ci_builds.stage)
+ SQL
+
+ update_column_in_batches(:ci_builds, :stage_id, stage_id) do |table, query|
+ query.where(table[:stage_id].eq(nil))
+ end
+ end
+
+ def down
+ disable_statement_timeout
+
+ update_column_in_batches(:ci_builds, :stage_id, nil)
+ end
+end
diff --git a/db/post_migrate/20170621102400_add_stage_id_index_to_builds.rb b/db/post_migrate/20170621102400_add_stage_id_index_to_builds.rb
new file mode 100644
index 00000000000..7d6609b18bf
--- /dev/null
+++ b/db/post_migrate/20170621102400_add_stage_id_index_to_builds.rb
@@ -0,0 +1,21 @@
+class AddStageIdIndexToBuilds < ActiveRecord::Migration
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+
+ disable_ddl_transaction!
+
+ def up
+ unless index_exists?(:ci_builds, :stage_id)
+ add_concurrent_foreign_key(:ci_builds, :ci_stages, column: :stage_id, on_delete: :cascade)
+ add_concurrent_index(:ci_builds, :stage_id)
+ end
+ end
+
+ def down
+ if index_exists?(:ci_builds, :stage_id)
+ remove_foreign_key(:ci_builds, column: :stage_id)
+ remove_concurrent_index(:ci_builds, :stage_id)
+ end
+ end
+end
diff --git a/db/schema.rb b/db/schema.rb
index 16c6742b3bd..028556bdccf 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -11,7 +11,7 @@
#
# It's strongly recommended that you check this file into your version control system.
-ActiveRecord::Schema.define(version: 20170619144837) do
+ActiveRecord::Schema.define(version: 20170621102400) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
diff --git a/doc/administration/gitaly/index.md b/doc/administration/gitaly/index.md
index 48929910a9c..332457cb384 100644
--- a/doc/administration/gitaly/index.md
+++ b/doc/administration/gitaly/index.md
@@ -33,6 +33,145 @@ prometheus_listen_addr = "localhost:9236"
Changes to `/home/git/gitaly/config.toml` are applied when you run `service
gitlab restart`.
+## Running Gitaly on its own server
+
+> This is an optional way to deploy Gitaly which can benefit GitLab
+installations that are larger than a single machine. Most
+installations will be better served with the default configuration
+used by Omnibus and the GitLab source installation guide.
+
+Starting with GitLab 9.4 it is possible to run Gitaly on a different
+server from the rest of the application. This can improve performance
+when running GitLab with its repositories stored on an NFS server.
+
+At the moment (GitLab 9.4) Gitaly is not yet a replacement for NFS
+because some parts of GitLab still bypass Gitaly when accessing Git
+repositories. If you choose to deploy Gitaly on your NFS server you
+must still also mount your Git shares on your GitLab application
+servers.
+
+Gitaly network traffic is unencrypted so you should use a firewall to
+restrict access to your Gitaly server.
+
+Below we describe how to configure a Gitaly server at address
+`gitaly.internal:9999` with secret token `abc123secret`. We assume
+your GitLab installation has two repository storages, `default` and
+`storage1`.
+
+### Client side token configuration
+
+Start by configuring a token on the client side.
+
+Omnibus installations:
+
+```ruby
+# /etc/gitlab/gitlab.rb
+gitlab_rails['gitaly_token'] = 'abc123secret'
+```
+
+Source installations:
+
+```yaml
+# /home/git/gitlab/config/gitlab.yml
+gitlab:
+ gitaly:
+ token: 'abc123secret'
+```
+
+You need to reconfigure (Omnibus) or restart (source) for these
+changes to be picked up.
+
+### Gitaly server configuration
+
+Next, on the Gitaly server, we need to configure storage paths, enable
+the network listener and configure the token.
+
+Note: if you want to reduce the risk of downtime when you enable
+authentication you can temporarily disable enforcement, see [the
+documentation on configuring Gitaly
+authentication](https://gitlab.com/gitlab-org/gitaly/blob/master/doc/configuration/README.md#authentication)
+.
+
+In most or all cases the storage paths below end in `/repositories`. Check the
+directory layout on your Gitaly server to be sure.
+
+Omnibus installations:
+
+```ruby
+# /etc/gitlab/gitlab.rb
+gitaly['listen_addr'] = '0.0.0.0:9999'
+gitaly['auth_token'] = 'abc123secret'
+gitaly['storage'] = [
+ { 'name' => 'default', 'path' => '/path/to/default/repositories' },
+ { 'name' => 'storage1', 'path' => '/path/to/storage1/repositories' },
+]
+```
+
+Source installations:
+
+```toml
+# /home/git/gitaly/config.toml
+listen_addr = '0.0.0.0:9999'
+
+[auth]
+token = 'abc123secret'
+
+[[storage]
+name = 'default'
+path = '/path/to/default/repositories'
+
+[[storage]]
+name = 'storage1'
+path = '/path/to/storage1/repositories'
+```
+
+Again, reconfigure (Omnibus) or restart (source).
+
+### Converting clients to use the Gitaly server
+
+Now as the final step update the client machines to switch from using
+their local Gitaly service to the new Gitaly server you just
+configured. This is a risky step because if there is any sort of
+network, firewall, or name resolution problem preventing your GitLab
+server from reaching the Gitaly server then all Gitaly requests will
+fail.
+
+We assume that your Gitaly server can be reached at
+`gitaly.internal:9999` from your GitLab server, and that your GitLab
+NFS shares are mounted at `/mnt/gitlab/default` and
+`/mnt/gitlab/storage1` respectively.
+
+Omnibus installations:
+
+```ruby
+# /etc/gitlab/gitlab.rb
+git_data_dirs({
+ { 'default' => { 'path' => '/mnt/gitlab/default', 'gitaly_address' => 'tcp://gitlab.internal:9999' } },
+ { 'storage1' => { 'path' => '/mnt/gitlab/storage1', 'gitaly_address' => 'tcp://gitlab.internal:9999' } },
+})
+```
+
+Source installations:
+
+```yaml
+# /home/git/gitlab/config/gitlab.yml
+gitlab:
+ repositories:
+ storages:
+ default:
+ path: /mnt/gitlab/default/repositories
+ gitaly_address: tcp://gitlab.internal:9999
+ storage1:
+ path: /mnt/gitlab/storage1/repositories
+ gitaly_address: tcp://gitlab.internal:9999
+```
+
+Now reconfigure (Omnibus) or restart (source). When you tail the
+Gitaly logs on your Gitaly server (`sudo gitlab-ctl tail gitaly` or
+`tail -f /home/git/gitlab/log/gitaly.log`) you should see requests
+coming in. One sure way to trigger a Gitaly request is to clone a
+repository from your GitLab server over HTTP.
+
## Configuring GitLab to not use Gitaly
Gitaly is still an optional component in GitLab 9.3. This means you
diff --git a/doc/administration/high_availability/nfs.md b/doc/administration/high_availability/nfs.md
index d8e76d6ab94..bd6b7327aed 100644
--- a/doc/administration/high_availability/nfs.md
+++ b/doc/administration/high_availability/nfs.md
@@ -1,12 +1,35 @@
# NFS
-## Required NFS Server features
+You can view information and options set for each of the mounted NFS file
+systems by running `sudo nfsstat -m`.
+
+## NFS Server features
+
+### Required features
**File locking**: GitLab **requires** advisory file locking, which is only
supported natively in NFS version 4. NFSv3 also supports locking as long as
Linux Kernel 2.6.5+ is used. We recommend using version 4 and do not
specifically test NFSv3.
+### Recommended options
+
+When you define your NFS exports, we recommend you also add the following
+options:
+
+- `no_root_squash` - NFS normally changes the `root` user to `nobody`. This is
+ a good security measure when NFS shares will be accessed by many different
+ users. However, in this case only GitLab will use the NFS share so it
+ is safe. GitLab recommends the `no_root_squash` setting because we need to
+ manage file permissions automatically. Without the setting you may receive
+ errors when the Omnibus package tries to alter permissions. Note that GitLab
+ and other bundled components do **not** run as `root` but as non-privileged
+ users. The recommendation for `no_root_squash` is to allow the Omnibus package
+ to set ownership and permissions on files, as needed.
+- `sync` - Force synchronous behavior. Default is asynchronous and under certain
+ circumstances it could lead to data loss if a failure occurs before data has
+ synced.
+
## AWS Elastic File System
GitLab does not recommend using AWS Elastic File System (EFS).
@@ -26,27 +49,10 @@ GitLab does not recommend using EFS with GitLab.
For more details on another person's experience with EFS, see
[Amazon's Elastic File System: Burst Credits](https://www.rawkode.io/2017/04/amazons-elastic-file-system-burst-credits/)
-### Recommended options
-
-When you define your NFS exports, we recommend you also add the following
-options:
-
-- `no_root_squash` - NFS normally changes the `root` user to `nobody`. This is
- a good security measure when NFS shares will be accessed by many different
- users. However, in this case only GitLab will use the NFS share so it
- is safe. GitLab recommends the `no_root_squash` setting because we need to
- manage file permissions automatically. Without the setting you may receive
- errors when the Omnibus package tries to alter permissions. Note that GitLab
- and other bundled components do **not** run as `root` but as non-privileged
- users. The recommendation for `no_root_squash` is to allow the Omnibus package
- to set ownership and permissions on files, as needed.
-- `sync` - Force synchronous behavior. Default is asynchronous and under certain
- circumstances it could lead to data loss if a failure occurs before data has
- synced.
-
## NFS Client mount options
-Below is an example of an NFS mount point we use on GitLab.com:
+Below is an example of an NFS mount point defined in `/etc/fstab` we use on
+GitLab.com:
```
10.1.1.1:/var/opt/gitlab/git-data /var/opt/gitlab/git-data nfs4 defaults,soft,rsize=1048576,wsize=1048576,noatime,nobootwait,lookupcache=positive 0 2
diff --git a/doc/administration/high_availability/redis_source.md b/doc/administration/high_availability/redis_source.md
index 3629772b8af..fe982ea83c2 100644
--- a/doc/administration/high_availability/redis_source.md
+++ b/doc/administration/high_availability/redis_source.md
@@ -364,3 +364,4 @@ When in doubt, please read [Redis Sentinel documentation](http://redis.io/topics
[downloads]: https://about.gitlab.com/downloads
[restart]: ../restart_gitlab.md#installations-from-source
[it]: https://gitlab.com/gitlab-org/gitlab-ce/uploads/c4cc8cd353604bd80315f9384035ff9e/The_Internet_IT_Crowd.png
+[resque]: https://gitlab.com/gitlab-org/gitlab-ce/blob/master/config/resque.yml.example
diff --git a/doc/install/requirements.md b/doc/install/requirements.md
index e5aaccdeadf..643fe5b686b 100644
--- a/doc/install/requirements.md
+++ b/doc/install/requirements.md
@@ -94,11 +94,11 @@ installation (e.g. the number of users, projects, etc).
We currently support the following databases:
-- PostgreSQL
-- MySQL/MariaDB
+- PostgreSQL (highly recommended)
+- MySQL/MariaDB (doesn't support all features)
We **highly recommend** the use of PostgreSQL instead of MySQL/MariaDB as not all
-features of GitLab may work with MySQL/MariaDB:
+features of GitLab work with MySQL/MariaDB:
1. MySQL support for subgroups was [dropped with GitLab 9.3][post].
See [issue #30472][30472] for more information.
diff --git a/lib/api/access_requests.rb b/lib/api/access_requests.rb
index a5c9f0b509c..c9b5f58c557 100644
--- a/lib/api/access_requests.rb
+++ b/lib/api/access_requests.rb
@@ -68,8 +68,8 @@ module API
delete ":id/access_requests/:user_id" do
source = find_source(source_type, params[:id])
- ::Members::DestroyService.new(source, current_user, params).
- execute(:requesters)
+ ::Members::DestroyService.new(source, current_user, params)
+ .execute(:requesters)
end
end
end
diff --git a/lib/api/branches.rb b/lib/api/branches.rb
index f35084a582a..3d816f8771d 100644
--- a/lib/api/branches.rb
+++ b/lib/api/branches.rb
@@ -102,8 +102,8 @@ module API
post ":id/repository/branches" do
authorize_push_project
- result = CreateBranchService.new(user_project, current_user).
- execute(params[:branch], params[:ref])
+ result = CreateBranchService.new(user_project, current_user)
+ .execute(params[:branch], params[:ref])
if result[:status] == :success
present result[:branch],
@@ -121,8 +121,8 @@ module API
delete ":id/repository/branches/:branch", requirements: { branch: /.+/ } do
authorize_push_project
- result = DeleteBranchService.new(user_project, current_user).
- execute(params[:branch])
+ result = DeleteBranchService.new(user_project, current_user)
+ .execute(params[:branch])
if result[:status] != :success
render_api_error!(result[:message], result[:return_code])
diff --git a/lib/api/entities.rb b/lib/api/entities.rb
index 675bc52a983..aa91451c9f4 100644
--- a/lib/api/entities.rb
+++ b/lib/api/entities.rb
@@ -484,9 +484,9 @@ module API
expose :job_events
# Expose serialized properties
expose :properties do |service, options|
- field_names = service.fields.
- select { |field| options[:include_passwords] || field[:type] != 'password' }.
- map { |field| field[:name] }
+ field_names = service.fields
+ .select { |field| options[:include_passwords] || field[:type] != 'password' }
+ .map { |field| field[:name] }
service.properties.slice(*field_names)
end
end
diff --git a/lib/api/notes.rb b/lib/api/notes.rb
index e281e3230fd..01ca62b593f 100644
--- a/lib/api/notes.rb
+++ b/lib/api/notes.rb
@@ -33,8 +33,8 @@ module API
# paginate() only works with a relation. This could lead to a
# mismatch between the pagination headers info and the actual notes
# array returned, but this is really a edge-case.
- paginate(noteable.notes).
- reject { |n| n.cross_reference_not_visible_for?(current_user) }
+ paginate(noteable.notes)
+ .reject { |n| n.cross_reference_not_visible_for?(current_user) }
present notes, with: Entities::Note
else
not_found!("Notes")
diff --git a/lib/api/tags.rb b/lib/api/tags.rb
index c7b1efe0bfa..633a858f8c7 100644
--- a/lib/api/tags.rb
+++ b/lib/api/tags.rb
@@ -44,8 +44,8 @@ module API
post ':id/repository/tags' do
authorize_push_project
- result = ::Tags::CreateService.new(user_project, current_user).
- execute(params[:tag_name], params[:ref], params[:message], params[:release_description])
+ result = ::Tags::CreateService.new(user_project, current_user)
+ .execute(params[:tag_name], params[:ref], params[:message], params[:release_description])
if result[:status] == :success
present result[:tag],
@@ -63,8 +63,8 @@ module API
delete ":id/repository/tags/:tag_name", requirements: { tag_name: /.+/ } do
authorize_push_project
- result = ::Tags::DestroyService.new(user_project, current_user).
- execute(params[:tag_name])
+ result = ::Tags::DestroyService.new(user_project, current_user)
+ .execute(params[:tag_name])
if result[:status] != :success
render_api_error!(result[:message], result[:return_code])
@@ -81,8 +81,8 @@ module API
post ':id/repository/tags/:tag_name/release', requirements: { tag_name: /.+/ } do
authorize_push_project
- result = CreateReleaseService.new(user_project, current_user).
- execute(params[:tag_name], params[:description])
+ result = CreateReleaseService.new(user_project, current_user)
+ .execute(params[:tag_name], params[:description])
if result[:status] == :success
present result[:release], with: Entities::Release
@@ -101,8 +101,8 @@ module API
put ':id/repository/tags/:tag_name/release', requirements: { tag_name: /.+/ } do
authorize_push_project
- result = UpdateReleaseService.new(user_project, current_user).
- execute(params[:tag_name], params[:description])
+ result = UpdateReleaseService.new(user_project, current_user)
+ .execute(params[:tag_name], params[:description])
if result[:status] == :success
present result[:release], with: Entities::Release
diff --git a/lib/api/users.rb b/lib/api/users.rb
index bfb69d6dc18..c10e3364382 100644
--- a/lib/api/users.rb
+++ b/lib/api/users.rb
@@ -103,13 +103,13 @@ module API
if user.persisted?
present user, with: Entities::UserPublic
else
- conflict!('Email has already been taken') if User.
- where(email: user.email).
- count > 0
+ conflict!('Email has already been taken') if User
+ .where(email: user.email)
+ .count > 0
- conflict!('Username has already been taken') if User.
- where(username: user.username).
- count > 0
+ conflict!('Username has already been taken') if User
+ .where(username: user.username)
+ .count > 0
render_validation_error!(user)
end
@@ -133,12 +133,12 @@ module API
not_found!('User') unless user
conflict!('Email has already been taken') if params[:email] &&
- User.where(email: params[:email]).
- where.not(id: user.id).count > 0
+ User.where(email: params[:email])
+ .where.not(id: user.id).count > 0
conflict!('Username has already been taken') if params[:username] &&
- User.where(username: params[:username]).
- where.not(id: user.id).count > 0
+ User.where(username: params[:username])
+ .where.not(id: user.id).count > 0
user_params = declared_params(include_missing: false)
identity_attrs = user_params.slice(:provider, :extern_uid)
@@ -517,9 +517,9 @@ module API
get "activities" do
authenticated_as_admin!
- activities = User.
- where(User.arel_table[:last_activity_on].gteq(params[:from])).
- reorder(last_activity_on: :asc)
+ activities = User
+ .where(User.arel_table[:last_activity_on].gteq(params[:from]))
+ .reorder(last_activity_on: :asc)
present paginate(activities), with: Entities::UserActivity
end
diff --git a/lib/api/v3/branches.rb b/lib/api/v3/branches.rb
index 0a877b960f6..81b13249892 100644
--- a/lib/api/v3/branches.rb
+++ b/lib/api/v3/branches.rb
@@ -26,8 +26,8 @@ module API
delete ":id/repository/branches/:branch", requirements: { branch: /.+/ } do
authorize_push_project
- result = DeleteBranchService.new(user_project, current_user).
- execute(params[:branch])
+ result = DeleteBranchService.new(user_project, current_user)
+ .execute(params[:branch])
if result[:status] == :success
status(200)
@@ -55,8 +55,8 @@ module API
end
post ":id/repository/branches" do
authorize_push_project
- result = CreateBranchService.new(user_project, current_user).
- execute(params[:branch_name], params[:ref])
+ result = CreateBranchService.new(user_project, current_user)
+ .execute(params[:branch_name], params[:ref])
if result[:status] == :success
present result[:branch],
diff --git a/lib/api/v3/entities.rb b/lib/api/v3/entities.rb
index 7c5065dee90..c848f52723b 100644
--- a/lib/api/v3/entities.rb
+++ b/lib/api/v3/entities.rb
@@ -245,9 +245,9 @@ module API
expose :job_events, as: :build_events
# Expose serialized properties
expose :properties do |service, options|
- field_names = service.fields.
- select { |field| options[:include_passwords] || field[:type] != 'password' }.
- map { |field| field[:name] }
+ field_names = service.fields
+ .select { |field| options[:include_passwords] || field[:type] != 'password' }
+ .map { |field| field[:name] }
service.properties.slice(*field_names)
end
end
diff --git a/lib/api/v3/helpers.rb b/lib/api/v3/helpers.rb
index d9e76560d03..4e63aa01c1a 100644
--- a/lib/api/v3/helpers.rb
+++ b/lib/api/v3/helpers.rb
@@ -38,7 +38,10 @@ module API
projects = projects.where(visibility_level: Gitlab::VisibilityLevel.level_value(params[:visibility]))
end
- projects = projects.where(archived: params[:archived])
+ unless params[:archived].nil?
+ projects = projects.where(archived: to_boolean(params[:archived]))
+ end
+
projects.reorder(params[:order_by] => params[:sort])
end
end
diff --git a/lib/api/v3/notes.rb b/lib/api/v3/notes.rb
index 009ec5c6bbd..23fe95e42e4 100644
--- a/lib/api/v3/notes.rb
+++ b/lib/api/v3/notes.rb
@@ -34,8 +34,8 @@ module API
# paginate() only works with a relation. This could lead to a
# mismatch between the pagination headers info and the actual notes
# array returned, but this is really a edge-case.
- paginate(noteable.notes).
- reject { |n| n.cross_reference_not_visible_for?(current_user) }
+ paginate(noteable.notes)
+ .reject { |n| n.cross_reference_not_visible_for?(current_user) }
present notes, with: ::API::V3::Entities::Note
else
not_found!("Notes")
diff --git a/lib/api/v3/projects.rb b/lib/api/v3/projects.rb
index 20976b9dd08..eb090453b48 100644
--- a/lib/api/v3/projects.rb
+++ b/lib/api/v3/projects.rb
@@ -69,7 +69,7 @@ module API
end
params :filter_params do
- optional :archived, type: Boolean, default: false, desc: 'Limit by archived status'
+ optional :archived, type: Boolean, default: nil, desc: 'Limit by archived status'
optional :visibility, type: String, values: %w[public internal private],
desc: 'Limit by visibility'
optional :search, type: String, desc: 'Return list of authorized projects matching the search criteria'
diff --git a/lib/api/v3/tags.rb b/lib/api/v3/tags.rb
index c2541de2f50..7e5875cd030 100644
--- a/lib/api/v3/tags.rb
+++ b/lib/api/v3/tags.rb
@@ -22,8 +22,8 @@ module API
delete ":id/repository/tags/:tag_name", requirements: { tag_name: /.+/ } do
authorize_push_project
- result = ::Tags::DestroyService.new(user_project, current_user).
- execute(params[:tag_name])
+ result = ::Tags::DestroyService.new(user_project, current_user)
+ .execute(params[:tag_name])
if result[:status] == :success
status(200)
diff --git a/lib/api/v3/users.rb b/lib/api/v3/users.rb
index f4cda3b2eba..37020019e07 100644
--- a/lib/api/v3/users.rb
+++ b/lib/api/v3/users.rb
@@ -50,13 +50,13 @@ module API
if user.persisted?
present user, with: ::API::Entities::UserPublic
else
- conflict!('Email has already been taken') if User.
- where(email: user.email).
- count > 0
+ conflict!('Email has already been taken') if User
+ .where(email: user.email)
+ .count > 0
- conflict!('Username has already been taken') if User.
- where(username: user.username).
- count > 0
+ conflict!('Username has already been taken') if User
+ .where(username: user.username)
+ .count > 0
render_validation_error!(user)
end
@@ -137,11 +137,11 @@ module API
user = User.find_by(id: params[:id])
not_found!('User') unless user
- events = user.events.
- merge(ProjectsFinder.new(current_user: current_user).execute).
- references(:project).
- with_associations.
- recent
+ events = user.events
+ .merge(ProjectsFinder.new(current_user: current_user).execute)
+ .references(:project)
+ .with_associations
+ .recent
present paginate(events), with: ::API::V3::Entities::Event
end
diff --git a/lib/banzai/reference_extractor.rb b/lib/banzai/reference_extractor.rb
index 8e3b0c4db79..7e6357f8a00 100644
--- a/lib/banzai/reference_extractor.rb
+++ b/lib/banzai/reference_extractor.rb
@@ -10,8 +10,8 @@ module Banzai
end
def references(type, project, current_user = nil)
- processor = Banzai::ReferenceParser[type].
- new(project, current_user)
+ processor = Banzai::ReferenceParser[type]
+ .new(project, current_user)
processor.process(html_documents)
end
diff --git a/lib/banzai/reference_parser/issue_parser.rb b/lib/banzai/reference_parser/issue_parser.rb
index 89ec715ddf6..9fd4bd68d43 100644
--- a/lib/banzai/reference_parser/issue_parser.rb
+++ b/lib/banzai/reference_parser/issue_parser.rb
@@ -9,8 +9,8 @@ module Banzai
issues = issues_for_nodes(nodes)
- readable_issues = Ability.
- issues_readable_by_user(issues.values, user).to_set
+ readable_issues = Ability
+ .issues_readable_by_user(issues.values, user).to_set
nodes.select do |node|
readable_issues.include?(issues[node])
diff --git a/lib/banzai/reference_parser/user_parser.rb b/lib/banzai/reference_parser/user_parser.rb
index 3efbd2fd631..4d336068861 100644
--- a/lib/banzai/reference_parser/user_parser.rb
+++ b/lib/banzai/reference_parser/user_parser.rb
@@ -99,8 +99,8 @@ module Banzai
def find_users_for_projects(ids)
return [] if ids.empty?
- collection_objects_for_ids(Project, ids).
- flat_map { |p| p.team.members.to_a }
+ collection_objects_for_ids(Project, ids)
+ .flat_map { |p| p.team.members.to_a }
end
def can_read_reference?(user, ref_project, node)
diff --git a/lib/ci/charts.rb b/lib/ci/charts.rb
index 3decc3b1a26..6063d6f45e8 100644
--- a/lib/ci/charts.rb
+++ b/lib/ci/charts.rb
@@ -2,10 +2,10 @@ module Ci
module Charts
module DailyInterval
def grouped_count(query)
- query.
- group("DATE(#{Ci::Build.table_name}.created_at)").
- count(:created_at).
- transform_keys { |date| date.strftime(@format) }
+ query
+ .group("DATE(#{Ci::Build.table_name}.created_at)")
+ .count(:created_at)
+ .transform_keys { |date| date.strftime(@format) }
end
def interval_step
@@ -16,14 +16,14 @@ module Ci
module MonthlyInterval
def grouped_count(query)
if Gitlab::Database.postgresql?
- query.
- group("to_char(#{Ci::Build.table_name}.created_at, '01 Month YYYY')").
- count(:created_at).
- transform_keys(&:squish)
+ query
+ .group("to_char(#{Ci::Build.table_name}.created_at, '01 Month YYYY')")
+ .count(:created_at)
+ .transform_keys(&:squish)
else
- query.
- group("DATE_FORMAT(#{Ci::Build.table_name}.created_at, '01 %M %Y')").
- count(:created_at)
+ query
+ .group("DATE_FORMAT(#{Ci::Build.table_name}.created_at, '01 %M %Y')")
+ .count(:created_at)
end
end
@@ -46,8 +46,8 @@ module Ci
end
def collect
- query = project.builds.
- where("? > #{Ci::Build.table_name}.created_at AND #{Ci::Build.table_name}.created_at > ?", @to, @from)
+ query = project.builds
+ .where("? > #{Ci::Build.table_name}.created_at AND #{Ci::Build.table_name}.created_at > ?", @to, @from)
totals_count = grouped_count(query)
success_count = grouped_count(query.success)
diff --git a/lib/feature.rb b/lib/feature.rb
index 5650a1c1334..d3d972564af 100644
--- a/lib/feature.rb
+++ b/lib/feature.rb
@@ -39,8 +39,6 @@ class Feature
get(key).disable
end
- private
-
def flipper
@flipper ||= begin
adapter = Flipper::Adapters::ActiveRecord.new(
diff --git a/lib/github/import.rb b/lib/github/import.rb
index b20614b3060..ff5d7db2705 100644
--- a/lib/github/import.rb
+++ b/lib/github/import.rb
@@ -172,7 +172,7 @@ module Github
next unless merge_request.new_record? && pull_request.valid?
begin
- restore_branches(pull_request)
+ pull_request.restore_branches!
author_id = user_id(pull_request.author, project.creator_id)
description = format_description(pull_request.description, pull_request.author)
@@ -208,7 +208,7 @@ module Github
rescue => e
error(:pull_request, pull_request.url, e.message)
ensure
- clean_up_restored_branches(pull_request)
+ pull_request.remove_restored_branches!
end
end
@@ -325,32 +325,6 @@ module Github
end
end
- def restore_branches(pull_request)
- restore_source_branch(pull_request) unless pull_request.source_branch_exists?
- restore_target_branch(pull_request) unless pull_request.target_branch_exists?
- end
-
- def restore_source_branch(pull_request)
- repository.create_branch(pull_request.source_branch_name, pull_request.source_branch_sha)
- end
-
- def restore_target_branch(pull_request)
- repository.create_branch(pull_request.target_branch_name, pull_request.target_branch_sha)
- end
-
- def remove_branch(name)
- repository.delete_branch(name)
- rescue Rugged::ReferenceError
- errors << { type: :branch, url: nil, error: "Could not clean up restored branch: #{name}" }
- end
-
- def clean_up_restored_branches(pull_request)
- return if pull_request.opened?
-
- remove_branch(pull_request.source_branch_name) unless pull_request.source_branch_exists?
- remove_branch(pull_request.target_branch_name) unless pull_request.target_branch_exists?
- end
-
def label_ids(labels)
labels.map { |attrs| cached[:label_ids][attrs.fetch('name')] }.compact
end
diff --git a/lib/github/representation/branch.rb b/lib/github/representation/branch.rb
index d1dac6944f0..c6fa928d565 100644
--- a/lib/github/representation/branch.rb
+++ b/lib/github/representation/branch.rb
@@ -26,13 +26,25 @@ module Github
end
def exists?
- branch_exists? && commit_exists?
+ @exists ||= branch_exists? && commit_exists?
end
def valid?
sha.present? && ref.present?
end
+ def restore!(name)
+ repository.create_branch(name, sha)
+ rescue Gitlab::Git::Repository::InvalidRef => e
+ Rails.logger.error("#{self.class.name}: Could not restore branch #{name}: #{e}")
+ end
+
+ def remove!(name)
+ repository.delete_branch(name)
+ rescue Rugged::ReferenceError => e
+ Rails.logger.error("#{self.class.name}: Could not remove branch #{name}: #{e}")
+ end
+
private
def branch_exists?
diff --git a/lib/github/representation/pull_request.rb b/lib/github/representation/pull_request.rb
index ac9c8283b4b..55461097e8a 100644
--- a/lib/github/representation/pull_request.rb
+++ b/lib/github/representation/pull_request.rb
@@ -1,8 +1,6 @@
module Github
module Representation
class PullRequest < Representation::Issuable
- attr_reader :project
-
delegate :user, :repo, :ref, :sha, to: :source_branch, prefix: true
delegate :user, :exists?, :repo, :ref, :sha, :short_sha, to: :target_branch, prefix: true
@@ -10,10 +8,6 @@ module Github
project
end
- def source_branch_exists?
- !cross_project? && source_branch.exists?
- end
-
def source_branch_name
@source_branch_name ||=
if cross_project? || !source_branch_exists?
@@ -23,6 +17,12 @@ module Github
end
end
+ def source_branch_exists?
+ return @source_branch_exists if defined?(@source_branch_exists)
+
+ @source_branch_exists = !cross_project? && source_branch.exists?
+ end
+
def target_project
project
end
@@ -31,6 +31,10 @@ module Github
@target_branch_name ||= target_branch_exists? ? target_branch_ref : target_branch_name_prefixed
end
+ def target_branch_exists?
+ @target_branch_exists ||= target_branch.exists?
+ end
+
def state
return 'merged' if raw['state'] == 'closed' && raw['merged_at'].present?
return 'closed' if raw['state'] == 'closed'
@@ -46,6 +50,18 @@ module Github
source_branch.valid? && target_branch.valid?
end
+ def restore_branches!
+ restore_source_branch!
+ restore_target_branch!
+ end
+
+ def remove_restored_branches!
+ return if opened?
+
+ remove_source_branch!
+ remove_target_branch!
+ end
+
private
def project
@@ -73,6 +89,32 @@ module Github
source_branch_repo.id != target_branch_repo.id
end
+
+ def restore_source_branch!
+ return if source_branch_exists?
+
+ source_branch.restore!(source_branch_name)
+ end
+
+ def restore_target_branch!
+ return if target_branch_exists?
+
+ target_branch.restore!(target_branch_name)
+ end
+
+ def remove_source_branch!
+ # We should remove the source/target branches only if they were
+ # restored. Otherwise, we'll remove branches like 'master' that
+ # target_branch_exists? returns true. In other words, we need
+ # to clean up only the restored branches that (source|target)_branch_exists?
+ # returns false for the first time it has been called, because of
+ # this that is important to memoize these values.
+ source_branch.remove!(source_branch_name) unless source_branch_exists?
+ end
+
+ def remove_target_branch!
+ target_branch.remove!(target_branch_name) unless target_branch_exists?
+ end
end
end
end
diff --git a/lib/gitlab/background_migration.rb b/lib/gitlab/background_migration.rb
index 914a3b72abd..d95ecd7b291 100644
--- a/lib/gitlab/background_migration.rb
+++ b/lib/gitlab/background_migration.rb
@@ -5,8 +5,8 @@ module Gitlab
#
# steal_class - The name of the class for which to steal jobs.
def self.steal(steal_class)
- queue = Sidekiq::Queue.
- new(BackgroundMigrationWorker.sidekiq_options['queue'])
+ queue = Sidekiq::Queue
+ .new(BackgroundMigrationWorker.sidekiq_options['queue'])
queue.each do |job|
migration_class, migration_args = job.args
diff --git a/lib/gitlab/cache/ci/project_pipeline_status.rb b/lib/gitlab/cache/ci/project_pipeline_status.rb
index 4fc9a075edc..9c2e09943b0 100644
--- a/lib/gitlab/cache/ci/project_pipeline_status.rb
+++ b/lib/gitlab/cache/ci/project_pipeline_status.rb
@@ -50,8 +50,8 @@ module Gitlab
ref: pipeline.ref
}
- new(pipeline.project, pipeline_info: pipeline_info).
- store_in_cache_if_needed
+ new(pipeline.project, pipeline_info: pipeline_info)
+ .store_in_cache_if_needed
end
def initialize(project, pipeline_info: {}, loaded_from_cache: nil)
diff --git a/lib/gitlab/ci/pipeline_duration.rb b/lib/gitlab/ci/pipeline_duration.rb
index a210e76acaa..3208cc2bef6 100644
--- a/lib/gitlab/ci/pipeline_duration.rb
+++ b/lib/gitlab/ci/pipeline_duration.rb
@@ -87,8 +87,8 @@ module Gitlab
def from_pipeline(pipeline)
status = %w[success failed running canceled]
- builds = pipeline.builds.latest.
- where(status: status).where.not(started_at: nil).order(:started_at)
+ builds = pipeline.builds.latest
+ .where(status: status).where.not(started_at: nil).order(:started_at)
from_builds(builds)
end
diff --git a/lib/gitlab/conflict/file_collection.rb b/lib/gitlab/conflict/file_collection.rb
index 6e73361cad1..1611eba31da 100644
--- a/lib/gitlab/conflict/file_collection.rb
+++ b/lib/gitlab/conflict/file_collection.rb
@@ -16,9 +16,9 @@ module Gitlab
project = merge_request.source_project
new(merge_request, project).tap do |file_collection|
- project.
- repository.
- with_repo_branch_commit(merge_request.target_project.repository, merge_request.target_branch) do
+ project
+ .repository
+ .with_repo_branch_commit(merge_request.target_project.repository, merge_request.target_branch) do
yield file_collection
end
diff --git a/lib/gitlab/contributions_calendar.rb b/lib/gitlab/contributions_calendar.rb
index 060e013183f..bf557103cfd 100644
--- a/lib/gitlab/contributions_calendar.rb
+++ b/lib/gitlab/contributions_calendar.rb
@@ -16,14 +16,14 @@ module Gitlab
# Can't use Event.contributions here because we need to check 3 different
# project_features for the (currently) 3 different contribution types
date_from = 1.year.ago
- repo_events = event_counts(date_from, :repository).
- having(action: Event::PUSHED)
- issue_events = event_counts(date_from, :issues).
- having(action: [Event::CREATED, Event::CLOSED], target_type: "Issue")
- mr_events = event_counts(date_from, :merge_requests).
- having(action: [Event::MERGED, Event::CREATED, Event::CLOSED], target_type: "MergeRequest")
- note_events = event_counts(date_from, :merge_requests).
- having(action: [Event::COMMENTED], target_type: "Note")
+ repo_events = event_counts(date_from, :repository)
+ .having(action: Event::PUSHED)
+ issue_events = event_counts(date_from, :issues)
+ .having(action: [Event::CREATED, Event::CLOSED], target_type: "Issue")
+ mr_events = event_counts(date_from, :merge_requests)
+ .having(action: [Event::MERGED, Event::CREATED, Event::CLOSED], target_type: "MergeRequest")
+ note_events = event_counts(date_from, :merge_requests)
+ .having(action: [Event::COMMENTED], target_type: "Note")
union = Gitlab::SQL::Union.new([repo_events, issue_events, mr_events, note_events])
events = Event.find_by_sql(union.to_sql).map(&:attributes)
@@ -34,9 +34,9 @@ module Gitlab
end
def events_by_date(date)
- events = Event.contributions.where(author_id: contributor.id).
- where(created_at: date.beginning_of_day..date.end_of_day).
- where(project_id: projects)
+ events = Event.contributions.where(author_id: contributor.id)
+ .where(created_at: date.beginning_of_day..date.end_of_day)
+ .where(project_id: projects)
# Use visible_to_user? instead of the complicated logic in activity_dates
# because we're only viewing the events for a single day.
@@ -60,20 +60,20 @@ module Gitlab
# use IN(project_ids...) instead. It's the intersection of two users so
# the list will be (relatively) short
@contributed_project_ids ||= projects.uniq.pluck(:id)
- authed_projects = Project.where(id: @contributed_project_ids).
- with_feature_available_for_user(feature, current_user).
- reorder(nil).
- select(:id)
+ authed_projects = Project.where(id: @contributed_project_ids)
+ .with_feature_available_for_user(feature, current_user)
+ .reorder(nil)
+ .select(:id)
- conditions = t[:created_at].gteq(date_from.beginning_of_day).
- and(t[:created_at].lteq(Date.today.end_of_day)).
- and(t[:author_id].eq(contributor.id))
+ conditions = t[:created_at].gteq(date_from.beginning_of_day)
+ .and(t[:created_at].lteq(Date.today.end_of_day))
+ .and(t[:author_id].eq(contributor.id))
- Event.reorder(nil).
- select(t[:project_id], t[:target_type], t[:action], 'date(created_at) AS date', 'count(id) as total_amount').
- group(t[:project_id], t[:target_type], t[:action], 'date(created_at)').
- where(conditions).
- having(t[:project_id].in(Arel::Nodes::SqlLiteral.new(authed_projects.to_sql)))
+ Event.reorder(nil)
+ .select(t[:project_id], t[:target_type], t[:action], 'date(created_at) AS date', 'count(id) as total_amount')
+ .group(t[:project_id], t[:target_type], t[:action], 'date(created_at)')
+ .where(conditions)
+ .having(t[:project_id].in(Arel::Nodes::SqlLiteral.new(authed_projects.to_sql)))
end
end
end
diff --git a/lib/gitlab/cycle_analytics/base_query.rb b/lib/gitlab/cycle_analytics/base_query.rb
index d560dca45c8..58729d3ced8 100644
--- a/lib/gitlab/cycle_analytics/base_query.rb
+++ b/lib/gitlab/cycle_analytics/base_query.rb
@@ -12,17 +12,17 @@ module Gitlab
end
def stage_query
- query = mr_closing_issues_table.join(issue_table).on(issue_table[:id].eq(mr_closing_issues_table[:issue_id])).
- join(issue_metrics_table).on(issue_table[:id].eq(issue_metrics_table[:issue_id])).
- where(issue_table[:project_id].eq(@project.id)).
- where(issue_table[:deleted_at].eq(nil)).
- where(issue_table[:created_at].gteq(@options[:from]))
+ query = mr_closing_issues_table.join(issue_table).on(issue_table[:id].eq(mr_closing_issues_table[:issue_id]))
+ .join(issue_metrics_table).on(issue_table[:id].eq(issue_metrics_table[:issue_id]))
+ .where(issue_table[:project_id].eq(@project.id))
+ .where(issue_table[:deleted_at].eq(nil))
+ .where(issue_table[:created_at].gteq(@options[:from]))
# Load merge_requests
- query = query.join(mr_table, Arel::Nodes::OuterJoin).
- on(mr_table[:id].eq(mr_closing_issues_table[:merge_request_id])).
- join(mr_metrics_table).
- on(mr_table[:id].eq(mr_metrics_table[:merge_request_id]))
+ query = query.join(mr_table, Arel::Nodes::OuterJoin)
+ .on(mr_table[:id].eq(mr_closing_issues_table[:merge_request_id]))
+ .join(mr_metrics_table)
+ .on(mr_table[:id].eq(mr_metrics_table[:merge_request_id]))
query
end
diff --git a/lib/gitlab/database/median.rb b/lib/gitlab/database/median.rb
index 23890e5f493..059054ac9ff 100644
--- a/lib/gitlab/database/median.rb
+++ b/lib/gitlab/database/median.rb
@@ -29,10 +29,10 @@ module Gitlab
end
def mysql_median_datetime_sql(arel_table, query_so_far, column_sym)
- query = arel_table.
- from(arel_table.project(Arel.sql('*')).order(arel_table[column_sym]).as(arel_table.table_name)).
- project(average([arel_table[column_sym]], 'median')).
- where(
+ query = arel_table
+ .from(arel_table.project(Arel.sql('*')).order(arel_table[column_sym]).as(arel_table.table_name))
+ .project(average([arel_table[column_sym]], 'median'))
+ .where(
Arel::Nodes::Between.new(
Arel.sql("(select @row_id := @row_id + 1)"),
Arel::Nodes::And.new(
@@ -67,8 +67,8 @@ module Gitlab
cte_table = Arel::Table.new("ordered_records")
cte = Arel::Nodes::As.new(
cte_table,
- arel_table.
- project(
+ arel_table
+ .project(
arel_table[column_sym].as(column_sym.to_s),
Arel::Nodes::Over.new(Arel::Nodes::NamedFunction.new("row_number", []),
Arel::Nodes::Window.new.order(arel_table[column_sym])).as('row_id'),
@@ -79,8 +79,8 @@ module Gitlab
# From the CTE, select either the middle row or the middle two rows (this is accomplished
# by 'where cte.row_id between cte.ct / 2.0 AND cte.ct / 2.0 + 1'). Find the average of the
# selected rows, and this is the median value.
- cte_table.project(average([extract_epoch(cte_table[column_sym])], "median")).
- where(
+ cte_table.project(average([extract_epoch(cte_table[column_sym])], "median"))
+ .where(
Arel::Nodes::Between.new(
cte_table[:row_id],
Arel::Nodes::And.new(
@@ -88,9 +88,9 @@ module Gitlab
(cte_table[:ct] / Arel.sql('2.0') + 1)]
)
)
- ).
- with(query_so_far, cte).
- to_sql
+ )
+ .with(query_so_far, cte)
+ .to_sql
end
private
diff --git a/lib/gitlab/database/migration_helpers.rb b/lib/gitlab/database/migration_helpers.rb
index 9181202a091..60cce9c6d9e 100644
--- a/lib/gitlab/database/migration_helpers.rb
+++ b/lib/gitlab/database/migration_helpers.rb
@@ -245,19 +245,19 @@ module Gitlab
start_id = exec_query(start_arel.to_sql).to_hash.first['id'].to_i
loop do
- stop_arel = table.project(table[:id]).
- where(table[:id].gteq(start_id)).
- order(table[:id].asc).
- take(1).
- skip(batch_size)
+ stop_arel = table.project(table[:id])
+ .where(table[:id].gteq(start_id))
+ .order(table[:id].asc)
+ .take(1)
+ .skip(batch_size)
stop_arel = yield table, stop_arel if block_given?
stop_row = exec_query(stop_arel.to_sql).to_hash.first
- update_arel = Arel::UpdateManager.new(ActiveRecord::Base).
- table(table).
- set([[table[column], value]]).
- where(table[:id].gteq(start_id))
+ update_arel = Arel::UpdateManager.new(ActiveRecord::Base)
+ .table(table)
+ .set([[table[column], value]])
+ .where(table[:id].gteq(start_id))
if stop_row
stop_id = stop_row['id'].to_i
@@ -586,15 +586,15 @@ module Gitlab
quoted_replacement = Arel::Nodes::Quoted.new(replacement.to_s)
if Database.mysql?
- locate = Arel::Nodes::NamedFunction.
- new('locate', [quoted_pattern, column])
- insert_in_place = Arel::Nodes::NamedFunction.
- new('insert', [column, locate, pattern.size, quoted_replacement])
+ locate = Arel::Nodes::NamedFunction
+ .new('locate', [quoted_pattern, column])
+ insert_in_place = Arel::Nodes::NamedFunction
+ .new('insert', [column, locate, pattern.size, quoted_replacement])
Arel::Nodes::SqlLiteral.new(insert_in_place.to_sql)
else
- replace = Arel::Nodes::NamedFunction.
- new("regexp_replace", [column, quoted_pattern, quoted_replacement])
+ replace = Arel::Nodes::NamedFunction
+ .new("regexp_replace", [column, quoted_pattern, quoted_replacement])
Arel::Nodes::SqlLiteral.new(replace.to_sql)
end
end
diff --git a/lib/gitlab/database/rename_reserved_paths_migration/v1/rename_base.rb b/lib/gitlab/database/rename_reserved_paths_migration/v1/rename_base.rb
index d60fd4bb551..d8163d7da11 100644
--- a/lib/gitlab/database/rename_reserved_paths_migration/v1/rename_base.rb
+++ b/lib/gitlab/database/rename_reserved_paths_migration/v1/rename_base.rb
@@ -27,8 +27,8 @@ module Gitlab
new_full_path = join_routable_path(namespace_path, new_path)
# skips callbacks & validations
- routable.class.where(id: routable).
- update_all(path: new_path)
+ routable.class.where(id: routable)
+ .update_all(path: new_path)
rename_routes(old_full_path, new_full_path)
diff --git a/lib/gitlab/database/rename_reserved_paths_migration/v1/rename_namespaces.rb b/lib/gitlab/database/rename_reserved_paths_migration/v1/rename_namespaces.rb
index 2958ad4b8e5..da7e2cb2e85 100644
--- a/lib/gitlab/database/rename_reserved_paths_migration/v1/rename_namespaces.rb
+++ b/lib/gitlab/database/rename_reserved_paths_migration/v1/rename_namespaces.rb
@@ -18,8 +18,8 @@ module Gitlab
when :top_level
MigrationClasses::Namespace.where(parent_id: nil)
end
- with_paths = MigrationClasses::Route.arel_table[:path].
- matches_any(path_patterns)
+ with_paths = MigrationClasses::Route.arel_table[:path]
+ .matches_any(path_patterns)
namespaces.joins(:route).where(with_paths)
end
@@ -52,15 +52,15 @@ module Gitlab
end
def repo_paths_for_namespace(namespace)
- projects_for_namespace(namespace).distinct.select(:repository_storage).
- map(&:repository_storage_path)
+ projects_for_namespace(namespace).distinct.select(:repository_storage)
+ .map(&:repository_storage_path)
end
def projects_for_namespace(namespace)
namespace_ids = child_ids_for_parent(namespace, ids: [namespace.id])
- namespace_or_children = MigrationClasses::Project.
- arel_table[:namespace_id].
- in(namespace_ids)
+ namespace_or_children = MigrationClasses::Project
+ .arel_table[:namespace_id]
+ .in(namespace_ids)
MigrationClasses::Project.where(namespace_or_children)
end
diff --git a/lib/gitlab/downtime_check.rb b/lib/gitlab/downtime_check.rb
index ab9537ed7d7..941244694e2 100644
--- a/lib/gitlab/downtime_check.rb
+++ b/lib/gitlab/downtime_check.rb
@@ -50,8 +50,8 @@ module Gitlab
# Returns the class for the given migration file path.
def class_for_migration_file(path)
- File.basename(path, File.extname(path)).split('_', 2).last.camelize.
- constantize
+ File.basename(path, File.extname(path)).split('_', 2).last.camelize
+ .constantize
end
# Returns true if the given migration can be performed without downtime.
diff --git a/lib/gitlab/git/blob.rb b/lib/gitlab/git/blob.rb
index 33a7624e303..a7aceab4c14 100644
--- a/lib/gitlab/git/blob.rb
+++ b/lib/gitlab/git/blob.rb
@@ -14,6 +14,51 @@ module Gitlab
class << self
def find(repository, sha, path)
+ Gitlab::GitalyClient.migrate(:project_raw_show) do |is_enabled|
+ if is_enabled
+ find_by_gitaly(repository, sha, path)
+ else
+ find_by_rugged(repository, sha, path)
+ end
+ end
+ end
+
+ def find_by_gitaly(repository, sha, path)
+ path = path.sub(/\A\/*/, '')
+ path = '/' if path.empty?
+ name = File.basename(path)
+ entry = Gitlab::GitalyClient::Commit.new(repository).tree_entry(sha, path, MAX_DATA_DISPLAY_SIZE)
+ return unless entry
+
+ case entry.type
+ when :COMMIT
+ new(
+ id: entry.oid,
+ name: name,
+ size: 0,
+ data: '',
+ path: path,
+ commit_id: sha
+ )
+ when :BLOB
+ # EncodingDetector checks the first 1024 * 1024 bytes for NUL byte, libgit2 checks
+ # only the first 8000 (https://github.com/libgit2/libgit2/blob/2ed855a9e8f9af211e7274021c2264e600c0f86b/src/filter.h#L15),
+ # which is what we use below to keep a consistent behavior.
+ detect = CharlockHolmes::EncodingDetector.new(8000).detect(entry.data)
+ new(
+ id: entry.oid,
+ name: name,
+ size: entry.size,
+ data: entry.data.dup,
+ mode: entry.mode.to_s(8),
+ path: path,
+ commit_id: sha,
+ binary: detect && detect[:type] == :binary
+ )
+ end
+ end
+
+ def find_by_rugged(repository, sha, path)
commit = repository.lookup(sha)
root_tree = commit.tree
diff --git a/lib/gitlab/git/diff.rb b/lib/gitlab/git/diff.rb
index f825568f194..cf7829a583b 100644
--- a/lib/gitlab/git/diff.rb
+++ b/lib/gitlab/git/diff.rb
@@ -318,7 +318,7 @@ module Gitlab
end
def init_from_gitaly(diff)
- @diff = diff.patch if diff.respond_to?(:patch)
+ @diff = encode!(diff.patch) if diff.respond_to?(:patch)
@new_path = encode!(diff.to_path.dup)
@old_path = encode!(diff.from_path.dup)
@a_mode = diff.old_mode.to_s(8)
diff --git a/lib/gitlab/gitaly_client/commit.rb b/lib/gitlab/gitaly_client/commit.rb
index 73c1848c95f..b8877619797 100644
--- a/lib/gitlab/gitaly_client/commit.rb
+++ b/lib/gitlab/gitaly_client/commit.rb
@@ -36,6 +36,26 @@ module Gitlab
end
end
+ def tree_entry(ref, path, limit = nil)
+ request = Gitaly::TreeEntryRequest.new(
+ repository: @gitaly_repo,
+ revision: ref,
+ path: path.dup.force_encoding(Encoding::ASCII_8BIT),
+ limit: limit.to_i
+ )
+
+ response = GitalyClient.call(@repository.storage, :commit, :tree_entry, request)
+ entry = response.first
+ return unless entry.oid.present?
+
+ if entry.type == :BLOB
+ rest_of_data = response.reduce("") { |memo, msg| memo << msg.data }
+ entry.data += rest_of_data
+ end
+
+ entry
+ end
+
private
def commit_diff_request_params(commit, options = {})
diff --git a/lib/gitlab/gitaly_client/diff_stitcher.rb b/lib/gitlab/gitaly_client/diff_stitcher.rb
index d84e8d752dc..65d81dc5d46 100644
--- a/lib/gitlab/gitaly_client/diff_stitcher.rb
+++ b/lib/gitlab/gitaly_client/diff_stitcher.rb
@@ -13,7 +13,10 @@ module Gitlab
@rpc_response.each do |diff_msg|
if current_diff.nil?
diff_params = diff_msg.to_h.slice(*GitalyClient::Diff::FIELDS)
- diff_params[:patch] = diff_msg.raw_patch_data
+ # gRPC uses frozen strings by default, and we need to have an unfrozen string as it
+ # gets processed further down the line. So we unfreeze the first chunk of the patch
+ # in case it's the only chunk we receive for this diff.
+ diff_params[:patch] = diff_msg.raw_patch_data.dup
current_diff = GitalyClient::Diff.new(diff_params)
else
diff --git a/lib/gitlab/group_hierarchy.rb b/lib/gitlab/group_hierarchy.rb
index 357c076e874..5a31e56cb30 100644
--- a/lib/gitlab/group_hierarchy.rb
+++ b/lib/gitlab/group_hierarchy.rb
@@ -67,11 +67,11 @@ module Gitlab
union = SQL::Union.new([model.unscoped.from(ancestors_table),
model.unscoped.from(descendants_table)])
- model.
- unscoped.
- with.
- recursive(ancestors.to_arel, descendants.to_arel).
- from("(#{union.to_sql}) #{model.table_name}")
+ model
+ .unscoped
+ .with
+ .recursive(ancestors.to_arel, descendants.to_arel)
+ .from("(#{union.to_sql}) #{model.table_name}")
end
private
@@ -82,10 +82,10 @@ module Gitlab
cte << ancestors_base.except(:order)
# Recursively get all the ancestors of the base set.
- cte << model.
- from([groups_table, cte.table]).
- where(groups_table[:id].eq(cte.table[:parent_id])).
- except(:order)
+ cte << model
+ .from([groups_table, cte.table])
+ .where(groups_table[:id].eq(cte.table[:parent_id]))
+ .except(:order)
cte
end
@@ -96,10 +96,10 @@ module Gitlab
cte << descendants_base.except(:order)
# Recursively get all the descendants of the base set.
- cte << model.
- from([groups_table, cte.table]).
- where(groups_table[:parent_id].eq(cte.table[:id])).
- except(:order)
+ cte << model
+ .from([groups_table, cte.table])
+ .where(groups_table[:parent_id].eq(cte.table[:id]))
+ .except(:order)
cte
end
diff --git a/lib/gitlab/highlight.rb b/lib/gitlab/highlight.rb
index 6b24da030df..5408a1a6838 100644
--- a/lib/gitlab/highlight.rb
+++ b/lib/gitlab/highlight.rb
@@ -1,8 +1,8 @@
module Gitlab
class Highlight
def self.highlight(blob_name, blob_content, repository: nil, plain: false)
- new(blob_name, blob_content, repository: repository).
- highlight(blob_content, continue: false, plain: plain)
+ new(blob_name, blob_content, repository: repository)
+ .highlight(blob_content, continue: false, plain: plain)
end
attr_reader :blob_name
diff --git a/lib/gitlab/ldap/user.rb b/lib/gitlab/ldap/user.rb
index 5e299e26c54..39180dc17d9 100644
--- a/lib/gitlab/ldap/user.rb
+++ b/lib/gitlab/ldap/user.rb
@@ -10,9 +10,9 @@ module Gitlab
class << self
def find_by_uid_and_provider(uid, provider)
# LDAP distinguished name is case-insensitive
- identity = ::Identity.
- where(provider: provider).
- iwhere(extern_uid: uid).last
+ identity = ::Identity
+ .where(provider: provider)
+ .iwhere(extern_uid: uid).last
identity && identity.user
end
end
diff --git a/lib/gitlab/metrics/influx_db.rb b/lib/gitlab/metrics/influx_db.rb
index 3a39791edbf..d7c56463aac 100644
--- a/lib/gitlab/metrics/influx_db.rb
+++ b/lib/gitlab/metrics/influx_db.rb
@@ -157,8 +157,8 @@ module Gitlab
host = settings[:host]
port = settings[:port]
- InfluxDB::Client.
- new(udp: { host: host, port: port })
+ InfluxDB::Client
+ .new(udp: { host: host, port: port })
end
end
end
diff --git a/lib/gitlab/metrics/system.rb b/lib/gitlab/metrics/system.rb
index 3aaebb3e9c3..aba3e0df382 100644
--- a/lib/gitlab/metrics/system.rb
+++ b/lib/gitlab/metrics/system.rb
@@ -34,13 +34,13 @@ module Gitlab
# THREAD_CPUTIME is not supported on OS X
if Process.const_defined?(:CLOCK_THREAD_CPUTIME_ID)
def self.cpu_time
- Process.
- clock_gettime(Process::CLOCK_THREAD_CPUTIME_ID, :millisecond)
+ Process
+ .clock_gettime(Process::CLOCK_THREAD_CPUTIME_ID, :millisecond)
end
else
def self.cpu_time
- Process.
- clock_gettime(Process::CLOCK_PROCESS_CPUTIME_ID, :millisecond)
+ Process
+ .clock_gettime(Process::CLOCK_PROCESS_CPUTIME_ID, :millisecond)
end
end
diff --git a/lib/gitlab/other_markup.rb b/lib/gitlab/other_markup.rb
index 31a24460f0f..fc3f21233dd 100644
--- a/lib/gitlab/other_markup.rb
+++ b/lib/gitlab/other_markup.rb
@@ -6,8 +6,8 @@ module Gitlab
# input - the source text in a markup format
#
def self.render(file_name, input, context)
- html = GitHub::Markup.render(file_name, input).
- force_encoding(input.encoding)
+ html = GitHub::Markup.render(file_name, input)
+ .force_encoding(input.encoding)
context[:pipeline] = :markup
html = Banzai.render(html, context)
diff --git a/lib/gitlab/performance_bar/peek_query_tracker.rb b/lib/gitlab/performance_bar/peek_query_tracker.rb
index 7ab80f5ee0f..574ae8731a5 100644
--- a/lib/gitlab/performance_bar/peek_query_tracker.rb
+++ b/lib/gitlab/performance_bar/peek_query_tracker.rb
@@ -3,8 +3,8 @@ module Gitlab
module PerformanceBar
module PeekQueryTracker
def sorted_queries
- PEEK_DB_CLIENT.query_details.
- sort { |a, b| b[:duration] <=> a[:duration] }
+ PEEK_DB_CLIENT.query_details
+ .sort { |a, b| b[:duration] <=> a[:duration] }
end
def results
diff --git a/lib/gitlab/project_authorizations/with_nested_groups.rb b/lib/gitlab/project_authorizations/with_nested_groups.rb
index bb0df1e3dad..15b8beacf60 100644
--- a/lib/gitlab/project_authorizations/with_nested_groups.rb
+++ b/lib/gitlab/project_authorizations/with_nested_groups.rb
@@ -28,34 +28,34 @@ module Gitlab
# Projects that belong directly to any of the groups the user has
# access to.
- Namespace.
- unscoped.
- select([alias_as_column(projects[:id], 'project_id'),
- cte_alias[:access_level]]).
- from(cte_alias).
- joins(:projects),
+ Namespace
+ .unscoped
+ .select([alias_as_column(projects[:id], 'project_id'),
+ cte_alias[:access_level]])
+ .from(cte_alias)
+ .joins(:projects),
# Projects shared with any of the namespaces the user has access to.
- Namespace.
- unscoped.
- select([links[:project_id],
- least(cte_alias[:access_level],
- links[:group_access],
- 'access_level')]).
- from(cte_alias).
- joins('INNER JOIN project_group_links ON project_group_links.group_id = namespaces.id').
- joins('INNER JOIN projects ON projects.id = project_group_links.project_id').
- joins('INNER JOIN namespaces p_ns ON p_ns.id = projects.namespace_id').
- where('p_ns.share_with_group_lock IS FALSE')
+ Namespace
+ .unscoped
+ .select([links[:project_id],
+ least(cte_alias[:access_level],
+ links[:group_access],
+ 'access_level')])
+ .from(cte_alias)
+ .joins('INNER JOIN project_group_links ON project_group_links.group_id = namespaces.id')
+ .joins('INNER JOIN projects ON projects.id = project_group_links.project_id')
+ .joins('INNER JOIN namespaces p_ns ON p_ns.id = projects.namespace_id')
+ .where('p_ns.share_with_group_lock IS FALSE')
]
union = Gitlab::SQL::Union.new(relations)
- ProjectAuthorization.
- unscoped.
- with.
- recursive(cte.to_arel).
- select_from_union(union)
+ ProjectAuthorization
+ .unscoped
+ .with
+ .recursive(cte.to_arel)
+ .select_from_union(union)
end
private
@@ -68,17 +68,17 @@ module Gitlab
namespaces = Namespace.arel_table
# Namespaces the user is a member of.
- cte << user.groups.
- select([namespaces[:id], members[:access_level]]).
- except(:order)
+ cte << user.groups
+ .select([namespaces[:id], members[:access_level]])
+ .except(:order)
# Sub groups of any groups the user is a member of.
cte << Group.select([namespaces[:id],
greatest(members[:access_level],
- cte.table[:access_level], 'access_level')]).
- joins(join_cte(cte)).
- joins(join_members).
- except(:order)
+ cte.table[:access_level], 'access_level')])
+ .joins(join_cte(cte))
+ .joins(join_members)
+ .except(:order)
cte
end
@@ -88,11 +88,11 @@ module Gitlab
members = Member.arel_table
namespaces = Namespace.arel_table
- cond = members[:source_id].
- eq(namespaces[:id]).
- and(members[:source_type].eq('Namespace')).
- and(members[:requested_at].eq(nil)).
- and(members[:user_id].eq(user.id))
+ cond = members[:source_id]
+ .eq(namespaces[:id])
+ .and(members[:source_type].eq('Namespace'))
+ .and(members[:requested_at].eq(nil))
+ .and(members[:user_id].eq(user.id))
Arel::Nodes::OuterJoin.new(members, Arel::Nodes::On.new(cond))
end
diff --git a/lib/gitlab/project_authorizations/without_nested_groups.rb b/lib/gitlab/project_authorizations/without_nested_groups.rb
index 627e8c5fba2..ad87540e6c2 100644
--- a/lib/gitlab/project_authorizations/without_nested_groups.rb
+++ b/lib/gitlab/project_authorizations/without_nested_groups.rb
@@ -26,9 +26,9 @@ module Gitlab
union = Gitlab::SQL::Union.new(relations)
- ProjectAuthorization.
- unscoped.
- select_from_union(union)
+ ProjectAuthorization
+ .unscoped
+ .select_from_union(union)
end
end
end
diff --git a/lib/gitlab/prometheus/additional_metrics_parser.rb b/lib/gitlab/prometheus/additional_metrics_parser.rb
new file mode 100644
index 00000000000..cb95daf2260
--- /dev/null
+++ b/lib/gitlab/prometheus/additional_metrics_parser.rb
@@ -0,0 +1,34 @@
+module Gitlab
+ module Prometheus
+ module AdditionalMetricsParser
+ extend self
+
+ def load_groups_from_yaml
+ additional_metrics_raw.map(&method(:group_from_entry))
+ end
+
+ private
+
+ def validate!(obj)
+ raise ParsingError.new(obj.errors.full_messages.join('\n')) unless obj.valid?
+ end
+
+ def group_from_entry(entry)
+ entry[:name] = entry.delete(:group)
+ entry[:metrics]&.map! do |entry|
+ Metric.new(entry).tap(&method(:validate!))
+ end
+
+ MetricGroup.new(entry).tap(&method(:validate!))
+ end
+
+ def additional_metrics_raw
+ load_yaml_file&.map(&:deep_symbolize_keys).freeze
+ end
+
+ def load_yaml_file
+ @loaded_yaml_file ||= YAML.load_file(Rails.root.join('config/prometheus/additional_metrics.yml'))
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/prometheus/metric.rb b/lib/gitlab/prometheus/metric.rb
new file mode 100644
index 00000000000..f54b2c6aaff
--- /dev/null
+++ b/lib/gitlab/prometheus/metric.rb
@@ -0,0 +1,16 @@
+module Gitlab
+ module Prometheus
+ class Metric
+ include ActiveModel::Model
+
+ attr_accessor :title, :required_metrics, :weight, :y_label, :queries
+
+ validates :title, :required_metrics, :weight, :y_label, :queries, presence: true
+
+ def initialize(params = {})
+ super(params)
+ @y_label ||= 'Values'
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/prometheus/metric_group.rb b/lib/gitlab/prometheus/metric_group.rb
new file mode 100644
index 00000000000..729fef34b35
--- /dev/null
+++ b/lib/gitlab/prometheus/metric_group.rb
@@ -0,0 +1,14 @@
+module Gitlab
+ module Prometheus
+ class MetricGroup
+ include ActiveModel::Model
+
+ attr_accessor :name, :priority, :metrics
+ validates :name, :priority, :metrics, presence: true
+
+ def self.all
+ AdditionalMetricsParser.load_groups_from_yaml
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/prometheus/parsing_error.rb b/lib/gitlab/prometheus/parsing_error.rb
new file mode 100644
index 00000000000..49cc0e16080
--- /dev/null
+++ b/lib/gitlab/prometheus/parsing_error.rb
@@ -0,0 +1,5 @@
+module Gitlab
+ module Prometheus
+ ParsingError = Class.new(StandardError)
+ end
+end
diff --git a/lib/gitlab/prometheus/queries/additional_metrics_deployment_query.rb b/lib/gitlab/prometheus/queries/additional_metrics_deployment_query.rb
new file mode 100644
index 00000000000..67c69d9ccf3
--- /dev/null
+++ b/lib/gitlab/prometheus/queries/additional_metrics_deployment_query.rb
@@ -0,0 +1,22 @@
+module Gitlab
+ module Prometheus
+ module Queries
+ class AdditionalMetricsDeploymentQuery < BaseQuery
+ include QueryAdditionalMetrics
+
+ def query(deployment_id)
+ Deployment.find_by(id: deployment_id).try do |deployment|
+ query_context = {
+ environment_slug: deployment.environment.slug,
+ environment_filter: %{container_name!="POD",environment="#{deployment.environment.slug}"},
+ timeframe_start: (deployment.created_at - 30.minutes).to_f,
+ timeframe_end: (deployment.created_at + 30.minutes).to_f
+ }
+
+ query_metrics(query_context)
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/prometheus/queries/additional_metrics_environment_query.rb b/lib/gitlab/prometheus/queries/additional_metrics_environment_query.rb
new file mode 100644
index 00000000000..b5a679ddd79
--- /dev/null
+++ b/lib/gitlab/prometheus/queries/additional_metrics_environment_query.rb
@@ -0,0 +1,22 @@
+module Gitlab
+ module Prometheus
+ module Queries
+ class AdditionalMetricsEnvironmentQuery < BaseQuery
+ include QueryAdditionalMetrics
+
+ def query(environment_id)
+ Environment.find_by(id: environment_id).try do |environment|
+ query_context = {
+ environment_slug: environment.slug,
+ environment_filter: %{container_name!="POD",environment="#{environment.slug}"},
+ timeframe_start: 8.hours.ago.to_f,
+ timeframe_end: Time.now.to_f
+ }
+
+ query_metrics(query_context)
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/prometheus/queries/base_query.rb b/lib/gitlab/prometheus/queries/base_query.rb
index 2a2eb4ae57f..c60828165bd 100644
--- a/lib/gitlab/prometheus/queries/base_query.rb
+++ b/lib/gitlab/prometheus/queries/base_query.rb
@@ -3,7 +3,7 @@ module Gitlab
module Queries
class BaseQuery
attr_accessor :client
- delegate :query_range, :query, to: :client, prefix: true
+ delegate :query_range, :query, :label_values, :series, to: :client, prefix: true
def raw_memory_usage_query(environment_slug)
%{avg(container_memory_usage_bytes{container_name!="POD",environment="#{environment_slug}"}) / 2^20}
diff --git a/lib/gitlab/prometheus/queries/deployment_query.rb b/lib/gitlab/prometheus/queries/deployment_query.rb
index 2cc08731f8d..170f483540e 100644
--- a/lib/gitlab/prometheus/queries/deployment_query.rb
+++ b/lib/gitlab/prometheus/queries/deployment_query.rb
@@ -1,26 +1,31 @@
-module Gitlab::Prometheus::Queries
- class DeploymentQuery < BaseQuery
- def query(deployment_id)
- deployment = Deployment.find_by(id: deployment_id)
- environment_slug = deployment.environment.slug
+module Gitlab
+ module Prometheus
+ module Queries
+ class DeploymentQuery < BaseQuery
+ def query(deployment_id)
+ Deployment.find_by(id: deployment_id).try do |deployment|
+ environment_slug = deployment.environment.slug
- memory_query = raw_memory_usage_query(environment_slug)
- memory_avg_query = %{avg(avg_over_time(container_memory_usage_bytes{container_name!="POD",environment="#{environment_slug}"}[30m]))}
- cpu_query = raw_cpu_usage_query(environment_slug)
- cpu_avg_query = %{avg(rate(container_cpu_usage_seconds_total{container_name!="POD",environment="#{environment_slug}"}[30m])) * 100}
+ memory_query = raw_memory_usage_query(environment_slug)
+ memory_avg_query = %{avg(avg_over_time(container_memory_usage_bytes{container_name!="POD",environment="#{environment_slug}"}[30m]))}
+ cpu_query = raw_cpu_usage_query(environment_slug)
+ cpu_avg_query = %{avg(rate(container_cpu_usage_seconds_total{container_name!="POD",environment="#{environment_slug}"}[30m])) * 100}
- timeframe_start = (deployment.created_at - 30.minutes).to_f
- timeframe_end = (deployment.created_at + 30.minutes).to_f
+ timeframe_start = (deployment.created_at - 30.minutes).to_f
+ timeframe_end = (deployment.created_at + 30.minutes).to_f
- {
- memory_values: client_query_range(memory_query, start: timeframe_start, stop: timeframe_end),
- memory_before: client_query(memory_avg_query, time: deployment.created_at.to_f),
- memory_after: client_query(memory_avg_query, time: timeframe_end),
+ {
+ memory_values: client_query_range(memory_query, start: timeframe_start, stop: timeframe_end),
+ memory_before: client_query(memory_avg_query, time: deployment.created_at.to_f),
+ memory_after: client_query(memory_avg_query, time: timeframe_end),
- cpu_values: client_query_range(cpu_query, start: timeframe_start, stop: timeframe_end),
- cpu_before: client_query(cpu_avg_query, time: deployment.created_at.to_f),
- cpu_after: client_query(cpu_avg_query, time: timeframe_end)
- }
+ cpu_values: client_query_range(cpu_query, start: timeframe_start, stop: timeframe_end),
+ cpu_before: client_query(cpu_avg_query, time: deployment.created_at.to_f),
+ cpu_after: client_query(cpu_avg_query, time: timeframe_end)
+ }
+ end
+ end
+ end
end
end
end
diff --git a/lib/gitlab/prometheus/queries/environment_query.rb b/lib/gitlab/prometheus/queries/environment_query.rb
index 01d756d7284..66f29d95177 100644
--- a/lib/gitlab/prometheus/queries/environment_query.rb
+++ b/lib/gitlab/prometheus/queries/environment_query.rb
@@ -1,20 +1,25 @@
-module Gitlab::Prometheus::Queries
- class EnvironmentQuery < BaseQuery
- def query(environment_id)
- environment = Environment.find_by(id: environment_id)
- environment_slug = environment.slug
- timeframe_start = 8.hours.ago.to_f
- timeframe_end = Time.now.to_f
+module Gitlab
+ module Prometheus
+ module Queries
+ class EnvironmentQuery < BaseQuery
+ def query(environment_id)
+ Environment.find_by(id: environment_id).try do |environment|
+ environment_slug = environment.slug
+ timeframe_start = 8.hours.ago.to_f
+ timeframe_end = Time.now.to_f
- memory_query = raw_memory_usage_query(environment_slug)
- cpu_query = raw_cpu_usage_query(environment_slug)
+ memory_query = raw_memory_usage_query(environment_slug)
+ cpu_query = raw_cpu_usage_query(environment_slug)
- {
- memory_values: client_query_range(memory_query, start: timeframe_start, stop: timeframe_end),
- memory_current: client_query(memory_query, time: timeframe_end),
- cpu_values: client_query_range(cpu_query, start: timeframe_start, stop: timeframe_end),
- cpu_current: client_query(cpu_query, time: timeframe_end)
- }
+ {
+ memory_values: client_query_range(memory_query, start: timeframe_start, stop: timeframe_end),
+ memory_current: client_query(memory_query, time: timeframe_end),
+ cpu_values: client_query_range(cpu_query, start: timeframe_start, stop: timeframe_end),
+ cpu_current: client_query(cpu_query, time: timeframe_end)
+ }
+ end
+ end
+ end
end
end
end
diff --git a/lib/gitlab/prometheus/queries/matched_metrics_query.rb b/lib/gitlab/prometheus/queries/matched_metrics_query.rb
new file mode 100644
index 00000000000..d4894c87f8d
--- /dev/null
+++ b/lib/gitlab/prometheus/queries/matched_metrics_query.rb
@@ -0,0 +1,80 @@
+module Gitlab
+ module Prometheus
+ module Queries
+ class MatchedMetricsQuery < BaseQuery
+ MAX_QUERY_ITEMS = 40.freeze
+
+ def query
+ groups_data.map do |group, data|
+ {
+ group: group.name,
+ priority: group.priority,
+ active_metrics: data[:active_metrics],
+ metrics_missing_requirements: data[:metrics_missing_requirements]
+ }
+ end
+ end
+
+ private
+
+ def groups_data
+ metrics_groups = groups_with_active_metrics(Gitlab::Prometheus::MetricGroup.all)
+ lookup = active_series_lookup(metrics_groups)
+
+ groups = {}
+
+ metrics_groups.each do |group|
+ groups[group] ||= { active_metrics: 0, metrics_missing_requirements: 0 }
+ active_metrics = group.metrics.count { |metric| metric.required_metrics.all?(&lookup.method(:has_key?)) }
+
+ groups[group][:active_metrics] += active_metrics
+ groups[group][:metrics_missing_requirements] += group.metrics.count - active_metrics
+ end
+
+ groups
+ end
+
+ def active_series_lookup(metric_groups)
+ timeframe_start = 8.hours.ago
+ timeframe_end = Time.now
+
+ series = metric_groups.flat_map(&:metrics).flat_map(&:required_metrics).uniq
+
+ lookup = series.each_slice(MAX_QUERY_ITEMS).flat_map do |batched_series|
+ client_series(*batched_series, start: timeframe_start, stop: timeframe_end)
+ .select(&method(:has_matching_label))
+ .map { |series_info| [series_info['__name__'], true] }
+ end
+ lookup.to_h
+ end
+
+ def has_matching_label(series_info)
+ series_info.key?('environment')
+ end
+
+ def available_metrics
+ @available_metrics ||= client_label_values || []
+ end
+
+ def filter_active_metrics(metric_group)
+ metric_group.metrics.select! do |metric|
+ metric.required_metrics.all?(&available_metrics.method(:include?))
+ end
+ metric_group
+ end
+
+ def groups_with_active_metrics(metric_groups)
+ metric_groups.map(&method(:filter_active_metrics)).select { |group| group.metrics.any? }
+ end
+
+ def metrics_with_required_series(metric_groups)
+ metric_groups.flat_map do |group|
+ group.metrics.select do |metric|
+ metric.required_metrics.all?(&available_metrics.method(:include?))
+ end
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/prometheus/queries/query_additional_metrics.rb b/lib/gitlab/prometheus/queries/query_additional_metrics.rb
new file mode 100644
index 00000000000..e44be770544
--- /dev/null
+++ b/lib/gitlab/prometheus/queries/query_additional_metrics.rb
@@ -0,0 +1,73 @@
+module Gitlab
+ module Prometheus
+ module Queries
+ module QueryAdditionalMetrics
+ def query_metrics(query_context)
+ query_processor = method(:process_query).curry[query_context]
+
+ groups = matched_metrics.map do |group|
+ metrics = group.metrics.map do |metric|
+ {
+ title: metric.title,
+ weight: metric.weight,
+ y_label: metric.y_label,
+ queries: metric.queries.map(&query_processor).select(&method(:query_with_result))
+ }
+ end
+
+ {
+ group: group.name,
+ priority: group.priority,
+ metrics: metrics.select(&method(:metric_with_any_queries))
+ }
+ end
+
+ groups.select(&method(:group_with_any_metrics))
+ end
+
+ private
+
+ def metric_with_any_queries(metric)
+ metric[:queries]&.count&.> 0
+ end
+
+ def group_with_any_metrics(group)
+ group[:metrics]&.count&.> 0
+ end
+
+ def query_with_result(query)
+ query[:result]&.any? do |item|
+ item&.[](:values)&.any? || item&.[](:value)&.any?
+ end
+ end
+
+ def process_query(context, query)
+ query_with_result = query.dup
+ result =
+ if query.key?(:query_range)
+ client_query_range(query[:query_range] % context, start: context[:timeframe_start], stop: context[:timeframe_end])
+ else
+ client_query(query[:query] % context, time: context[:timeframe_end])
+ end
+ query_with_result[:result] = result&.map(&:deep_symbolize_keys)
+ query_with_result
+ end
+
+ def available_metrics
+ @available_metrics ||= client_label_values || []
+ end
+
+ def matched_metrics
+ result = Gitlab::Prometheus::MetricGroup.all.map do |group|
+ group.metrics.select! do |metric|
+ metric.required_metrics.all?(&available_metrics.method(:include?))
+ end
+ group
+ end
+
+ result.select { |group| group.metrics.any? }
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/prometheus_client.rb b/lib/gitlab/prometheus_client.rb
index 5b51a1779dd..aa94614bf18 100644
--- a/lib/gitlab/prometheus_client.rb
+++ b/lib/gitlab/prometheus_client.rb
@@ -29,6 +29,14 @@ module Gitlab
end
end
+ def label_values(name = '__name__')
+ json_api_get("label/#{name}/values")
+ end
+
+ def series(*matches, start: 8.hours.ago, stop: Time.now)
+ json_api_get('series', 'match': matches, start: start.to_f, end: stop.to_f)
+ end
+
private
def json_api_get(type, args = {})
diff --git a/lib/gitlab/shell.rb b/lib/gitlab/shell.rb
index b1d6ea665b7..22554236c38 100644
--- a/lib/gitlab/shell.rb
+++ b/lib/gitlab/shell.rb
@@ -30,8 +30,8 @@ module Gitlab
end
def version_required
- @version_required ||= File.read(Rails.root.
- join('GITLAB_SHELL_VERSION')).strip
+ @version_required ||= File.read(Rails.root
+ .join('GITLAB_SHELL_VERSION')).strip
end
def strip_key(key)
diff --git a/lib/gitlab/sherlock/line_profiler.rb b/lib/gitlab/sherlock/line_profiler.rb
index aa1468bff6b..b5f9d040047 100644
--- a/lib/gitlab/sherlock/line_profiler.rb
+++ b/lib/gitlab/sherlock/line_profiler.rb
@@ -77,8 +77,8 @@ module Gitlab
line_samples << LineSample.new(duration, events)
end
- samples << FileSample.
- new(file, line_samples, total_duration, total_events)
+ samples << FileSample
+ .new(file, line_samples, total_duration, total_events)
end
samples
diff --git a/lib/gitlab/sherlock/query.rb b/lib/gitlab/sherlock/query.rb
index 99e56e923eb..948bf5e6528 100644
--- a/lib/gitlab/sherlock/query.rb
+++ b/lib/gitlab/sherlock/query.rb
@@ -105,10 +105,10 @@ module Gitlab
end
def format_sql(query)
- query.each_line.
- map { |line| line.strip }.
- join("\n").
- gsub(PREFIX_NEWLINE) { "\n#{$1} " }
+ query.each_line
+ .map { |line| line.strip }
+ .join("\n")
+ .gsub(PREFIX_NEWLINE) { "\n#{$1} " }
end
end
end
diff --git a/lib/gitlab/sql/recursive_cte.rb b/lib/gitlab/sql/recursive_cte.rb
index 5b1b03820a3..16ec002f139 100644
--- a/lib/gitlab/sql/recursive_cte.rb
+++ b/lib/gitlab/sql/recursive_cte.rb
@@ -52,10 +52,10 @@ module Gitlab
# Applies the CTE to the given relation, returning a new one that will
# query from it.
def apply_to(relation)
- relation.except(:where).
- with.
- recursive(to_arel).
- from(alias_to(relation.model.arel_table))
+ relation.except(:where)
+ .with
+ .recursive(to_arel)
+ .from(alias_to(relation.model.arel_table))
end
end
end
diff --git a/locale/es/gitlab.po b/locale/es/gitlab.po
index f661cbddf5f..cc44a06cbc5 100644
--- a/locale/es/gitlab.po
+++ b/locale/es/gitlab.po
@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: gitlab 1.0.0\n"
"Report-Msgid-Bugs-To: \n"
-"PO-Revision-Date: 2017-06-19 15:22-0500\n"
+"PO-Revision-Date: 2017-06-21 12:09-0500\n"
"Language-Team: Spanish\n"
"Language: es\n"
"MIME-Version: 1.0\n"
@@ -17,6 +17,16 @@ msgstr ""
"Last-Translator: Bob Van Landuyt <bob@gitlab.com>\n"
"X-Generator: Poedit 2.0.2\n"
+msgid "%d additional commit has been omitted to prevent performance issues."
+msgid_plural "%d additional commits have been omitted to prevent performance issues."
+msgstr[0] "%d cambio adicional ha sido omitido para evitar problemas de rendimiento."
+msgstr[1] "%d cambios adicionales han sido omitidos para evitar problemas de rendimiento."
+
+msgid "%d commit"
+msgid_plural "%d commits"
+msgstr[0] "%d cambio"
+msgstr[1] "%d cambios"
+
msgid "%{commit_author_link} committed %{commit_timeago}"
msgstr "%{commit_author_link} cambió %{commit_timeago}"
@@ -70,8 +80,17 @@ msgstr "Cambiar rama"
msgid "Branches"
msgstr "Ramas"
+msgid "Browse Directory"
+msgstr "Examinar directorio"
+
+msgid "Browse File"
+msgstr "Examinar archivo"
+
+msgid "Browse Files"
+msgstr "Examinar archivos"
+
msgid "Browse files"
-msgstr "Examinar los archivos"
+msgstr "Examinar archivos"
msgid "ByAuthor|by"
msgstr "por"
@@ -177,6 +196,9 @@ msgstr "Agregar %{file_name}"
msgid "Commits"
msgstr "Cambios"
+msgid "Commits feed"
+msgstr "Feed de cambios"
+
msgid "Commits|History"
msgstr "Historial"
@@ -329,6 +351,9 @@ msgstr "Error al eliminar la programación del pipeline"
msgid "Files"
msgstr "Archivos"
+msgid "Filter by commit message"
+msgstr "Filtrar por mensaje del cambio"
+
msgid "Find by path"
msgstr "Buscar por ruta"
@@ -957,6 +982,9 @@ msgstr "Hacer clic para subir"
msgid "Use your global notification setting"
msgstr "Utiliza tu configuración de notificación global"
+msgid "View open merge request"
+msgstr "Ver solicitud de fusión abierta"
+
msgid "VisibilityLevel|Internal"
msgstr "Interno"
@@ -993,12 +1021,12 @@ msgstr "Vas a transferir %{project_name_with_namespace} a otro propietario. ¿Es
msgid "You can only add files when you are on a branch"
msgstr "Solo puedes agregar archivos cuando estás en una rama"
+msgid "You have reached your project limit"
+msgstr "Has alcanzado el límite de tu proyecto"
+
msgid "You must sign in to star a project"
msgstr "Debes iniciar sesión para destacar un proyecto"
-msgid "You have reached your project limit"
-msgstr ""
-
msgid "You need permission."
msgstr "Necesitas permisos."
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index a2e32b478d3..07f9efeb495 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -8,8 +8,8 @@ msgid ""
msgstr ""
"Project-Id-Version: gitlab 1.0.0\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2017-06-19 15:13-0500\n"
-"PO-Revision-Date: 2017-06-19 15:13-0500\n"
+"POT-Creation-Date: 2017-06-19 15:50-0500\n"
+"PO-Revision-Date: 2017-06-19 15:50-0500\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"Language: \n"
@@ -18,6 +18,16 @@ msgstr ""
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\n"
+msgid "%d additional commit have been omitted to prevent performance issues."
+msgid_plural "%d additional commits have been omitted to prevent performance issues."
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "%d commit"
+msgid_plural "%d commits"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%{commit_author_link} committed %{commit_timeago}"
msgstr ""
@@ -71,6 +81,15 @@ msgstr ""
msgid "Branches"
msgstr ""
+msgid "Browse Directory"
+msgstr ""
+
+msgid "Browse File"
+msgstr ""
+
+msgid "Browse Files"
+msgstr ""
+
msgid "Browse files"
msgstr ""
@@ -178,6 +197,9 @@ msgstr ""
msgid "Commits"
msgstr ""
+msgid "Commits feed"
+msgstr ""
+
msgid "Commits|History"
msgstr ""
@@ -330,6 +352,9 @@ msgstr ""
msgid "Files"
msgstr ""
+msgid "Filter by commit message"
+msgstr ""
+
msgid "Find by path"
msgstr ""
@@ -958,6 +983,9 @@ msgstr ""
msgid "Use your global notification setting"
msgstr ""
+msgid "View open merge request"
+msgstr ""
+
msgid "VisibilityLevel|Internal"
msgstr ""
diff --git a/locale/zh_CN/gitlab.po b/locale/zh_CN/gitlab.po
index 8d994ff3c4f..8ba95093b82 100644
--- a/locale/zh_CN/gitlab.po
+++ b/locale/zh_CN/gitlab.po
@@ -1,39 +1,241 @@
-# SOME DESCRIPTIVE TITLE.
-# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
-# This file is distributed under the same license as the gitlab package.
-# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
-#
+# Huang Tao <htve@outlook.com>, 2017. #zanata
+# Xiaogang Wen <xiaogang@gitlab.com>, 2017.
msgid ""
msgstr ""
"Project-Id-Version: gitlab 1.0.0\n"
"Report-Msgid-Bugs-To: \n"
-"PO-Revision-Date: 2017-05-04 19:24-0500\n"
-"Last-Translator: HuangTao <htve@outlook.com>, 2017\n"
-"Language-Team: Chinese (China) (https://www.transifex.com/gitlab-zh/teams/7517"
-"7/zh_CN/)\n"
+"POT-Creation-Date: 2017-06-15 21:59-0500\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"Language: zh_CN\n"
-"Plural-Forms: nplurals=1; plural=0;\n"
+"PO-Revision-Date: 2017-06-19 09:57-0400\n"
+"Last-Translator: Huang Tao <htve@outlook.com>\n"
+"Language-Team: Chinese (China) (https://translate.zanata.org/project/view/GitLab)\n"
+"Language: zh-CN\n"
+"X-Generator: Zanata 3.9.6\n"
+"Plural-Forms: nplurals=1; plural=0\n"
+
+msgid "%{commit_author_link} committed %{commit_timeago}"
+msgstr "由 %{commit_author_link} 提交于 %{commit_timeago}"
+
+msgid "About auto deploy"
+msgstr "关于自动部署"
+
+msgid "Active"
+msgstr "启用"
+
+msgid "Activity"
+msgstr "活动"
+
+msgid "Add Changelog"
+msgstr "添加更新日志"
+
+msgid "Add Contribution guide"
+msgstr "添加贡献指南"
+
+msgid "Add License"
+msgstr "添加许可证"
+
+msgid "Add an SSH key to your profile to pull or push via SSH."
+msgstr "新建一个用于推送或拉取的 SSH 秘钥到账号中。"
+
+msgid "Add new directory"
+msgstr "添加目录"
+
+msgid "Archived project! Repository is read-only"
+msgstr "项目已归档!存储库为只读状态"
msgid "Are you sure you want to delete this pipeline schedule?"
+msgstr "确定要删除此流水线计划吗?"
+
+msgid "Attach a file by drag &amp; drop or %{upload_link}"
+msgstr "拖放文件到此处或者 %{upload_link}"
+
+msgid "Branch"
+msgid_plural "Branches"
+msgstr[0] "分支"
+
+msgid ""
+"Branch <strong>%{branch_name}</strong> was created. To set up auto deploy, "
+"choose a GitLab CI Yaml template and commit your changes. "
+"%{link_to_autodeploy_doc}"
msgstr ""
+"已创建分支 <strong>%{branch_name}</strong> 。如需设置自动部署, 请选择合适的 GitLab CI Yaml "
+"模板并提交更改。%{link_to_autodeploy_doc}"
+
+msgid "Branches"
+msgstr "分支"
+
+msgid "Browse files"
+msgstr "浏览文件"
msgid "ByAuthor|by"
msgstr "作者:"
+msgid "CI configuration"
+msgstr "CI 配置"
+
msgid "Cancel"
-msgstr ""
+msgstr "取消"
+
+msgid "ChangeTypeActionLabel|Pick into branch"
+msgstr "选择分支"
+
+msgid "ChangeTypeActionLabel|Revert in branch"
+msgstr "还原分支"
+
+msgid "ChangeTypeAction|Cherry-pick"
+msgstr "优选"
+
+msgid "ChangeTypeAction|Revert"
+msgstr "还原"
+
+msgid "Changelog"
+msgstr "更新日志"
+
+msgid "Charts"
+msgstr "统计图"
+
+msgid "Cherry-pick this commit"
+msgstr "优选此提交"
+
+msgid "Cherry-pick this merge request"
+msgstr "优选此合并请求"
+
+msgid "CiStatusLabel|canceled"
+msgstr "已取消"
+
+msgid "CiStatusLabel|created"
+msgstr "已创建"
+
+msgid "CiStatusLabel|failed"
+msgstr "已失败"
+
+msgid "CiStatusLabel|manual action"
+msgstr "手动操作"
+
+msgid "CiStatusLabel|passed"
+msgstr "已通过"
+
+msgid "CiStatusLabel|passed with warnings"
+msgstr "已通过但有警告"
+
+msgid "CiStatusLabel|pending"
+msgstr "等待中"
+
+msgid "CiStatusLabel|skipped"
+msgstr "已跳过"
+
+msgid "CiStatusLabel|waiting for manual action"
+msgstr "等待手动操作"
+
+msgid "CiStatusText|blocked"
+msgstr "已阻塞"
+
+msgid "CiStatusText|canceled"
+msgstr "已取消"
+
+msgid "CiStatusText|created"
+msgstr "已创建"
+
+msgid "CiStatusText|failed"
+msgstr "已失败"
+
+msgid "CiStatusText|manual"
+msgstr "手动操作"
+
+msgid "CiStatusText|passed"
+msgstr "已通过"
+
+msgid "CiStatusText|pending"
+msgstr "等待中"
+
+msgid "CiStatusText|skipped"
+msgstr "已跳过"
+
+msgid "CiStatus|running"
+msgstr "运行中"
msgid "Commit"
msgid_plural "Commits"
msgstr[0] "提交"
+msgid "Commit message"
+msgstr "提交信息"
+
+msgid "CommitBoxTitle|Commit"
+msgstr "提交"
+
+msgid "CommitMessage|Add %{file_name}"
+msgstr "添加 %{file_name}"
+
+msgid "Commits"
+msgstr "提交"
+
+msgid "Commits|History"
+msgstr "历史"
+
+msgid "Committed by"
+msgstr "提交者:"
+
+msgid "Compare"
+msgstr "比较"
+
+msgid "Contribution guide"
+msgstr "贡献指南"
+
+msgid "Contributors"
+msgstr "贡献者"
+
+msgid "Copy URL to clipboard"
+msgstr "复制 URL 到剪贴板"
+
+msgid "Copy commit SHA to clipboard"
+msgstr "复制提交 SHA 的值到剪贴板"
+
+msgid "Create New Directory"
+msgstr "创建新目录"
+
+msgid "Create directory"
+msgstr "创建目录"
+
+msgid "Create empty bare repository"
+msgstr "创建空的存储库"
+
+msgid "Create merge request"
+msgstr "创建合并请求"
+
+msgid "Create new..."
+msgstr "创建..."
+
+msgid "CreateNewFork|Fork"
+msgstr "派生"
+
+msgid "CreateTag|Tag"
+msgstr "标签"
+
msgid "Cron Timezone"
+msgstr "Cron 时区"
+
+msgid "Cron syntax"
+msgstr "Cron 语法"
+
+msgid "Custom notification events"
+msgstr "自定义通知事件"
+
+msgid ""
+"Custom notification levels are the same as participating levels. With custom "
+"notification levels you will also receive notifications for select events. "
+"To find out more, check out %{notification_link}."
msgstr ""
+"自定义通知级别继承自参与级别。使用自定义通知级别,您会收到参与级别及选定事件的通知。想了解更多信息,请查看 %{notification_link}."
+
+msgid "Cycle Analytics"
+msgstr "周期分析"
-msgid "Cycle Analytics gives an overview of how much time it takes to go from idea to production in your project."
+msgid ""
+"Cycle Analytics gives an overview of how much time it takes to go from idea "
+"to production in your project."
msgstr "周期分析概述了项目从想法到产品实现的各阶段所需的时间。"
msgid "CycleAnalyticsStage|Code"
@@ -57,30 +259,81 @@ msgstr "预发布"
msgid "CycleAnalyticsStage|Test"
msgstr "测试"
+msgid "Define a custom pattern with cron syntax"
+msgstr "使用 Cron 语法定义自定义模式"
+
msgid "Delete"
-msgstr ""
+msgstr "删除"
msgid "Deploy"
msgid_plural "Deploys"
msgstr[0] "部署"
msgid "Description"
-msgstr ""
+msgstr "描述"
+
+msgid "Directory name"
+msgstr "目录名称"
+
+msgid "Don't show again"
+msgstr "不再显示"
+
+msgid "Download"
+msgstr "下载"
+
+msgid "Download tar"
+msgstr "下载 tar"
+
+msgid "Download tar.bz2"
+msgstr "下载 tar.bz2"
+
+msgid "Download tar.gz"
+msgstr "下载 tar.gz"
+
+msgid "Download zip"
+msgstr "下载 zip"
+
+msgid "DownloadArtifacts|Download"
+msgstr "下载"
+
+msgid "DownloadCommit|Email Patches"
+msgstr "电子邮件补丁"
+
+msgid "DownloadCommit|Plain Diff"
+msgstr "差异文件"
+
+msgid "DownloadSource|Download"
+msgstr "下载"
msgid "Edit"
-msgstr ""
+msgstr "编辑"
msgid "Edit Pipeline Schedule %{id}"
-msgstr ""
+msgstr "编辑 %{id} 流水线计划"
+
+msgid "Every day (at 4:00am)"
+msgstr "每日执行(凌晨 4 点)"
+
+msgid "Every month (on the 1st at 4:00am)"
+msgstr "每月执行(每月 1 日凌晨 4 点)"
+
+msgid "Every week (Sundays at 4:00am)"
+msgstr "每周执行(周日凌晨 4 点)"
msgid "Failed to change the owner"
-msgstr ""
+msgstr "无法变更所有者"
msgid "Failed to remove the pipeline schedule"
-msgstr ""
+msgstr "无法删除流水线计划"
-msgid "Filter"
-msgstr ""
+msgid "Files"
+msgstr "文件"
+
+msgid "Find by path"
+msgstr "按路径查找"
+
+msgid "Find file"
+msgstr "查找文件"
msgid "FirstPushedBy|First"
msgstr "首次推送"
@@ -88,24 +341,70 @@ msgstr "首次推送"
msgid "FirstPushedBy|pushed by"
msgstr "推送者:"
+msgid "Fork"
+msgid_plural "Forks"
+msgstr[0] "派生"
+
+msgid "ForkedFromProjectPath|Forked from"
+msgstr "派生自"
+
msgid "From issue creation until deploy to production"
msgstr "从创建议题到部署至生产环境"
msgid "From merge request merge until deploy to production"
msgstr "从合并请求被合并后到部署至生产环境"
+msgid "Go to your fork"
+msgstr "跳转到派生项目"
+
+msgid "GoToYourFork|Fork"
+msgstr "跳转到派生项目"
+
+msgid "Home"
+msgstr "首页"
+
+msgid "Housekeeping successfully started"
+msgstr "已开始维护"
+
+msgid "Import repository"
+msgstr "导入存储库"
+
msgid "Interval Pattern"
-msgstr ""
+msgstr "循环周期"
msgid "Introducing Cycle Analytics"
msgstr "周期分析简介"
+msgid "LFSStatus|Disabled"
+msgstr "停用"
+
+msgid "LFSStatus|Enabled"
+msgstr "启用"
+
msgid "Last %d day"
msgid_plural "Last %d days"
-msgstr[0] "最后 %d 天"
+msgstr[0] "最近 %d 天"
msgid "Last Pipeline"
-msgstr ""
+msgstr "最新流水线"
+
+msgid "Last Update"
+msgstr "最后更新"
+
+msgid "Last commit"
+msgstr "最后提交"
+
+msgid "Learn more in the"
+msgstr "了解更多"
+
+msgid "Learn more in the|pipeline schedules documentation"
+msgstr "流水线计划文档"
+
+msgid "Leave group"
+msgstr "退出群组"
+
+msgid "Leave project"
+msgstr "退出项目"
msgid "Limited to showing %d event at most"
msgid_plural "Limited to showing %d events at most"
@@ -114,15 +413,45 @@ msgstr[0] "最多显示 %d 个事件"
msgid "Median"
msgstr "中位数"
+msgid "MissingSSHKeyWarningLink|add an SSH key"
+msgstr "新建 SSH 公钥"
+
msgid "New Issue"
msgid_plural "New Issues"
-msgstr[0] "新议题"
+msgstr[0] "新建议题"
msgid "New Pipeline Schedule"
-msgstr ""
+msgstr "创建流水线计划"
+
+msgid "New branch"
+msgstr "新建分支"
+
+msgid "New directory"
+msgstr "新建目录"
+
+msgid "New file"
+msgstr "新建文件"
+
+msgid "New issue"
+msgstr "新建议题"
+
+msgid "New merge request"
+msgstr "新建合并请求"
+
+msgid "New schedule"
+msgstr "新建计划"
+
+msgid "New snippet"
+msgstr "新建代码片段"
+
+msgid "New tag"
+msgstr "新建标签"
+
+msgid "No repository"
+msgstr "没有存储库"
msgid "No schedules"
-msgstr ""
+msgstr "没有计划"
msgid "Not available"
msgstr "数据不足"
@@ -130,54 +459,185 @@ msgstr "数据不足"
msgid "Not enough data"
msgstr "数据不足"
+msgid "Notification events"
+msgstr "通知事件"
+
+msgid "NotificationEvent|Close issue"
+msgstr "关闭议题"
+
+msgid "NotificationEvent|Close merge request"
+msgstr "关闭合并请求"
+
+msgid "NotificationEvent|Failed pipeline"
+msgstr "流水线失败"
+
+msgid "NotificationEvent|Merge merge request"
+msgstr "合并请求被合并"
+
+msgid "NotificationEvent|New issue"
+msgstr "新建议题"
+
+msgid "NotificationEvent|New merge request"
+msgstr "新建合并请求"
+
+msgid "NotificationEvent|New note"
+msgstr "新建评论"
+
+msgid "NotificationEvent|Reassign issue"
+msgstr "重新指派议题"
+
+msgid "NotificationEvent|Reassign merge request"
+msgstr "重新指派合并请求"
+
+msgid "NotificationEvent|Reopen issue"
+msgstr "重启议题"
+
+msgid "NotificationEvent|Successful pipeline"
+msgstr "流水线成功完成"
+
+msgid "NotificationLevel|Custom"
+msgstr "自定义"
+
+msgid "NotificationLevel|Disabled"
+msgstr "停用"
+
+msgid "NotificationLevel|Global"
+msgstr "全局"
+
+msgid "NotificationLevel|On mention"
+msgstr "提及"
+
+msgid "NotificationLevel|Participate"
+msgstr "参与"
+
+msgid "NotificationLevel|Watch"
+msgstr "关注"
+
+msgid "OfSearchInADropdown|Filter"
+msgstr "筛选"
+
msgid "OpenedNDaysAgo|Opened"
msgstr "开始于"
+msgid "Options"
+msgstr "操作"
+
msgid "Owner"
-msgstr ""
+msgstr "所有者"
+
+msgid "Pipeline"
+msgstr "流水线"
msgid "Pipeline Health"
msgstr "流水线健康指标"
msgid "Pipeline Schedule"
-msgstr ""
+msgstr "流水线计划"
msgid "Pipeline Schedules"
-msgstr ""
+msgstr "流水线计划"
msgid "PipelineSchedules|Activated"
-msgstr ""
+msgstr "是否启用"
msgid "PipelineSchedules|Active"
-msgstr ""
+msgstr "已启用"
msgid "PipelineSchedules|All"
-msgstr ""
+msgstr "所有"
msgid "PipelineSchedules|Inactive"
-msgstr ""
+msgstr "未启用"
msgid "PipelineSchedules|Next Run"
-msgstr ""
+msgstr "下次运行时间"
msgid "PipelineSchedules|None"
-msgstr ""
+msgstr "无"
msgid "PipelineSchedules|Provide a short description for this pipeline"
-msgstr ""
+msgstr "为此流水线提供简短描述"
msgid "PipelineSchedules|Take ownership"
-msgstr ""
+msgstr "取得所有者"
msgid "PipelineSchedules|Target"
-msgstr ""
+msgstr "目标"
+
+msgid "PipelineSheduleIntervalPattern|Custom"
+msgstr "自定义"
+
+msgid "Pipeline|with stage"
+msgstr "于阶段"
+
+msgid "Pipeline|with stages"
+msgstr "于阶段"
+
+msgid "Project '%{project_name}' queued for deletion."
+msgstr "项目 '%{project_name}' 已进入删除队列。"
+
+msgid "Project '%{project_name}' was successfully created."
+msgstr "项目 '%{project_name}' 已创建成功。"
+
+msgid "Project '%{project_name}' was successfully updated."
+msgstr "项目 '%{project_name}' 已更新完成。"
+
+msgid "Project '%{project_name}' will be deleted."
+msgstr "项目 '%{project_name}' 将被删除。"
+
+msgid "Project access must be granted explicitly to each user."
+msgstr "项目访问权限必须明确授权给每个用户。"
+
+msgid "Project export could not be deleted."
+msgstr "无法删除项目导出。"
+
+msgid "Project export has been deleted."
+msgstr "项目导出已被删除。"
+
+msgid ""
+"Project export link has expired. Please generate a new export from your "
+"project settings."
+msgstr "项目导出链接已过期。请从项目设置中重新生成项目导出。"
+
+msgid "Project export started. A download link will be sent by email."
+msgstr "项目导出已开始。下载链接将通过电子邮件发送。"
+
+msgid "Project home"
+msgstr "项目首页"
+
+msgid "ProjectFeature|Disabled"
+msgstr "停用"
+
+msgid "ProjectFeature|Everyone with access"
+msgstr "任何对项目有访问权的人"
+
+msgid "ProjectFeature|Only team members"
+msgstr "只限团队成员"
+
+msgid "ProjectFileTree|Name"
+msgstr "名称"
+
+msgid "ProjectLastActivity|Never"
+msgstr "从未"
msgid "ProjectLifecycle|Stage"
-msgstr "项目生命周期"
+msgstr "阶段"
+
+msgid "ProjectNetworkGraph|Graph"
+msgstr "分支图"
msgid "Read more"
msgstr "了解更多"
+msgid "Readme"
+msgstr "自述文件"
+
+msgid "RefSwitcher|Branches"
+msgstr "分支"
+
+msgid "RefSwitcher|Tags"
+msgstr "标签"
+
msgid "Related Commits"
msgstr "相关的提交"
@@ -196,58 +656,163 @@ msgstr "相关的合并请求"
msgid "Related Merged Requests"
msgstr "相关已合并的合并请求"
+msgid "Remind later"
+msgstr "稍后提醒"
+
+msgid "Remove project"
+msgstr "删除项目"
+
+msgid "Request Access"
+msgstr "申请权限"
+
+msgid "Revert this commit"
+msgstr "还原此提交"
+
+msgid "Revert this merge request"
+msgstr "还原此合并请求"
+
msgid "Save pipeline schedule"
-msgstr ""
+msgstr "保存流水线计划"
msgid "Schedule a new pipeline"
-msgstr ""
+msgstr "新建流水线计划"
+
+msgid "Scheduling Pipelines"
+msgstr "流水线计划"
+
+msgid "Search branches and tags"
+msgstr "搜索分支和标签"
+
+msgid "Select Archive Format"
+msgstr "选择下载格式"
msgid "Select a timezone"
-msgstr ""
+msgstr "选择时区"
msgid "Select target branch"
-msgstr ""
+msgstr "选择目标分支"
+
+msgid "Set a password on your account to pull or push via %{protocol}"
+msgstr "为账号创建一个用于推送或拉取的 %{protocol} 密码。"
+
+msgid "Set up CI"
+msgstr "设置 CI"
+
+msgid "Set up Koding"
+msgstr "设置 Koding"
+
+msgid "Set up auto deploy"
+msgstr "设置自动部署"
+
+msgid "SetPasswordToCloneLink|set a password"
+msgstr "设置密码"
msgid "Showing %d event"
msgid_plural "Showing %d events"
msgstr[0] "显示 %d 个事件"
+msgid "Source code"
+msgstr "源代码"
+
+msgid "StarProject|Star"
+msgstr "星标"
+
+msgid "Start a %{new_merge_request} with these changes"
+msgstr "由此更改 %{new_merge_request}"
+
+msgid "Switch branch/tag"
+msgstr "切换分支/标签"
+
+msgid "Tag"
+msgid_plural "Tags"
+msgstr[0] "标签"
+
+msgid "Tags"
+msgstr "标签"
+
msgid "Target Branch"
-msgstr ""
+msgstr "目标分支"
-msgid "The coding stage shows the time from the first commit to creating the merge request. The data will automatically be added here once you create your first merge request."
+msgid ""
+"The coding stage shows the time from the first commit to creating the merge "
+"request. The data will automatically be added here once you create your "
+"first merge request."
msgstr "编码阶段概述了从第一次提交到创建合并请求的时间。创建第一个合并请求后,数据将自动添加到此处。"
msgid "The collection of events added to the data gathered for that stage."
-msgstr "与该阶段相关的事件。"
+msgstr "与该阶段相关的事件集合。"
-msgid "The issue stage shows the time it takes from creating an issue to assigning the issue to a milestone, or add the issue to a list on your Issue Board. Begin creating issues to see data for this stage."
-msgstr "议题阶段概述了从创建议题到将议题设置里程碑或将议题添加到议题看板的时间。开始创建议题以查看此阶段的数据。"
+msgid "The fork relationship has been removed."
+msgstr "派生关系已被删除。"
+
+msgid ""
+"The issue stage shows the time it takes from creating an issue to assigning "
+"the issue to a milestone, or add the issue to a list on your Issue Board. "
+"Begin creating issues to see data for this stage."
+msgstr "议题阶段概述了从创建议题到将议题添加到里程碑或议题看板所花费的时间。创建第一个议题后,数据将自动添加到此处.。"
msgid "The phase of the development lifecycle."
msgstr "项目生命周期中的各个阶段。"
-msgid "The planning stage shows the time from the previous step to pushing your first commit. This time will be added automatically once you push your first commit."
-msgstr "计划阶段概述了从议题添加到日程后到推送首次提交的时间。当首次推送提交后,数据将自动添加到此处。"
+msgid ""
+"The pipelines schedule runs pipelines in the future, repeatedly, for "
+"specific branches or tags. Those scheduled pipelines will inherit limited "
+"project access based on their associated user."
+msgstr "流水线计划会周期性重复运行指定分支或标签的流水线。这些流水线将根据其关联用户继承有限的项目访问权限。"
-msgid "The production stage shows the total time it takes between creating an issue and deploying the code to production. The data will be automatically added once you have completed the full idea to production cycle."
+msgid ""
+"The planning stage shows the time from the previous step to pushing your "
+"first commit. This time will be added automatically once you push your first "
+"commit."
+msgstr "计划阶段概述了从议题添加到日程到推送首次提交的时间。当首次推送提交后,数据将自动添加到此处。"
+
+msgid ""
+"The production stage shows the total time it takes between creating an issue "
+"and deploying the code to production. The data will be automatically added "
+"once you have completed the full idea to production cycle."
msgstr "生产阶段概述了从创建一个议题到将代码部署到生产环境的总时间。当完成想法到部署生产的循环,数据将自动添加到此处。"
-msgid "The review stage shows the time from creating the merge request to merging it. The data will automatically be added after you merge your first merge request."
+msgid "The project can be accessed by any logged in user."
+msgstr "该项目允许已登录的用户访问。"
+
+msgid "The project can be accessed without any authentication."
+msgstr "该项目允许任何人访问。"
+
+msgid "The repository for this project does not exist."
+msgstr "此项目的存储库不存在。"
+
+msgid ""
+"The review stage shows the time from creating the merge request to merging "
+"it. The data will automatically be added after you merge your first merge "
+"request."
msgstr "评审阶段概述了从创建合并请求到被合并的时间。当创建第一个合并请求后,数据将自动添加到此处。"
-msgid "The staging stage shows the time between merging the MR and deploying code to the production environment. The data will be automatically added once you deploy to production for the first time."
+msgid ""
+"The staging stage shows the time between merging the MR and deploying code "
+"to the production environment. The data will be automatically added once you "
+"deploy to production for the first time."
msgstr "预发布阶段概述了从合并请求被合并到部署至生产环境的总时间。首次部署到生产环境后,数据将自动添加到此处。"
-msgid "The testing stage shows the time GitLab CI takes to run every pipeline for the related merge request. The data will automatically be added after your first pipeline finishes running."
-msgstr "测试阶段概述了GitLab CI为相关合并请求运行每个流水线所需的时间。当第一个流水线运行完成后,数据将自动添加到此处。"
+msgid ""
+"The testing stage shows the time GitLab CI takes to run every pipeline for "
+"the related merge request. The data will automatically be added after your "
+"first pipeline finishes running."
+msgstr "测试阶段概述了 GitLab CI 为相关合并请求运行每个流水线所需的时间。当第一个流水线运行完成后,数据将自动添加到此处。"
msgid "The time taken by each data entry gathered by that stage."
msgstr "该阶段每条数据所花的时间"
-msgid "The value lying at the midpoint of a series of observed values. E.g., between 3, 5, 9, the median is 5. Between 3, 5, 7, 8, the median is (5+7)/2 = 6."
+msgid ""
+"The value lying at the midpoint of a series of observed values. E.g., "
+"between 3, 5, 9, the median is 5. Between 3, 5, 7, 8, the median is (5+7)/2 ="
+" 6."
msgstr "中位数是一个数列中最中间的值。例如在 3、5、9 之间,中位数是 5。在 3、5、7、8 之间,中位数是 (5 + 7)/ 2 = 6。"
+msgid ""
+"This means you can not push code until you create an empty repository or "
+"import existing one."
+msgstr "在创建一个空的存储库或导入现有存储库之前,将无法推送代码。"
+
msgid "Time before an issue gets scheduled"
msgstr "议题被列入日程表的时间"
@@ -260,6 +825,129 @@ msgstr "从创建合并请求到被合并或关闭的时间"
msgid "Time until first merge request"
msgstr "创建第一个合并请求之前的时间"
+msgid "Timeago|%s days ago"
+msgstr " %s 天前"
+
+msgid "Timeago|%s days remaining"
+msgstr "剩余 %s 天"
+
+msgid "Timeago|%s hours remaining"
+msgstr "剩余 %s 小时"
+
+msgid "Timeago|%s minutes ago"
+msgstr " %s 分钟前"
+
+msgid "Timeago|%s minutes remaining"
+msgstr "剩余 %s 分钟"
+
+msgid "Timeago|%s months ago"
+msgstr " %s 个月前"
+
+msgid "Timeago|%s months remaining"
+msgstr "剩余 %s 月"
+
+msgid "Timeago|%s seconds remaining"
+msgstr "剩余 %s 秒"
+
+msgid "Timeago|%s weeks ago"
+msgstr " %s 星期前"
+
+msgid "Timeago|%s weeks remaining"
+msgstr "剩余 %s 星期"
+
+msgid "Timeago|%s years ago"
+msgstr " %s 年前"
+
+msgid "Timeago|%s years remaining"
+msgstr "剩余 %s 年"
+
+msgid "Timeago|1 day remaining"
+msgstr "剩余 1 天"
+
+msgid "Timeago|1 hour remaining"
+msgstr "剩余 1 小时"
+
+msgid "Timeago|1 minute remaining"
+msgstr "剩余 1 分钟"
+
+msgid "Timeago|1 month remaining"
+msgstr "剩余 1 个月"
+
+msgid "Timeago|1 week remaining"
+msgstr "剩余 1 星期"
+
+msgid "Timeago|1 year remaining"
+msgstr "剩余 1 年"
+
+msgid "Timeago|Past due"
+msgstr "逾期"
+
+msgid "Timeago|a day ago"
+msgstr " 1 天前"
+
+msgid "Timeago|a month ago"
+msgstr " 1 个月前"
+
+msgid "Timeago|a week ago"
+msgstr " 1 星期前"
+
+msgid "Timeago|a while"
+msgstr "刚刚"
+
+msgid "Timeago|a year ago"
+msgstr " 1 年前"
+
+msgid "Timeago|about %s hours ago"
+msgstr "约 %s 小时前"
+
+msgid "Timeago|about a minute ago"
+msgstr "约 1 分钟前"
+
+msgid "Timeago|about an hour ago"
+msgstr "约 1 小时前"
+
+msgid "Timeago|in %s days"
+msgstr " %s 天后"
+
+msgid "Timeago|in %s hours"
+msgstr " %s 小时后"
+
+msgid "Timeago|in %s minutes"
+msgstr " %s 分钟后"
+
+msgid "Timeago|in %s months"
+msgstr " %s 个月后"
+
+msgid "Timeago|in %s seconds"
+msgstr " %s 秒后"
+
+msgid "Timeago|in %s weeks"
+msgstr " %s 星期后"
+
+msgid "Timeago|in %s years"
+msgstr " %s 年后"
+
+msgid "Timeago|in 1 day"
+msgstr " 1 天后"
+
+msgid "Timeago|in 1 hour"
+msgstr " 1 小时后"
+
+msgid "Timeago|in 1 minute"
+msgstr " 1 分钟后"
+
+msgid "Timeago|in 1 month"
+msgstr " 1 月后"
+
+msgid "Timeago|in 1 week"
+msgstr " 1 星期后"
+
+msgid "Timeago|in 1 year"
+msgstr " 1 年后"
+
+msgid "Timeago|less than a minute ago"
+msgstr "不到 1 分钟前"
+
msgid "Time|hr"
msgid_plural "Time|hrs"
msgstr[0] "小时"
@@ -277,18 +965,108 @@ msgstr "总时间"
msgid "Total test time for all commits/merges"
msgstr "所有提交和合并的总测试时间"
+msgid "Unstar"
+msgstr "取消星标"
+
+msgid "Upload New File"
+msgstr "上传新文件"
+
+msgid "Upload file"
+msgstr "上传文件"
+
+msgid "Use your global notification setting"
+msgstr "使用全局通知设置"
+
+msgid "VisibilityLevel|Internal"
+msgstr "内部"
+
+msgid "VisibilityLevel|Private"
+msgstr "私有"
+
+msgid "VisibilityLevel|Public"
+msgstr "公开"
+
msgid "Want to see the data? Please ask an administrator for access."
msgstr "权限不足。如需查看相关数据,请向管理员申请权限。"
msgid "We don't have enough data to show this stage."
msgstr "该阶段的数据不足,无法显示。"
+msgid "Withdraw Access Request"
+msgstr "取消权限申请"
+
+msgid ""
+"You are going to remove %{project_name_with_namespace}.\n"
+"Removed project CANNOT be restored!\n"
+"Are you ABSOLUTELY sure?"
+msgstr "即将要删除 %{project_name_with_namespace}。\n"
+"已删除的项目无法恢复!\n"
+"确定继续吗?"
+
+msgid ""
+"You are going to remove the fork relationship to source project "
+"%{forked_from_project}. Are you ABSOLUTELY sure?"
+msgstr "即将删除与源项目 %{forked_from_project} 的派生关系。确定继续吗?"
+
+msgid ""
+"You are going to transfer %{project_name_with_namespace} to another owner. "
+"Are you ABSOLUTELY sure?"
+msgstr "即将 %{project_name_with_namespace} 转移给另一个所有者。确定继续吗?"
+
+msgid "You can only add files when you are on a branch"
+msgstr "只能在分支上添加文件"
+
msgid "You have reached your project limit"
-msgstr ""
+msgstr "您已达到项目数量限制"
+
+msgid "You must sign in to star a project"
+msgstr "必须登录才能对项目加星标"
msgid "You need permission."
-msgstr "您需要相关的权限。"
+msgstr "需要相关的权限。"
+
+msgid "You will not get any notifications via email"
+msgstr "不会收到任何通知邮件"
+
+msgid "You will only receive notifications for the events you choose"
+msgstr "只接收选择的事件通知"
+
+msgid ""
+"You will only receive notifications for threads you have participated in"
+msgstr "只接收参与的主题的通知"
+
+msgid "You will receive notifications for any activity"
+msgstr "接收所有活动的通知"
+
+msgid ""
+"You will receive notifications only for comments in which you were "
+"@mentioned"
+msgstr "只接收评论中提及(@)您的通知"
+
+msgid ""
+"You won't be able to pull or push project code via %{protocol} until you "
+"%{set_password_link} on your account"
+msgstr "在账号中 %{set_password_link} 之前将无法通过 %{protocol} 拉取或推送代码。"
+
+msgid ""
+"You won't be able to pull or push project code via SSH until you "
+"%{add_ssh_key_link} to your profile"
+msgstr "在账号中 %{add_ssh_key_link} 之前将无法通过 SSH 拉取或推送代码。"
+
+msgid "Your name"
+msgstr "您的名字"
msgid "day"
msgid_plural "days"
msgstr[0] "天"
+
+msgid "new merge request"
+msgstr "新建合并请求"
+
+msgid "notification emails"
+msgstr "通知邮件"
+
+msgid "parent"
+msgid_plural "parents"
+msgstr[0] "父级"
+
diff --git a/spec/controllers/admin/users_controller_spec.rb b/spec/controllers/admin/users_controller_spec.rb
index 7d6c317482f..69928a906c6 100644
--- a/spec/controllers/admin/users_controller_spec.rb
+++ b/spec/controllers/admin/users_controller_spec.rb
@@ -116,8 +116,8 @@ describe Admin::UsersController do
it 'displays an alert' do
go
- expect(flash[:notice]).
- to eq 'Two-factor Authentication has been disabled for this user'
+ expect(flash[:notice])
+ .to eq 'Two-factor Authentication has been disabled for this user'
end
def go
diff --git a/spec/controllers/application_controller_spec.rb b/spec/controllers/application_controller_spec.rb
index 3f99e2ff596..a2720c9b81e 100644
--- a/spec/controllers/application_controller_spec.rb
+++ b/spec/controllers/application_controller_spec.rb
@@ -99,6 +99,36 @@ describe ApplicationController do
end
end
+ describe 'response format' do
+ controller(described_class) do
+ def index
+ respond_to do |format|
+ format.json do
+ head :ok
+ end
+ end
+ end
+ end
+
+ context 'when format is handled' do
+ let(:requested_format) { :json }
+
+ it 'returns 200 response' do
+ get :index, private_token: user.private_token, format: requested_format
+
+ expect(response).to have_http_status 200
+ end
+ end
+
+ context 'when format is not handled' do
+ it 'returns 404 response' do
+ get :index, private_token: user.private_token
+
+ expect(response).to have_http_status 404
+ end
+ end
+ end
+
describe '#authenticate_user_from_rss_token' do
describe "authenticating a user from an RSS token" do
controller(described_class) do
diff --git a/spec/controllers/import/bitbucket_controller_spec.rb b/spec/controllers/import/bitbucket_controller_spec.rb
index 0be7bc6a045..8ef10dabd4c 100644
--- a/spec/controllers/import/bitbucket_controller_spec.rb
+++ b/spec/controllers/import/bitbucket_controller_spec.rb
@@ -31,8 +31,8 @@ describe Import::BitbucketController do
expires_at: expires_at,
expires_in: expires_in,
refresh_token: refresh_token)
- allow_any_instance_of(OAuth2::Client).
- to receive(:get_token).and_return(access_token)
+ allow_any_instance_of(OAuth2::Client)
+ .to receive(:get_token).and_return(access_token)
stub_omniauth_provider('bitbucket')
get :callback
@@ -93,9 +93,9 @@ describe Import::BitbucketController do
context "when the repository owner is the Bitbucket user" do
context "when the Bitbucket user and GitLab user's usernames match" do
it "takes the current user's namespace" do
- expect(Gitlab::BitbucketImport::ProjectCreator).
- to receive(:new).with(bitbucket_repo, bitbucket_repo.name, user.namespace, user, access_params).
- and_return(double(execute: true))
+ expect(Gitlab::BitbucketImport::ProjectCreator)
+ .to receive(:new).with(bitbucket_repo, bitbucket_repo.name, user.namespace, user, access_params)
+ .and_return(double(execute: true))
post :create, format: :js
end
@@ -105,9 +105,9 @@ describe Import::BitbucketController do
let(:bitbucket_username) { "someone_else" }
it "takes the current user's namespace" do
- expect(Gitlab::BitbucketImport::ProjectCreator).
- to receive(:new).with(bitbucket_repo, bitbucket_repo.name, user.namespace, user, access_params).
- and_return(double(execute: true))
+ expect(Gitlab::BitbucketImport::ProjectCreator)
+ .to receive(:new).with(bitbucket_repo, bitbucket_repo.name, user.namespace, user, access_params)
+ .and_return(double(execute: true))
post :create, format: :js
end
@@ -141,9 +141,9 @@ describe Import::BitbucketController do
end
it "takes the existing namespace" do
- expect(Gitlab::BitbucketImport::ProjectCreator).
- to receive(:new).with(bitbucket_repo, bitbucket_repo.name, existing_namespace, user, access_params).
- and_return(double(execute: true))
+ expect(Gitlab::BitbucketImport::ProjectCreator)
+ .to receive(:new).with(bitbucket_repo, bitbucket_repo.name, existing_namespace, user, access_params)
+ .and_return(double(execute: true))
post :create, format: :js
end
@@ -151,8 +151,8 @@ describe Import::BitbucketController do
context "when the namespace is not owned by the GitLab user" do
it "doesn't create a project" do
- expect(Gitlab::BitbucketImport::ProjectCreator).
- not_to receive(:new)
+ expect(Gitlab::BitbucketImport::ProjectCreator)
+ .not_to receive(:new)
post :create, format: :js
end
@@ -162,16 +162,16 @@ describe Import::BitbucketController do
context "when a namespace with the Bitbucket user's username doesn't exist" do
context "when current user can create namespaces" do
it "creates the namespace" do
- expect(Gitlab::BitbucketImport::ProjectCreator).
- to receive(:new).and_return(double(execute: true))
+ expect(Gitlab::BitbucketImport::ProjectCreator)
+ .to receive(:new).and_return(double(execute: true))
expect { post :create, format: :js }.to change(Namespace, :count).by(1)
end
it "takes the new namespace" do
- expect(Gitlab::BitbucketImport::ProjectCreator).
- to receive(:new).with(bitbucket_repo, bitbucket_repo.name, an_instance_of(Group), user, access_params).
- and_return(double(execute: true))
+ expect(Gitlab::BitbucketImport::ProjectCreator)
+ .to receive(:new).with(bitbucket_repo, bitbucket_repo.name, an_instance_of(Group), user, access_params)
+ .and_return(double(execute: true))
post :create, format: :js
end
@@ -183,16 +183,16 @@ describe Import::BitbucketController do
end
it "doesn't create the namespace" do
- expect(Gitlab::BitbucketImport::ProjectCreator).
- to receive(:new).and_return(double(execute: true))
+ expect(Gitlab::BitbucketImport::ProjectCreator)
+ .to receive(:new).and_return(double(execute: true))
expect { post :create, format: :js }.not_to change(Namespace, :count)
end
it "takes the current user's namespace" do
- expect(Gitlab::BitbucketImport::ProjectCreator).
- to receive(:new).with(bitbucket_repo, bitbucket_repo.name, user.namespace, user, access_params).
- and_return(double(execute: true))
+ expect(Gitlab::BitbucketImport::ProjectCreator)
+ .to receive(:new).with(bitbucket_repo, bitbucket_repo.name, user.namespace, user, access_params)
+ .and_return(double(execute: true))
post :create, format: :js
end
@@ -210,9 +210,9 @@ describe Import::BitbucketController do
end
it 'takes the selected namespace and name' do
- expect(Gitlab::BitbucketImport::ProjectCreator).
- to receive(:new).with(bitbucket_repo, test_name, nested_namespace, user, access_params).
- and_return(double(execute: true))
+ expect(Gitlab::BitbucketImport::ProjectCreator)
+ .to receive(:new).with(bitbucket_repo, test_name, nested_namespace, user, access_params)
+ .and_return(double(execute: true))
post :create, { target_namespace: nested_namespace.full_path, new_name: test_name, format: :js }
end
@@ -222,26 +222,26 @@ describe Import::BitbucketController do
let(:test_name) { 'test_name' }
it 'takes the selected namespace and name' do
- expect(Gitlab::BitbucketImport::ProjectCreator).
- to receive(:new).with(bitbucket_repo, test_name, kind_of(Namespace), user, access_params).
- and_return(double(execute: true))
+ expect(Gitlab::BitbucketImport::ProjectCreator)
+ .to receive(:new).with(bitbucket_repo, test_name, kind_of(Namespace), user, access_params)
+ .and_return(double(execute: true))
post :create, { target_namespace: 'foo/bar', new_name: test_name, format: :js }
end
it 'creates the namespaces' do
- allow(Gitlab::BitbucketImport::ProjectCreator).
- to receive(:new).with(bitbucket_repo, test_name, kind_of(Namespace), user, access_params).
- and_return(double(execute: true))
+ allow(Gitlab::BitbucketImport::ProjectCreator)
+ .to receive(:new).with(bitbucket_repo, test_name, kind_of(Namespace), user, access_params)
+ .and_return(double(execute: true))
expect { post :create, { target_namespace: 'foo/bar', new_name: test_name, format: :js } }
.to change { Namespace.count }.by(2)
end
it 'new namespace has the right parent' do
- allow(Gitlab::BitbucketImport::ProjectCreator).
- to receive(:new).with(bitbucket_repo, test_name, kind_of(Namespace), user, access_params).
- and_return(double(execute: true))
+ allow(Gitlab::BitbucketImport::ProjectCreator)
+ .to receive(:new).with(bitbucket_repo, test_name, kind_of(Namespace), user, access_params)
+ .and_return(double(execute: true))
post :create, { target_namespace: 'foo/bar', new_name: test_name, format: :js }
@@ -254,17 +254,17 @@ describe Import::BitbucketController do
let!(:parent_namespace) { create(:group, name: 'foo', owner: user) }
it 'takes the selected namespace and name' do
- expect(Gitlab::BitbucketImport::ProjectCreator).
- to receive(:new).with(bitbucket_repo, test_name, kind_of(Namespace), user, access_params).
- and_return(double(execute: true))
+ expect(Gitlab::BitbucketImport::ProjectCreator)
+ .to receive(:new).with(bitbucket_repo, test_name, kind_of(Namespace), user, access_params)
+ .and_return(double(execute: true))
post :create, { target_namespace: 'foo/foobar/bar', new_name: test_name, format: :js }
end
it 'creates the namespaces' do
- allow(Gitlab::BitbucketImport::ProjectCreator).
- to receive(:new).with(bitbucket_repo, test_name, kind_of(Namespace), user, access_params).
- and_return(double(execute: true))
+ allow(Gitlab::BitbucketImport::ProjectCreator)
+ .to receive(:new).with(bitbucket_repo, test_name, kind_of(Namespace), user, access_params)
+ .and_return(double(execute: true))
expect { post :create, { target_namespace: 'foo/foobar/bar', new_name: test_name, format: :js } }
.to change { Namespace.count }.by(2)
diff --git a/spec/controllers/import/github_controller_spec.rb b/spec/controllers/import/github_controller_spec.rb
index 95696e14b6c..45c3fa075ef 100644
--- a/spec/controllers/import/github_controller_spec.rb
+++ b/spec/controllers/import/github_controller_spec.rb
@@ -21,10 +21,10 @@ describe Import::GithubController do
describe "GET callback" do
it "updates access token" do
token = "asdasd12345"
- allow_any_instance_of(Gitlab::GithubImport::Client).
- to receive(:get_token).and_return(token)
- allow_any_instance_of(Gitlab::GithubImport::Client).
- to receive(:github_options).and_return({})
+ allow_any_instance_of(Gitlab::GithubImport::Client)
+ .to receive(:get_token).and_return(token)
+ allow_any_instance_of(Gitlab::GithubImport::Client)
+ .to receive(:github_options).and_return({})
stub_omniauth_provider('github')
get :callback
diff --git a/spec/controllers/import/gitlab_controller_spec.rb b/spec/controllers/import/gitlab_controller_spec.rb
index 3afd09063d7..997107dadea 100644
--- a/spec/controllers/import/gitlab_controller_spec.rb
+++ b/spec/controllers/import/gitlab_controller_spec.rb
@@ -18,8 +18,8 @@ describe Import::GitlabController do
describe "GET callback" do
it "updates access token" do
- allow_any_instance_of(Gitlab::GitlabImport::Client).
- to receive(:get_token).and_return(token)
+ allow_any_instance_of(Gitlab::GitlabImport::Client)
+ .to receive(:get_token).and_return(token)
stub_omniauth_provider('gitlab')
get :callback
@@ -78,9 +78,9 @@ describe Import::GitlabController do
context "when the repository owner is the GitLab.com user" do
context "when the GitLab.com user and GitLab server user's usernames match" do
it "takes the current user's namespace" do
- expect(Gitlab::GitlabImport::ProjectCreator).
- to receive(:new).with(gitlab_repo, user.namespace, user, access_params).
- and_return(double(execute: true))
+ expect(Gitlab::GitlabImport::ProjectCreator)
+ .to receive(:new).with(gitlab_repo, user.namespace, user, access_params)
+ .and_return(double(execute: true))
post :create, format: :js
end
@@ -90,9 +90,9 @@ describe Import::GitlabController do
let(:gitlab_username) { "someone_else" }
it "takes the current user's namespace" do
- expect(Gitlab::GitlabImport::ProjectCreator).
- to receive(:new).with(gitlab_repo, user.namespace, user, access_params).
- and_return(double(execute: true))
+ expect(Gitlab::GitlabImport::ProjectCreator)
+ .to receive(:new).with(gitlab_repo, user.namespace, user, access_params)
+ .and_return(double(execute: true))
post :create, format: :js
end
@@ -116,9 +116,9 @@ describe Import::GitlabController do
end
it "takes the existing namespace" do
- expect(Gitlab::GitlabImport::ProjectCreator).
- to receive(:new).with(gitlab_repo, existing_namespace, user, access_params).
- and_return(double(execute: true))
+ expect(Gitlab::GitlabImport::ProjectCreator)
+ .to receive(:new).with(gitlab_repo, existing_namespace, user, access_params)
+ .and_return(double(execute: true))
post :create, format: :js
end
@@ -126,8 +126,8 @@ describe Import::GitlabController do
context "when the namespace is not owned by the GitLab server user" do
it "doesn't create a project" do
- expect(Gitlab::GitlabImport::ProjectCreator).
- not_to receive(:new)
+ expect(Gitlab::GitlabImport::ProjectCreator)
+ .not_to receive(:new)
post :create, format: :js
end
@@ -137,16 +137,16 @@ describe Import::GitlabController do
context "when a namespace with the GitLab.com user's username doesn't exist" do
context "when current user can create namespaces" do
it "creates the namespace" do
- expect(Gitlab::GitlabImport::ProjectCreator).
- to receive(:new).and_return(double(execute: true))
+ expect(Gitlab::GitlabImport::ProjectCreator)
+ .to receive(:new).and_return(double(execute: true))
expect { post :create, format: :js }.to change(Namespace, :count).by(1)
end
it "takes the new namespace" do
- expect(Gitlab::GitlabImport::ProjectCreator).
- to receive(:new).with(gitlab_repo, an_instance_of(Group), user, access_params).
- and_return(double(execute: true))
+ expect(Gitlab::GitlabImport::ProjectCreator)
+ .to receive(:new).with(gitlab_repo, an_instance_of(Group), user, access_params)
+ .and_return(double(execute: true))
post :create, format: :js
end
@@ -158,16 +158,16 @@ describe Import::GitlabController do
end
it "doesn't create the namespace" do
- expect(Gitlab::GitlabImport::ProjectCreator).
- to receive(:new).and_return(double(execute: true))
+ expect(Gitlab::GitlabImport::ProjectCreator)
+ .to receive(:new).and_return(double(execute: true))
expect { post :create, format: :js }.not_to change(Namespace, :count)
end
it "takes the current user's namespace" do
- expect(Gitlab::GitlabImport::ProjectCreator).
- to receive(:new).with(gitlab_repo, user.namespace, user, access_params).
- and_return(double(execute: true))
+ expect(Gitlab::GitlabImport::ProjectCreator)
+ .to receive(:new).with(gitlab_repo, user.namespace, user, access_params)
+ .and_return(double(execute: true))
post :create, format: :js
end
@@ -183,9 +183,9 @@ describe Import::GitlabController do
end
it 'takes the selected namespace and name' do
- expect(Gitlab::GitlabImport::ProjectCreator).
- to receive(:new).with(gitlab_repo, nested_namespace, user, access_params).
- and_return(double(execute: true))
+ expect(Gitlab::GitlabImport::ProjectCreator)
+ .to receive(:new).with(gitlab_repo, nested_namespace, user, access_params)
+ .and_return(double(execute: true))
post :create, { target_namespace: nested_namespace.full_path, format: :js }
end
@@ -195,26 +195,26 @@ describe Import::GitlabController do
let(:test_name) { 'test_name' }
it 'takes the selected namespace and name' do
- expect(Gitlab::GitlabImport::ProjectCreator).
- to receive(:new).with(gitlab_repo, kind_of(Namespace), user, access_params).
- and_return(double(execute: true))
+ expect(Gitlab::GitlabImport::ProjectCreator)
+ .to receive(:new).with(gitlab_repo, kind_of(Namespace), user, access_params)
+ .and_return(double(execute: true))
post :create, { target_namespace: 'foo/bar', format: :js }
end
it 'creates the namespaces' do
- allow(Gitlab::GitlabImport::ProjectCreator).
- to receive(:new).with(gitlab_repo, kind_of(Namespace), user, access_params).
- and_return(double(execute: true))
+ allow(Gitlab::GitlabImport::ProjectCreator)
+ .to receive(:new).with(gitlab_repo, kind_of(Namespace), user, access_params)
+ .and_return(double(execute: true))
expect { post :create, { target_namespace: 'foo/bar', format: :js } }
.to change { Namespace.count }.by(2)
end
it 'new namespace has the right parent' do
- allow(Gitlab::GitlabImport::ProjectCreator).
- to receive(:new).with(gitlab_repo, kind_of(Namespace), user, access_params).
- and_return(double(execute: true))
+ allow(Gitlab::GitlabImport::ProjectCreator)
+ .to receive(:new).with(gitlab_repo, kind_of(Namespace), user, access_params)
+ .and_return(double(execute: true))
post :create, { target_namespace: 'foo/bar', format: :js }
@@ -227,17 +227,17 @@ describe Import::GitlabController do
let!(:parent_namespace) { create(:group, name: 'foo', owner: user) }
it 'takes the selected namespace and name' do
- expect(Gitlab::GitlabImport::ProjectCreator).
- to receive(:new).with(gitlab_repo, kind_of(Namespace), user, access_params).
- and_return(double(execute: true))
+ expect(Gitlab::GitlabImport::ProjectCreator)
+ .to receive(:new).with(gitlab_repo, kind_of(Namespace), user, access_params)
+ .and_return(double(execute: true))
post :create, { target_namespace: 'foo/foobar/bar', format: :js }
end
it 'creates the namespaces' do
- allow(Gitlab::GitlabImport::ProjectCreator).
- to receive(:new).with(gitlab_repo, kind_of(Namespace), user, access_params).
- and_return(double(execute: true))
+ allow(Gitlab::GitlabImport::ProjectCreator)
+ .to receive(:new).with(gitlab_repo, kind_of(Namespace), user, access_params)
+ .and_return(double(execute: true))
expect { post :create, { target_namespace: 'foo/foobar/bar', format: :js } }
.to change { Namespace.count }.by(2)
diff --git a/spec/controllers/projects/blob_controller_spec.rb b/spec/controllers/projects/blob_controller_spec.rb
index 3b3caa9d3e6..c20cf6a4291 100644
--- a/spec/controllers/projects/blob_controller_spec.rb
+++ b/spec/controllers/projects/blob_controller_spec.rb
@@ -47,8 +47,8 @@ describe Projects::BlobController do
context 'redirect to tree' do
let(:id) { 'markdown/doc' }
it 'redirects' do
- expect(subject).
- to redirect_to("/#{project.path_with_namespace}/tree/markdown/doc")
+ expect(subject)
+ .to redirect_to("/#{project.path_with_namespace}/tree/markdown/doc")
end
end
end
diff --git a/spec/controllers/projects/branches_controller_spec.rb b/spec/controllers/projects/branches_controller_spec.rb
index f9e21f9d8f6..14426b09c73 100644
--- a/spec/controllers/projects/branches_controller_spec.rb
+++ b/spec/controllers/projects/branches_controller_spec.rb
@@ -32,8 +32,8 @@ describe Projects::BranchesController do
let(:branch) { "merge_branch" }
let(:ref) { "master" }
it 'redirects' do
- expect(subject).
- to redirect_to("/#{project.path_with_namespace}/tree/merge_branch")
+ expect(subject)
+ .to redirect_to("/#{project.path_with_namespace}/tree/merge_branch")
end
end
@@ -41,8 +41,8 @@ describe Projects::BranchesController do
let(:branch) { "<script>alert('merge');</script>" }
let(:ref) { "master" }
it 'redirects' do
- expect(subject).
- to redirect_to("/#{project.path_with_namespace}/tree/alert('merge');")
+ expect(subject)
+ .to redirect_to("/#{project.path_with_namespace}/tree/alert('merge');")
end
end
@@ -81,8 +81,8 @@ describe Projects::BranchesController do
branch_name: branch,
issue_iid: issue.iid
- expect(subject).
- to redirect_to("/#{project.path_with_namespace}/tree/1-feature-branch")
+ expect(subject)
+ .to redirect_to("/#{project.path_with_namespace}/tree/1-feature-branch")
end
it 'posts a system note' do
diff --git a/spec/controllers/projects/commit_controller_spec.rb b/spec/controllers/projects/commit_controller_spec.rb
index 7fb08df1950..e10da40eaab 100644
--- a/spec/controllers/projects/commit_controller_spec.rb
+++ b/spec/controllers/projects/commit_controller_spec.rb
@@ -66,8 +66,8 @@ describe Projects::CommitController do
end
it "does not escape Html" do
- allow_any_instance_of(Commit).to receive(:"to_#{format}").
- and_return('HTML entities &<>" ')
+ allow_any_instance_of(Commit).to receive(:"to_#{format}")
+ .and_return('HTML entities &<>" ')
go(id: commit.id, format: format)
diff --git a/spec/controllers/projects/deployments_controller_spec.rb b/spec/controllers/projects/deployments_controller_spec.rb
index 4c69443314d..0dbfcf97f6f 100644
--- a/spec/controllers/projects/deployments_controller_spec.rb
+++ b/spec/controllers/projects/deployments_controller_spec.rb
@@ -42,6 +42,7 @@ describe Projects::DeploymentsController do
before do
allow(controller).to receive(:deployment).and_return(deployment)
end
+
context 'when metrics are disabled' do
before do
allow(deployment).to receive(:has_metrics?).and_return false
@@ -108,6 +109,69 @@ describe Projects::DeploymentsController do
end
end
+ describe 'GET #additional_metrics' do
+ let(:deployment) { create(:deployment, project: project, environment: environment) }
+
+ before do
+ allow(controller).to receive(:deployment).and_return(deployment)
+ end
+
+ context 'when metrics are disabled' do
+ before do
+ allow(deployment).to receive(:has_metrics?).and_return false
+ end
+
+ it 'responds with not found' do
+ get :metrics, deployment_params(id: deployment.id)
+
+ expect(response).to be_not_found
+ end
+ end
+
+ context 'when metrics are enabled' do
+ let(:prometheus_service) { double('prometheus_service') }
+
+ before do
+ allow(deployment.project).to receive(:prometheus_service).and_return(prometheus_service)
+ end
+
+ context 'when environment has no metrics' do
+ before do
+ expect(deployment).to receive(:additional_metrics).and_return({})
+ end
+
+ it 'returns a empty response 204 response' do
+ get :additional_metrics, deployment_params(id: deployment.id, format: :json)
+ expect(response).to have_http_status(204)
+ expect(response.body).to eq('')
+ end
+ end
+
+ context 'when environment has some metrics' do
+ let(:empty_metrics) do
+ {
+ success: true,
+ metrics: {},
+ last_update: 42
+ }
+ end
+
+ before do
+ expect(deployment).to receive(:additional_metrics).and_return(empty_metrics)
+ end
+
+ it 'returns a metrics JSON document' do
+ get :additional_metrics, deployment_params(id: deployment.id, format: :json)
+
+ expect(response).to be_ok
+ expect(json_response['success']).to be(true)
+ expect(json_response['metrics']).to eq({})
+ expect(json_response['last_update']).to eq(42)
+ end
+ end
+ end
+ end
+
def deployment_params(opts = {})
opts.reverse_merge(namespace_id: project.namespace,
project_id: project,
diff --git a/spec/controllers/projects/environments_controller_spec.rb b/spec/controllers/projects/environments_controller_spec.rb
index fcc79902a3c..ad0b046742d 100644
--- a/spec/controllers/projects/environments_controller_spec.rb
+++ b/spec/controllers/projects/environments_controller_spec.rb
@@ -235,14 +235,14 @@ describe Projects::EnvironmentsController do
context 'and valid id' do
it 'returns the first terminal for the environment' do
- expect_any_instance_of(Environment).
- to receive(:terminals).
- and_return([:fake_terminal])
+ expect_any_instance_of(Environment)
+ .to receive(:terminals)
+ .and_return([:fake_terminal])
- expect(Gitlab::Workhorse).
- to receive(:terminal_websocket).
- with(:fake_terminal).
- and_return(workhorse: :response)
+ expect(Gitlab::Workhorse)
+ .to receive(:terminal_websocket)
+ .with(:fake_terminal)
+ .and_return(workhorse: :response)
get :terminal_websocket_authorize, environment_params
@@ -318,6 +318,48 @@ describe Projects::EnvironmentsController do
end
end
+ describe 'GET #additional_metrics' do
+ before do
+ allow(controller).to receive(:environment).and_return(environment)
+ end
+
+ context 'when environment has no metrics' do
+ before do
+ expect(environment).to receive(:additional_metrics).and_return(nil)
+ end
+
+ context 'when requesting metrics as JSON' do
+ it 'returns a metrics JSON document' do
+ get :additional_metrics, environment_params(format: :json)
+
+ expect(response).to have_http_status(204)
+ expect(json_response).to eq({})
+ end
+ end
+ end
+
+ context 'when environment has some metrics' do
+ before do
+ expect(environment)
+ .to receive(:additional_metrics)
+ .and_return({
+ success: true,
+ data: {},
+ last_update: 42
+ })
+ end
+
+ it 'returns a metrics JSON document' do
+ get :additional_metrics, environment_params(format: :json)
+
+ expect(response).to be_ok
+ expect(json_response['success']).to be(true)
+ expect(json_response['data']).to eq({})
+ expect(json_response['last_update']).to eq(42)
+ end
+ end
+ end
+
def environment_params(opts = {})
opts.reverse_merge(namespace_id: project.namespace,
project_id: project,
diff --git a/spec/controllers/projects/issues_controller_spec.rb b/spec/controllers/projects/issues_controller_spec.rb
index eed3c3a9098..9f98427a86b 100644
--- a/spec/controllers/projects/issues_controller_spec.rb
+++ b/spec/controllers/projects/issues_controller_spec.rb
@@ -328,8 +328,8 @@ describe Projects::IssuesController do
it 'redirect to issue page' do
update_verified_issue
- expect(response).
- to redirect_to(namespace_project_issue_path(project.namespace, project, issue))
+ expect(response)
+ .to redirect_to(namespace_project_issue_path(project.namespace, project, issue))
end
it 'accepts an issue after recaptcha is verified' do
@@ -343,8 +343,8 @@ describe Projects::IssuesController do
it 'does not mark spam log as recaptcha_verified when it does not belong to current_user' do
spam_log = create(:spam_log)
- expect { update_issue(spam_log_id: spam_log.id, recaptcha_verification: true) }.
- not_to change { SpamLog.last.recaptcha_verified }
+ expect { update_issue(spam_log_id: spam_log.id, recaptcha_verification: true) }
+ .not_to change { SpamLog.last.recaptcha_verified }
end
end
end
@@ -685,8 +685,8 @@ describe Projects::IssuesController do
it 'does not mark spam log as recaptcha_verified when it does not belong to current_user' do
spam_log = create(:spam_log)
- expect { post_new_issue({}, { spam_log_id: spam_log.id, recaptcha_verification: true } ) }.
- not_to change { SpamLog.last.recaptcha_verified }
+ expect { post_new_issue({}, { spam_log_id: spam_log.id, recaptcha_verification: true } ) }
+ .not_to change { SpamLog.last.recaptcha_verified }
end
end
end
diff --git a/spec/controllers/projects/mattermosts_controller_spec.rb b/spec/controllers/projects/mattermosts_controller_spec.rb
index c5abf11cfa5..422a8b6fac0 100644
--- a/spec/controllers/projects/mattermosts_controller_spec.rb
+++ b/spec/controllers/projects/mattermosts_controller_spec.rb
@@ -11,8 +11,8 @@ describe Projects::MattermostsController do
describe 'GET #new' do
before do
- allow_any_instance_of(MattermostSlashCommandsService).
- to receive(:list_teams).and_return([])
+ allow_any_instance_of(MattermostSlashCommandsService)
+ .to receive(:list_teams).and_return([])
end
it 'accepts the request' do
diff --git a/spec/controllers/projects/merge_requests_controller_spec.rb b/spec/controllers/projects/merge_requests_controller_spec.rb
index d8a3a510f97..6817c2652fd 100644
--- a/spec/controllers/projects/merge_requests_controller_spec.rb
+++ b/spec/controllers/projects/merge_requests_controller_spec.rb
@@ -783,8 +783,8 @@ describe Projects::MergeRequestsController do
describe 'GET conflicts' do
context 'when the conflicts cannot be resolved in the UI' do
before do
- allow_any_instance_of(Gitlab::Conflict::Parser).
- to receive(:parse).and_raise(Gitlab::Conflict::Parser::UnmergeableFile)
+ allow_any_instance_of(Gitlab::Conflict::Parser)
+ .to receive(:parse).and_raise(Gitlab::Conflict::Parser::UnmergeableFile)
get :conflicts,
namespace_id: merge_request_with_conflicts.project.namespace.to_param,
@@ -926,8 +926,8 @@ describe Projects::MergeRequestsController do
context 'when the conflicts cannot be resolved in the UI' do
before do
- allow_any_instance_of(Gitlab::Conflict::Parser).
- to receive(:parse).and_raise(Gitlab::Conflict::Parser::UnmergeableFile)
+ allow_any_instance_of(Gitlab::Conflict::Parser)
+ .to receive(:parse).and_raise(Gitlab::Conflict::Parser::UnmergeableFile)
conflict_for_path('files/ruby/regex.rb')
end
@@ -959,9 +959,9 @@ describe Projects::MergeRequestsController do
end
it 'returns the file in JSON format' do
- content = MergeRequests::Conflicts::ListService.new(merge_request_with_conflicts).
- file_for_path(path, path).
- content
+ content = MergeRequests::Conflicts::ListService.new(merge_request_with_conflicts)
+ .file_for_path(path, path)
+ .content
expect(json_response).to include('old_path' => path,
'new_path' => path,
@@ -1085,9 +1085,9 @@ describe Projects::MergeRequestsController do
context 'when a file has identical content to the conflict' do
before do
- content = MergeRequests::Conflicts::ListService.new(merge_request_with_conflicts).
- file_for_path('files/ruby/popen.rb', 'files/ruby/popen.rb').
- content
+ content = MergeRequests::Conflicts::ListService.new(merge_request_with_conflicts)
+ .file_for_path('files/ruby/popen.rb', 'files/ruby/popen.rb')
+ .content
resolved_files = [
{
@@ -1153,9 +1153,9 @@ describe Projects::MergeRequestsController do
end
it 'calls MergeRequests::AssignIssuesService' do
- expect(MergeRequests::AssignIssuesService).to receive(:new).
- with(project, user, merge_request: merge_request).
- and_return(double(execute: { count: 1 }))
+ expect(MergeRequests::AssignIssuesService).to receive(:new)
+ .with(project, user, merge_request: merge_request)
+ .and_return(double(execute: { count: 1 }))
post_assign_issues
end
diff --git a/spec/controllers/projects/prometheus_controller_spec.rb b/spec/controllers/projects/prometheus_controller_spec.rb
new file mode 100644
index 00000000000..eddf7275975
--- /dev/null
+++ b/spec/controllers/projects/prometheus_controller_spec.rb
@@ -0,0 +1,59 @@
+require('spec_helper')
+
+describe Projects::PrometheusController do
+ let(:user) { create(:user) }
+ let!(:project) { create(:empty_project) }
+
+ let(:prometheus_service) { double('prometheus_service') }
+
+ before do
+ allow(controller).to receive(:project).and_return(project)
+ allow(project).to receive(:prometheus_service).and_return(prometheus_service)
+
+ project.add_master(user)
+ sign_in(user)
+ end
+
+ describe 'GET #active_metrics' do
+ context 'when prometheus metrics are enabled' do
+ context 'when data is not present' do
+ before do
+ allow(prometheus_service).to receive(:matched_metrics).and_return({})
+ end
+
+ it 'returns no content response' do
+ get :active_metrics, project_params(format: :json)
+
+ expect(response).to have_http_status(204)
+ end
+ end
+
+ context 'when data is available' do
+ let(:sample_response) { { some_data: 1 } }
+
+ before do
+ allow(prometheus_service).to receive(:matched_metrics).and_return(sample_response)
+ end
+
+ it 'returns no content response' do
+ get :active_metrics, project_params(format: :json)
+
+ expect(response).to have_http_status(200)
+ expect(json_response).to eq(sample_response.deep_stringify_keys)
+ end
+ end
+
+ context 'when requesting non json response' do
+ it 'returns not found response' do
+ get :active_metrics, project_params
+
+ expect(response).to have_http_status(404)
+ end
+ end
+ end
+ end
+
+ def project_params(opts = {})
+ opts.reverse_merge(namespace_id: project.namespace, project_id: project)
+ end
+end
diff --git a/spec/controllers/projects/raw_controller_spec.rb b/spec/controllers/projects/raw_controller_spec.rb
index 952071af57f..b4eaab29fed 100644
--- a/spec/controllers/projects/raw_controller_spec.rb
+++ b/spec/controllers/projects/raw_controller_spec.rb
@@ -15,8 +15,8 @@ describe Projects::RawController do
expect(response).to have_http_status(200)
expect(response.header['Content-Type']).to eq('text/plain; charset=utf-8')
- expect(response.header['Content-Disposition']).
- to eq('inline')
+ expect(response.header['Content-Disposition'])
+ .to eq('inline')
expect(response.header[Gitlab::Workhorse::SEND_DATA_HEADER]).to start_with('git-blob:')
end
end
@@ -88,8 +88,8 @@ describe Projects::RawController do
expect(response).to have_http_status(200)
expect(response.header['Content-Type']).to eq('text/plain; charset=utf-8')
- expect(response.header['Content-Disposition']).
- to eq('inline')
+ expect(response.header['Content-Disposition'])
+ .to eq('inline')
expect(response.header[Gitlab::Workhorse::SEND_DATA_HEADER]).to start_with('git-blob:')
end
end
diff --git a/spec/controllers/projects/services_controller_spec.rb b/spec/controllers/projects/services_controller_spec.rb
index 23b463c0082..4dc227a36d4 100644
--- a/spec/controllers/projects/services_controller_spec.rb
+++ b/spec/controllers/projects/services_controller_spec.rb
@@ -67,8 +67,8 @@ describe Projects::ServicesController do
put :test, namespace_id: project.namespace.id, project_id: project.id, id: service.id, service: service_params
expect(response.status).to eq(200)
- expect(JSON.parse(response.body)).
- to eq('error' => true, 'message' => 'Test failed.', 'service_response' => 'Bad test')
+ expect(JSON.parse(response.body))
+ .to eq('error' => true, 'message' => 'Test failed.', 'service_response' => 'Bad test')
end
end
end
diff --git a/spec/controllers/projects/snippets_controller_spec.rb b/spec/controllers/projects/snippets_controller_spec.rb
index 2434f822c6f..ec0b7f8c967 100644
--- a/spec/controllers/projects/snippets_controller_spec.rb
+++ b/spec/controllers/projects/snippets_controller_spec.rb
@@ -103,21 +103,21 @@ describe Projects::SnippetsController do
context 'when the snippet is private' do
it 'creates the snippet' do
- expect { create_snippet(project, visibility_level: Snippet::PRIVATE) }.
- to change { Snippet.count }.by(1)
+ expect { create_snippet(project, visibility_level: Snippet::PRIVATE) }
+ .to change { Snippet.count }.by(1)
end
end
context 'when the snippet is public' do
it 'rejects the shippet' do
- expect { create_snippet(project, visibility_level: Snippet::PUBLIC) }.
- not_to change { Snippet.count }
+ expect { create_snippet(project, visibility_level: Snippet::PUBLIC) }
+ .not_to change { Snippet.count }
expect(response).to render_template(:new)
end
it 'creates a spam log' do
- expect { create_snippet(project, visibility_level: Snippet::PUBLIC) }.
- to change { SpamLog.count }.by(1)
+ expect { create_snippet(project, visibility_level: Snippet::PUBLIC) }
+ .to change { SpamLog.count }.by(1)
end
it 'renders :new with recaptcha disabled' do
@@ -183,8 +183,8 @@ describe Projects::SnippetsController do
let(:visibility_level) { Snippet::PRIVATE }
it 'updates the snippet' do
- expect { update_snippet(title: 'Foo') }.
- to change { snippet.reload.title }.to('Foo')
+ expect { update_snippet(title: 'Foo') }
+ .to change { snippet.reload.title }.to('Foo')
end
end
@@ -192,13 +192,13 @@ describe Projects::SnippetsController do
let(:visibility_level) { Snippet::PUBLIC }
it 'rejects the shippet' do
- expect { update_snippet(title: 'Foo') }.
- not_to change { snippet.reload.title }
+ expect { update_snippet(title: 'Foo') }
+ .not_to change { snippet.reload.title }
end
it 'creates a spam log' do
- expect { update_snippet(title: 'Foo') }.
- to change { SpamLog.count }.by(1)
+ expect { update_snippet(title: 'Foo') }
+ .to change { SpamLog.count }.by(1)
end
it 'renders :edit with recaptcha disabled' do
@@ -237,13 +237,13 @@ describe Projects::SnippetsController do
let(:visibility_level) { Snippet::PRIVATE }
it 'rejects the shippet' do
- expect { update_snippet(title: 'Foo', visibility_level: Snippet::PUBLIC) }.
- not_to change { snippet.reload.title }
+ expect { update_snippet(title: 'Foo', visibility_level: Snippet::PUBLIC) }
+ .not_to change { snippet.reload.title }
end
it 'creates a spam log' do
- expect { update_snippet(title: 'Foo', visibility_level: Snippet::PUBLIC) }.
- to change { SpamLog.count }.by(1)
+ expect { update_snippet(title: 'Foo', visibility_level: Snippet::PUBLIC) }
+ .to change { SpamLog.count }.by(1)
end
it 'renders :edit with recaptcha disabled' do
diff --git a/spec/controllers/projects/tree_controller_spec.rb b/spec/controllers/projects/tree_controller_spec.rb
index a43dad5756d..16cd2e076e5 100644
--- a/spec/controllers/projects/tree_controller_spec.rb
+++ b/spec/controllers/projects/tree_controller_spec.rb
@@ -82,8 +82,8 @@ describe Projects::TreeController do
let(:id) { 'master/README.md' }
it 'redirects' do
redirect_url = "/#{project.path_with_namespace}/blob/master/README.md"
- expect(subject).
- to redirect_to(redirect_url)
+ expect(subject)
+ .to redirect_to(redirect_url)
end
end
end
@@ -106,8 +106,8 @@ describe Projects::TreeController do
let(:branch_name) { 'master-test'}
it 'redirects to the new directory' do
- expect(subject).
- to redirect_to("/#{project.path_with_namespace}/tree/#{branch_name}/#{path}")
+ expect(subject)
+ .to redirect_to("/#{project.path_with_namespace}/tree/#{branch_name}/#{path}")
expect(flash[:notice]).to eq('The directory has been successfully created.')
end
end
@@ -117,8 +117,8 @@ describe Projects::TreeController do
let(:branch_name) { 'master'}
it 'does not allow overwriting of existing files' do
- expect(subject).
- to redirect_to("/#{project.path_with_namespace}/tree/master")
+ expect(subject)
+ .to redirect_to("/#{project.path_with_namespace}/tree/master")
expect(flash[:alert]).to eq('A file with this name already exists')
end
end
diff --git a/spec/controllers/sent_notifications_controller_spec.rb b/spec/controllers/sent_notifications_controller_spec.rb
index 0cc8a3b68eb..917bd44c91b 100644
--- a/spec/controllers/sent_notifications_controller_spec.rb
+++ b/spec/controllers/sent_notifications_controller_spec.rb
@@ -87,8 +87,8 @@ describe SentNotificationsController, type: :controller do
end
it 'redirects to the issue page' do
- expect(response).
- to redirect_to(namespace_project_issue_path(project.namespace, project, issue))
+ expect(response)
+ .to redirect_to(namespace_project_issue_path(project.namespace, project, issue))
end
end
@@ -113,8 +113,8 @@ describe SentNotificationsController, type: :controller do
end
it 'redirects to the merge request page' do
- expect(response).
- to redirect_to(namespace_project_merge_request_path(project.namespace, project, merge_request))
+ expect(response)
+ .to redirect_to(namespace_project_merge_request_path(project.namespace, project, merge_request))
end
end
end
diff --git a/spec/controllers/sessions_controller_spec.rb b/spec/controllers/sessions_controller_spec.rb
index 03f4b0ba343..bf922260b2f 100644
--- a/spec/controllers/sessions_controller_spec.rb
+++ b/spec/controllers/sessions_controller_spec.rb
@@ -9,8 +9,8 @@ describe SessionsController do
context 'when auto sign-in is enabled' do
before do
stub_omniauth_setting(auto_sign_in_with_provider: :saml)
- allow(controller).to receive(:omniauth_authorize_path).with(:user, :saml).
- and_return('/saml')
+ allow(controller).to receive(:omniauth_authorize_path).with(:user, :saml)
+ .and_return('/saml')
end
context 'and no auto_sign_in param is passed' do
@@ -89,8 +89,8 @@ describe SessionsController do
context 'remember_me field' do
it 'sets a remember_user_token cookie when enabled' do
allow(controller).to receive(:find_user).and_return(user)
- expect(controller).
- to receive(:remember_me).with(user).and_call_original
+ expect(controller)
+ .to receive(:remember_me).with(user).and_call_original
authenticate_2fa(remember_me: '1', otp_attempt: user.current_otp)
@@ -228,8 +228,8 @@ describe SessionsController do
it 'sets a remember_user_token cookie when enabled' do
allow(U2fRegistration).to receive(:authenticate).and_return(true)
allow(controller).to receive(:find_user).and_return(user)
- expect(controller).
- to receive(:remember_me).with(user).and_call_original
+ expect(controller)
+ .to receive(:remember_me).with(user).and_call_original
authenticate_2fa_u2f(remember_me: '1', login: user.username, device_response: "{}")
@@ -262,8 +262,8 @@ describe SessionsController do
it 'redirects correctly for referer on same host with params' do
search_path = '/search?search=seed_project'
- allow(controller.request).to receive(:referer).
- and_return('http://%{host}%{path}' % { host: Gitlab.config.gitlab.host, path: search_path })
+ allow(controller.request).to receive(:referer)
+ .and_return('http://%{host}%{path}' % { host: Gitlab.config.gitlab.host, path: search_path })
get(:new, redirect_to_referer: :yes)
diff --git a/spec/controllers/snippets_controller_spec.rb b/spec/controllers/snippets_controller_spec.rb
index b1339b2a185..430d1208cd1 100644
--- a/spec/controllers/snippets_controller_spec.rb
+++ b/spec/controllers/snippets_controller_spec.rb
@@ -222,20 +222,20 @@ describe SnippetsController do
context 'when the snippet is private' do
it 'creates the snippet' do
- expect { create_snippet(visibility_level: Snippet::PRIVATE) }.
- to change { Snippet.count }.by(1)
+ expect { create_snippet(visibility_level: Snippet::PRIVATE) }
+ .to change { Snippet.count }.by(1)
end
end
context 'when the snippet is public' do
it 'rejects the shippet' do
- expect { create_snippet(visibility_level: Snippet::PUBLIC) }.
- not_to change { Snippet.count }
+ expect { create_snippet(visibility_level: Snippet::PUBLIC) }
+ .not_to change { Snippet.count }
end
it 'creates a spam log' do
- expect { create_snippet(visibility_level: Snippet::PUBLIC) }.
- to change { SpamLog.count }.by(1)
+ expect { create_snippet(visibility_level: Snippet::PUBLIC) }
+ .to change { SpamLog.count }.by(1)
end
it 'renders :new with recaptcha disabled' do
@@ -296,8 +296,8 @@ describe SnippetsController do
let(:visibility_level) { Snippet::PRIVATE }
it 'updates the snippet' do
- expect { update_snippet(title: 'Foo') }.
- to change { snippet.reload.title }.to('Foo')
+ expect { update_snippet(title: 'Foo') }
+ .to change { snippet.reload.title }.to('Foo')
end
end
@@ -305,13 +305,13 @@ describe SnippetsController do
let(:visibility_level) { Snippet::PRIVATE }
it 'rejects the snippet' do
- expect { update_snippet(title: 'Foo', visibility_level: Snippet::PUBLIC) }.
- not_to change { snippet.reload.title }
+ expect { update_snippet(title: 'Foo', visibility_level: Snippet::PUBLIC) }
+ .not_to change { snippet.reload.title }
end
it 'creates a spam log' do
- expect { update_snippet(title: 'Foo', visibility_level: Snippet::PUBLIC) }.
- to change { SpamLog.count }.by(1)
+ expect { update_snippet(title: 'Foo', visibility_level: Snippet::PUBLIC) }
+ .to change { SpamLog.count }.by(1)
end
it 'renders :edit with recaptcha disabled' do
@@ -350,13 +350,13 @@ describe SnippetsController do
let(:visibility_level) { Snippet::PUBLIC }
it 'rejects the shippet' do
- expect { update_snippet(title: 'Foo') }.
- not_to change { snippet.reload.title }
+ expect { update_snippet(title: 'Foo') }
+ .not_to change { snippet.reload.title }
end
it 'creates a spam log' do
- expect { update_snippet(title: 'Foo') }.
- to change { SpamLog.count }.by(1)
+ expect { update_snippet(title: 'Foo') }
+ .to change { SpamLog.count }.by(1)
end
it 'renders :edit with recaptcha disabled' do
diff --git a/spec/factories/projects.rb b/spec/factories/projects.rb
index e17e50db143..aef1c17a239 100644
--- a/spec/factories/projects.rb
+++ b/spec/factories/projects.rb
@@ -118,8 +118,8 @@ FactoryGirl.define do
builds_access_level = [evaluator.builds_access_level, evaluator.repository_access_level].min
merge_requests_access_level = [evaluator.merge_requests_access_level, evaluator.repository_access_level].min
- project.project_feature.
- update_attributes!(
+ project.project_feature
+ .update_attributes!(
wiki_access_level: evaluator.wiki_access_level,
builds_access_level: builds_access_level,
snippets_access_level: evaluator.snippets_access_level,
diff --git a/spec/factories/services.rb b/spec/factories/services.rb
index e7366a7fd1c..30bc25cf88a 100644
--- a/spec/factories/services.rb
+++ b/spec/factories/services.rb
@@ -25,6 +25,14 @@ FactoryGirl.define do
})
end
+ factory :prometheus_service do
+ project factory: :empty_project
+ active true
+ properties({
+ api_url: 'https://prometheus.example.com/'
+ })
+ end
+
factory :jira_service do
project factory: :empty_project
active true
diff --git a/spec/features/admin/admin_projects_spec.rb b/spec/features/admin/admin_projects_spec.rb
index ad8f1d496f2..a4ce3e1d5ee 100644
--- a/spec/features/admin/admin_projects_spec.rb
+++ b/spec/features/admin/admin_projects_spec.rb
@@ -57,8 +57,8 @@ describe "Admin::Projects", feature: true do
before do
create(:group, name: 'Web')
- allow_any_instance_of(Projects::TransferService).
- to receive(:move_uploads_to_new_namespace).and_return(true)
+ allow_any_instance_of(Projects::TransferService)
+ .to receive(:move_uploads_to_new_namespace).and_return(true)
end
it 'transfers project to group web', js: true do
diff --git a/spec/features/admin/admin_users_spec.rb b/spec/features/admin/admin_users_spec.rb
index 2d5f0987ea2..6dbc697642f 100644
--- a/spec/features/admin/admin_users_spec.rb
+++ b/spec/features/admin/admin_users_spec.rb
@@ -78,10 +78,10 @@ describe "Admin::Users", feature: true do
it "applies defaults to user" do
click_button "Create user"
user = User.find_by(username: 'bang')
- expect(user.projects_limit).
- to eq(Gitlab.config.gitlab.default_projects_limit)
- expect(user.can_create_group).
- to eq(Gitlab.config.gitlab.default_can_create_group)
+ expect(user.projects_limit)
+ .to eq(Gitlab.config.gitlab.default_projects_limit)
+ expect(user.can_create_group)
+ .to eq(Gitlab.config.gitlab.default_can_create_group)
end
it "creates user with valid data" do
diff --git a/spec/features/atom/dashboard_spec.rb b/spec/features/atom/dashboard_spec.rb
index 1df058b023c..2f4bb45d74b 100644
--- a/spec/features/atom/dashboard_spec.rb
+++ b/spec/features/atom/dashboard_spec.rb
@@ -35,8 +35,8 @@ describe "Dashboard Feed", feature: true do
end
it "has issue comment event" do
- expect(body).
- to have_content("#{user.name} commented on issue ##{issue.iid}")
+ expect(body)
+ .to have_content("#{user.name} commented on issue ##{issue.iid}")
end
end
end
diff --git a/spec/features/atom/issues_spec.rb b/spec/features/atom/issues_spec.rb
index c8ef4533b98..b94ad973fed 100644
--- a/spec/features/atom/issues_spec.rb
+++ b/spec/features/atom/issues_spec.rb
@@ -18,8 +18,8 @@ describe 'Issues Feed', feature: true do
gitlab_sign_in user
visit namespace_project_issues_path(project.namespace, project, :atom)
- expect(response_headers['Content-Type']).
- to have_content('application/atom+xml')
+ expect(response_headers['Content-Type'])
+ .to have_content('application/atom+xml')
expect(body).to have_selector('title', text: "#{project.name} issues")
expect(body).to have_selector('author email', text: issue.author_public_email)
expect(body).to have_selector('assignees assignee email', text: issue.assignees.first.public_email)
@@ -33,8 +33,8 @@ describe 'Issues Feed', feature: true do
visit namespace_project_issues_path(project.namespace, project, :atom,
private_token: user.private_token)
- expect(response_headers['Content-Type']).
- to have_content('application/atom+xml')
+ expect(response_headers['Content-Type'])
+ .to have_content('application/atom+xml')
expect(body).to have_selector('title', text: "#{project.name} issues")
expect(body).to have_selector('author email', text: issue.author_public_email)
expect(body).to have_selector('assignees assignee email', text: issue.assignees.first.public_email)
@@ -48,8 +48,8 @@ describe 'Issues Feed', feature: true do
visit namespace_project_issues_path(project.namespace, project, :atom,
rss_token: user.rss_token)
- expect(response_headers['Content-Type']).
- to have_content('application/atom+xml')
+ expect(response_headers['Content-Type'])
+ .to have_content('application/atom+xml')
expect(body).to have_selector('title', text: "#{project.name} issues")
expect(body).to have_selector('author email', text: issue.author_public_email)
expect(body).to have_selector('assignees assignee email', text: issue.assignees.first.public_email)
diff --git a/spec/features/atom/users_spec.rb b/spec/features/atom/users_spec.rb
index fae5aaa52bd..44ae7204bcf 100644
--- a/spec/features/atom/users_spec.rb
+++ b/spec/features/atom/users_spec.rb
@@ -55,8 +55,8 @@ describe "User Feed", feature: true do
end
it 'has issue comment event' do
- expect(body).
- to have_content("#{safe_name} commented on issue ##{issue.iid}")
+ expect(body)
+ .to have_content("#{safe_name} commented on issue ##{issue.iid}")
end
it 'has XHTML summaries in issue descriptions' do
diff --git a/spec/features/dashboard/issues_spec.rb b/spec/features/dashboard/issues_spec.rb
index 6f21cfd322d..a57962abbda 100644
--- a/spec/features/dashboard/issues_spec.rb
+++ b/spec/features/dashboard/issues_spec.rb
@@ -59,6 +59,11 @@ RSpec.describe 'Dashboard Issues', feature: true do
expect(page).to have_content(other_issue.title)
end
+ it 'state filter tabs work' do
+ find('#state-closed').click
+ expect(page).to have_current_path(issues_dashboard_url(assignee_id: current_user.id, scope: 'all', state: 'closed'), url: true)
+ end
+
it_behaves_like "it has an RSS button with current_user's RSS token"
it_behaves_like "an autodiscoverable RSS feed with current_user's RSS token"
end
diff --git a/spec/features/gitlab_flavored_markdown_spec.rb b/spec/features/gitlab_flavored_markdown_spec.rb
index 1da88d3b1d2..2d13af2a52a 100644
--- a/spec/features/gitlab_flavored_markdown_spec.rb
+++ b/spec/features/gitlab_flavored_markdown_spec.rb
@@ -20,8 +20,8 @@ describe "GitLab Flavored Markdown", feature: true do
let(:commit) { project.commit }
before do
- allow_any_instance_of(Commit).to receive(:title).
- and_return("fix #{issue.to_reference}\n\nask #{fred.to_reference} for details")
+ allow_any_instance_of(Commit).to receive(:title)
+ .and_return("fix #{issue.to_reference}\n\nask #{fred.to_reference} for details")
end
it "renders title in commits#index" do
diff --git a/spec/features/login_spec.rb b/spec/features/login_spec.rb
index b43e6a06a07..53b8ba5b0f7 100644
--- a/spec/features/login_spec.rb
+++ b/spec/features/login_spec.rb
@@ -122,8 +122,8 @@ feature 'Login', feature: true do
end
it 'invalidates the used code' do
- expect { enter_code(codes.sample) }.
- to change { user.reload.otp_backup_codes.size }.by(-1)
+ expect { enter_code(codes.sample) }
+ .to change { user.reload.otp_backup_codes.size }.by(-1)
end
end
diff --git a/spec/features/markdown_spec.rb b/spec/features/markdown_spec.rb
index ba930de937d..534be3ab5a7 100644
--- a/spec/features/markdown_spec.rb
+++ b/spec/features/markdown_spec.rb
@@ -58,8 +58,8 @@ describe 'GitLab Markdown', feature: true do
end
it 'allows Markdown in tables' do
- expect(doc.at_css('td:contains("Baz")').children.to_html).
- to eq '<strong>Baz</strong>'
+ expect(doc.at_css('td:contains("Baz")').children.to_html)
+ .to eq '<strong>Baz</strong>'
end
it 'parses fenced code blocks' do
@@ -158,14 +158,14 @@ describe 'GitLab Markdown', feature: true do
describe 'Edge Cases' do
it 'allows markup inside link elements' do
aggregate_failures do
- expect(doc.at_css('a[href="#link-emphasis"]').to_html).
- to eq %{<a href="#link-emphasis"><em>text</em></a>}
+ expect(doc.at_css('a[href="#link-emphasis"]').to_html)
+ .to eq %{<a href="#link-emphasis"><em>text</em></a>}
- expect(doc.at_css('a[href="#link-strong"]').to_html).
- to eq %{<a href="#link-strong"><strong>text</strong></a>}
+ expect(doc.at_css('a[href="#link-strong"]').to_html)
+ .to eq %{<a href="#link-strong"><strong>text</strong></a>}
- expect(doc.at_css('a[href="#link-code"]').to_html).
- to eq %{<a href="#link-code"><code>text</code></a>}
+ expect(doc.at_css('a[href="#link-code"]').to_html)
+ .to eq %{<a href="#link-code"><code>text</code></a>}
end
end
end
diff --git a/spec/features/merge_requests/closes_issues_spec.rb b/spec/features/merge_requests/closes_issues_spec.rb
index 371aa2bdaa7..365b2555c35 100644
--- a/spec/features/merge_requests/closes_issues_spec.rb
+++ b/spec/features/merge_requests/closes_issues_spec.rb
@@ -36,7 +36,7 @@ feature 'Merge Request closing issues message', feature: true, js: true do
let(:merge_request_description) { "Description\n\nclosing #{issue_1.to_reference}, #{issue_2.to_reference}" }
it 'does not display closing issue message' do
- expect(page).to have_content("Closed issues #{issue_1.to_reference} and #{issue_2.to_reference}")
+ expect(page).to have_content("Closes issues #{issue_1.to_reference} and #{issue_2.to_reference}")
end
end
@@ -44,7 +44,7 @@ feature 'Merge Request closing issues message', feature: true, js: true do
let(:merge_request_description) { "Description\n\nRefers to #{issue_1.to_reference} and #{issue_2.to_reference}" }
it 'does not display closing issue message' do
- expect(page).to have_content("Issues #{issue_1.to_reference} and #{issue_2.to_reference} are mentioned but were not closed")
+ expect(page).to have_content("Issues #{issue_1.to_reference} and #{issue_2.to_reference} are mentioned but will not be closed.")
end
end
@@ -52,8 +52,8 @@ feature 'Merge Request closing issues message', feature: true, js: true do
let(:merge_request_title) { "closes #{issue_1.to_reference}\n\n refers to #{issue_2.to_reference}" }
it 'does not display closing issue message' do
- expect(page).to have_content("Closed issue #{issue_1.to_reference}")
- expect(page).to have_content("Issue #{issue_2.to_reference} is mentioned but was not closed")
+ expect(page).to have_content("Closes issue #{issue_1.to_reference}.")
+ expect(page).to have_content("Issue #{issue_2.to_reference} is mentioned but will not be closed.")
end
end
@@ -61,7 +61,7 @@ feature 'Merge Request closing issues message', feature: true, js: true do
let(:merge_request_title) { "closing #{issue_1.to_reference}, #{issue_2.to_reference}" }
it 'does not display closing issue message' do
- expect(page).to have_content("Closed issues #{issue_1.to_reference} and #{issue_2.to_reference}")
+ expect(page).to have_content("Closes issues #{issue_1.to_reference} and #{issue_2.to_reference}")
end
end
@@ -69,7 +69,7 @@ feature 'Merge Request closing issues message', feature: true, js: true do
let(:merge_request_title) { "Refers to #{issue_1.to_reference} and #{issue_2.to_reference}" }
it 'does not display closing issue message' do
- expect(page).to have_content("Issues #{issue_1.to_reference} and #{issue_2.to_reference} are mentioned but were not closed")
+ expect(page).to have_content("Issues #{issue_1.to_reference} and #{issue_2.to_reference} are mentioned but will not be closed.")
end
end
@@ -77,8 +77,8 @@ feature 'Merge Request closing issues message', feature: true, js: true do
let(:merge_request_title) { "closes #{issue_1.to_reference}\n\n refers to #{issue_2.to_reference}" }
it 'does not display closing issue message' do
- expect(page).to have_content("Closed issue #{issue_1.to_reference}")
- expect(page).to have_content("Issue #{issue_2.to_reference} is mentioned but was not closed")
+ expect(page).to have_content("Closes issue #{issue_1.to_reference}. Issue #{issue_2.to_reference} is mentioned but will not be closed.")
+ expect(page).to have_content("Issue #{issue_2.to_reference} is mentioned but will not be closed.")
end
end
end
diff --git a/spec/features/merge_requests/user_posts_notes_spec.rb b/spec/features/merge_requests/user_posts_notes_spec.rb
index 70652fcce8c..12f987e12ea 100644
--- a/spec/features/merge_requests/user_posts_notes_spec.rb
+++ b/spec/features/merge_requests/user_posts_notes_spec.rb
@@ -22,8 +22,8 @@ describe 'Merge requests > User posts notes', :js do
describe 'the note form' do
it 'is valid' do
is_expected.to have_css('.js-main-target-form', visible: true, count: 1)
- expect(find('.js-main-target-form .js-comment-button').value).
- to eq('Comment')
+ expect(find('.js-main-target-form .js-comment-button').value)
+ .to eq('Comment')
page.within('.js-main-target-form') do
expect(page).not_to have_link('Cancel')
end
@@ -123,8 +123,8 @@ describe 'Merge requests > User posts notes', :js do
page.within("#note_#{note.id}") do
is_expected.to have_css('.note_edited_ago')
- expect(find('.note_edited_ago').text).
- to match(/less than a minute ago/)
+ expect(find('.note_edited_ago').text)
+ .to match(/less than a minute ago/)
end
end
end
diff --git a/spec/features/snippets/notes_on_personal_snippets_spec.rb b/spec/features/snippets/notes_on_personal_snippets_spec.rb
index 6f7e6f543b2..d310e7501ec 100644
--- a/spec/features/snippets/notes_on_personal_snippets_spec.rb
+++ b/spec/features/snippets/notes_on_personal_snippets_spec.rb
@@ -46,8 +46,8 @@ describe 'Comments on personal snippets', :js, feature: true do
context 'when submitting a note' do
it 'shows a valid form' do
is_expected.to have_css('.js-main-target-form', visible: true, count: 1)
- expect(find('.js-main-target-form .js-comment-button').value).
- to eq('Comment')
+ expect(find('.js-main-target-form .js-comment-button').value)
+ .to eq('Comment')
page.within('.js-main-target-form') do
expect(page).not_to have_link('Cancel')
diff --git a/spec/fixtures/api/schemas/prometheus/additional_metrics_query_result.json b/spec/fixtures/api/schemas/prometheus/additional_metrics_query_result.json
new file mode 100644
index 00000000000..47b5d283b8c
--- /dev/null
+++ b/spec/fixtures/api/schemas/prometheus/additional_metrics_query_result.json
@@ -0,0 +1,58 @@
+{
+ "items": {
+ "properties": {
+ "group": {
+ "type": "string"
+ },
+ "metrics": {
+ "items": {
+ "properties": {
+ "queries": {
+ "items": {
+ "properties": {
+ "query_range": {
+ "type": "string"
+ },
+ "query": {
+ "type": "string"
+ },
+ "result": {
+ "type": "any"
+ }
+ },
+ "type": "object"
+ },
+ "type": "array"
+ },
+ "title": {
+ "type": "string"
+ },
+ "weight": {
+ "type": "integer"
+ },
+ "y_label": {
+ "type": "string"
+ }
+ },
+ "type": "object"
+ },
+ "required": [
+ "metrics",
+ "title",
+ "weight"
+ ],
+ "type": "array"
+ },
+ "priority": {
+ "type": "integer"
+ }
+ },
+ "type": "object"
+ },
+ "required": [
+ "group",
+ "priority",
+ "metrics"
+ ],
+ "type": "array"
+} \ No newline at end of file
diff --git a/spec/helpers/application_helper_spec.rb b/spec/helpers/application_helper_spec.rb
index c1966c273db..56daeffde27 100644
--- a/spec/helpers/application_helper_spec.rb
+++ b/spec/helpers/application_helper_spec.rb
@@ -61,14 +61,14 @@ describe ApplicationHelper do
project = create(:empty_project, avatar: File.open(uploaded_image_temp_path))
avatar_url = "/uploads/system/project/avatar/#{project.id}/banana_sample.gif"
- expect(helper.project_icon(project.full_path).to_s).
- to eq "<img src=\"#{avatar_url}\" alt=\"Banana sample\" />"
+ expect(helper.project_icon(project.full_path).to_s)
+ .to eq "<img src=\"#{avatar_url}\" alt=\"Banana sample\" />"
allow(ActionController::Base).to receive(:asset_host).and_return(gitlab_host)
avatar_url = "#{gitlab_host}/uploads/system/project/avatar/#{project.id}/banana_sample.gif"
- expect(helper.project_icon(project.full_path).to_s).
- to eq "<img src=\"#{avatar_url}\" alt=\"Banana sample\" />"
+ expect(helper.project_icon(project.full_path).to_s)
+ .to eq "<img src=\"#{avatar_url}\" alt=\"Banana sample\" />"
end
it 'gives uploaded icon when present' do
@@ -87,8 +87,8 @@ describe ApplicationHelper do
context 'using an email' do
context 'when there is a matching user' do
it 'returns a relative URL for the avatar' do
- expect(helper.avatar_icon(user.email).to_s).
- to eq("/uploads/system/user/avatar/#{user.id}/banana_sample.gif")
+ expect(helper.avatar_icon(user.email).to_s)
+ .to eq("/uploads/system/user/avatar/#{user.id}/banana_sample.gif")
end
context 'when an asset_host is set in the config' do
@@ -99,15 +99,15 @@ describe ApplicationHelper do
end
it 'returns an absolute URL on that asset host' do
- expect(helper.avatar_icon(user.email, only_path: false).to_s).
- to eq("#{asset_host}/uploads/system/user/avatar/#{user.id}/banana_sample.gif")
+ expect(helper.avatar_icon(user.email, only_path: false).to_s)
+ .to eq("#{asset_host}/uploads/system/user/avatar/#{user.id}/banana_sample.gif")
end
end
context 'when only_path is set to false' do
it 'returns an absolute URL for the avatar' do
- expect(helper.avatar_icon(user.email, only_path: false).to_s).
- to eq("#{gitlab_host}/uploads/system/user/avatar/#{user.id}/banana_sample.gif")
+ expect(helper.avatar_icon(user.email, only_path: false).to_s)
+ .to eq("#{gitlab_host}/uploads/system/user/avatar/#{user.id}/banana_sample.gif")
end
end
@@ -119,8 +119,8 @@ describe ApplicationHelper do
end
it 'returns a relative URL with the correct prefix' do
- expect(helper.avatar_icon(user.email).to_s).
- to eq("/gitlab/uploads/system/user/avatar/#{user.id}/banana_sample.gif")
+ expect(helper.avatar_icon(user.email).to_s)
+ .to eq("/gitlab/uploads/system/user/avatar/#{user.id}/banana_sample.gif")
end
end
end
@@ -137,15 +137,15 @@ describe ApplicationHelper do
describe 'using a user' do
context 'when only_path is true' do
it 'returns a relative URL for the avatar' do
- expect(helper.avatar_icon(user, only_path: true).to_s).
- to eq("/uploads/system/user/avatar/#{user.id}/banana_sample.gif")
+ expect(helper.avatar_icon(user, only_path: true).to_s)
+ .to eq("/uploads/system/user/avatar/#{user.id}/banana_sample.gif")
end
end
context 'when only_path is false' do
it 'returns an absolute URL for the avatar' do
- expect(helper.avatar_icon(user, only_path: false).to_s).
- to eq("#{gitlab_host}/uploads/system/user/avatar/#{user.id}/banana_sample.gif")
+ expect(helper.avatar_icon(user, only_path: false).to_s)
+ .to eq("#{gitlab_host}/uploads/system/user/avatar/#{user.id}/banana_sample.gif")
end
end
end
@@ -176,22 +176,22 @@ describe ApplicationHelper do
it 'returns a valid Gravatar URL' do
stub_config_setting(https: false)
- expect(helper.gravatar_icon(user_email)).
- to match('http://www.gravatar.com/avatar/b58c6f14d292556214bd64909bcdb118')
+ expect(helper.gravatar_icon(user_email))
+ .to match('http://www.gravatar.com/avatar/b58c6f14d292556214bd64909bcdb118')
end
it 'uses HTTPs when configured' do
stub_config_setting(https: true)
- expect(helper.gravatar_icon(user_email)).
- to match('https://secure.gravatar.com')
+ expect(helper.gravatar_icon(user_email))
+ .to match('https://secure.gravatar.com')
end
it 'returns custom gravatar path when gravatar_url is set' do
stub_gravatar_setting(plain_url: 'http://example.local/?s=%{size}&hash=%{hash}')
- expect(gravatar_icon(user_email, 20)).
- to eq('http://example.local/?s=40&hash=b58c6f14d292556214bd64909bcdb118')
+ expect(gravatar_icon(user_email, 20))
+ .to eq('http://example.local/?s=40&hash=b58c6f14d292556214bd64909bcdb118')
end
it 'accepts a custom size argument' do
@@ -263,8 +263,8 @@ describe ApplicationHelper do
end
it 'accepts a custom html_class' do
- expect(element(html_class: 'custom_class').attr('class')).
- to eq 'js-timeago custom_class'
+ expect(element(html_class: 'custom_class').attr('class'))
+ .to eq 'js-timeago custom_class'
end
it 'accepts a custom tooltip placement' do
diff --git a/spec/helpers/broadcast_messages_helper_spec.rb b/spec/helpers/broadcast_messages_helper_spec.rb
index c6e3c5c2368..9bec0f9f432 100644
--- a/spec/helpers/broadcast_messages_helper_spec.rb
+++ b/spec/helpers/broadcast_messages_helper_spec.rb
@@ -33,8 +33,8 @@ describe BroadcastMessagesHelper do
it 'allows custom style' do
broadcast_message = double(color: '#f2dede', font: '#b94a48')
- expect(helper.broadcast_message_style(broadcast_message)).
- to match('background-color: #f2dede; color: #b94a48')
+ expect(helper.broadcast_message_style(broadcast_message))
+ .to match('background-color: #f2dede; color: #b94a48')
end
end
diff --git a/spec/helpers/commits_helper_spec.rb b/spec/helpers/commits_helper_spec.rb
index a2c008790f9..c245bb439db 100644
--- a/spec/helpers/commits_helper_spec.rb
+++ b/spec/helpers/commits_helper_spec.rb
@@ -9,8 +9,8 @@ describe CommitsHelper do
author_email: 'my@email.com" onmouseover="alert(1)'
)
- expect(helper.commit_author_link(commit)).
- not_to include('onmouseover="alert(1)"')
+ expect(helper.commit_author_link(commit))
+ .not_to include('onmouseover="alert(1)"')
end
end
@@ -22,8 +22,8 @@ describe CommitsHelper do
committer_email: 'my@email.com" onmouseover="alert(1)'
)
- expect(helper.commit_committer_link(commit)).
- not_to include('onmouseover="alert(1)"')
+ expect(helper.commit_committer_link(commit))
+ .not_to include('onmouseover="alert(1)"')
end
end
diff --git a/spec/helpers/form_helper_spec.rb b/spec/helpers/form_helper_spec.rb
index b20373a96fb..18cf0031d5f 100644
--- a/spec/helpers/form_helper_spec.rb
+++ b/spec/helpers/form_helper_spec.rb
@@ -11,18 +11,18 @@ describe FormHelper do
it 'renders an alert div' do
model = double(errors: errors_stub('Error 1'))
- expect(helper.form_errors(model)).
- to include('<div class="alert alert-danger" id="error_explanation">')
+ expect(helper.form_errors(model))
+ .to include('<div class="alert alert-danger" id="error_explanation">')
end
it 'contains a summary message' do
single_error = double(errors: errors_stub('A'))
multi_errors = double(errors: errors_stub('A', 'B', 'C'))
- expect(helper.form_errors(single_error)).
- to include('<h4>The form contains the following error:')
- expect(helper.form_errors(multi_errors)).
- to include('<h4>The form contains the following errors:')
+ expect(helper.form_errors(single_error))
+ .to include('<h4>The form contains the following error:')
+ expect(helper.form_errors(multi_errors))
+ .to include('<h4>The form contains the following errors:')
end
it 'renders each message' do
diff --git a/spec/helpers/groups_helper_spec.rb b/spec/helpers/groups_helper_spec.rb
index 0337afa4452..a7c06e577a2 100644
--- a/spec/helpers/groups_helper_spec.rb
+++ b/spec/helpers/groups_helper_spec.rb
@@ -8,8 +8,8 @@ describe GroupsHelper do
group = create(:group)
group.avatar = fixture_file_upload(avatar_file_path)
group.save!
- expect(group_icon(group.path).to_s).
- to match("/uploads/system/group/avatar/#{group.id}/banana_sample.gif")
+ expect(group_icon(group.path).to_s)
+ .to match("/uploads/system/group/avatar/#{group.id}/banana_sample.gif")
end
it 'gives default avatar_icon when no avatar is present' do
diff --git a/spec/helpers/import_helper_spec.rb b/spec/helpers/import_helper_spec.rb
index 10f293cddf5..9afff47f4e9 100644
--- a/spec/helpers/import_helper_spec.rb
+++ b/spec/helpers/import_helper_spec.rb
@@ -29,21 +29,21 @@ describe ImportHelper do
context 'when provider is "github"' do
context 'when provider does not specify a custom URL' do
it 'uses default GitHub URL' do
- allow(Gitlab.config.omniauth).to receive(:providers).
- and_return([Settingslogic.new('name' => 'github')])
+ allow(Gitlab.config.omniauth).to receive(:providers)
+ .and_return([Settingslogic.new('name' => 'github')])
- expect(helper.provider_project_link('github', 'octocat/Hello-World')).
- to include('href="https://github.com/octocat/Hello-World"')
+ expect(helper.provider_project_link('github', 'octocat/Hello-World'))
+ .to include('href="https://github.com/octocat/Hello-World"')
end
end
context 'when provider specify a custom URL' do
it 'uses custom URL' do
- allow(Gitlab.config.omniauth).to receive(:providers).
- and_return([Settingslogic.new('name' => 'github', 'url' => 'https://github.company.com')])
+ allow(Gitlab.config.omniauth).to receive(:providers)
+ .and_return([Settingslogic.new('name' => 'github', 'url' => 'https://github.company.com')])
- expect(helper.provider_project_link('github', 'octocat/Hello-World')).
- to include('href="https://github.company.com/octocat/Hello-World"')
+ expect(helper.provider_project_link('github', 'octocat/Hello-World'))
+ .to include('href="https://github.company.com/octocat/Hello-World"')
end
end
end
@@ -54,8 +54,8 @@ describe ImportHelper do
end
it 'uses given host' do
- expect(helper.provider_project_link('gitea', 'octocat/Hello-World')).
- to include('href="https://try.gitea.io/octocat/Hello-World"')
+ expect(helper.provider_project_link('gitea', 'octocat/Hello-World'))
+ .to include('href="https://try.gitea.io/octocat/Hello-World"')
end
end
end
diff --git a/spec/helpers/issuables_helper_spec.rb b/spec/helpers/issuables_helper_spec.rb
index 8fcf7f5fa15..15cb620199d 100644
--- a/spec/helpers/issuables_helper_spec.rb
+++ b/spec/helpers/issuables_helper_spec.rb
@@ -40,23 +40,23 @@ describe IssuablesHelper do
end
it 'returns "Open" when state is :opened' do
- expect(helper.issuables_state_counter_text(:issues, :opened)).
- to eq('<span>Open</span> <span class="badge">42</span>')
+ expect(helper.issuables_state_counter_text(:issues, :opened))
+ .to eq('<span>Open</span> <span class="badge">42</span>')
end
it 'returns "Closed" when state is :closed' do
- expect(helper.issuables_state_counter_text(:issues, :closed)).
- to eq('<span>Closed</span> <span class="badge">42</span>')
+ expect(helper.issuables_state_counter_text(:issues, :closed))
+ .to eq('<span>Closed</span> <span class="badge">42</span>')
end
it 'returns "Merged" when state is :merged' do
- expect(helper.issuables_state_counter_text(:merge_requests, :merged)).
- to eq('<span>Merged</span> <span class="badge">42</span>')
+ expect(helper.issuables_state_counter_text(:merge_requests, :merged))
+ .to eq('<span>Merged</span> <span class="badge">42</span>')
end
it 'returns "All" when state is :all' do
- expect(helper.issuables_state_counter_text(:merge_requests, :all)).
- to eq('<span>All</span> <span class="badge">42</span>')
+ expect(helper.issuables_state_counter_text(:merge_requests, :all))
+ .to eq('<span>All</span> <span class="badge">42</span>')
end
end
@@ -81,13 +81,13 @@ describe IssuablesHelper do
expect(helper).to receive(:params).twice.and_return(params)
expect(helper).to receive(:issuables_count_for_state).with(:issues, :opened).and_return(42)
- expect(helper.issuables_state_counter_text(:issues, :opened)).
- to eq('<span>Open</span> <span class="badge">42</span>')
+ expect(helper.issuables_state_counter_text(:issues, :opened))
+ .to eq('<span>Open</span> <span class="badge">42</span>')
expect(helper).not_to receive(:issuables_count_for_state)
- expect(helper.issuables_state_counter_text(:issues, :opened)).
- to eq('<span>Open</span> <span class="badge">42</span>')
+ expect(helper.issuables_state_counter_text(:issues, :opened))
+ .to eq('<span>Open</span> <span class="badge">42</span>')
end
it 'does not take some keys into account in the cache key' do
@@ -100,8 +100,8 @@ describe IssuablesHelper do
}.with_indifferent_access)
expect(helper).to receive(:issuables_count_for_state).with(:issues, :opened).and_return(42)
- expect(helper.issuables_state_counter_text(:issues, :opened)).
- to eq('<span>Open</span> <span class="badge">42</span>')
+ expect(helper.issuables_state_counter_text(:issues, :opened))
+ .to eq('<span>Open</span> <span class="badge">42</span>')
expect(helper).to receive(:params).and_return({
author_id: '11',
@@ -112,22 +112,22 @@ describe IssuablesHelper do
}.with_indifferent_access)
expect(helper).not_to receive(:issuables_count_for_state)
- expect(helper.issuables_state_counter_text(:issues, :opened)).
- to eq('<span>Open</span> <span class="badge">42</span>')
+ expect(helper.issuables_state_counter_text(:issues, :opened))
+ .to eq('<span>Open</span> <span class="badge">42</span>')
end
it 'does not take params order into account in the cache key' do
expect(helper).to receive(:params).and_return('author_id' => '11', 'state' => 'opened')
expect(helper).to receive(:issuables_count_for_state).with(:issues, :opened).and_return(42)
- expect(helper.issuables_state_counter_text(:issues, :opened)).
- to eq('<span>Open</span> <span class="badge">42</span>')
+ expect(helper.issuables_state_counter_text(:issues, :opened))
+ .to eq('<span>Open</span> <span class="badge">42</span>')
expect(helper).to receive(:params).and_return('state' => 'opened', 'author_id' => '11')
expect(helper).not_to receive(:issuables_count_for_state)
- expect(helper.issuables_state_counter_text(:issues, :opened)).
- to eq('<span>Open</span> <span class="badge">42</span>')
+ expect(helper.issuables_state_counter_text(:issues, :opened))
+ .to eq('<span>Open</span> <span class="badge">42</span>')
end
end
end
diff --git a/spec/helpers/issues_helper_spec.rb b/spec/helpers/issues_helper_spec.rb
index 540cb0ab1e0..00db98fd9d2 100644
--- a/spec/helpers/issues_helper_spec.rb
+++ b/spec/helpers/issues_helper_spec.rb
@@ -93,8 +93,8 @@ describe IssuesHelper do
award = build_stubbed(:award_emoji, user: build_stubbed(:user, name: 'Jane'))
awards = Array.new(5, award).push(my_award)
- expect(award_user_list(awards, current_user, limit: 2)).
- to eq("You, Jane, and 4 more.")
+ expect(award_user_list(awards, current_user, limit: 2))
+ .to eq("You, Jane, and 4 more.")
end
end
diff --git a/spec/helpers/labels_helper_spec.rb b/spec/helpers/labels_helper_spec.rb
index 7cf535fadae..a8d6044fda7 100644
--- a/spec/helpers/labels_helper_spec.rb
+++ b/spec/helpers/labels_helper_spec.rb
@@ -55,8 +55,8 @@ describe LabelsHelper do
context 'without block' do
it 'uses render_colored_label as the link content' do
- expect(self).to receive(:render_colored_label).
- with(label, tooltip: true).and_return('Foo')
+ expect(self).to receive(:render_colored_label)
+ .with(label, tooltip: true).and_return('Foo')
expect(link_to_label(label)).to match('Foo')
end
end
diff --git a/spec/helpers/markup_helper_spec.rb b/spec/helpers/markup_helper_spec.rb
index 2a0de0b0656..b4226f96a04 100644
--- a/spec/helpers/markup_helper_spec.rb
+++ b/spec/helpers/markup_helper_spec.rb
@@ -68,8 +68,8 @@ describe MarkupHelper do
expect(doc.css('a')[0].text).to eq 'This should finally fix '
# First issue link
- expect(doc.css('a')[1].attr('href')).
- to eq namespace_project_issue_path(project.namespace, project, issues[0])
+ expect(doc.css('a')[1].attr('href'))
+ .to eq namespace_project_issue_path(project.namespace, project, issues[0])
expect(doc.css('a')[1].text).to eq issues[0].to_reference
# Internal commit link
@@ -77,8 +77,8 @@ describe MarkupHelper do
expect(doc.css('a')[2].text).to eq ' and '
# Second issue link
- expect(doc.css('a')[3].attr('href')).
- to eq namespace_project_issue_path(project.namespace, project, issues[1])
+ expect(doc.css('a')[3].attr('href'))
+ .to eq namespace_project_issue_path(project.namespace, project, issues[1])
expect(doc.css('a')[3].text).to eq issues[1].to_reference
# Trailing commit link
@@ -98,8 +98,8 @@ describe MarkupHelper do
it "escapes HTML passed in as the body" do
actual = "This is a <h1>test</h1> - see #{issues[0].to_reference}"
- expect(helper.link_to_gfm(actual, link)).
- to match('&lt;h1&gt;test&lt;/h1&gt;')
+ expect(helper.link_to_gfm(actual, link))
+ .to match('&lt;h1&gt;test&lt;/h1&gt;')
end
it 'ignores reference links when they are the entire body' do
@@ -110,8 +110,8 @@ describe MarkupHelper do
it 'replaces commit message with emoji to link' do
actual = link_to_gfm(':book: Book', '/foo')
- expect(actual).
- to eq '<gl-emoji title="open book" data-name="book" data-unicode-version="6.0">📖</gl-emoji><a href="/foo"> Book</a>'
+ expect(actual)
+ .to eq '<gl-emoji title="open book" data-name="book" data-unicode-version="6.0">📖</gl-emoji><a href="/foo"> Book</a>'
end
end
diff --git a/spec/helpers/merge_requests_helper_spec.rb b/spec/helpers/merge_requests_helper_spec.rb
index f2c9d927388..493a4ff9a93 100644
--- a/spec/helpers/merge_requests_helper_spec.rb
+++ b/spec/helpers/merge_requests_helper_spec.rb
@@ -15,8 +15,8 @@ describe MergeRequestsHelper do
end
it 'does not include api credentials in a link' do
- allow(ci_service).
- to receive(:build_page).and_return("http://secretuser:secretpass@jenkins.example.com:8888/job/test1/scm/bySHA1/12d65c")
+ allow(ci_service)
+ .to receive(:build_page).and_return("http://secretuser:secretpass@jenkins.example.com:8888/job/test1/scm/bySHA1/12d65c")
expect(helper.ci_build_details_path(merge_request)).not_to match("secret")
end
end
diff --git a/spec/helpers/page_layout_helper_spec.rb b/spec/helpers/page_layout_helper_spec.rb
index dff2784f21f..95b4032616e 100644
--- a/spec/helpers/page_layout_helper_spec.rb
+++ b/spec/helpers/page_layout_helper_spec.rb
@@ -86,8 +86,8 @@ describe PageLayoutHelper do
it 'raises ArgumentError when given more than two attributes' do
map = { foo: 'foo', bar: 'bar', baz: 'baz' }
- expect { helper.page_card_attributes(map) }.
- to raise_error(ArgumentError, /more than two attributes/)
+ expect { helper.page_card_attributes(map) }
+ .to raise_error(ArgumentError, /more than two attributes/)
end
it 'rejects blank values' do
diff --git a/spec/helpers/preferences_helper_spec.rb b/spec/helpers/preferences_helper_spec.rb
index 2c0e9975f73..a04c87b08eb 100644
--- a/spec/helpers/preferences_helper_spec.rb
+++ b/spec/helpers/preferences_helper_spec.rb
@@ -29,15 +29,15 @@ describe PreferencesHelper do
describe 'user_color_scheme' do
context 'with a user' do
it "returns user's scheme's css_class" do
- allow(helper).to receive(:current_user).
- and_return(double(color_scheme_id: 3))
+ allow(helper).to receive(:current_user)
+ .and_return(double(color_scheme_id: 3))
expect(helper.user_color_scheme).to eq 'solarized-light'
end
it 'returns the default when id is invalid' do
- allow(helper).to receive(:current_user).
- and_return(double(color_scheme_id: Gitlab::ColorSchemes.count + 5))
+ allow(helper).to receive(:current_user)
+ .and_return(double(color_scheme_id: Gitlab::ColorSchemes.count + 5))
end
end
@@ -45,8 +45,8 @@ describe PreferencesHelper do
it 'returns the default theme' do
stub_user
- expect(helper.user_color_scheme).
- to eq Gitlab::ColorSchemes.default.css_class
+ expect(helper.user_color_scheme)
+ .to eq Gitlab::ColorSchemes.default.css_class
end
end
end
@@ -55,8 +55,8 @@ describe PreferencesHelper do
if messages.empty?
allow(helper).to receive(:current_user).and_return(nil)
else
- allow(helper).to receive(:current_user).
- and_return(double('user', messages))
+ allow(helper).to receive(:current_user)
+ .and_return(double('user', messages))
end
end
diff --git a/spec/javascripts/boards/board_new_issue_spec.js b/spec/javascripts/boards/board_new_issue_spec.js
index 832877de71c..c0a7323a505 100644
--- a/spec/javascripts/boards/board_new_issue_spec.js
+++ b/spec/javascripts/boards/board_new_issue_spec.js
@@ -12,6 +12,7 @@ import './mock_data';
describe('Issue boards new issue form', () => {
let vm;
let list;
+ let newIssueMock;
const promiseReturn = {
json() {
return {
@@ -21,7 +22,11 @@ describe('Issue boards new issue form', () => {
};
const submitIssue = () => {
- vm.$el.querySelector('.btn-success').click();
+ const dummySubmitEvent = {
+ preventDefault() {},
+ };
+ vm.$refs.submitButton = vm.$el.querySelector('.btn-success');
+ return vm.submit(dummySubmitEvent);
};
beforeEach((done) => {
@@ -32,29 +37,35 @@ describe('Issue boards new issue form', () => {
gl.issueBoards.BoardsStore.create();
gl.IssueBoardsApp = new Vue();
- setTimeout(() => {
- list = new List(listObj);
-
- spyOn(gl.boardService, 'newIssue').and.callFake(() => new Promise((resolve, reject) => {
- if (vm.title === 'error') {
- reject();
- } else {
- resolve(promiseReturn);
- }
- }));
-
- vm = new BoardNewIssueComp({
- propsData: {
- list,
- },
- }).$mount();
-
- done();
- }, 0);
+ list = new List(listObj);
+
+ newIssueMock = Promise.resolve(promiseReturn);
+ spyOn(list, 'newIssue').and.callFake(() => newIssueMock);
+
+ vm = new BoardNewIssueComp({
+ propsData: {
+ list,
+ },
+ }).$mount();
+
+ Vue.nextTick()
+ .then(done)
+ .catch(done.fail);
});
- afterEach(() => {
- Vue.http.interceptors = _.without(Vue.http.interceptors, boardsMockInterceptor);
+ it('calls submit if submit button is clicked', (done) => {
+ spyOn(vm, 'submit');
+ vm.title = 'Testing Title';
+
+ Vue.nextTick()
+ .then(() => {
+ vm.$el.querySelector('.btn-success').click();
+
+ expect(vm.submit.calls.count()).toBe(1);
+ expect(vm.$refs['submit-button']).toBe(vm.$el.querySelector('.btn-success'));
+ })
+ .then(done)
+ .catch(done.fail);
});
it('disables submit button if title is empty', () => {
@@ -64,136 +75,122 @@ describe('Issue boards new issue form', () => {
it('enables submit button if title is not empty', (done) => {
vm.title = 'Testing Title';
- setTimeout(() => {
- expect(vm.$el.querySelector('.form-control').value).toBe('Testing Title');
- expect(vm.$el.querySelector('.btn-success').disabled).not.toBe(true);
-
- done();
- }, 0);
+ Vue.nextTick()
+ .then(() => {
+ expect(vm.$el.querySelector('.form-control').value).toBe('Testing Title');
+ expect(vm.$el.querySelector('.btn-success').disabled).not.toBe(true);
+ })
+ .then(done)
+ .catch(done.fail);
});
it('clears title after clicking cancel', (done) => {
vm.$el.querySelector('.btn-default').click();
- setTimeout(() => {
- expect(vm.title).toBe('');
- done();
- }, 0);
+ Vue.nextTick()
+ .then(() => {
+ expect(vm.title).toBe('');
+ })
+ .then(done)
+ .catch(done.fail);
});
it('does not create new issue if title is empty', (done) => {
- submitIssue();
-
- setTimeout(() => {
- expect(gl.boardService.newIssue).not.toHaveBeenCalled();
- done();
- }, 0);
+ submitIssue()
+ .then(() => {
+ expect(list.newIssue).not.toHaveBeenCalled();
+ })
+ .then(done)
+ .catch(done.fail);
});
describe('submit success', () => {
it('creates new issue', (done) => {
vm.title = 'submit title';
- setTimeout(() => {
- submitIssue();
-
- expect(gl.boardService.newIssue).toHaveBeenCalled();
- done();
- }, 0);
+ Vue.nextTick()
+ .then(submitIssue)
+ .then(() => {
+ expect(list.newIssue).toHaveBeenCalled();
+ })
+ .then(done)
+ .catch(done.fail);
});
it('enables button after submit', (done) => {
vm.title = 'submit issue';
- setTimeout(() => {
- submitIssue();
-
- expect(vm.$el.querySelector('.btn-success').disabled).toBe(false);
- done();
- }, 0);
+ Vue.nextTick()
+ .then(submitIssue)
+ .then(() => {
+ expect(vm.$el.querySelector('.btn-success').disabled).toBe(false);
+ })
+ .then(done)
+ .catch(done.fail);
});
it('clears title after submit', (done) => {
vm.title = 'submit issue';
- Vue.nextTick(() => {
- submitIssue();
-
- setTimeout(() => {
+ Vue.nextTick()
+ .then(submitIssue)
+ .then(() => {
expect(vm.title).toBe('');
- done();
- }, 0);
- });
- });
-
- it('adds new issue to top of list after submit request', (done) => {
- vm.title = 'submit issue';
-
- setTimeout(() => {
- submitIssue();
-
- setTimeout(() => {
- expect(list.issues.length).toBe(2);
- expect(list.issues[0].title).toBe('submit issue');
- expect(list.issues[0].subscribed).toBe(true);
- done();
- }, 0);
- }, 0);
+ })
+ .then(done)
+ .catch(done.fail);
});
it('sets detail issue after submit', (done) => {
expect(gl.issueBoards.BoardsStore.detail.issue.title).toBe(undefined);
vm.title = 'submit issue';
- setTimeout(() => {
- submitIssue();
-
- setTimeout(() => {
+ Vue.nextTick()
+ .then(submitIssue)
+ .then(() => {
expect(gl.issueBoards.BoardsStore.detail.issue.title).toBe('submit issue');
- done();
- }, 0);
- }, 0);
+ })
+ .then(done)
+ .catch(done.fail);
});
it('sets detail list after submit', (done) => {
vm.title = 'submit issue';
- setTimeout(() => {
- submitIssue();
-
- setTimeout(() => {
+ Vue.nextTick()
+ .then(submitIssue)
+ .then(() => {
expect(gl.issueBoards.BoardsStore.detail.list.id).toBe(list.id);
- done();
- }, 0);
- }, 0);
+ })
+ .then(done)
+ .catch(done.fail);
});
});
describe('submit error', () => {
- it('removes issue', (done) => {
+ beforeEach(() => {
+ newIssueMock = Promise.reject(new Error('My hovercraft is full of eels!'));
vm.title = 'error';
+ });
- setTimeout(() => {
- submitIssue();
-
- setTimeout(() => {
+ it('removes issue', (done) => {
+ Vue.nextTick()
+ .then(submitIssue)
+ .then(() => {
expect(list.issues.length).toBe(1);
- done();
- }, 0);
- }, 0);
+ })
+ .then(done)
+ .catch(done.fail);
});
it('shows error', (done) => {
- vm.title = 'error';
-
- setTimeout(() => {
- submitIssue();
-
- setTimeout(() => {
+ Vue.nextTick()
+ .then(submitIssue)
+ .then(() => {
expect(vm.error).toBe(true);
- done();
- }, 0);
- }, 0);
+ })
+ .then(done)
+ .catch(done.fail);
});
});
});
diff --git a/spec/javascripts/boards/list_spec.js b/spec/javascripts/boards/list_spec.js
index 8e3d9fd77a0..db50829a276 100644
--- a/spec/javascripts/boards/list_spec.js
+++ b/spec/javascripts/boards/list_spec.js
@@ -150,4 +150,41 @@ describe('List model', () => {
expect(list.getIssues).toHaveBeenCalled();
});
});
+
+ describe('newIssue', () => {
+ beforeEach(() => {
+ spyOn(gl.boardService, 'newIssue').and.returnValue(Promise.resolve({
+ json() {
+ return {
+ iid: 42,
+ };
+ },
+ }));
+ });
+
+ it('adds new issue to top of list', (done) => {
+ list.issues.push(new ListIssue({
+ title: 'Testing',
+ iid: _.random(10000),
+ confidential: false,
+ labels: [list.label],
+ assignees: [],
+ }));
+ const dummyIssue = new ListIssue({
+ title: 'new issue',
+ iid: _.random(10000),
+ confidential: false,
+ labels: [list.label],
+ assignees: [],
+ });
+
+ list.newIssue(dummyIssue)
+ .then(() => {
+ expect(list.issues.length).toBe(2);
+ expect(list.issues[0]).toBe(dummyIssue);
+ })
+ .then(done)
+ .catch(done.fail);
+ });
+ });
});
diff --git a/spec/javascripts/filtered_search/filtered_search_manager_spec.js b/spec/javascripts/filtered_search/filtered_search_manager_spec.js
index f67cd356ef1..16ae649ee60 100644
--- a/spec/javascripts/filtered_search/filtered_search_manager_spec.js
+++ b/spec/javascripts/filtered_search/filtered_search_manager_spec.js
@@ -48,18 +48,23 @@ describe('Filtered Search Manager', () => {
</div>
`);
+ spyOn(gl.FilteredSearchDropdownManager.prototype, 'setDropdown').and.callFake(() => {});
+ });
+
+ const initializeManager = () => {
+ /* eslint-disable jasmine/no-unsafe-spy */
spyOn(gl.FilteredSearchManager.prototype, 'loadSearchParamsFromURL').and.callFake(() => {});
spyOn(gl.FilteredSearchManager.prototype, 'tokenChange').and.callFake(() => {});
- spyOn(gl.FilteredSearchDropdownManager.prototype, 'setDropdown').and.callFake(() => {});
spyOn(gl.FilteredSearchDropdownManager.prototype, 'updateDropdownOffset').and.callFake(() => {});
spyOn(gl.utils, 'getParameterByName').and.returnValue(null);
spyOn(gl.FilteredSearchVisualTokens, 'unselectTokens').and.callThrough();
+ /* eslint-enable jasmine/no-unsafe-spy */
input = document.querySelector('.filtered-search');
tokensContainer = document.querySelector('.tokens-container');
manager = new gl.FilteredSearchManager();
manager.setup();
- });
+ };
afterEach(() => {
manager.cleanup();
@@ -67,33 +72,34 @@ describe('Filtered Search Manager', () => {
describe('class constructor', () => {
const isLocalStorageAvailable = 'isLocalStorageAvailable';
- let filteredSearchManager;
beforeEach(() => {
spyOn(RecentSearchesService, 'isAvailable').and.returnValue(isLocalStorageAvailable);
spyOn(recentSearchesStoreSrc, 'default');
spyOn(RecentSearchesRoot.prototype, 'render');
-
- filteredSearchManager = new gl.FilteredSearchManager();
- filteredSearchManager.setup();
-
- return filteredSearchManager;
});
it('should instantiate RecentSearchesStore with isLocalStorageAvailable', () => {
+ manager = new gl.FilteredSearchManager();
+
expect(RecentSearchesService.isAvailable).toHaveBeenCalled();
expect(recentSearchesStoreSrc.default).toHaveBeenCalledWith({
isLocalStorageAvailable,
allowedKeys: gl.FilteredSearchTokenKeys.getKeys(),
});
});
+ });
+
+ describe('setup', () => {
+ beforeEach(() => {
+ manager = new gl.FilteredSearchManager();
+ });
it('should not instantiate Flash if an RecentSearchesServiceError is caught', () => {
spyOn(RecentSearchesService.prototype, 'fetch').and.callFake(() => Promise.reject(new RecentSearchesServiceError()));
spyOn(window, 'Flash');
- filteredSearchManager = new gl.FilteredSearchManager();
- filteredSearchManager.setup();
+ manager.setup();
expect(window.Flash).not.toHaveBeenCalled();
});
@@ -102,10 +108,12 @@ describe('Filtered Search Manager', () => {
describe('searchState', () => {
beforeEach(() => {
spyOn(gl.FilteredSearchManager.prototype, 'search').and.callFake(() => {});
+ initializeManager();
});
it('should blur button', () => {
const e = {
+ preventDefault: () => {},
currentTarget: {
blur: () => {},
},
@@ -118,6 +126,7 @@ describe('Filtered Search Manager', () => {
it('should not call search if there is no state', () => {
const e = {
+ preventDefault: () => {},
currentTarget: {
blur: () => {},
},
@@ -129,6 +138,7 @@ describe('Filtered Search Manager', () => {
it('should call search when there is state', () => {
const e = {
+ preventDefault: () => {},
currentTarget: {
blur: () => {},
dataset: {
@@ -145,6 +155,10 @@ describe('Filtered Search Manager', () => {
describe('search', () => {
const defaultParams = '?scope=all&utf8=%E2%9C%93&state=opened';
+ beforeEach(() => {
+ initializeManager();
+ });
+
it('should search with a single word', (done) => {
input.value = 'searchTerm';
@@ -194,6 +208,10 @@ describe('Filtered Search Manager', () => {
});
describe('handleInputPlaceholder', () => {
+ beforeEach(() => {
+ initializeManager();
+ });
+
it('should render placeholder when there is no input', () => {
expect(input.placeholder).toEqual(placeholder);
});
@@ -220,6 +238,10 @@ describe('Filtered Search Manager', () => {
});
describe('checkForBackspace', () => {
+ beforeEach(() => {
+ initializeManager();
+ });
+
describe('tokens and no input', () => {
beforeEach(() => {
tokensContainer.innerHTML = FilteredSearchSpecHelper.createTokensContainerHTML(
@@ -257,6 +279,10 @@ describe('Filtered Search Manager', () => {
});
describe('removeToken', () => {
+ beforeEach(() => {
+ initializeManager();
+ });
+
it('removes token even when it is already selected', () => {
tokensContainer.innerHTML = FilteredSearchSpecHelper.createTokensContainerHTML(
FilteredSearchSpecHelper.createFilterVisualTokenHTML('milestone', 'none', true),
@@ -288,6 +314,7 @@ describe('Filtered Search Manager', () => {
describe('removeSelectedTokenKeydown', () => {
beforeEach(() => {
+ initializeManager();
tokensContainer.innerHTML = FilteredSearchSpecHelper.createTokensContainerHTML(
FilteredSearchSpecHelper.createFilterVisualTokenHTML('milestone', 'none', true),
);
@@ -341,27 +368,39 @@ describe('Filtered Search Manager', () => {
spyOn(gl.FilteredSearchVisualTokens, 'removeSelectedToken').and.callThrough();
spyOn(gl.FilteredSearchManager.prototype, 'handleInputPlaceholder').and.callThrough();
spyOn(gl.FilteredSearchManager.prototype, 'toggleClearSearchButton').and.callThrough();
- manager.removeSelectedToken();
+ initializeManager();
});
it('calls FilteredSearchVisualTokens.removeSelectedToken', () => {
+ manager.removeSelectedToken();
+
expect(gl.FilteredSearchVisualTokens.removeSelectedToken).toHaveBeenCalled();
});
it('calls handleInputPlaceholder', () => {
+ manager.removeSelectedToken();
+
expect(manager.handleInputPlaceholder).toHaveBeenCalled();
});
it('calls toggleClearSearchButton', () => {
+ manager.removeSelectedToken();
+
expect(manager.toggleClearSearchButton).toHaveBeenCalled();
});
it('calls update dropdown offset', () => {
+ manager.removeSelectedToken();
+
expect(manager.dropdownManager.updateDropdownOffset).toHaveBeenCalled();
});
});
describe('toggleInputContainerFocus', () => {
+ beforeEach(() => {
+ initializeManager();
+ });
+
it('toggles on focus', () => {
input.focus();
expect(document.querySelector('.filtered-search-box').classList.contains('focus')).toEqual(true);
diff --git a/spec/javascripts/fixtures/merge_requests.rb b/spec/javascripts/fixtures/merge_requests.rb
index 0715f4d5f6b..daaddd8f390 100644
--- a/spec/javascripts/fixtures/merge_requests.rb
+++ b/spec/javascripts/fixtures/merge_requests.rb
@@ -55,20 +55,27 @@ describe Projects::MergeRequestsController, '(JavaScript fixtures)', type: :cont
render_merge_request(example.description, merge_request)
end
- it 'merge_requests/changes_tab_with_comments.json' do |example|
+ it 'merge_requests/inline_changes_tab_with_comments.json' do |example|
create(:diff_note_on_merge_request, project: project, author: admin, position: position, noteable: merge_request)
create(:note_on_merge_request, author: admin, project: project, noteable: merge_request)
render_merge_request(example.description, merge_request, action: :diffs, format: :json)
end
+ it 'merge_requests/parallel_changes_tab_with_comments.json' do |example|
+ create(:diff_note_on_merge_request, project: project, author: admin, position: position, noteable: merge_request)
+ create(:note_on_merge_request, author: admin, project: project, noteable: merge_request)
+ render_merge_request(example.description, merge_request, action: :diffs, format: :json, view: 'parallel')
+ end
+
private
- def render_merge_request(fixture_file_name, merge_request, action: :show, format: :html)
+ def render_merge_request(fixture_file_name, merge_request, action: :show, format: :html, view: 'inline')
get action,
namespace_id: project.namespace.to_param,
project_id: project,
id: merge_request.to_param,
- format: format
+ format: format,
+ view: view
expect(response).to be_success
store_frontend_fixture(response, fixture_file_name)
diff --git a/spec/javascripts/fixtures/prometheus_service.rb b/spec/javascripts/fixtures/prometheus_service.rb
new file mode 100644
index 00000000000..3200577b326
--- /dev/null
+++ b/spec/javascripts/fixtures/prometheus_service.rb
@@ -0,0 +1,30 @@
+require 'spec_helper'
+
+describe Projects::ServicesController, '(JavaScript fixtures)', type: :controller do
+ include JavaScriptFixturesHelpers
+
+ let(:admin) { create(:admin) }
+ let(:namespace) { create(:namespace, name: 'frontend-fixtures' )}
+ let(:project) { create(:project_empty_repo, namespace: namespace, path: 'services-project') }
+ let!(:service) { create(:prometheus_service, project: project) }
+
+ render_views
+
+ before(:all) do
+ clean_frontend_fixtures('services/prometheus')
+ end
+
+ before(:each) do
+ sign_in(admin)
+ end
+
+ it 'services/prometheus/prometheus_service.html.raw' do |example|
+ get :edit,
+ namespace_id: namespace,
+ project_id: project,
+ id: service.to_param
+
+ expect(response).to be_success
+ store_frontend_fixture(response, example.description)
+ end
+end
diff --git a/spec/javascripts/groups/groups_spec.js b/spec/javascripts/groups/groups_spec.js
index 2a77f7259da..aaffb56fa94 100644
--- a/spec/javascripts/groups/groups_spec.js
+++ b/spec/javascripts/groups/groups_spec.js
@@ -1,4 +1,5 @@
import Vue from 'vue';
+import eventHub from '~/groups/event_hub';
import groupFolderComponent from '~/groups/components/group_folder.vue';
import groupItemComponent from '~/groups/components/group_item.vue';
import groupsComponent from '~/groups/components/groups.vue';
@@ -46,6 +47,12 @@ describe('Groups Component', () => {
expect(component.$el.querySelector('#group-1120')).toBeDefined();
});
+ it('should respect the order of groups', () => {
+ const wrap = component.$el.querySelector('.groups-list-tree-container > .group-list-tree');
+ expect(wrap.querySelector('.group-row:nth-child(1)').id).toBe('group-12');
+ expect(wrap.querySelector('.group-row:nth-child(2)').id).toBe('group-1119');
+ });
+
it('should render group and its subgroup', () => {
const lists = component.$el.querySelectorAll('.group-list-tree');
@@ -54,11 +61,26 @@ describe('Groups Component', () => {
expect(lists[0].querySelector('#group-1119').classList.contains('is-open')).toBe(true);
expect(lists[0].querySelector('#group-1119').classList.contains('has-subgroups')).toBe(true);
- expect(lists[2].querySelector('#group-1120').textContent).toContain(groups[1119].subGroups[1120].name);
+ expect(lists[2].querySelector('#group-1120').textContent).toContain(groups.id1119.subGroups.id1120.name);
});
it('should remove prefix of parent group', () => {
expect(component.$el.querySelector('#group-12 #group-1128 .title').textContent).toContain('level2 / level3 / level4');
});
+
+ it('should remove the group after leaving the group', (done) => {
+ spyOn(window, 'confirm').and.returnValue(true);
+
+ eventHub.$on('leaveGroup', (group, collection) => {
+ store.removeGroup(group, collection);
+ });
+
+ component.$el.querySelector('#group-12 .leave-group').click();
+
+ Vue.nextTick(() => {
+ expect(component.$el.querySelector('#group-12')).toBeNull();
+ done();
+ });
+ });
});
});
diff --git a/spec/javascripts/issue_show/components/app_spec.js b/spec/javascripts/issue_show/components/app_spec.js
index 276e01fc82f..9df92318864 100644
--- a/spec/javascripts/issue_show/components/app_spec.js
+++ b/spec/javascripts/issue_show/components/app_spec.js
@@ -3,17 +3,9 @@ import '~/render_math';
import '~/render_gfm';
import issuableApp from '~/issue_show/components/app.vue';
import eventHub from '~/issue_show/event_hub';
+import Poll from '~/lib/utils/poll';
import issueShowData from '../mock_data';
-const issueShowInterceptor = data => (request, next) => {
- next(request.respondWith(JSON.stringify(data), {
- status: 200,
- headers: {
- 'POLL-INTERVAL': 1,
- },
- }));
-};
-
function formatText(text) {
return text.trim().replace(/\s\s+/g, ' ');
}
@@ -24,10 +16,10 @@ describe('Issuable output', () => {
let vm;
beforeEach(() => {
- const IssuableDescriptionComponent = Vue.extend(issuableApp);
- Vue.http.interceptors.push(issueShowInterceptor(issueShowData.initialRequest));
-
spyOn(eventHub, '$emit');
+ spyOn(Poll.prototype, 'makeRequest');
+
+ const IssuableDescriptionComponent = Vue.extend(issuableApp);
vm = new IssuableDescriptionComponent({
propsData: {
@@ -54,9 +46,18 @@ describe('Issuable output', () => {
});
it('should render a title/description/edited and update title/description/edited on update', (done) => {
- setTimeout(() => {
- const editedText = vm.$el.querySelector('.edited-text');
+ vm.poll.options.successCallback({
+ json() {
+ return issueShowData.initialRequest;
+ },
+ });
+ let editedText;
+ Vue.nextTick()
+ .then(() => {
+ editedText = vm.$el.querySelector('.edited-text');
+ })
+ .then(() => {
expect(document.querySelector('title').innerText).toContain('this is a title (#1)');
expect(vm.$el.querySelector('.title').innerHTML).toContain('<p>this is a title</p>');
expect(vm.$el.querySelector('.wiki').innerHTML).toContain('<p>this is a description!</p>');
@@ -64,22 +65,27 @@ describe('Issuable output', () => {
expect(formatText(editedText.innerText)).toMatch(/Edited[\s\S]+?by Some User/);
expect(editedText.querySelector('.author_link').href).toMatch(/\/some_user$/);
expect(editedText.querySelector('time')).toBeTruthy();
-
- Vue.http.interceptors.push(issueShowInterceptor(issueShowData.secondRequest));
-
- setTimeout(() => {
- expect(document.querySelector('title').innerText).toContain('2 (#1)');
- expect(vm.$el.querySelector('.title').innerHTML).toContain('<p>2</p>');
- expect(vm.$el.querySelector('.wiki').innerHTML).toContain('<p>42</p>');
- expect(vm.$el.querySelector('.js-task-list-field').value).toContain('42');
- expect(vm.$el.querySelector('.edited-text')).toBeTruthy();
- expect(formatText(vm.$el.querySelector('.edited-text').innerText)).toMatch(/Edited[\s\S]+?by Other User/);
- expect(editedText.querySelector('.author_link').href).toMatch(/\/other_user$/);
- expect(editedText.querySelector('time')).toBeTruthy();
-
- done();
+ })
+ .then(() => {
+ vm.poll.options.successCallback({
+ json() {
+ return issueShowData.secondRequest;
+ },
});
- });
+ })
+ .then(Vue.nextTick)
+ .then(() => {
+ expect(document.querySelector('title').innerText).toContain('2 (#1)');
+ expect(vm.$el.querySelector('.title').innerHTML).toContain('<p>2</p>');
+ expect(vm.$el.querySelector('.wiki').innerHTML).toContain('<p>42</p>');
+ expect(vm.$el.querySelector('.js-task-list-field').value).toContain('42');
+ expect(vm.$el.querySelector('.edited-text')).toBeTruthy();
+ expect(formatText(vm.$el.querySelector('.edited-text').innerText)).toMatch(/Edited[\s\S]+?by Other User/);
+ expect(editedText.querySelector('.author_link').href).toMatch(/\/other_user$/);
+ expect(editedText.querySelector('time')).toBeTruthy();
+ })
+ .then(done)
+ .catch(done.fail);
});
it('shows actions if permissions are correct', (done) => {
@@ -344,21 +350,23 @@ describe('Issuable output', () => {
describe('open form', () => {
it('shows locked warning if form is open & data is different', (done) => {
- Vue.http.interceptors.push(issueShowInterceptor(issueShowData.initialRequest));
+ vm.poll.options.successCallback({
+ json() {
+ return issueShowData.initialRequest;
+ },
+ });
Vue.nextTick()
- .then(() => new Promise((resolve) => {
- setTimeout(resolve);
- }))
.then(() => {
vm.openForm();
- Vue.http.interceptors.push(issueShowInterceptor(issueShowData.secondRequest));
-
- return new Promise((resolve) => {
- setTimeout(resolve);
+ vm.poll.options.successCallback({
+ json() {
+ return issueShowData.secondRequest;
+ },
});
})
+ .then(Vue.nextTick)
.then(() => {
expect(
vm.formState.lockedWarningVisible,
@@ -367,9 +375,8 @@ describe('Issuable output', () => {
expect(
vm.$el.querySelector('.alert'),
).not.toBeNull();
-
- done();
})
+ .then(done)
.catch(done.fail);
});
});
diff --git a/spec/javascripts/merge_request_notes_spec.js b/spec/javascripts/merge_request_notes_spec.js
index b6d0ce02c4f..9e9eb17d439 100644
--- a/spec/javascripts/merge_request_notes_spec.js
+++ b/spec/javascripts/merge_request_notes_spec.js
@@ -15,7 +15,7 @@ describe('Merge request notes', () => {
gl.utils = gl.utils || {};
const discussionTabFixture = 'merge_requests/diff_comment.html.raw';
- const changesTabJsonFixture = 'merge_requests/changes_tab_with_comments.json';
+ const changesTabJsonFixture = 'merge_requests/inline_changes_tab_with_comments.json';
preloadFixtures(discussionTabFixture, changesTabJsonFixture);
describe('Discussion tab with diff comments', () => {
diff --git a/spec/javascripts/merge_request_tabs_spec.js b/spec/javascripts/merge_request_tabs_spec.js
index 9916d2c1e21..bb6b5d852d3 100644
--- a/spec/javascripts/merge_request_tabs_spec.js
+++ b/spec/javascripts/merge_request_tabs_spec.js
@@ -22,7 +22,15 @@ import 'vendor/jquery.scrollTo';
};
$.extend(stubLocation, defaults, stubs || {});
};
- preloadFixtures('merge_requests/merge_request_with_task_list.html.raw', 'merge_requests/diff_comment.html.raw');
+
+ const inlineChangesTabJsonFixture = 'merge_requests/inline_changes_tab_with_comments.json';
+ const parallelChangesTabJsonFixture = 'merge_requests/parallel_changes_tab_with_comments.json';
+ preloadFixtures(
+ 'merge_requests/merge_request_with_task_list.html.raw',
+ 'merge_requests/diff_comment.html.raw',
+ inlineChangesTabJsonFixture,
+ parallelChangesTabJsonFixture
+ );
beforeEach(function () {
this.class = new gl.MergeRequestTabs({ stubLocation: stubLocation });
@@ -271,6 +279,19 @@ import 'vendor/jquery.scrollTo';
});
describe('loadDiff', function () {
+ beforeEach(() => {
+ loadFixtures('merge_requests/diff_comment.html.raw');
+ spyOn(window.gl.utils, 'getPagePath').and.returnValue('merge_requests');
+ window.gl.ImageFile = () => {};
+ window.notes = new Notes('', []);
+ spyOn(window.notes, 'toggleDiffNote').and.callThrough();
+ });
+
+ afterEach(() => {
+ delete window.gl.ImageFile;
+ delete window.notes;
+ });
+
it('requires an absolute pathname', function () {
spyOn($, 'ajax').and.callFake(function (options) {
expect(options.url).toEqual('/foo/bar/merge_requests/1/diffs.json');
@@ -279,43 +300,112 @@ import 'vendor/jquery.scrollTo';
this.class.loadDiff('/foo/bar/merge_requests/1/diffs');
});
- describe('with note fragment hash', () => {
+ describe('with inline diff', () => {
+ let noteId;
+ let noteLineNumId;
+
beforeEach(() => {
- loadFixtures('merge_requests/diff_comment.html.raw');
- spyOn(window.gl.utils, 'getPagePath').and.returnValue('merge_requests');
- window.notes = new Notes('', []);
- spyOn(window.notes, 'toggleDiffNote').and.callThrough();
- });
+ const diffsResponse = getJSONFixture(inlineChangesTabJsonFixture);
+
+ const $html = $(diffsResponse.html);
+ noteId = $html.find('.note').attr('id');
+ noteLineNumId = $html
+ .find('.note')
+ .closest('.notes_holder')
+ .prev('.line_holder')
+ .find('a[data-linenumber]')
+ .attr('href')
+ .replace('#', '');
- afterEach(() => {
- delete window.notes;
+ spyOn($, 'ajax').and.callFake(function (options) {
+ options.success(diffsResponse);
+ });
});
- it('should expand and scroll to linked fragment hash #note_xxx', function () {
- const noteId = 'note_1';
- spyOn(window.gl.utils, 'getLocationHash').and.returnValue(noteId);
- spyOn($, 'ajax').and.callFake(function (options) {
- options.success({ html: `<div id="${noteId}">foo</div>` });
+ describe('with note fragment hash', () => {
+ it('should expand and scroll to linked fragment hash #note_xxx', function () {
+ spyOn(window.gl.utils, 'getLocationHash').and.returnValue(noteId);
+ this.class.loadDiff('/foo/bar/merge_requests/1/diffs');
+
+ expect(noteId.length).toBeGreaterThan(0);
+ expect(window.notes.toggleDiffNote).toHaveBeenCalledWith({
+ target: jasmine.any(Object),
+ lineType: 'old',
+ forceShow: true,
+ });
});
- this.class.loadDiff('/foo/bar/merge_requests/1/diffs');
+ it('should gracefully ignore non-existant fragment hash', function () {
+ spyOn(window.gl.utils, 'getLocationHash').and.returnValue('note_something-that-does-not-exist');
+ this.class.loadDiff('/foo/bar/merge_requests/1/diffs');
- expect(window.notes.toggleDiffNote).toHaveBeenCalledWith({
- target: jasmine.any(Object),
- lineType: 'old',
- forceShow: true,
+ expect(window.notes.toggleDiffNote).not.toHaveBeenCalled();
});
});
- it('should gracefully ignore non-existant fragment hash', function () {
- spyOn(window.gl.utils, 'getLocationHash').and.returnValue('note_something-that-does-not-exist');
+ describe('with line number fragment hash', () => {
+ it('should gracefully ignore line number fragment hash', function () {
+ spyOn(window.gl.utils, 'getLocationHash').and.returnValue(noteLineNumId);
+ this.class.loadDiff('/foo/bar/merge_requests/1/diffs');
+
+ expect(noteLineNumId.length).toBeGreaterThan(0);
+ expect(window.notes.toggleDiffNote).not.toHaveBeenCalled();
+ });
+ });
+ });
+
+ describe('with parallel diff', () => {
+ let noteId;
+ let noteLineNumId;
+
+ beforeEach(() => {
+ const diffsResponse = getJSONFixture(parallelChangesTabJsonFixture);
+
+ const $html = $(diffsResponse.html);
+ noteId = $html.find('.note').attr('id');
+ noteLineNumId = $html
+ .find('.note')
+ .closest('.notes_holder')
+ .prev('.line_holder')
+ .find('a[data-linenumber]')
+ .attr('href')
+ .replace('#', '');
+
spyOn($, 'ajax').and.callFake(function (options) {
- options.success({ html: '' });
+ options.success(diffsResponse);
});
+ });
+
+ describe('with note fragment hash', () => {
+ it('should expand and scroll to linked fragment hash #note_xxx', function () {
+ spyOn(window.gl.utils, 'getLocationHash').and.returnValue(noteId);
- this.class.loadDiff('/foo/bar/merge_requests/1/diffs');
+ this.class.loadDiff('/foo/bar/merge_requests/1/diffs');
- expect(window.notes.toggleDiffNote).not.toHaveBeenCalled();
+ expect(noteId.length).toBeGreaterThan(0);
+ expect(window.notes.toggleDiffNote).toHaveBeenCalledWith({
+ target: jasmine.any(Object),
+ lineType: 'new',
+ forceShow: true,
+ });
+ });
+
+ it('should gracefully ignore non-existant fragment hash', function () {
+ spyOn(window.gl.utils, 'getLocationHash').and.returnValue('note_something-that-does-not-exist');
+ this.class.loadDiff('/foo/bar/merge_requests/1/diffs');
+
+ expect(window.notes.toggleDiffNote).not.toHaveBeenCalled();
+ });
+ });
+
+ describe('with line number fragment hash', () => {
+ it('should gracefully ignore line number fragment hash', function () {
+ spyOn(window.gl.utils, 'getLocationHash').and.returnValue(noteLineNumId);
+ this.class.loadDiff('/foo/bar/merge_requests/1/diffs');
+
+ expect(noteLineNumId.length).toBeGreaterThan(0);
+ expect(window.notes.toggleDiffNote).not.toHaveBeenCalled();
+ });
});
});
});
diff --git a/spec/javascripts/prometheus_metrics/mock_data.js b/spec/javascripts/prometheus_metrics/mock_data.js
new file mode 100644
index 00000000000..3af56df92e2
--- /dev/null
+++ b/spec/javascripts/prometheus_metrics/mock_data.js
@@ -0,0 +1,41 @@
+export const metrics = [
+ {
+ group: 'Kubernetes',
+ priority: 1,
+ active_metrics: 4,
+ metrics_missing_requirements: 0,
+ },
+ {
+ group: 'HAProxy',
+ priority: 2,
+ active_metrics: 3,
+ metrics_missing_requirements: 0,
+ },
+ {
+ group: 'Apache',
+ priority: 3,
+ active_metrics: 5,
+ metrics_missing_requirements: 0,
+ },
+];
+
+export const missingVarMetrics = [
+ {
+ group: 'Kubernetes',
+ priority: 1,
+ active_metrics: 4,
+ metrics_missing_requirements: 0,
+ },
+ {
+ group: 'HAProxy',
+ priority: 2,
+ active_metrics: 3,
+ metrics_missing_requirements: 1,
+ },
+ {
+ group: 'Apache',
+ priority: 3,
+ active_metrics: 5,
+ metrics_missing_requirements: 3,
+ },
+];
diff --git a/spec/javascripts/prometheus_metrics/prometheus_metrics_spec.js b/spec/javascripts/prometheus_metrics/prometheus_metrics_spec.js
new file mode 100644
index 00000000000..2b3a821dbd9
--- /dev/null
+++ b/spec/javascripts/prometheus_metrics/prometheus_metrics_spec.js
@@ -0,0 +1,158 @@
+import PrometheusMetrics from '~/prometheus_metrics/prometheus_metrics';
+import PANEL_STATE from '~/prometheus_metrics/constants';
+import { metrics, missingVarMetrics } from './mock_data';
+
+describe('PrometheusMetrics', () => {
+ const FIXTURE = 'services/prometheus/prometheus_service.html.raw';
+ preloadFixtures(FIXTURE);
+
+ beforeEach(() => {
+ loadFixtures(FIXTURE);
+ });
+
+ describe('constructor', () => {
+ let prometheusMetrics;
+
+ beforeEach(() => {
+ prometheusMetrics = new PrometheusMetrics('.js-prometheus-metrics-monitoring');
+ });
+
+ it('should initialize wrapper element refs on class object', () => {
+ expect(prometheusMetrics.$wrapper).toBeDefined();
+ expect(prometheusMetrics.$monitoredMetricsPanel).toBeDefined();
+ expect(prometheusMetrics.$monitoredMetricsCount).toBeDefined();
+ expect(prometheusMetrics.$monitoredMetricsLoading).toBeDefined();
+ expect(prometheusMetrics.$monitoredMetricsEmpty).toBeDefined();
+ expect(prometheusMetrics.$monitoredMetricsList).toBeDefined();
+ expect(prometheusMetrics.$missingEnvVarPanel).toBeDefined();
+ expect(prometheusMetrics.$panelToggle).toBeDefined();
+ expect(prometheusMetrics.$missingEnvVarMetricCount).toBeDefined();
+ expect(prometheusMetrics.$missingEnvVarMetricsList).toBeDefined();
+ });
+
+ it('should initialize metadata on class object', () => {
+ expect(prometheusMetrics.backOffRequestCounter).toEqual(0);
+ expect(prometheusMetrics.activeMetricsEndpoint).toContain('/test');
+ });
+ });
+
+ describe('showMonitoringMetricsPanelState', () => {
+ let prometheusMetrics;
+
+ beforeEach(() => {
+ prometheusMetrics = new PrometheusMetrics('.js-prometheus-metrics-monitoring');
+ });
+
+ it('should show loading state when called with `loading`', () => {
+ prometheusMetrics.showMonitoringMetricsPanelState(PANEL_STATE.LOADING);
+
+ expect(prometheusMetrics.$monitoredMetricsLoading.hasClass('hidden')).toBeFalsy();
+ expect(prometheusMetrics.$monitoredMetricsEmpty.hasClass('hidden')).toBeTruthy();
+ expect(prometheusMetrics.$monitoredMetricsList.hasClass('hidden')).toBeTruthy();
+ });
+
+ it('should show metrics list when called with `list`', () => {
+ prometheusMetrics.showMonitoringMetricsPanelState(PANEL_STATE.LIST);
+
+ expect(prometheusMetrics.$monitoredMetricsLoading.hasClass('hidden')).toBeTruthy();
+ expect(prometheusMetrics.$monitoredMetricsEmpty.hasClass('hidden')).toBeTruthy();
+ expect(prometheusMetrics.$monitoredMetricsList.hasClass('hidden')).toBeFalsy();
+ });
+
+ it('should show empty state when called with `empty`', () => {
+ prometheusMetrics.showMonitoringMetricsPanelState(PANEL_STATE.EMPTY);
+
+ expect(prometheusMetrics.$monitoredMetricsLoading.hasClass('hidden')).toBeTruthy();
+ expect(prometheusMetrics.$monitoredMetricsEmpty.hasClass('hidden')).toBeFalsy();
+ expect(prometheusMetrics.$monitoredMetricsList.hasClass('hidden')).toBeTruthy();
+ });
+ });
+
+ describe('populateActiveMetrics', () => {
+ let prometheusMetrics;
+
+ beforeEach(() => {
+ prometheusMetrics = new PrometheusMetrics('.js-prometheus-metrics-monitoring');
+ });
+
+ it('should show monitored metrics list', () => {
+ prometheusMetrics.populateActiveMetrics(metrics);
+
+ const $metricsListLi = prometheusMetrics.$monitoredMetricsList.find('li');
+
+ expect(prometheusMetrics.$monitoredMetricsLoading.hasClass('hidden')).toBeTruthy();
+ expect(prometheusMetrics.$monitoredMetricsList.hasClass('hidden')).toBeFalsy();
+
+ expect(prometheusMetrics.$monitoredMetricsCount.text()).toEqual('12');
+ expect($metricsListLi.length).toEqual(metrics.length);
+ expect($metricsListLi.first().find('.badge').text()).toEqual(`${metrics[0].active_metrics}`);
+ });
+
+ it('should show missing environment variables list', () => {
+ prometheusMetrics.populateActiveMetrics(missingVarMetrics);
+
+ expect(prometheusMetrics.$monitoredMetricsLoading.hasClass('hidden')).toBeTruthy();
+ expect(prometheusMetrics.$missingEnvVarPanel.hasClass('hidden')).toBeFalsy();
+
+ expect(prometheusMetrics.$missingEnvVarMetricCount.text()).toEqual('2');
+ expect(prometheusMetrics.$missingEnvVarPanel.find('li').length).toEqual(2);
+ expect(prometheusMetrics.$missingEnvVarPanel.find('.flash-container')).toBeDefined();
+ });
+ });
+
+ describe('loadActiveMetrics', () => {
+ let prometheusMetrics;
+
+ beforeEach(() => {
+ prometheusMetrics = new PrometheusMetrics('.js-prometheus-metrics-monitoring');
+ });
+
+ it('should show loader animation while response is being loaded and hide it when request is complete', (done) => {
+ const deferred = $.Deferred();
+ spyOn($, 'getJSON').and.returnValue(deferred.promise());
+
+ prometheusMetrics.loadActiveMetrics();
+
+ expect(prometheusMetrics.$monitoredMetricsLoading.hasClass('hidden')).toBeFalsy();
+ expect($.getJSON).toHaveBeenCalledWith(prometheusMetrics.activeMetricsEndpoint);
+
+ deferred.resolve({ data: metrics, success: true });
+
+ setTimeout(() => {
+ expect(prometheusMetrics.$monitoredMetricsLoading.hasClass('hidden')).toBeTruthy();
+ done();
+ });
+ });
+
+ it('should show empty state if response failed to load', (done) => {
+ const deferred = $.Deferred();
+ spyOn($, 'getJSON').and.returnValue(deferred.promise());
+ spyOn(prometheusMetrics, 'populateActiveMetrics');
+
+ prometheusMetrics.loadActiveMetrics();
+
+ deferred.reject();
+
+ setTimeout(() => {
+ expect(prometheusMetrics.$monitoredMetricsLoading.hasClass('hidden')).toBeTruthy();
+ expect(prometheusMetrics.$monitoredMetricsEmpty.hasClass('hidden')).toBeFalsy();
+ done();
+ });
+ });
+
+ it('should populate metrics list once response is loaded', (done) => {
+ const deferred = $.Deferred();
+ spyOn($, 'getJSON').and.returnValue(deferred.promise());
+ spyOn(prometheusMetrics, 'populateActiveMetrics');
+
+ prometheusMetrics.loadActiveMetrics();
+
+ deferred.resolve({ data: metrics, success: true });
+
+ setTimeout(() => {
+ expect(prometheusMetrics.populateActiveMetrics).toHaveBeenCalledWith(metrics);
+ done();
+ });
+ });
+ });
+});
diff --git a/spec/javascripts/test_bundle.js b/spec/javascripts/test_bundle.js
index f0d51bd0902..075b72f35b2 100644
--- a/spec/javascripts/test_bundle.js
+++ b/spec/javascripts/test_bundle.js
@@ -22,6 +22,19 @@ window.gl = window.gl || {};
window.gl.TEST_HOST = 'http://test.host';
window.gon = window.gon || {};
+let hasUnhandledPromiseRejections = false;
+
+window.addEventListener('unhandledrejection', (event) => {
+ hasUnhandledPromiseRejections = true;
+ console.error('Unhandled promise rejection:');
+ console.error(event.reason.stack || event.reason);
+});
+
+const checkUnhandledPromiseRejections = (done) => {
+ expect(hasUnhandledPromiseRejections).toBe(false);
+ done();
+};
+
// HACK: Chrome 59 disconnects if there are too many synchronous tests in a row
// because it appears to lock up the thread that communicates to Karma's socket
// This async beforeEach gets called on every spec and releases the JS thread long
@@ -63,6 +76,10 @@ testsContext.keys().forEach(function (path) {
}
});
+it('has no unhandled Promise rejections', (done) => {
+ setTimeout(checkUnhandledPromiseRejections(done), 1000);
+});
+
// if we're generating coverage reports, make sure to include all files so
// that we can catch files with 0% coverage
// see: https://github.com/deepsweet/istanbul-instrumenter-loader/issues/15
diff --git a/spec/javascripts/vue_mr_widget/components/mr_widget_related_links_spec.js b/spec/javascripts/vue_mr_widget/components/mr_widget_related_links_spec.js
index 6a44c54cdee..f6e0c3dfb74 100644
--- a/spec/javascripts/vue_mr_widget/components/mr_widget_related_links_spec.js
+++ b/spec/javascripts/vue_mr_widget/components/mr_widget_related_links_spec.js
@@ -1,31 +1,20 @@
import Vue from 'vue';
-import MRWidgetRelatedLinks from '~/vue_merge_request_widget/components/mr_widget_related_links';
+import relatedLinksComponent from '~/vue_merge_request_widget/components/mr_widget_related_links';
-describe('MRWidgetRelatedLinks', () => {
- let vm;
-
- beforeEach(() => {
- const Component = Vue.extend(MRWidgetRelatedLinks);
- vm = new Component({
- el: document.createElement('div'),
- propsData: {
- isMerged: false,
- relatedLinks: {},
- },
- });
- });
+const createComponent = (data) => {
+ const Component = Vue.extend(relatedLinksComponent);
- afterEach(() => {
- vm.$destroy();
+ return new Component({
+ el: document.createElement('div'),
+ propsData: data,
});
+};
+describe('MRWidgetRelatedLinks', () => {
describe('props', () => {
it('should have props', () => {
- const { isMerged, relatedLinks } = MRWidgetRelatedLinks.props;
+ const { relatedLinks } = relatedLinksComponent.props;
- expect(isMerged).toBeDefined();
- expect(isMerged.type).toBe(Boolean);
- expect(isMerged.required).toBeTruthy();
expect(relatedLinks).toBeDefined();
expect(relatedLinks.type instanceof Object).toBeTruthy();
expect(relatedLinks.required).toBeTruthy();
@@ -33,38 +22,16 @@ describe('MRWidgetRelatedLinks', () => {
});
describe('computed', () => {
- describe('closingText', () => {
- const dummyIssueLabel = 'dummy label';
-
- beforeEach(() => {
- spyOn(vm, 'issueLabel').and.returnValue(dummyIssueLabel);
- });
-
- it('outputs text for closing issues', () => {
- vm.isMerged = false;
-
- const text = vm.closingText;
-
- expect(text).toBe(`Closes ${dummyIssueLabel}`);
- });
-
- it('outputs text for closed issues', () => {
- vm.isMerged = true;
-
- const text = vm.closingText;
-
- expect(text).toBe(`Closed ${dummyIssueLabel}`);
- });
- });
-
describe('hasLinks', () => {
it('should return correct value when we have links reference', () => {
- vm.relatedLinks = {
- closing: '/foo',
- mentioned: '/foo',
- assignToMe: '/foo',
+ const data = {
+ relatedLinks: {
+ closing: '/foo',
+ mentioned: '/foo',
+ assignToMe: '/foo',
+ },
};
-
+ const vm = createComponent(data);
expect(vm.hasLinks).toBeTruthy();
vm.relatedLinks.closing = null;
@@ -77,160 +44,95 @@ describe('MRWidgetRelatedLinks', () => {
expect(vm.hasLinks).toBeFalsy();
});
});
-
- describe('mentionedText', () => {
- it('outputs text for one mentioned issue before merging', () => {
- vm.isMerged = false;
- spyOn(vm, 'hasMultipleIssues').and.returnValue(false);
-
- const text = vm.mentionedText;
-
- expect(text).toBe('is mentioned but will not be closed');
- });
-
- it('outputs text for one mentioned issue after merging', () => {
- vm.isMerged = true;
- spyOn(vm, 'hasMultipleIssues').and.returnValue(false);
-
- const text = vm.mentionedText;
-
- expect(text).toBe('is mentioned but was not closed');
- });
-
- it('outputs text for multiple mentioned issue before merging', () => {
- vm.isMerged = false;
- spyOn(vm, 'hasMultipleIssues').and.returnValue(true);
-
- const text = vm.mentionedText;
-
- expect(text).toBe('are mentioned but will not be closed');
- });
-
- it('outputs text for multiple mentioned issue after merging', () => {
- vm.isMerged = true;
- spyOn(vm, 'hasMultipleIssues').and.returnValue(true);
-
- const text = vm.mentionedText;
-
- expect(text).toBe('are mentioned but were not closed');
- });
- });
});
describe('methods', () => {
- const relatedLinks = {
- oneIssue: '<a href="#">#7</a>',
- twoIssues: '<a href="#">#23</a> and <a>#42</a>',
- threeIssues: '<a href="#">#1</a>, <a>#2</a>, and <a>#3</a>',
+ const data = {
+ relatedLinks: {
+ closing: '<a href="#">#23</a> and <a>#42</a>',
+ mentioned: '<a href="#">#7</a>',
+ },
};
-
- beforeEach(() => {
- vm.relatedLinks = relatedLinks;
- });
+ const vm = createComponent(data);
describe('hasMultipleIssues', () => {
it('should return true if the given text has multiple issues', () => {
- expect(vm.hasMultipleIssues(relatedLinks.twoIssues)).toBeTruthy();
- expect(vm.hasMultipleIssues(relatedLinks.threeIssues)).toBeTruthy();
+ expect(vm.hasMultipleIssues(data.relatedLinks.closing)).toBeTruthy();
});
it('should return false if the given text has one issue', () => {
- expect(vm.hasMultipleIssues(relatedLinks.oneIssue)).toBeFalsy();
+ expect(vm.hasMultipleIssues(data.relatedLinks.mentioned)).toBeFalsy();
});
});
describe('issueLabel', () => {
it('should return true if the given text has multiple issues', () => {
- expect(vm.issueLabel('twoIssues')).toEqual('issues');
- expect(vm.issueLabel('threeIssues')).toEqual('issues');
+ expect(vm.issueLabel('closing')).toEqual('issues');
+ });
+
+ it('should return false if the given text has one issue', () => {
+ expect(vm.issueLabel('mentioned')).toEqual('issue');
+ });
+ });
+
+ describe('verbLabel', () => {
+ it('should return true if the given text has multiple issues', () => {
+ expect(vm.verbLabel('closing')).toEqual('are');
});
it('should return false if the given text has one issue', () => {
- expect(vm.issueLabel('oneIssue')).toEqual('issue');
+ expect(vm.verbLabel('mentioned')).toEqual('is');
});
});
});
describe('template', () => {
- it('should have only have closing issues text', (done) => {
- vm.relatedLinks = {
- closing: '<a href="#">#23</a> and <a>#42</a>',
- };
-
- Vue.nextTick()
- .then(() => {
- const content = vm.$el.textContent.replace(/\n(\s)+/g, ' ').trim();
+ it('should have only have closing issues text', () => {
+ const vm = createComponent({
+ relatedLinks: {
+ closing: '<a href="#">#23</a> and <a>#42</a>',
+ },
+ });
+ const content = vm.$el.textContent.replace(/\n(\s)+/g, ' ').trim();
- expect(content).toContain('Closes issues #23 and #42');
- expect(content).not.toContain('mentioned');
- })
- .then(done)
- .catch(done.fail);
+ expect(content).toContain('Closes issues #23 and #42');
+ expect(content).not.toContain('mentioned');
});
- it('should have only have mentioned issues text', (done) => {
- vm.relatedLinks = {
- mentioned: '<a href="#">#7</a>',
- };
-
- Vue.nextTick()
- .then(() => {
- expect(vm.$el.innerText).toContain('issue #7');
- expect(vm.$el.innerText).toContain('is mentioned but will not be closed');
- expect(vm.$el.innerText).not.toContain('Closes');
- })
- .then(done)
- .catch(done.fail);
- });
+ it('should have only have mentioned issues text', () => {
+ const vm = createComponent({
+ relatedLinks: {
+ mentioned: '<a href="#">#7</a>',
+ },
+ });
- it('should have closing and mentioned issues at the same time', (done) => {
- vm.relatedLinks = {
- closing: '<a href="#">#7</a>',
- mentioned: '<a href="#">#23</a> and <a>#42</a>',
- };
-
- Vue.nextTick()
- .then(() => {
- const content = vm.$el.textContent.replace(/\n(\s)+/g, ' ').trim();
-
- expect(content).toContain('Closes issue #7.');
- expect(content).toContain('issues #23 and #42');
- expect(content).toContain('are mentioned but will not be closed');
- })
- .then(done)
- .catch(done.fail);
+ expect(vm.$el.innerText).toContain('issue #7');
+ expect(vm.$el.innerText).toContain('is mentioned but will not be closed.');
+ expect(vm.$el.innerText).not.toContain('Closes');
});
- it('should have assing issues link', (done) => {
- vm.relatedLinks = {
- assignToMe: '<a href="#">Assign yourself to these issues</a>',
- };
-
- Vue.nextTick()
- .then(() => {
- expect(vm.$el.innerText).toContain('Assign yourself to these issues');
- })
- .then(done)
- .catch(done.fail);
+ it('should have closing and mentioned issues at the same time', () => {
+ const vm = createComponent({
+ relatedLinks: {
+ closing: '<a href="#">#7</a>',
+ mentioned: '<a href="#">#23</a> and <a>#42</a>',
+ },
+ });
+ const content = vm.$el.textContent.replace(/\n(\s)+/g, ' ').trim();
+
+ expect(content).toContain('Closes issue #7.');
+ expect(content).toContain('issues #23 and #42');
+ expect(content).toContain('are mentioned but will not be closed.');
});
- it('should use different wording after merging', (done) => {
- vm.isMerged = true;
- vm.relatedLinks = {
- closing: '<a href="#">#7</a>',
- mentioned: '<a href="#">#23</a> and <a>#42</a>',
- };
-
- Vue.nextTick()
- .then(() => {
- const content = vm.$el.textContent.replace(/\n(\s)+/g, ' ').trim();
-
- expect(content).toContain('Closed issue #7.');
- expect(content).toContain('issues #23 and #42');
- expect(content).toContain('are mentioned but were not closed');
- })
- .then(done)
- .catch(done.fail);
+ it('should have assing issues link', () => {
+ const vm = createComponent({
+ relatedLinks: {
+ assignToMe: '<a href="#">Assign yourself to these issues</a>',
+ },
+ });
+
+ expect(vm.$el.innerText).toContain('Assign yourself to these issues');
});
});
});
diff --git a/spec/javascripts/vue_mr_widget/mr_widget_options_spec.js b/spec/javascripts/vue_mr_widget/mr_widget_options_spec.js
index 425dff89439..3a0c50b750f 100644
--- a/spec/javascripts/vue_mr_widget/mr_widget_options_spec.js
+++ b/spec/javascripts/vue_mr_widget/mr_widget_options_spec.js
@@ -48,13 +48,12 @@ describe('mrWidgetOptions', () => {
});
describe('shouldRenderMergeHelp', () => {
- it('should return false after merging', () => {
- vm.mr.isMerged = true;
+ it('should return false for the initial merged state', () => {
expect(vm.shouldRenderMergeHelp).toBeFalsy();
});
- it('should return true before merging', () => {
- vm.mr.isMerged = false;
+ it('should return true for a state which requires help widget', () => {
+ vm.mr.state = 'conflicts';
expect(vm.shouldRenderMergeHelp).toBeTruthy();
});
});
diff --git a/spec/javascripts/vue_mr_widget/stores/mr_widget_store_spec.js b/spec/javascripts/vue_mr_widget/stores/mr_widget_store_spec.js
index 71285866302..56dd0198ae2 100644
--- a/spec/javascripts/vue_mr_widget/stores/mr_widget_store_spec.js
+++ b/spec/javascripts/vue_mr_widget/stores/mr_widget_store_spec.js
@@ -18,17 +18,5 @@ describe('MergeRequestStore', () => {
store.setData({ ...mockData, work_in_progress: !mockData.work_in_progress });
expect(store.hasSHAChanged).toBe(false);
});
-
- it('sets isMerged to true for merged state', () => {
- store.setData({ ...mockData, state: 'merged' });
-
- expect(store.isMerged).toBe(true);
- });
-
- it('sets isMerged to false for readyToMerge state', () => {
- store.setData({ ...mockData, state: 'readyToMerge' });
-
- expect(store.isMerged).toBe(false);
- });
});
});
diff --git a/spec/lib/banzai/cross_project_reference_spec.rb b/spec/lib/banzai/cross_project_reference_spec.rb
index deaabceef1c..787212581e2 100644
--- a/spec/lib/banzai/cross_project_reference_spec.rb
+++ b/spec/lib/banzai/cross_project_reference_spec.rb
@@ -24,8 +24,8 @@ describe Banzai::CrossProjectReference, lib: true do
it 'returns the referenced project' do
project2 = double('referenced project')
- expect(Project).to receive(:find_by_full_path).
- with('cross/reference').and_return(project2)
+ expect(Project).to receive(:find_by_full_path)
+ .with('cross/reference').and_return(project2)
expect(project_from_ref('cross/reference')).to eq project2
end
diff --git a/spec/lib/banzai/filter/abstract_reference_filter_spec.rb b/spec/lib/banzai/filter/abstract_reference_filter_spec.rb
index 787c2372c5b..27532f96f56 100644
--- a/spec/lib/banzai/filter/abstract_reference_filter_spec.rb
+++ b/spec/lib/banzai/filter/abstract_reference_filter_spec.rb
@@ -23,11 +23,11 @@ describe Banzai::Filter::AbstractReferenceFilter do
doc = Nokogiri::HTML.fragment('')
filter = described_class.new(doc, project: project)
- expect(filter).to receive(:references_per_project).
- and_return({ project.path_with_namespace => Set.new(%w[1]) })
+ expect(filter).to receive(:references_per_project)
+ .and_return({ project.path_with_namespace => Set.new(%w[1]) })
- expect(filter.projects_per_reference).
- to eq({ project.path_with_namespace => project })
+ expect(filter.projects_per_reference)
+ .to eq({ project.path_with_namespace => project })
end
end
@@ -37,26 +37,26 @@ describe Banzai::Filter::AbstractReferenceFilter do
context 'with RequestStore disabled' do
it 'returns a list of Projects for a list of paths' do
- expect(filter.find_projects_for_paths([project.path_with_namespace])).
- to eq([project])
+ expect(filter.find_projects_for_paths([project.path_with_namespace]))
+ .to eq([project])
end
it "return an empty array for paths that don't exist" do
- expect(filter.find_projects_for_paths(['nonexistent/project'])).
- to eq([])
+ expect(filter.find_projects_for_paths(['nonexistent/project']))
+ .to eq([])
end
end
context 'with RequestStore enabled', :request_store do
it 'returns a list of Projects for a list of paths' do
- expect(filter.find_projects_for_paths([project.path_with_namespace])).
- to eq([project])
+ expect(filter.find_projects_for_paths([project.path_with_namespace]))
+ .to eq([project])
end
context "when no project with that path exists" do
it "returns no value" do
- expect(filter.find_projects_for_paths(['nonexistent/project'])).
- to eq([])
+ expect(filter.find_projects_for_paths(['nonexistent/project']))
+ .to eq([])
end
it "adds the ref to the project refs cache" do
@@ -75,8 +75,8 @@ describe Banzai::Filter::AbstractReferenceFilter do
end
it "return an empty array for paths that don't exist" do
- expect(filter.find_projects_for_paths(['nonexistent/project'])).
- to eq([])
+ expect(filter.find_projects_for_paths(['nonexistent/project']))
+ .to eq([])
end
end
end
diff --git a/spec/lib/banzai/filter/commit_range_reference_filter_spec.rb b/spec/lib/banzai/filter/commit_range_reference_filter_spec.rb
index deadc36524c..fc67c7ec3c4 100644
--- a/spec/lib/banzai/filter/commit_range_reference_filter_spec.rb
+++ b/spec/lib/banzai/filter/commit_range_reference_filter_spec.rb
@@ -28,15 +28,15 @@ describe Banzai::Filter::CommitRangeReferenceFilter, lib: true do
it 'links to a valid two-dot reference' do
doc = reference_filter("See #{reference2}")
- expect(doc.css('a').first.attr('href')).
- to eq urls.namespace_project_compare_url(project.namespace, project, range2.to_param)
+ expect(doc.css('a').first.attr('href'))
+ .to eq urls.namespace_project_compare_url(project.namespace, project, range2.to_param)
end
it 'links to a valid three-dot reference' do
doc = reference_filter("See #{reference}")
- expect(doc.css('a').first.attr('href')).
- to eq urls.namespace_project_compare_url(project.namespace, project, range.to_param)
+ expect(doc.css('a').first.attr('href'))
+ .to eq urls.namespace_project_compare_url(project.namespace, project, range.to_param)
end
it 'links to a valid short ID' do
@@ -105,15 +105,15 @@ describe Banzai::Filter::CommitRangeReferenceFilter, lib: true do
it 'links to a valid reference' do
doc = reference_filter("See #{reference}")
- expect(doc.css('a').first.attr('href')).
- to eq urls.namespace_project_compare_url(project2.namespace, project2, range.to_param)
+ expect(doc.css('a').first.attr('href'))
+ .to eq urls.namespace_project_compare_url(project2.namespace, project2, range.to_param)
end
it 'link has valid text' do
doc = reference_filter("Fixed (#{reference}.)")
- expect(doc.css('a').first.text).
- to eql("#{project2.path_with_namespace}@#{commit1.short_id}...#{commit2.short_id}")
+ expect(doc.css('a').first.text)
+ .to eql("#{project2.path_with_namespace}@#{commit1.short_id}...#{commit2.short_id}")
end
it 'has valid text' do
@@ -140,15 +140,15 @@ describe Banzai::Filter::CommitRangeReferenceFilter, lib: true do
it 'links to a valid reference' do
doc = reference_filter("See #{reference}")
- expect(doc.css('a').first.attr('href')).
- to eq urls.namespace_project_compare_url(project2.namespace, project2, range.to_param)
+ expect(doc.css('a').first.attr('href'))
+ .to eq urls.namespace_project_compare_url(project2.namespace, project2, range.to_param)
end
it 'link has valid text' do
doc = reference_filter("Fixed (#{reference}.)")
- expect(doc.css('a').first.text).
- to eql("#{project2.path}@#{commit1.short_id}...#{commit2.short_id}")
+ expect(doc.css('a').first.text)
+ .to eql("#{project2.path}@#{commit1.short_id}...#{commit2.short_id}")
end
it 'has valid text' do
@@ -175,15 +175,15 @@ describe Banzai::Filter::CommitRangeReferenceFilter, lib: true do
it 'links to a valid reference' do
doc = reference_filter("See #{reference}")
- expect(doc.css('a').first.attr('href')).
- to eq urls.namespace_project_compare_url(project2.namespace, project2, range.to_param)
+ expect(doc.css('a').first.attr('href'))
+ .to eq urls.namespace_project_compare_url(project2.namespace, project2, range.to_param)
end
it 'link has valid text' do
doc = reference_filter("Fixed (#{reference}.)")
- expect(doc.css('a').first.text).
- to eql("#{project2.path}@#{commit1.short_id}...#{commit2.short_id}")
+ expect(doc.css('a').first.text)
+ .to eql("#{project2.path}@#{commit1.short_id}...#{commit2.short_id}")
end
it 'has valid text' do
@@ -214,8 +214,8 @@ describe Banzai::Filter::CommitRangeReferenceFilter, lib: true do
it 'links to a valid reference' do
doc = reference_filter("See #{reference}")
- expect(doc.css('a').first.attr('href')).
- to eq reference
+ expect(doc.css('a').first.attr('href'))
+ .to eq reference
end
it 'links with adjacent text' do
diff --git a/spec/lib/banzai/filter/commit_reference_filter_spec.rb b/spec/lib/banzai/filter/commit_reference_filter_spec.rb
index a19aac61229..c4d8d3b6682 100644
--- a/spec/lib/banzai/filter/commit_reference_filter_spec.rb
+++ b/spec/lib/banzai/filter/commit_reference_filter_spec.rb
@@ -26,8 +26,8 @@ describe Banzai::Filter::CommitReferenceFilter, lib: true do
doc = reference_filter("See #{reference[0...size]}")
expect(doc.css('a').first.text).to eq commit.short_id
- expect(doc.css('a').first.attr('href')).
- to eq urls.namespace_project_commit_url(project.namespace, project, reference)
+ expect(doc.css('a').first.attr('href'))
+ .to eq urls.namespace_project_commit_url(project.namespace, project, reference)
end
end
@@ -180,8 +180,8 @@ describe Banzai::Filter::CommitReferenceFilter, lib: true do
it 'links to a valid reference' do
doc = reference_filter("See #{reference}")
- expect(doc.css('a').first.attr('href')).
- to eq urls.namespace_project_commit_url(project2.namespace, project2, commit.id)
+ expect(doc.css('a').first.attr('href'))
+ .to eq urls.namespace_project_commit_url(project2.namespace, project2, commit.id)
end
it 'links with adjacent text' do
diff --git a/spec/lib/banzai/filter/external_issue_reference_filter_spec.rb b/spec/lib/banzai/filter/external_issue_reference_filter_spec.rb
index 76cefe112fb..a4bb043f8f1 100644
--- a/spec/lib/banzai/filter/external_issue_reference_filter_spec.rb
+++ b/spec/lib/banzai/filter/external_issue_reference_filter_spec.rb
@@ -58,8 +58,8 @@ describe Banzai::Filter::ExternalIssueReferenceFilter, lib: true do
end
it 'escapes the title attribute' do
- allow(project.external_issue_tracker).to receive(:title).
- and_return(%{"></a>whatever<a title="})
+ allow(project.external_issue_tracker).to receive(:title)
+ .and_return(%{"></a>whatever<a title="})
doc = filter("Issue #{reference}")
expect(doc.text).to eq "Issue #{reference}"
diff --git a/spec/lib/banzai/filter/issue_reference_filter_spec.rb b/spec/lib/banzai/filter/issue_reference_filter_spec.rb
index f1082495fcc..e5c1deb338b 100644
--- a/spec/lib/banzai/filter/issue_reference_filter_spec.rb
+++ b/spec/lib/banzai/filter/issue_reference_filter_spec.rb
@@ -49,8 +49,8 @@ describe Banzai::Filter::IssueReferenceFilter, lib: true do
it 'links to a valid reference' do
doc = reference_filter("Fixed #{reference}")
- expect(doc.css('a').first.attr('href')).
- to eq helper.url_for_issue(issue.iid, project)
+ expect(doc.css('a').first.attr('href'))
+ .to eq helper.url_for_issue(issue.iid, project)
end
it 'links with adjacent text' do
@@ -137,9 +137,9 @@ describe Banzai::Filter::IssueReferenceFilter, lib: true do
let(:reference) { "#{project2.path_with_namespace}##{issue.iid}" }
it 'ignores valid references when cross-reference project uses external tracker' do
- expect_any_instance_of(described_class).to receive(:find_object).
- with(project2, issue.iid).
- and_return(nil)
+ expect_any_instance_of(described_class).to receive(:find_object)
+ .with(project2, issue.iid)
+ .and_return(nil)
exp = act = "Issue #{reference}"
expect(reference_filter(act).to_html).to eq exp
@@ -148,8 +148,8 @@ describe Banzai::Filter::IssueReferenceFilter, lib: true do
it 'links to a valid reference' do
doc = reference_filter("See #{reference}")
- expect(doc.css('a').first.attr('href')).
- to eq helper.url_for_issue(issue.iid, project2)
+ expect(doc.css('a').first.attr('href'))
+ .to eq helper.url_for_issue(issue.iid, project2)
end
it 'link has valid text' do
@@ -181,9 +181,9 @@ describe Banzai::Filter::IssueReferenceFilter, lib: true do
let(:reference) { "#{project2.path_with_namespace}##{issue.iid}" }
it 'ignores valid references when cross-reference project uses external tracker' do
- expect_any_instance_of(described_class).to receive(:find_object).
- with(project2, issue.iid).
- and_return(nil)
+ expect_any_instance_of(described_class).to receive(:find_object)
+ .with(project2, issue.iid)
+ .and_return(nil)
exp = act = "Issue #{reference}"
expect(reference_filter(act).to_html).to eq exp
@@ -192,8 +192,8 @@ describe Banzai::Filter::IssueReferenceFilter, lib: true do
it 'links to a valid reference' do
doc = reference_filter("See #{reference}")
- expect(doc.css('a').first.attr('href')).
- to eq helper.url_for_issue(issue.iid, project2)
+ expect(doc.css('a').first.attr('href'))
+ .to eq helper.url_for_issue(issue.iid, project2)
end
it 'link has valid text' do
@@ -225,9 +225,9 @@ describe Banzai::Filter::IssueReferenceFilter, lib: true do
let(:reference) { "#{project2.path}##{issue.iid}" }
it 'ignores valid references when cross-reference project uses external tracker' do
- expect_any_instance_of(described_class).to receive(:find_object).
- with(project2, issue.iid).
- and_return(nil)
+ expect_any_instance_of(described_class).to receive(:find_object)
+ .with(project2, issue.iid)
+ .and_return(nil)
exp = act = "Issue #{reference}"
expect(reference_filter(act).to_html).to eq exp
@@ -236,8 +236,8 @@ describe Banzai::Filter::IssueReferenceFilter, lib: true do
it 'links to a valid reference' do
doc = reference_filter("See #{reference}")
- expect(doc.css('a').first.attr('href')).
- to eq helper.url_for_issue(issue.iid, project2)
+ expect(doc.css('a').first.attr('href'))
+ .to eq helper.url_for_issue(issue.iid, project2)
end
it 'link has valid text' do
@@ -270,8 +270,8 @@ describe Banzai::Filter::IssueReferenceFilter, lib: true do
it 'links to a valid reference' do
doc = reference_filter("See #{reference}")
- expect(doc.css('a').first.attr('href')).
- to eq reference
+ expect(doc.css('a').first.attr('href'))
+ .to eq reference
end
it 'links with adjacent text' do
@@ -292,8 +292,8 @@ describe Banzai::Filter::IssueReferenceFilter, lib: true do
it 'links to a valid reference' do
doc = reference_filter("See #{reference_link}")
- expect(doc.css('a').first.attr('href')).
- to eq helper.url_for_issue(issue.iid, project2)
+ expect(doc.css('a').first.attr('href'))
+ .to eq helper.url_for_issue(issue.iid, project2)
end
it 'links with adjacent text' do
@@ -314,8 +314,8 @@ describe Banzai::Filter::IssueReferenceFilter, lib: true do
it 'links to a valid reference' do
doc = reference_filter("See #{reference_link}")
- expect(doc.css('a').first.attr('href')).
- to eq helper.url_for_issue(issue.iid, project2) + "#note_123"
+ expect(doc.css('a').first.attr('href'))
+ .to eq helper.url_for_issue(issue.iid, project2) + "#note_123"
end
it 'links with adjacent text' do
@@ -330,14 +330,14 @@ describe Banzai::Filter::IssueReferenceFilter, lib: true do
doc = Nokogiri::HTML.fragment('')
filter = described_class.new(doc, project: project)
- expect(filter).to receive(:projects_per_reference).
- and_return({ project.path_with_namespace => project })
+ expect(filter).to receive(:projects_per_reference)
+ .and_return({ project.path_with_namespace => project })
- expect(filter).to receive(:references_per_project).
- and_return({ project.path_with_namespace => Set.new([issue.iid]) })
+ expect(filter).to receive(:references_per_project)
+ .and_return({ project.path_with_namespace => Set.new([issue.iid]) })
- expect(filter.issues_per_project).
- to eq({ project => { issue.iid => issue } })
+ expect(filter.issues_per_project)
+ .to eq({ project => { issue.iid => issue } })
end
end
@@ -348,14 +348,14 @@ describe Banzai::Filter::IssueReferenceFilter, lib: true do
expect(project).to receive(:default_issues_tracker?).and_return(false)
- expect(filter).to receive(:projects_per_reference).
- and_return({ project.path_with_namespace => project })
+ expect(filter).to receive(:projects_per_reference)
+ .and_return({ project.path_with_namespace => project })
- expect(filter).to receive(:references_per_project).
- and_return({ project.path_with_namespace => Set.new([1]) })
+ expect(filter).to receive(:references_per_project)
+ .and_return({ project.path_with_namespace => Set.new([1]) })
- expect(filter.issues_per_project[project][1]).
- to be_an_instance_of(ExternalIssue)
+ expect(filter.issues_per_project[project][1])
+ .to be_an_instance_of(ExternalIssue)
end
end
end
diff --git a/spec/lib/banzai/filter/label_reference_filter_spec.rb b/spec/lib/banzai/filter/label_reference_filter_spec.rb
index 284641fb20a..cb3cf982351 100644
--- a/spec/lib/banzai/filter/label_reference_filter_spec.rb
+++ b/spec/lib/banzai/filter/label_reference_filter_spec.rb
@@ -72,8 +72,8 @@ describe Banzai::Filter::LabelReferenceFilter, lib: true do
it 'links to a valid reference' do
doc = reference_filter("See #{reference}")
- expect(doc.css('a').first.attr('href')).to eq urls.
- namespace_project_issues_url(project.namespace, project, label_name: label.name)
+ expect(doc.css('a').first.attr('href')).to eq urls
+ .namespace_project_issues_url(project.namespace, project, label_name: label.name)
end
it 'links with adjacent text' do
@@ -95,8 +95,8 @@ describe Banzai::Filter::LabelReferenceFilter, lib: true do
it 'links to a valid reference' do
doc = reference_filter("See #{reference}")
- expect(doc.css('a').first.attr('href')).to eq urls.
- namespace_project_issues_url(project.namespace, project, label_name: label.name)
+ expect(doc.css('a').first.attr('href')).to eq urls
+ .namespace_project_issues_url(project.namespace, project, label_name: label.name)
expect(doc.text).to eq 'See gfm'
end
@@ -119,8 +119,8 @@ describe Banzai::Filter::LabelReferenceFilter, lib: true do
it 'links to a valid reference' do
doc = reference_filter("See #{reference}")
- expect(doc.css('a').first.attr('href')).to eq urls.
- namespace_project_issues_url(project.namespace, project, label_name: label.name)
+ expect(doc.css('a').first.attr('href')).to eq urls
+ .namespace_project_issues_url(project.namespace, project, label_name: label.name)
expect(doc.text).to eq 'See 2fa'
end
@@ -143,8 +143,8 @@ describe Banzai::Filter::LabelReferenceFilter, lib: true do
it 'links to a valid reference' do
doc = reference_filter("See #{reference}")
- expect(doc.css('a').first.attr('href')).to eq urls.
- namespace_project_issues_url(project.namespace, project, label_name: label.name)
+ expect(doc.css('a').first.attr('href')).to eq urls
+ .namespace_project_issues_url(project.namespace, project, label_name: label.name)
expect(doc.text).to eq 'See ?g.fm&'
end
@@ -168,8 +168,8 @@ describe Banzai::Filter::LabelReferenceFilter, lib: true do
it 'links to a valid reference' do
doc = reference_filter("See #{reference}")
- expect(doc.css('a').first.attr('href')).to eq urls.
- namespace_project_issues_url(project.namespace, project, label_name: label.name)
+ expect(doc.css('a').first.attr('href')).to eq urls
+ .namespace_project_issues_url(project.namespace, project, label_name: label.name)
expect(doc.text).to eq 'See gfm references'
end
@@ -192,8 +192,8 @@ describe Banzai::Filter::LabelReferenceFilter, lib: true do
it 'links to a valid reference' do
doc = reference_filter("See #{reference}")
- expect(doc.css('a').first.attr('href')).to eq urls.
- namespace_project_issues_url(project.namespace, project, label_name: label.name)
+ expect(doc.css('a').first.attr('href')).to eq urls
+ .namespace_project_issues_url(project.namespace, project, label_name: label.name)
expect(doc.text).to eq 'See 2 factor authentication'
end
@@ -216,8 +216,8 @@ describe Banzai::Filter::LabelReferenceFilter, lib: true do
it 'links to a valid reference' do
doc = reference_filter("See #{reference}")
- expect(doc.css('a').first.attr('href')).to eq urls.
- namespace_project_issues_url(project.namespace, project, label_name: label.name)
+ expect(doc.css('a').first.attr('href')).to eq urls
+ .namespace_project_issues_url(project.namespace, project, label_name: label.name)
expect(doc.text).to eq 'See g.fm & references?'
end
@@ -287,8 +287,8 @@ describe Banzai::Filter::LabelReferenceFilter, lib: true do
it 'links to a valid reference' do
doc = reference_filter("See #{reference}")
- expect(doc.css('a').first.attr('href')).to eq urls.
- namespace_project_issues_url(project.namespace, project, label_name: label.name)
+ expect(doc.css('a').first.attr('href')).to eq urls
+ .namespace_project_issues_url(project.namespace, project, label_name: label.name)
end
it 'links with adjacent text' do
@@ -324,8 +324,8 @@ describe Banzai::Filter::LabelReferenceFilter, lib: true do
it 'links to a valid reference' do
doc = reference_filter("See #{reference}", project: project)
- expect(doc.css('a').first.attr('href')).to eq urls.
- namespace_project_issues_url(project.namespace, project, label_name: group_label.name)
+ expect(doc.css('a').first.attr('href')).to eq urls
+ .namespace_project_issues_url(project.namespace, project, label_name: group_label.name)
expect(doc.text).to eq 'See gfm references'
end
@@ -347,8 +347,8 @@ describe Banzai::Filter::LabelReferenceFilter, lib: true do
it 'links to a valid reference' do
doc = reference_filter("See #{reference}", project: project)
- expect(doc.css('a').first.attr('href')).to eq urls.
- namespace_project_issues_url(project.namespace, project, label_name: group_label.name)
+ expect(doc.css('a').first.attr('href')).to eq urls
+ .namespace_project_issues_url(project.namespace, project, label_name: group_label.name)
expect(doc.text).to eq "See gfm references"
end
@@ -447,8 +447,8 @@ describe Banzai::Filter::LabelReferenceFilter, lib: true do
end
it 'has valid color' do
- expect(result.css('a span').first.attr('style')).
- to match /background-color: #00ff00/
+ expect(result.css('a span').first.attr('style'))
+ .to match /background-color: #00ff00/
end
it 'has valid link text' do
@@ -483,18 +483,18 @@ describe Banzai::Filter::LabelReferenceFilter, lib: true do
end
it 'has valid color' do
- expect(result.css('a span').first.attr('style')).
- to match /background-color: #00ff00/
+ expect(result.css('a span').first.attr('style'))
+ .to match /background-color: #00ff00/
end
it 'has valid link text' do
- expect(result.css('a').first.text).
- to eq "#{group_label.name} in #{another_project.name_with_namespace}"
+ expect(result.css('a').first.text)
+ .to eq "#{group_label.name} in #{another_project.name_with_namespace}"
end
it 'has valid text' do
- expect(result.text).
- to eq "See #{group_label.name} in #{another_project.name_with_namespace}"
+ expect(result.text)
+ .to eq "See #{group_label.name} in #{another_project.name_with_namespace}"
end
it 'ignores invalid IDs on the referenced label' do
@@ -513,25 +513,25 @@ describe Banzai::Filter::LabelReferenceFilter, lib: true do
let!(:result) { reference_filter("See #{reference}", project: project) }
it 'points to referenced project issues page' do
- expect(result.css('a').first.attr('href')).
- to eq urls.namespace_project_issues_url(another_project.namespace,
+ expect(result.css('a').first.attr('href'))
+ .to eq urls.namespace_project_issues_url(another_project.namespace,
another_project,
label_name: group_label.name)
end
it 'has valid color' do
- expect(result.css('a span').first.attr('style')).
- to match /background-color: #00ff00/
+ expect(result.css('a span').first.attr('style'))
+ .to match /background-color: #00ff00/
end
it 'has valid link text' do
- expect(result.css('a').first.text).
- to eq "#{group_label.name} in #{another_project.name}"
+ expect(result.css('a').first.text)
+ .to eq "#{group_label.name} in #{another_project.name}"
end
it 'has valid text' do
- expect(result.text).
- to eq "See #{group_label.name} in #{another_project.name}"
+ expect(result.text)
+ .to eq "See #{group_label.name} in #{another_project.name}"
end
it 'ignores invalid IDs on the referenced label' do
@@ -590,8 +590,8 @@ describe Banzai::Filter::LabelReferenceFilter, lib: true do
end
it 'has valid color' do
- expect(result.css('a span').first.attr('style')).
- to match /background-color: #00ff00/
+ expect(result.css('a span').first.attr('style'))
+ .to match /background-color: #00ff00/
end
it 'has valid link text' do
diff --git a/spec/lib/banzai/filter/merge_request_reference_filter_spec.rb b/spec/lib/banzai/filter/merge_request_reference_filter_spec.rb
index 40232f6e426..cd91681551e 100644
--- a/spec/lib/banzai/filter/merge_request_reference_filter_spec.rb
+++ b/spec/lib/banzai/filter/merge_request_reference_filter_spec.rb
@@ -36,8 +36,8 @@ describe Banzai::Filter::MergeRequestReferenceFilter, lib: true do
it 'links to a valid reference' do
doc = reference_filter("See #{reference}")
- expect(doc.css('a').first.attr('href')).to eq urls.
- namespace_project_merge_request_url(project.namespace, project, merge)
+ expect(doc.css('a').first.attr('href')).to eq urls
+ .namespace_project_merge_request_url(project.namespace, project, merge)
end
it 'links with adjacent text' do
@@ -107,8 +107,8 @@ describe Banzai::Filter::MergeRequestReferenceFilter, lib: true do
it 'links to a valid reference' do
doc = reference_filter("See #{reference}")
- expect(doc.css('a').first.attr('href')).
- to eq urls.namespace_project_merge_request_url(project2.namespace,
+ expect(doc.css('a').first.attr('href'))
+ .to eq urls.namespace_project_merge_request_url(project2.namespace,
project2, merge)
end
@@ -141,8 +141,8 @@ describe Banzai::Filter::MergeRequestReferenceFilter, lib: true do
it 'links to a valid reference' do
doc = reference_filter("See #{reference}")
- expect(doc.css('a').first.attr('href')).
- to eq urls.namespace_project_merge_request_url(project2.namespace,
+ expect(doc.css('a').first.attr('href'))
+ .to eq urls.namespace_project_merge_request_url(project2.namespace,
project2, merge)
end
@@ -175,8 +175,8 @@ describe Banzai::Filter::MergeRequestReferenceFilter, lib: true do
it 'links to a valid reference' do
doc = reference_filter("See #{reference}")
- expect(doc.css('a').first.attr('href')).
- to eq urls.namespace_project_merge_request_url(project2.namespace,
+ expect(doc.css('a').first.attr('href'))
+ .to eq urls.namespace_project_merge_request_url(project2.namespace,
project2, merge)
end
@@ -208,8 +208,8 @@ describe Banzai::Filter::MergeRequestReferenceFilter, lib: true do
it 'links to a valid reference' do
doc = reference_filter("See #{reference}")
- expect(doc.css('a').first.attr('href')).
- to eq reference
+ expect(doc.css('a').first.attr('href'))
+ .to eq reference
end
it 'links with adjacent text' do
diff --git a/spec/lib/banzai/filter/milestone_reference_filter_spec.rb b/spec/lib/banzai/filter/milestone_reference_filter_spec.rb
index a317c751d32..fe88b9cb73e 100644
--- a/spec/lib/banzai/filter/milestone_reference_filter_spec.rb
+++ b/spec/lib/banzai/filter/milestone_reference_filter_spec.rb
@@ -44,16 +44,16 @@ describe Banzai::Filter::MilestoneReferenceFilter, lib: true do
link = doc.css('a').first.attr('href')
expect(link).not_to match %r(https?://)
- expect(link).to eq urls.
- namespace_project_milestone_path(project.namespace, project, milestone)
+ expect(link).to eq urls
+ .namespace_project_milestone_path(project.namespace, project, milestone)
end
context 'Integer-based references' do
it 'links to a valid reference' do
doc = reference_filter("See #{reference}")
- expect(doc.css('a').first.attr('href')).to eq urls.
- namespace_project_milestone_url(project.namespace, project, milestone)
+ expect(doc.css('a').first.attr('href')).to eq urls
+ .namespace_project_milestone_url(project.namespace, project, milestone)
end
it 'links with adjacent text' do
@@ -75,8 +75,8 @@ describe Banzai::Filter::MilestoneReferenceFilter, lib: true do
it 'links to a valid reference' do
doc = reference_filter("See #{reference}")
- expect(doc.css('a').first.attr('href')).to eq urls.
- namespace_project_milestone_url(project.namespace, project, milestone)
+ expect(doc.css('a').first.attr('href')).to eq urls
+ .namespace_project_milestone_url(project.namespace, project, milestone)
expect(doc.text).to eq 'See gfm'
end
@@ -99,8 +99,8 @@ describe Banzai::Filter::MilestoneReferenceFilter, lib: true do
it 'links to a valid reference' do
doc = reference_filter("See #{reference}")
- expect(doc.css('a').first.attr('href')).to eq urls.
- namespace_project_milestone_url(project.namespace, project, milestone)
+ expect(doc.css('a').first.attr('href')).to eq urls
+ .namespace_project_milestone_url(project.namespace, project, milestone)
expect(doc.text).to eq 'See gfm references'
end
@@ -122,8 +122,8 @@ describe Banzai::Filter::MilestoneReferenceFilter, lib: true do
it 'links to a valid reference' do
doc = reference_filter("See #{reference}")
- expect(doc.css('a').first.attr('href')).to eq urls.
- namespace_project_milestone_url(project.namespace, project, milestone)
+ expect(doc.css('a').first.attr('href')).to eq urls
+ .namespace_project_milestone_url(project.namespace, project, milestone)
end
it 'links with adjacent text' do
@@ -156,8 +156,8 @@ describe Banzai::Filter::MilestoneReferenceFilter, lib: true do
let!(:result) { reference_filter("See #{reference}") }
it 'points to referenced project milestone page' do
- expect(result.css('a').first.attr('href')).to eq urls.
- namespace_project_milestone_url(another_project.namespace,
+ expect(result.css('a').first.attr('href')).to eq urls
+ .namespace_project_milestone_url(another_project.namespace,
another_project,
milestone)
end
@@ -165,15 +165,15 @@ describe Banzai::Filter::MilestoneReferenceFilter, lib: true do
it 'link has valid text' do
doc = reference_filter("See (#{reference}.)")
- expect(doc.css('a').first.text).
- to eq("#{milestone.name} in #{another_project.path_with_namespace}")
+ expect(doc.css('a').first.text)
+ .to eq("#{milestone.name} in #{another_project.path_with_namespace}")
end
it 'has valid text' do
doc = reference_filter("See (#{reference}.)")
- expect(doc.text).
- to eq("See (#{milestone.name} in #{another_project.path_with_namespace}.)")
+ expect(doc.text)
+ .to eq("See (#{milestone.name} in #{another_project.path_with_namespace}.)")
end
it 'escapes the name attribute' do
@@ -181,8 +181,8 @@ describe Banzai::Filter::MilestoneReferenceFilter, lib: true do
doc = reference_filter("See #{reference}")
- expect(doc.css('a').first.text).
- to eq "#{milestone.name} in #{another_project.path_with_namespace}"
+ expect(doc.css('a').first.text)
+ .to eq "#{milestone.name} in #{another_project.path_with_namespace}"
end
end
@@ -195,8 +195,8 @@ describe Banzai::Filter::MilestoneReferenceFilter, lib: true do
let!(:result) { reference_filter("See #{reference}") }
it 'points to referenced project milestone page' do
- expect(result.css('a').first.attr('href')).to eq urls.
- namespace_project_milestone_url(another_project.namespace,
+ expect(result.css('a').first.attr('href')).to eq urls
+ .namespace_project_milestone_url(another_project.namespace,
another_project,
milestone)
end
@@ -204,15 +204,15 @@ describe Banzai::Filter::MilestoneReferenceFilter, lib: true do
it 'link has valid text' do
doc = reference_filter("See (#{reference}.)")
- expect(doc.css('a').first.text).
- to eq("#{milestone.name} in #{another_project.path}")
+ expect(doc.css('a').first.text)
+ .to eq("#{milestone.name} in #{another_project.path}")
end
it 'has valid text' do
doc = reference_filter("See (#{reference}.)")
- expect(doc.text).
- to eq("See (#{milestone.name} in #{another_project.path}.)")
+ expect(doc.text)
+ .to eq("See (#{milestone.name} in #{another_project.path}.)")
end
it 'escapes the name attribute' do
@@ -220,8 +220,8 @@ describe Banzai::Filter::MilestoneReferenceFilter, lib: true do
doc = reference_filter("See #{reference}")
- expect(doc.css('a').first.text).
- to eq "#{milestone.name} in #{another_project.path}"
+ expect(doc.css('a').first.text)
+ .to eq "#{milestone.name} in #{another_project.path}"
end
end
@@ -234,8 +234,8 @@ describe Banzai::Filter::MilestoneReferenceFilter, lib: true do
let!(:result) { reference_filter("See #{reference}") }
it 'points to referenced project milestone page' do
- expect(result.css('a').first.attr('href')).to eq urls.
- namespace_project_milestone_url(another_project.namespace,
+ expect(result.css('a').first.attr('href')).to eq urls
+ .namespace_project_milestone_url(another_project.namespace,
another_project,
milestone)
end
@@ -243,15 +243,15 @@ describe Banzai::Filter::MilestoneReferenceFilter, lib: true do
it 'link has valid text' do
doc = reference_filter("See (#{reference}.)")
- expect(doc.css('a').first.text).
- to eq("#{milestone.name} in #{another_project.path}")
+ expect(doc.css('a').first.text)
+ .to eq("#{milestone.name} in #{another_project.path}")
end
it 'has valid text' do
doc = reference_filter("See (#{reference}.)")
- expect(doc.text).
- to eq("See (#{milestone.name} in #{another_project.path}.)")
+ expect(doc.text)
+ .to eq("See (#{milestone.name} in #{another_project.path}.)")
end
it 'escapes the name attribute' do
@@ -259,8 +259,8 @@ describe Banzai::Filter::MilestoneReferenceFilter, lib: true do
doc = reference_filter("See #{reference}")
- expect(doc.css('a').first.text).
- to eq "#{milestone.name} in #{another_project.path}"
+ expect(doc.css('a').first.text)
+ .to eq "#{milestone.name} in #{another_project.path}"
end
end
end
diff --git a/spec/lib/banzai/filter/redactor_filter_spec.rb b/spec/lib/banzai/filter/redactor_filter_spec.rb
index 97504aebed5..b81cdbb8957 100644
--- a/spec/lib/banzai/filter/redactor_filter_spec.rb
+++ b/spec/lib/banzai/filter/redactor_filter_spec.rb
@@ -33,9 +33,9 @@ describe Banzai::Filter::RedactorFilter, lib: true do
end
before do
- allow(Banzai::ReferenceParser).to receive(:[]).
- with('test').
- and_return(parser_class)
+ allow(Banzai::ReferenceParser).to receive(:[])
+ .with('test')
+ .and_return(parser_class)
end
context 'valid projects' do
diff --git a/spec/lib/banzai/filter/reference_filter_spec.rb b/spec/lib/banzai/filter/reference_filter_spec.rb
index 55e681f6faf..ba0fa4a609a 100644
--- a/spec/lib/banzai/filter/reference_filter_spec.rb
+++ b/spec/lib/banzai/filter/reference_filter_spec.rb
@@ -8,8 +8,8 @@ describe Banzai::Filter::ReferenceFilter, lib: true do
document = Nokogiri::HTML.fragment('<a href="foo">foo</a>')
filter = described_class.new(document, project: project)
- expect { |b| filter.each_node(&b) }.
- to yield_with_args(an_instance_of(Nokogiri::XML::Element))
+ expect { |b| filter.each_node(&b) }
+ .to yield_with_args(an_instance_of(Nokogiri::XML::Element))
end
it 'returns an Enumerator when no block is given' do
diff --git a/spec/lib/banzai/filter/relative_link_filter_spec.rb b/spec/lib/banzai/filter/relative_link_filter_spec.rb
index 1957ba739e2..1ce7bd7706e 100644
--- a/spec/lib/banzai/filter/relative_link_filter_spec.rb
+++ b/spec/lib/banzai/filter/relative_link_filter_spec.rb
@@ -72,15 +72,15 @@ describe Banzai::Filter::RelativeLinkFilter, lib: true do
it 'ignores ref if commit is passed' do
doc = filter(link('non/existent.file'), commit: project.commit('empty-branch') )
- expect(doc.at_css('a')['href']).
- to eq "/#{project_path}/#{ref}/non/existent.file" # non-existent files have no leading blob/raw/tree
+ expect(doc.at_css('a')['href'])
+ .to eq "/#{project_path}/#{ref}/non/existent.file" # non-existent files have no leading blob/raw/tree
end
shared_examples :valid_repository do
it 'rebuilds absolute URL for a file in the repo' do
doc = filter(link('/doc/api/README.md'))
- expect(doc.at_css('a')['href']).
- to eq "/#{project_path}/blob/#{ref}/doc/api/README.md"
+ expect(doc.at_css('a')['href'])
+ .to eq "/#{project_path}/blob/#{ref}/doc/api/README.md"
end
it 'ignores absolute URLs with two leading slashes' do
@@ -90,71 +90,71 @@ describe Banzai::Filter::RelativeLinkFilter, lib: true do
it 'rebuilds relative URL for a file in the repo' do
doc = filter(link('doc/api/README.md'))
- expect(doc.at_css('a')['href']).
- to eq "/#{project_path}/blob/#{ref}/doc/api/README.md"
+ expect(doc.at_css('a')['href'])
+ .to eq "/#{project_path}/blob/#{ref}/doc/api/README.md"
end
it 'rebuilds relative URL for a file in the repo with leading ./' do
doc = filter(link('./doc/api/README.md'))
- expect(doc.at_css('a')['href']).
- to eq "/#{project_path}/blob/#{ref}/doc/api/README.md"
+ expect(doc.at_css('a')['href'])
+ .to eq "/#{project_path}/blob/#{ref}/doc/api/README.md"
end
it 'rebuilds relative URL for a file in the repo up one directory' do
relative_link = link('../api/README.md')
doc = filter(relative_link, requested_path: 'doc/update/7.14-to-8.0.md')
- expect(doc.at_css('a')['href']).
- to eq "/#{project_path}/blob/#{ref}/doc/api/README.md"
+ expect(doc.at_css('a')['href'])
+ .to eq "/#{project_path}/blob/#{ref}/doc/api/README.md"
end
it 'rebuilds relative URL for a file in the repo up multiple directories' do
relative_link = link('../../../api/README.md')
doc = filter(relative_link, requested_path: 'doc/foo/bar/baz/README.md')
- expect(doc.at_css('a')['href']).
- to eq "/#{project_path}/blob/#{ref}/doc/api/README.md"
+ expect(doc.at_css('a')['href'])
+ .to eq "/#{project_path}/blob/#{ref}/doc/api/README.md"
end
it 'rebuilds relative URL for a file in the repository root' do
relative_link = link('../README.md')
doc = filter(relative_link, requested_path: 'doc/some-file.md')
- expect(doc.at_css('a')['href']).
- to eq "/#{project_path}/blob/#{ref}/README.md"
+ expect(doc.at_css('a')['href'])
+ .to eq "/#{project_path}/blob/#{ref}/README.md"
end
it 'rebuilds relative URL for a file in the repo with an anchor' do
doc = filter(link('README.md#section'))
- expect(doc.at_css('a')['href']).
- to eq "/#{project_path}/blob/#{ref}/README.md#section"
+ expect(doc.at_css('a')['href'])
+ .to eq "/#{project_path}/blob/#{ref}/README.md#section"
end
it 'rebuilds relative URL for a directory in the repo' do
doc = filter(link('doc/api/'))
- expect(doc.at_css('a')['href']).
- to eq "/#{project_path}/tree/#{ref}/doc/api"
+ expect(doc.at_css('a')['href'])
+ .to eq "/#{project_path}/tree/#{ref}/doc/api"
end
it 'rebuilds relative URL for an image in the repo' do
doc = filter(image('files/images/logo-black.png'))
- expect(doc.at_css('img')['src']).
- to eq "/#{project_path}/raw/#{ref}/files/images/logo-black.png"
+ expect(doc.at_css('img')['src'])
+ .to eq "/#{project_path}/raw/#{ref}/files/images/logo-black.png"
end
it 'rebuilds relative URL for link to an image in the repo' do
doc = filter(link('files/images/logo-black.png'))
- expect(doc.at_css('a')['href']).
- to eq "/#{project_path}/raw/#{ref}/files/images/logo-black.png"
+ expect(doc.at_css('a')['href'])
+ .to eq "/#{project_path}/raw/#{ref}/files/images/logo-black.png"
end
it 'rebuilds relative URL for a video in the repo' do
doc = filter(video('files/videos/intro.mp4'), commit: project.commit('video'), ref: 'video')
- expect(doc.at_css('video')['src']).
- to eq "/#{project_path}/raw/video/files/videos/intro.mp4"
+ expect(doc.at_css('video')['src'])
+ .to eq "/#{project_path}/raw/video/files/videos/intro.mp4"
end
it 'does not modify relative URL with an anchor only' do
diff --git a/spec/lib/banzai/filter/sanitization_filter_spec.rb b/spec/lib/banzai/filter/sanitization_filter_spec.rb
index fb7862f49a2..a8a0aa6d395 100644
--- a/spec/lib/banzai/filter/sanitization_filter_spec.rb
+++ b/spec/lib/banzai/filter/sanitization_filter_spec.rb
@@ -221,8 +221,8 @@ describe Banzai::Filter::SanitizationFilter, lib: true do
end
it 'disallows invalid URIs' do
- expect(Addressable::URI).to receive(:parse).with('foo://example.com').
- and_raise(Addressable::URI::InvalidURIError)
+ expect(Addressable::URI).to receive(:parse).with('foo://example.com')
+ .and_raise(Addressable::URI::InvalidURIError)
input = '<a href="foo://example.com">Foo</a>'
output = filter(input)
diff --git a/spec/lib/banzai/filter/snippet_reference_filter_spec.rb b/spec/lib/banzai/filter/snippet_reference_filter_spec.rb
index e036514d283..e851120bc3a 100644
--- a/spec/lib/banzai/filter/snippet_reference_filter_spec.rb
+++ b/spec/lib/banzai/filter/snippet_reference_filter_spec.rb
@@ -22,8 +22,8 @@ describe Banzai::Filter::SnippetReferenceFilter, lib: true do
it 'links to a valid reference' do
doc = reference_filter("See #{reference}")
- expect(doc.css('a').first.attr('href')).to eq urls.
- namespace_project_snippet_url(project.namespace, project, snippet)
+ expect(doc.css('a').first.attr('href')).to eq urls
+ .namespace_project_snippet_url(project.namespace, project, snippet)
end
it 'links with adjacent text' do
@@ -88,8 +88,8 @@ describe Banzai::Filter::SnippetReferenceFilter, lib: true do
it 'links to a valid reference' do
doc = reference_filter("See #{reference}")
- expect(doc.css('a').first.attr('href')).
- to eq urls.namespace_project_snippet_url(project2.namespace, project2, snippet)
+ expect(doc.css('a').first.attr('href'))
+ .to eq urls.namespace_project_snippet_url(project2.namespace, project2, snippet)
end
it 'link has valid text' do
@@ -121,8 +121,8 @@ describe Banzai::Filter::SnippetReferenceFilter, lib: true do
it 'links to a valid reference' do
doc = reference_filter("See #{reference}")
- expect(doc.css('a').first.attr('href')).
- to eq urls.namespace_project_snippet_url(project2.namespace, project2, snippet)
+ expect(doc.css('a').first.attr('href'))
+ .to eq urls.namespace_project_snippet_url(project2.namespace, project2, snippet)
end
it 'link has valid text' do
@@ -154,8 +154,8 @@ describe Banzai::Filter::SnippetReferenceFilter, lib: true do
it 'links to a valid reference' do
doc = reference_filter("See #{reference}")
- expect(doc.css('a').first.attr('href')).
- to eq urls.namespace_project_snippet_url(project2.namespace, project2, snippet)
+ expect(doc.css('a').first.attr('href'))
+ .to eq urls.namespace_project_snippet_url(project2.namespace, project2, snippet)
end
it 'link has valid text' do
@@ -186,8 +186,8 @@ describe Banzai::Filter::SnippetReferenceFilter, lib: true do
it 'links to a valid reference' do
doc = reference_filter("See #{reference}")
- expect(doc.css('a').first.attr('href')).
- to eq urls.namespace_project_snippet_url(project2.namespace, project2, snippet)
+ expect(doc.css('a').first.attr('href'))
+ .to eq urls.namespace_project_snippet_url(project2.namespace, project2, snippet)
end
it 'links with adjacent text' do
diff --git a/spec/lib/banzai/filter/upload_link_filter_spec.rb b/spec/lib/banzai/filter/upload_link_filter_spec.rb
index 639cac6406a..6327ca8bbfd 100644
--- a/spec/lib/banzai/filter/upload_link_filter_spec.rb
+++ b/spec/lib/banzai/filter/upload_link_filter_spec.rb
@@ -51,22 +51,22 @@ describe Banzai::Filter::UploadLinkFilter, lib: true do
context 'with a valid repository' do
it 'rebuilds relative URL for a link' do
doc = filter(link('/uploads/e90decf88d8f96fe9e1389afc2e4a91f/test.jpg'))
- expect(doc.at_css('a')['href']).
- to eq "#{Gitlab.config.gitlab.url}/#{project.path_with_namespace}/uploads/e90decf88d8f96fe9e1389afc2e4a91f/test.jpg"
+ expect(doc.at_css('a')['href'])
+ .to eq "#{Gitlab.config.gitlab.url}/#{project.path_with_namespace}/uploads/e90decf88d8f96fe9e1389afc2e4a91f/test.jpg"
doc = filter(nested_link('/uploads/e90decf88d8f96fe9e1389afc2e4a91f/test.jpg'))
- expect(doc.at_css('a')['href']).
- to eq "#{Gitlab.config.gitlab.url}/#{project.path_with_namespace}/uploads/e90decf88d8f96fe9e1389afc2e4a91f/test.jpg"
+ expect(doc.at_css('a')['href'])
+ .to eq "#{Gitlab.config.gitlab.url}/#{project.path_with_namespace}/uploads/e90decf88d8f96fe9e1389afc2e4a91f/test.jpg"
end
it 'rebuilds relative URL for an image' do
doc = filter(image('/uploads/e90decf88d8f96fe9e1389afc2e4a91f/test.jpg'))
- expect(doc.at_css('img')['src']).
- to eq "#{Gitlab.config.gitlab.url}/#{project.path_with_namespace}/uploads/e90decf88d8f96fe9e1389afc2e4a91f/test.jpg"
+ expect(doc.at_css('img')['src'])
+ .to eq "#{Gitlab.config.gitlab.url}/#{project.path_with_namespace}/uploads/e90decf88d8f96fe9e1389afc2e4a91f/test.jpg"
doc = filter(nested_image('/uploads/e90decf88d8f96fe9e1389afc2e4a91f/test.jpg'))
- expect(doc.at_css('img')['src']).
- to eq "#{Gitlab.config.gitlab.url}/#{project.path_with_namespace}/uploads/e90decf88d8f96fe9e1389afc2e4a91f/test.jpg"
+ expect(doc.at_css('img')['src'])
+ .to eq "#{Gitlab.config.gitlab.url}/#{project.path_with_namespace}/uploads/e90decf88d8f96fe9e1389afc2e4a91f/test.jpg"
end
it 'does not modify absolute URL' do
@@ -79,10 +79,10 @@ describe Banzai::Filter::UploadLinkFilter, lib: true do
escaped = Addressable::URI.escape(path)
# Stub these methods so the file doesn't actually need to be in the repo
- allow_any_instance_of(described_class).
- to receive(:file_exists?).and_return(true)
- allow_any_instance_of(described_class).
- to receive(:image?).with(path).and_return(true)
+ allow_any_instance_of(described_class)
+ .to receive(:file_exists?).and_return(true)
+ allow_any_instance_of(described_class)
+ .to receive(:image?).with(path).and_return(true)
doc = filter(image(escaped))
expect(doc.at_css('img')['src']).to match "#{Gitlab.config.gitlab.url}/#{project.path_with_namespace}/uploads/%ED%95%9C%EA%B8%80.png"
diff --git a/spec/lib/banzai/note_renderer_spec.rb b/spec/lib/banzai/note_renderer_spec.rb
index 49556074278..32764bee5eb 100644
--- a/spec/lib/banzai/note_renderer_spec.rb
+++ b/spec/lib/banzai/note_renderer_spec.rb
@@ -8,15 +8,15 @@ describe Banzai::NoteRenderer do
wiki = double(:wiki)
user = double(:user)
- expect(Banzai::ObjectRenderer).to receive(:new).
- with(project, user,
+ expect(Banzai::ObjectRenderer).to receive(:new)
+ .with(project, user,
requested_path: 'foo',
project_wiki: wiki,
- ref: 'bar').
- and_call_original
+ ref: 'bar')
+ .and_call_original
- expect_any_instance_of(Banzai::ObjectRenderer).
- to receive(:render).with([note], :note)
+ expect_any_instance_of(Banzai::ObjectRenderer)
+ .to receive(:render).with([note], :note)
described_class.render([note], project, user, 'foo', wiki, 'bar')
end
diff --git a/spec/lib/banzai/redactor_spec.rb b/spec/lib/banzai/redactor_spec.rb
index e6f2963193c..81ae5685b10 100644
--- a/spec/lib/banzai/redactor_spec.rb
+++ b/spec/lib/banzai/redactor_spec.rb
@@ -12,11 +12,11 @@ describe Banzai::Redactor do
end
it 'redacts an array of documents' do
- doc1 = Nokogiri::HTML.
- fragment('<a class="gfm" data-reference-type="issue">foo</a>')
+ doc1 = Nokogiri::HTML
+ .fragment('<a class="gfm" data-reference-type="issue">foo</a>')
- doc2 = Nokogiri::HTML.
- fragment('<a class="gfm" data-reference-type="issue">bar</a>')
+ doc2 = Nokogiri::HTML
+ .fragment('<a class="gfm" data-reference-type="issue">bar</a>')
redacted_data = redactor.redact([doc1, doc2])
@@ -93,9 +93,9 @@ describe Banzai::Redactor do
doc = Nokogiri::HTML.fragment('<a href="foo">foo</a>')
node = doc.children[0]
- expect(redactor).to receive(:nodes_visible_to_user).
- with([node]).
- and_return(Set.new)
+ expect(redactor).to receive(:nodes_visible_to_user)
+ .with([node])
+ .and_return(Set.new)
redactor.redact_document_nodes([{ document: doc, nodes: [node] }])
@@ -108,10 +108,10 @@ describe Banzai::Redactor do
doc = Nokogiri::HTML.fragment('<a data-reference-type="issue"></a>')
node = doc.children[0]
- expect_any_instance_of(Banzai::ReferenceParser::IssueParser).
- to receive(:nodes_visible_to_user).
- with(user, [node]).
- and_return([node])
+ expect_any_instance_of(Banzai::ReferenceParser::IssueParser)
+ .to receive(:nodes_visible_to_user)
+ .with(user, [node])
+ .and_return([node])
expect(redactor.nodes_visible_to_user([node])).to eq(Set.new([node]))
end
diff --git a/spec/lib/banzai/reference_parser/base_parser_spec.rb b/spec/lib/banzai/reference_parser/base_parser_spec.rb
index 76fab93821a..b444ca05b8e 100644
--- a/spec/lib/banzai/reference_parser/base_parser_spec.rb
+++ b/spec/lib/banzai/reference_parser/base_parser_spec.rb
@@ -54,8 +54,8 @@ describe Banzai::ReferenceParser::BaseParser, lib: true do
describe '#referenced_by' do
context 'when references_relation is implemented' do
it 'returns a collection of objects' do
- links = Nokogiri::HTML.fragment("<a data-foo='#{user.id}'></a>").
- children
+ links = Nokogiri::HTML.fragment("<a data-foo='#{user.id}'></a>")
+ .children
expect(subject).to receive(:references_relation).and_return(User)
expect(subject.referenced_by(links)).to eq([user])
@@ -66,8 +66,8 @@ describe Banzai::ReferenceParser::BaseParser, lib: true do
it 'raises NotImplementedError' do
links = Nokogiri::HTML.fragment('<a data-foo="1"></a>').children
- expect { subject.referenced_by(links) }.
- to raise_error(NotImplementedError)
+ expect { subject.referenced_by(links) }
+ .to raise_error(NotImplementedError)
end
end
end
@@ -80,8 +80,8 @@ describe Banzai::ReferenceParser::BaseParser, lib: true do
describe '#gather_attributes_per_project' do
it 'returns a Hash containing attribute values per project' do
- link = Nokogiri::HTML.fragment('<a data-project="1" data-foo="2"></a>').
- children[0]
+ link = Nokogiri::HTML.fragment('<a data-project="1" data-foo="2"></a>')
+ .children[0]
hash = subject.gather_attributes_per_project([link], 'data-foo')
@@ -95,19 +95,19 @@ describe Banzai::ReferenceParser::BaseParser, lib: true do
it 'returns a Hash grouping objects per node' do
link = double(:link)
- expect(link).to receive(:has_attribute?).
- with('data-user').
- and_return(true)
+ expect(link).to receive(:has_attribute?)
+ .with('data-user')
+ .and_return(true)
- expect(link).to receive(:attr).
- with('data-user').
- and_return(user.id.to_s)
+ expect(link).to receive(:attr)
+ .with('data-user')
+ .and_return(user.id.to_s)
nodes = [link]
- expect(subject).to receive(:unique_attribute_values).
- with(nodes, 'data-user').
- and_return([user.id.to_s])
+ expect(subject).to receive(:unique_attribute_values)
+ .with(nodes, 'data-user')
+ .and_return([user.id.to_s])
hash = subject.grouped_objects_for_nodes(nodes, User, 'data-user')
@@ -117,20 +117,20 @@ describe Banzai::ReferenceParser::BaseParser, lib: true do
it 'returns an empty Hash when entry does not exist in the database', :request_store do
link = double(:link)
- expect(link).to receive(:has_attribute?).
- with('data-user').
- and_return(true)
+ expect(link).to receive(:has_attribute?)
+ .with('data-user')
+ .and_return(true)
- expect(link).to receive(:attr).
- with('data-user').
- and_return('1')
+ expect(link).to receive(:attr)
+ .with('data-user')
+ .and_return('1')
nodes = [link]
bad_id = user.id + 100
- expect(subject).to receive(:unique_attribute_values).
- with(nodes, 'data-user').
- and_return([bad_id.to_s])
+ expect(subject).to receive(:unique_attribute_values)
+ .with(nodes, 'data-user')
+ .and_return([bad_id.to_s])
hash = subject.grouped_objects_for_nodes(nodes, User, 'data-user')
@@ -142,15 +142,15 @@ describe Banzai::ReferenceParser::BaseParser, lib: true do
it 'returns an Array of unique values' do
link = double(:link)
- expect(link).to receive(:has_attribute?).
- with('data-foo').
- twice.
- and_return(true)
+ expect(link).to receive(:has_attribute?)
+ .with('data-foo')
+ .twice
+ .and_return(true)
- expect(link).to receive(:attr).
- with('data-foo').
- twice.
- and_return('1')
+ expect(link).to receive(:attr)
+ .with('data-foo')
+ .twice
+ .and_return('1')
nodes = [link, link]
@@ -167,9 +167,9 @@ describe Banzai::ReferenceParser::BaseParser, lib: true do
instance = dummy.new(project, user)
document = Nokogiri::HTML.fragment('<a class="gfm"></a><a class="gfm" data-reference-type="test"></a>')
- expect(instance).to receive(:gather_references).
- with([document.children[1]]).
- and_return([user])
+ expect(instance).to receive(:gather_references)
+ .with([document.children[1]])
+ .and_return([user])
expect(instance.process([document])).to eq([user])
end
@@ -179,9 +179,9 @@ describe Banzai::ReferenceParser::BaseParser, lib: true do
let(:link) { double(:link) }
it 'does not process links a user can not reference' do
- expect(subject).to receive(:nodes_user_can_reference).
- with(user, [link]).
- and_return([])
+ expect(subject).to receive(:nodes_user_can_reference)
+ .with(user, [link])
+ .and_return([])
expect(subject).to receive(:referenced_by).with([])
@@ -189,13 +189,13 @@ describe Banzai::ReferenceParser::BaseParser, lib: true do
end
it 'does not process links a user can not see' do
- expect(subject).to receive(:nodes_user_can_reference).
- with(user, [link]).
- and_return([link])
+ expect(subject).to receive(:nodes_user_can_reference)
+ .with(user, [link])
+ .and_return([link])
- expect(subject).to receive(:nodes_visible_to_user).
- with(user, [link]).
- and_return([])
+ expect(subject).to receive(:nodes_visible_to_user)
+ .with(user, [link])
+ .and_return([])
expect(subject).to receive(:referenced_by).with([])
@@ -203,13 +203,13 @@ describe Banzai::ReferenceParser::BaseParser, lib: true do
end
it 'returns the references if a user can reference and see a link' do
- expect(subject).to receive(:nodes_user_can_reference).
- with(user, [link]).
- and_return([link])
+ expect(subject).to receive(:nodes_user_can_reference)
+ .with(user, [link])
+ .and_return([link])
- expect(subject).to receive(:nodes_visible_to_user).
- with(user, [link]).
- and_return([link])
+ expect(subject).to receive(:nodes_visible_to_user)
+ .with(user, [link])
+ .and_return([link])
expect(subject).to receive(:referenced_by).with([link])
@@ -221,8 +221,8 @@ describe Banzai::ReferenceParser::BaseParser, lib: true do
it 'delegates the permissions check to the Ability class' do
user = double(:user)
- expect(Ability).to receive(:allowed?).
- with(user, :read_project, project)
+ expect(Ability).to receive(:allowed?)
+ .with(user, :read_project, project)
subject.can?(user, :read_project, project)
end
@@ -230,8 +230,8 @@ describe Banzai::ReferenceParser::BaseParser, lib: true do
describe '#find_projects_for_hash_keys' do
it 'returns a list of Projects' do
- expect(subject.find_projects_for_hash_keys(project.id => project)).
- to eq([project])
+ expect(subject.find_projects_for_hash_keys(project.id => project))
+ .to eq([project])
end
end
@@ -243,8 +243,8 @@ describe Banzai::ReferenceParser::BaseParser, lib: true do
expect(collection).to receive(:where).twice.and_call_original
2.times do
- expect(subject.collection_objects_for_ids(collection, [user.id])).
- to eq([user])
+ expect(subject.collection_objects_for_ids(collection, [user.id]))
+ .to eq([user])
end
end
end
@@ -258,8 +258,8 @@ describe Banzai::ReferenceParser::BaseParser, lib: true do
end
it 'queries the collection on the first call' do
- expect(subject.collection_objects_for_ids(User, [user.id])).
- to eq([user])
+ expect(subject.collection_objects_for_ids(User, [user.id]))
+ .to eq([user])
end
it 'does not query previously queried objects' do
@@ -268,34 +268,34 @@ describe Banzai::ReferenceParser::BaseParser, lib: true do
expect(collection).to receive(:where).once.and_call_original
2.times do
- expect(subject.collection_objects_for_ids(collection, [user.id])).
- to eq([user])
+ expect(subject.collection_objects_for_ids(collection, [user.id]))
+ .to eq([user])
end
end
it 'casts String based IDs to Fixnums before querying objects' do
2.times do
- expect(subject.collection_objects_for_ids(User, [user.id.to_s])).
- to eq([user])
+ expect(subject.collection_objects_for_ids(User, [user.id.to_s]))
+ .to eq([user])
end
end
it 'queries any additional objects after the first call' do
other_user = create(:user)
- expect(subject.collection_objects_for_ids(User, [user.id])).
- to eq([user])
+ expect(subject.collection_objects_for_ids(User, [user.id]))
+ .to eq([user])
- expect(subject.collection_objects_for_ids(User, [user.id, other_user.id])).
- to eq([user, other_user])
+ expect(subject.collection_objects_for_ids(User, [user.id, other_user.id]))
+ .to eq([user, other_user])
end
it 'caches objects on a per collection class basis' do
- expect(subject.collection_objects_for_ids(User, [user.id])).
- to eq([user])
+ expect(subject.collection_objects_for_ids(User, [user.id]))
+ .to eq([user])
- expect(subject.collection_objects_for_ids(Project, [project.id])).
- to eq([project])
+ expect(subject.collection_objects_for_ids(Project, [project.id]))
+ .to eq([project])
end
end
end
diff --git a/spec/lib/banzai/reference_parser/commit_parser_spec.rb b/spec/lib/banzai/reference_parser/commit_parser_spec.rb
index 583ce63a8ab..a314a6119cb 100644
--- a/spec/lib/banzai/reference_parser/commit_parser_spec.rb
+++ b/spec/lib/banzai/reference_parser/commit_parser_spec.rb
@@ -32,30 +32,30 @@ describe Banzai::ReferenceParser::CommitParser, lib: true do
it 'returns an Array of commits' do
commit = double(:commit)
- allow_any_instance_of(Project).to receive(:valid_repo?).
- and_return(true)
+ allow_any_instance_of(Project).to receive(:valid_repo?)
+ .and_return(true)
- expect(subject).to receive(:find_commits).
- with(project, ['123']).
- and_return([commit])
+ expect(subject).to receive(:find_commits)
+ .with(project, ['123'])
+ .and_return([commit])
expect(subject.referenced_by([link])).to eq([commit])
end
it 'returns an empty Array when the commit could not be found' do
- allow_any_instance_of(Project).to receive(:valid_repo?).
- and_return(true)
+ allow_any_instance_of(Project).to receive(:valid_repo?)
+ .and_return(true)
- expect(subject).to receive(:find_commits).
- with(project, ['123']).
- and_return([])
+ expect(subject).to receive(:find_commits)
+ .with(project, ['123'])
+ .and_return([])
expect(subject.referenced_by([link])).to eq([])
end
it 'skips projects without valid repositories' do
- allow_any_instance_of(Project).to receive(:valid_repo?).
- and_return(false)
+ allow_any_instance_of(Project).to receive(:valid_repo?)
+ .and_return(false)
expect(subject.referenced_by([link])).to eq([])
end
@@ -63,8 +63,8 @@ describe Banzai::ReferenceParser::CommitParser, lib: true do
context 'when the link does not have a data-commit attribute' do
it 'returns an empty Array' do
- allow_any_instance_of(Project).to receive(:valid_repo?).
- and_return(true)
+ allow_any_instance_of(Project).to receive(:valid_repo?)
+ .and_return(true)
expect(subject.referenced_by([link])).to eq([])
end
@@ -73,8 +73,8 @@ describe Banzai::ReferenceParser::CommitParser, lib: true do
context 'when the link does not have a data-project attribute' do
it 'returns an empty Array' do
- allow_any_instance_of(Project).to receive(:valid_repo?).
- and_return(true)
+ allow_any_instance_of(Project).to receive(:valid_repo?)
+ .and_return(true)
expect(subject.referenced_by([link])).to eq([])
end
diff --git a/spec/lib/banzai/reference_parser/commit_range_parser_spec.rb b/spec/lib/banzai/reference_parser/commit_range_parser_spec.rb
index 8c0f5d7df97..5dca5e784da 100644
--- a/spec/lib/banzai/reference_parser/commit_range_parser_spec.rb
+++ b/spec/lib/banzai/reference_parser/commit_range_parser_spec.rb
@@ -32,17 +32,17 @@ describe Banzai::ReferenceParser::CommitRangeParser, lib: true do
it 'returns an Array of commit ranges' do
range = double(:range)
- expect(subject).to receive(:find_object).
- with(project, '123..456').
- and_return(range)
+ expect(subject).to receive(:find_object)
+ .with(project, '123..456')
+ .and_return(range)
expect(subject.referenced_by([link])).to eq([range])
end
it 'returns an empty Array when the commit range could not be found' do
- expect(subject).to receive(:find_object).
- with(project, '123..456').
- and_return(nil)
+ expect(subject).to receive(:find_object)
+ .with(project, '123..456')
+ .and_return(nil)
expect(subject.referenced_by([link])).to eq([])
end
@@ -88,17 +88,17 @@ describe Banzai::ReferenceParser::CommitRangeParser, lib: true do
it 'returns an Array of range objects' do
range = double(:commit)
- expect(subject).to receive(:find_object).
- with(project, '123..456').
- and_return(range)
+ expect(subject).to receive(:find_object)
+ .with(project, '123..456')
+ .and_return(range)
expect(subject.find_ranges(project, ['123..456'])).to eq([range])
end
it 'skips ranges that could not be found' do
- expect(subject).to receive(:find_object).
- with(project, '123..456').
- and_return(nil)
+ expect(subject).to receive(:find_object)
+ .with(project, '123..456')
+ .and_return(nil)
expect(subject.find_ranges(project, ['123..456'])).to eq([])
end
diff --git a/spec/lib/banzai/reference_parser/issue_parser_spec.rb b/spec/lib/banzai/reference_parser/issue_parser_spec.rb
index 7031c47231c..58e1a0c1bc1 100644
--- a/spec/lib/banzai/reference_parser/issue_parser_spec.rb
+++ b/spec/lib/banzai/reference_parser/issue_parser_spec.rb
@@ -18,17 +18,17 @@ describe Banzai::ReferenceParser::IssueParser, lib: true do
it_behaves_like "referenced feature visibility", "issues"
it 'returns the nodes when the user can read the issue' do
- expect(Ability).to receive(:issues_readable_by_user).
- with([issue], user).
- and_return([issue])
+ expect(Ability).to receive(:issues_readable_by_user)
+ .with([issue], user)
+ .and_return([issue])
expect(subject.nodes_visible_to_user(user, [link])).to eq([link])
end
it 'returns an empty Array when the user can not read the issue' do
- expect(Ability).to receive(:issues_readable_by_user).
- with([issue], user).
- and_return([])
+ expect(Ability).to receive(:issues_readable_by_user)
+ .with([issue], user)
+ .and_return([])
expect(subject.nodes_visible_to_user(user, [link])).to eq([])
end
diff --git a/spec/lib/banzai/reference_parser/user_parser_spec.rb b/spec/lib/banzai/reference_parser/user_parser_spec.rb
index 4d560667342..dfebb971f3a 100644
--- a/spec/lib/banzai/reference_parser/user_parser_spec.rb
+++ b/spec/lib/banzai/reference_parser/user_parser_spec.rb
@@ -96,17 +96,17 @@ describe Banzai::ReferenceParser::UserParser, lib: true do
end
it 'returns the nodes if the user can read the group' do
- expect(Ability).to receive(:allowed?).
- with(user, :read_group, group).
- and_return(true)
+ expect(Ability).to receive(:allowed?)
+ .with(user, :read_group, group)
+ .and_return(true)
expect(subject.nodes_visible_to_user(user, [link])).to eq([link])
end
it 'returns an empty Array if the user can not read the group' do
- expect(Ability).to receive(:allowed?).
- with(user, :read_group, group).
- and_return(false)
+ expect(Ability).to receive(:allowed?)
+ .with(user, :read_group, group)
+ .and_return(false)
expect(subject.nodes_visible_to_user(user, [link])).to eq([])
end
@@ -129,9 +129,9 @@ describe Banzai::ReferenceParser::UserParser, lib: true do
link['data-project'] = other_project.id.to_s
- expect(Ability).to receive(:allowed?).
- with(user, :read_project, other_project).
- and_return(true)
+ expect(Ability).to receive(:allowed?)
+ .with(user, :read_project, other_project)
+ .and_return(true)
expect(subject.nodes_visible_to_user(user, [link])).to eq([link])
end
@@ -141,9 +141,9 @@ describe Banzai::ReferenceParser::UserParser, lib: true do
link['data-project'] = other_project.id.to_s
- expect(Ability).to receive(:allowed?).
- with(user, :read_project, other_project).
- and_return(false)
+ expect(Ability).to receive(:allowed?)
+ .with(user, :read_project, other_project)
+ .and_return(false)
expect(subject.nodes_visible_to_user(user, [link])).to eq([])
end
diff --git a/spec/lib/container_registry/blob_spec.rb b/spec/lib/container_registry/blob_spec.rb
index ab010c6dfeb..175fd2e7e13 100644
--- a/spec/lib/container_registry/blob_spec.rb
+++ b/spec/lib/container_registry/blob_spec.rb
@@ -72,8 +72,8 @@ describe ContainerRegistry::Blob do
describe '#data' do
context 'when locally stored' do
before do
- stub_request(:get, 'http://registry.gitlab/v2/group/test/image/blobs/sha256:0123456789012345').
- to_return(
+ stub_request(:get, 'http://registry.gitlab/v2/group/test/image/blobs/sha256:0123456789012345')
+ .to_return(
status: 200,
headers: { 'Content-Type' => 'application/json' },
body: '{"key":"value"}')
@@ -97,9 +97,9 @@ describe ContainerRegistry::Blob do
context 'for a valid address' do
before do
- stub_request(:get, location).
- with { |request| !request.headers.include?('Authorization') }.
- to_return(
+ stub_request(:get, location)
+ .with { |request| !request.headers.include?('Authorization') }
+ .to_return(
status: 200,
headers: { 'Content-Type' => 'application/json' },
body: '{"key":"value"}')
diff --git a/spec/lib/container_registry/client_spec.rb b/spec/lib/container_registry/client_spec.rb
index ec03b533383..3df33f48adb 100644
--- a/spec/lib/container_registry/client_spec.rb
+++ b/spec/lib/container_registry/client_spec.rb
@@ -8,28 +8,28 @@ describe ContainerRegistry::Client do
describe '#blob' do
it 'GET /v2/:name/blobs/:digest' do
- stub_request(:get, "http://container-registry/v2/group/test/blobs/sha256:0123456789012345").
- with(headers: {
+ stub_request(:get, "http://container-registry/v2/group/test/blobs/sha256:0123456789012345")
+ .with(headers: {
'Accept' => 'application/octet-stream',
'Authorization' => "bearer #{token}"
- }).
- to_return(status: 200, body: "Blob")
+ })
+ .to_return(status: 200, body: "Blob")
expect(client.blob('group/test', 'sha256:0123456789012345')).to eq('Blob')
end
it 'follows 307 redirect for GET /v2/:name/blobs/:digest' do
- stub_request(:get, "http://container-registry/v2/group/test/blobs/sha256:0123456789012345").
- with(headers: {
+ stub_request(:get, "http://container-registry/v2/group/test/blobs/sha256:0123456789012345")
+ .with(headers: {
'Accept' => 'application/octet-stream',
'Authorization' => "bearer #{token}"
- }).
- to_return(status: 307, body: "", headers: { Location: 'http://redirected' })
+ })
+ .to_return(status: 307, body: "", headers: { Location: 'http://redirected' })
# We should probably use hash_excluding here, but that requires an update to WebMock:
# https://github.com/bblimke/webmock/blob/master/lib/webmock/matchers/hash_excluding_matcher.rb
- stub_request(:get, "http://redirected/").
- with { |request| !request.headers.include?('Authorization') }.
- to_return(status: 200, body: "Successfully redirected")
+ stub_request(:get, "http://redirected/")
+ .with { |request| !request.headers.include?('Authorization') }
+ .to_return(status: 200, body: "Successfully redirected")
response = client.blob('group/test', 'sha256:0123456789012345')
diff --git a/spec/lib/container_registry/tag_spec.rb b/spec/lib/container_registry/tag_spec.rb
index f8fffbdca41..cb4ae3be525 100644
--- a/spec/lib/container_registry/tag_spec.rb
+++ b/spec/lib/container_registry/tag_spec.rb
@@ -60,9 +60,9 @@ describe ContainerRegistry::Tag do
context 'manifest processing' do
context 'schema v1' do
before do
- stub_request(:get, 'http://registry.gitlab/v2/group/test/manifests/tag').
- with(headers: headers).
- to_return(
+ stub_request(:get, 'http://registry.gitlab/v2/group/test/manifests/tag')
+ .with(headers: headers)
+ .to_return(
status: 200,
body: File.read(Rails.root + 'spec/fixtures/container_registry/tag_manifest_1.json'),
headers: { 'Content-Type' => 'application/vnd.docker.distribution.manifest.v1+prettyjws' })
@@ -97,9 +97,9 @@ describe ContainerRegistry::Tag do
context 'schema v2' do
before do
- stub_request(:get, 'http://registry.gitlab/v2/group/test/manifests/tag').
- with(headers: headers).
- to_return(
+ stub_request(:get, 'http://registry.gitlab/v2/group/test/manifests/tag')
+ .with(headers: headers)
+ .to_return(
status: 200,
body: File.read(Rails.root + 'spec/fixtures/container_registry/tag_manifest.json'),
headers: { 'Content-Type' => 'application/vnd.docker.distribution.manifest.v2+json' })
@@ -134,9 +134,9 @@ describe ContainerRegistry::Tag do
context 'when locally stored' do
before do
- stub_request(:get, 'http://registry.gitlab/v2/group/test/blobs/sha256:d7a513a663c1a6dcdba9ed832ca53c02ac2af0c333322cd6ca92936d1d9917ac').
- with(headers: { 'Accept' => 'application/octet-stream' }).
- to_return(
+ stub_request(:get, 'http://registry.gitlab/v2/group/test/blobs/sha256:d7a513a663c1a6dcdba9ed832ca53c02ac2af0c333322cd6ca92936d1d9917ac')
+ .with(headers: { 'Accept' => 'application/octet-stream' })
+ .to_return(
status: 200,
body: File.read(Rails.root + 'spec/fixtures/container_registry/config_blob.json'))
end
@@ -146,14 +146,14 @@ describe ContainerRegistry::Tag do
context 'when externally stored' do
before do
- stub_request(:get, 'http://registry.gitlab/v2/group/test/blobs/sha256:d7a513a663c1a6dcdba9ed832ca53c02ac2af0c333322cd6ca92936d1d9917ac').
- with(headers: { 'Accept' => 'application/octet-stream' }).
- to_return(
+ stub_request(:get, 'http://registry.gitlab/v2/group/test/blobs/sha256:d7a513a663c1a6dcdba9ed832ca53c02ac2af0c333322cd6ca92936d1d9917ac')
+ .with(headers: { 'Accept' => 'application/octet-stream' })
+ .to_return(
status: 307,
headers: { 'Location' => 'http://external.com/blob/file' })
- stub_request(:get, 'http://external.com/blob/file').
- to_return(
+ stub_request(:get, 'http://external.com/blob/file')
+ .to_return(
status: 200,
body: File.read(Rails.root + 'spec/fixtures/container_registry/config_blob.json'))
end
diff --git a/spec/lib/extracts_path_spec.rb b/spec/lib/extracts_path_spec.rb
index 2b26a318583..f2132d485ab 100644
--- a/spec/lib/extracts_path_spec.rb
+++ b/spec/lib/extracts_path_spec.rb
@@ -14,8 +14,8 @@ describe ExtractsPath, lib: true do
repo = double(ref_names: ['master', 'foo/bar/baz', 'v1.0.0', 'v2.0.0',
'release/app', 'release/app/v1.0.0'])
allow(project).to receive(:repository).and_return(repo)
- allow(project).to receive(:path_with_namespace).
- and_return('gitlab/gitlab-ci')
+ allow(project).to receive(:path_with_namespace)
+ .and_return('gitlab/gitlab-ci')
allow(request).to receive(:format=)
end
diff --git a/spec/lib/feature_spec.rb b/spec/lib/feature_spec.rb
index 1d92a5cb33f..5cc3a3745e4 100644
--- a/spec/lib/feature_spec.rb
+++ b/spec/lib/feature_spec.rb
@@ -6,8 +6,8 @@ describe Feature, lib: true do
let(:key) { 'my_feature' }
it 'returns the Flipper feature' do
- expect_any_instance_of(Flipper::DSL).to receive(:feature).with(key).
- and_return(feature)
+ expect_any_instance_of(Flipper::DSL).to receive(:feature).with(key)
+ .and_return(feature)
expect(described_class.get(key)).to be(feature)
end
@@ -17,8 +17,8 @@ describe Feature, lib: true do
let(:features) { Set.new }
it 'returns the Flipper features as an array' do
- expect_any_instance_of(Flipper::DSL).to receive(:features).
- and_return(features)
+ expect_any_instance_of(Flipper::DSL).to receive(:features)
+ .and_return(features)
expect(described_class.all).to eq(features.to_a)
end
diff --git a/spec/lib/gitlab/background_migration_spec.rb b/spec/lib/gitlab/background_migration_spec.rb
index f2073b9bcb3..64f82fe27b2 100644
--- a/spec/lib/gitlab/background_migration_spec.rb
+++ b/spec/lib/gitlab/background_migration_spec.rb
@@ -5,9 +5,9 @@ describe Gitlab::BackgroundMigration do
it 'steals jobs from a queue' do
queue = [double(:job, args: ['Foo', [10, 20]])]
- allow(Sidekiq::Queue).to receive(:new).
- with(BackgroundMigrationWorker.sidekiq_options['queue']).
- and_return(queue)
+ allow(Sidekiq::Queue).to receive(:new)
+ .with(BackgroundMigrationWorker.sidekiq_options['queue'])
+ .and_return(queue)
expect(queue[0]).to receive(:delete)
@@ -19,9 +19,9 @@ describe Gitlab::BackgroundMigration do
it 'does not steal jobs for a different migration' do
queue = [double(:job, args: ['Foo', [10, 20]])]
- allow(Sidekiq::Queue).to receive(:new).
- with(BackgroundMigrationWorker.sidekiq_options['queue']).
- and_return(queue)
+ allow(Sidekiq::Queue).to receive(:new)
+ .with(BackgroundMigrationWorker.sidekiq_options['queue'])
+ .and_return(queue)
expect(described_class).not_to receive(:perform)
@@ -36,9 +36,9 @@ describe Gitlab::BackgroundMigration do
instance = double(:instance)
klass = double(:klass, new: instance)
- expect(described_class).to receive(:const_get).
- with('Foo').
- and_return(klass)
+ expect(described_class).to receive(:const_get)
+ .with('Foo')
+ .and_return(klass)
expect(instance).to receive(:perform).with(10, 20)
diff --git a/spec/lib/gitlab/bitbucket_import/importer_spec.rb b/spec/lib/gitlab/bitbucket_import/importer_spec.rb
index a7ee7f53a6b..d8beb05601c 100644
--- a/spec/lib/gitlab/bitbucket_import/importer_spec.rb
+++ b/spec/lib/gitlab/bitbucket_import/importer_spec.rb
@@ -86,11 +86,9 @@ describe Gitlab::BitbucketImport::Importer, lib: true do
headers: { "Content-Type" => "application/json" },
body: issues_statuses_sample_data.to_json)
- stub_request(:get, "https://api.bitbucket.org/2.0/repositories/namespace/repo?pagelen=50&sort=created_on").
- with(headers: { 'Accept' => '*/*', 'Accept-Encoding' => 'gzip;q=1.0,deflate;q=0.6,identity;q=0.3', 'Authorization' => 'Bearer', 'User-Agent' => 'Faraday v0.9.2' }).
- to_return(status: 200,
- body: "",
- headers: {})
+ stub_request(:get, "https://api.bitbucket.org/2.0/repositories/namespace/repo?pagelen=50&sort=created_on")
+ .with(headers: { 'Accept' => '*/*', 'Accept-Encoding' => 'gzip;q=1.0,deflate;q=0.6,identity;q=0.3', 'Authorization' => 'Bearer', 'User-Agent' => 'Faraday v0.9.2' })
+ .to_return(status: 200, body: "", headers: {})
sample_issues_statuses.each_with_index do |issue, index|
stub_request(
diff --git a/spec/lib/gitlab/cache/ci/project_pipeline_status_spec.rb b/spec/lib/gitlab/cache/ci/project_pipeline_status_spec.rb
index cfb5cba054e..07db6c3a640 100644
--- a/spec/lib/gitlab/cache/ci/project_pipeline_status_spec.rb
+++ b/spec/lib/gitlab/cache/ci/project_pipeline_status_spec.rb
@@ -37,11 +37,11 @@ describe Gitlab::Cache::Ci::ProjectPipelineStatus, :redis do
loaded_from_cache: false
)
- expect(described_class).to receive(:new).
- with(project_without_status,
+ expect(described_class).to receive(:new)
+ .with(project_without_status,
pipeline_info: empty_status,
- loaded_from_cache: false).
- and_return(fake_pipeline)
+ loaded_from_cache: false)
+ .and_return(fake_pipeline)
expect(fake_pipeline).to receive(:load_from_project)
expect(fake_pipeline).to receive(:store_in_cache)
@@ -112,12 +112,12 @@ describe Gitlab::Cache::Ci::ProjectPipelineStatus, :redis do
pipeline = build_stubbed(:ci_pipeline,
sha: '123456', status: 'success', ref: 'master')
fake_status = double
- expect(described_class).to receive(:new).
- with(pipeline.project,
+ expect(described_class).to receive(:new)
+ .with(pipeline.project,
pipeline_info: {
sha: '123456', status: 'success', ref: 'master'
- }).
- and_return(fake_status)
+ })
+ .and_return(fake_status)
expect(fake_status).to receive(:store_in_cache_if_needed)
diff --git a/spec/lib/gitlab/ci/build/artifacts/metadata_spec.rb b/spec/lib/gitlab/ci/build/artifacts/metadata_spec.rb
index eea01f91879..6a52ae01b2f 100644
--- a/spec/lib/gitlab/ci/build/artifacts/metadata_spec.rb
+++ b/spec/lib/gitlab/ci/build/artifacts/metadata_spec.rb
@@ -33,8 +33,8 @@ describe Gitlab::Ci::Build::Artifacts::Metadata do
subject { metadata('other_artifacts_0.1.2/').find_entries! }
it 'matches correct paths' do
- expect(subject.keys).
- to contain_exactly 'other_artifacts_0.1.2/',
+ expect(subject.keys)
+ .to contain_exactly 'other_artifacts_0.1.2/',
'other_artifacts_0.1.2/doc_sample.txt',
'other_artifacts_0.1.2/another-subdirectory/'
end
@@ -44,8 +44,8 @@ describe Gitlab::Ci::Build::Artifacts::Metadata do
subject { metadata('other_artifacts_0.1.2/another-subdirectory/').find_entries! }
it 'matches correct paths' do
- expect(subject.keys).
- to contain_exactly 'other_artifacts_0.1.2/another-subdirectory/',
+ expect(subject.keys)
+ .to contain_exactly 'other_artifacts_0.1.2/another-subdirectory/',
'other_artifacts_0.1.2/another-subdirectory/empty_directory/',
'other_artifacts_0.1.2/another-subdirectory/banana_sample.gif'
end
@@ -55,8 +55,8 @@ describe Gitlab::Ci::Build::Artifacts::Metadata do
subject { metadata('other_artifacts_0.1.2/', recursive: true).find_entries! }
it 'matches correct paths' do
- expect(subject.keys).
- to contain_exactly 'other_artifacts_0.1.2/',
+ expect(subject.keys)
+ .to contain_exactly 'other_artifacts_0.1.2/',
'other_artifacts_0.1.2/doc_sample.txt',
'other_artifacts_0.1.2/another-subdirectory/',
'other_artifacts_0.1.2/another-subdirectory/empty_directory/',
diff --git a/spec/lib/gitlab/closing_issue_extractor_spec.rb b/spec/lib/gitlab/closing_issue_extractor_spec.rb
index 97af1c2523d..ca68010cb89 100644
--- a/spec/lib/gitlab/closing_issue_extractor_spec.rb
+++ b/spec/lib/gitlab/closing_issue_extractor_spec.rb
@@ -306,58 +306,58 @@ describe Gitlab::ClosingIssueExtractor, lib: true do
it 'fetches issues in single line message' do
message = "Closes #{reference} and fix #{reference2}"
- expect(subject.closed_by_message(message)).
- to match_array([issue, other_issue])
+ expect(subject.closed_by_message(message))
+ .to match_array([issue, other_issue])
end
it 'fetches comma-separated issues references in single line message' do
message = "Closes #{reference}, closes #{reference2}"
- expect(subject.closed_by_message(message)).
- to match_array([issue, other_issue])
+ expect(subject.closed_by_message(message))
+ .to match_array([issue, other_issue])
end
it 'fetches comma-separated issues numbers in single line message' do
message = "Closes #{reference}, #{reference2} and #{reference3}"
- expect(subject.closed_by_message(message)).
- to match_array([issue, other_issue, third_issue])
+ expect(subject.closed_by_message(message))
+ .to match_array([issue, other_issue, third_issue])
end
it 'fetches issues in multi-line message' do
message = "Awesome commit (closes #{reference})\nAlso fixes #{reference2}"
- expect(subject.closed_by_message(message)).
- to match_array([issue, other_issue])
+ expect(subject.closed_by_message(message))
+ .to match_array([issue, other_issue])
end
it 'fetches issues in hybrid message' do
message = "Awesome commit (closes #{reference})\n"\
"Also fixing issues #{reference2}, #{reference3} and #4"
- expect(subject.closed_by_message(message)).
- to match_array([issue, other_issue, third_issue])
+ expect(subject.closed_by_message(message))
+ .to match_array([issue, other_issue, third_issue])
end
it "fetches cross-project references" do
message = "Closes #{reference} and #{cross_reference}"
- expect(subject.closed_by_message(message)).
- to match_array([issue, issue2])
+ expect(subject.closed_by_message(message))
+ .to match_array([issue, issue2])
end
it "fetches cross-project URL references" do
message = "Closes #{urls.namespace_project_issue_url(issue2.project.namespace, issue2.project, issue2)} and #{reference}"
- expect(subject.closed_by_message(message)).
- to match_array([issue, issue2])
+ expect(subject.closed_by_message(message))
+ .to match_array([issue, issue2])
end
it "ignores invalid cross-project URL references" do
message = "Closes https://google.com#{urls.namespace_project_issue_path(issue2.project.namespace, issue2.project, issue2)} and #{reference}"
- expect(subject.closed_by_message(message)).
- to match_array([issue])
+ expect(subject.closed_by_message(message))
+ .to match_array([issue])
end
end
end
diff --git a/spec/lib/gitlab/conflict/file_spec.rb b/spec/lib/gitlab/conflict/file_spec.rb
index 780ac0ad97e..585eeb77bd5 100644
--- a/spec/lib/gitlab/conflict/file_spec.rb
+++ b/spec/lib/gitlab/conflict/file_spec.rb
@@ -43,8 +43,8 @@ describe Gitlab::Conflict::File, lib: true do
end
it 'returns a file containing only the chosen parts of the resolved sections' do
- expect(resolved_lines.chunk { |line| line.type || 'both' }.map(&:first)).
- to eq(%w(both new both old both new both))
+ expect(resolved_lines.chunk { |line| line.type || 'both' }.map(&:first))
+ .to eq(%w(both new both old both new both))
end
end
@@ -52,14 +52,14 @@ describe Gitlab::Conflict::File, lib: true do
empty_hash = section_keys.map { |key| [key, nil] }.to_h
invalid_hash = section_keys.map { |key| [key, 'invalid'] }.to_h
- expect { conflict_file.resolve_lines({}) }.
- to raise_error(Gitlab::Conflict::File::MissingResolution)
+ expect { conflict_file.resolve_lines({}) }
+ .to raise_error(Gitlab::Conflict::File::MissingResolution)
- expect { conflict_file.resolve_lines(empty_hash) }.
- to raise_error(Gitlab::Conflict::File::MissingResolution)
+ expect { conflict_file.resolve_lines(empty_hash) }
+ .to raise_error(Gitlab::Conflict::File::MissingResolution)
- expect { conflict_file.resolve_lines(invalid_hash) }.
- to raise_error(Gitlab::Conflict::File::MissingResolution)
+ expect { conflict_file.resolve_lines(invalid_hash) }
+ .to raise_error(Gitlab::Conflict::File::MissingResolution)
end
end
@@ -250,8 +250,8 @@ FILE
describe '#as_json' do
it 'includes the blob path for the file' do
- expect(conflict_file.as_json[:blob_path]).
- to eq("/#{project.full_path}/blob/#{our_commit.oid}/files/ruby/regex.rb")
+ expect(conflict_file.as_json[:blob_path])
+ .to eq("/#{project.full_path}/blob/#{our_commit.oid}/files/ruby/regex.rb")
end
it 'includes the blob icon for the file' do
@@ -264,8 +264,8 @@ FILE
end
it 'includes the detected language of the conflict file' do
- expect(conflict_file.as_json(full_content: true)[:blob_ace_mode]).
- to eq('ruby')
+ expect(conflict_file.as_json(full_content: true)[:blob_ace_mode])
+ .to eq('ruby')
end
end
end
diff --git a/spec/lib/gitlab/conflict/parser_spec.rb b/spec/lib/gitlab/conflict/parser_spec.rb
index 2570f95dd21..aed57b75789 100644
--- a/spec/lib/gitlab/conflict/parser_spec.rb
+++ b/spec/lib/gitlab/conflict/parser_spec.rb
@@ -122,18 +122,18 @@ CONFLICT
context 'when the file contents include conflict delimiters' do
context 'when there is a non-start delimiter first' do
it 'raises UnexpectedDelimiter when there is a middle delimiter first' do
- expect { parse_text('=======') }.
- to raise_error(Gitlab::Conflict::Parser::UnexpectedDelimiter)
+ expect { parse_text('=======') }
+ .to raise_error(Gitlab::Conflict::Parser::UnexpectedDelimiter)
end
it 'raises UnexpectedDelimiter when there is an end delimiter first' do
- expect { parse_text('>>>>>>> README.md') }.
- to raise_error(Gitlab::Conflict::Parser::UnexpectedDelimiter)
+ expect { parse_text('>>>>>>> README.md') }
+ .to raise_error(Gitlab::Conflict::Parser::UnexpectedDelimiter)
end
it 'does not raise when there is an end delimiter for a different path first' do
- expect { parse_text('>>>>>>> some-other-path.md') }.
- not_to raise_error
+ expect { parse_text('>>>>>>> some-other-path.md') }
+ .not_to raise_error
end
end
@@ -142,18 +142,18 @@ CONFLICT
let(:end_text) { "\n=======\n>>>>>>> README.md" }
it 'raises UnexpectedDelimiter when it is followed by an end delimiter' do
- expect { parse_text(start_text + '>>>>>>> README.md' + end_text) }.
- to raise_error(Gitlab::Conflict::Parser::UnexpectedDelimiter)
+ expect { parse_text(start_text + '>>>>>>> README.md' + end_text) }
+ .to raise_error(Gitlab::Conflict::Parser::UnexpectedDelimiter)
end
it 'raises UnexpectedDelimiter when it is followed by another start delimiter' do
- expect { parse_text(start_text + start_text + end_text) }.
- to raise_error(Gitlab::Conflict::Parser::UnexpectedDelimiter)
+ expect { parse_text(start_text + start_text + end_text) }
+ .to raise_error(Gitlab::Conflict::Parser::UnexpectedDelimiter)
end
it 'does not raise when it is followed by a start delimiter for a different path' do
- expect { parse_text(start_text + '>>>>>>> some-other-path.md' + end_text) }.
- not_to raise_error
+ expect { parse_text(start_text + '>>>>>>> some-other-path.md' + end_text) }
+ .not_to raise_error
end
end
@@ -162,59 +162,59 @@ CONFLICT
let(:end_text) { "\n>>>>>>> README.md" }
it 'raises UnexpectedDelimiter when it is followed by another middle delimiter' do
- expect { parse_text(start_text + '=======' + end_text) }.
- to raise_error(Gitlab::Conflict::Parser::UnexpectedDelimiter)
+ expect { parse_text(start_text + '=======' + end_text) }
+ .to raise_error(Gitlab::Conflict::Parser::UnexpectedDelimiter)
end
it 'raises UnexpectedDelimiter when it is followed by a start delimiter' do
- expect { parse_text(start_text + start_text + end_text) }.
- to raise_error(Gitlab::Conflict::Parser::UnexpectedDelimiter)
+ expect { parse_text(start_text + start_text + end_text) }
+ .to raise_error(Gitlab::Conflict::Parser::UnexpectedDelimiter)
end
it 'does not raise when it is followed by a start delimiter for another path' do
- expect { parse_text(start_text + '<<<<<<< some-other-path.md' + end_text) }.
- not_to raise_error
+ expect { parse_text(start_text + '<<<<<<< some-other-path.md' + end_text) }
+ .not_to raise_error
end
end
it 'raises MissingEndDelimiter when there is no end delimiter at the end' do
start_text = "<<<<<<< README.md\n=======\n"
- expect { parse_text(start_text) }.
- to raise_error(Gitlab::Conflict::Parser::MissingEndDelimiter)
+ expect { parse_text(start_text) }
+ .to raise_error(Gitlab::Conflict::Parser::MissingEndDelimiter)
- expect { parse_text(start_text + '>>>>>>> some-other-path.md') }.
- to raise_error(Gitlab::Conflict::Parser::MissingEndDelimiter)
+ expect { parse_text(start_text + '>>>>>>> some-other-path.md') }
+ .to raise_error(Gitlab::Conflict::Parser::MissingEndDelimiter)
end
end
context 'other file types' do
it 'raises UnmergeableFile when lines is blank, indicating a binary file' do
- expect { parse_text('') }.
- to raise_error(Gitlab::Conflict::Parser::UnmergeableFile)
+ expect { parse_text('') }
+ .to raise_error(Gitlab::Conflict::Parser::UnmergeableFile)
- expect { parse_text(nil) }.
- to raise_error(Gitlab::Conflict::Parser::UnmergeableFile)
+ expect { parse_text(nil) }
+ .to raise_error(Gitlab::Conflict::Parser::UnmergeableFile)
end
it 'raises UnmergeableFile when the file is over 200 KB' do
- expect { parse_text('a' * 204801) }.
- to raise_error(Gitlab::Conflict::Parser::UnmergeableFile)
+ expect { parse_text('a' * 204801) }
+ .to raise_error(Gitlab::Conflict::Parser::UnmergeableFile)
end
# All text from Rugged has an encoding of ASCII_8BIT, so force that in
# these strings.
context 'when the file contains UTF-8 characters' do
it 'does not raise' do
- expect { parse_text("Espa\xC3\xB1a".force_encoding(Encoding::ASCII_8BIT)) }.
- not_to raise_error
+ expect { parse_text("Espa\xC3\xB1a".force_encoding(Encoding::ASCII_8BIT)) }
+ .not_to raise_error
end
end
context 'when the file contains non-UTF-8 characters' do
it 'raises UnsupportedEncoding' do
- expect { parse_text("a\xC4\xFC".force_encoding(Encoding::ASCII_8BIT)) }.
- to raise_error(Gitlab::Conflict::Parser::UnsupportedEncoding)
+ expect { parse_text("a\xC4\xFC".force_encoding(Encoding::ASCII_8BIT)) }
+ .to raise_error(Gitlab::Conflict::Parser::UnsupportedEncoding)
end
end
end
diff --git a/spec/lib/gitlab/data_builder/push_spec.rb b/spec/lib/gitlab/data_builder/push_spec.rb
index e59cba35b2f..73936969832 100644
--- a/spec/lib/gitlab/data_builder/push_spec.rb
+++ b/spec/lib/gitlab/data_builder/push_spec.rb
@@ -47,8 +47,8 @@ describe Gitlab::DataBuilder::Push, lib: true do
include_examples 'deprecated repository hook data'
it 'does not raise an error when given nil commits' do
- expect { described_class.build(spy, spy, spy, spy, spy, nil) }.
- not_to raise_error
+ expect { described_class.build(spy, spy, spy, spy, spy, nil) }
+ .not_to raise_error
end
end
end
diff --git a/spec/lib/gitlab/database/migration_helpers_spec.rb b/spec/lib/gitlab/database/migration_helpers_spec.rb
index 30aa463faf8..6a0485112c1 100644
--- a/spec/lib/gitlab/database/migration_helpers_spec.rb
+++ b/spec/lib/gitlab/database/migration_helpers_spec.rb
@@ -57,15 +57,15 @@ describe Gitlab::Database::MigrationHelpers, lib: true do
end
it 'creates the index concurrently' do
- expect(model).to receive(:add_index).
- with(:users, :foo, algorithm: :concurrently)
+ expect(model).to receive(:add_index)
+ .with(:users, :foo, algorithm: :concurrently)
model.add_concurrent_index(:users, :foo)
end
it 'creates unique index concurrently' do
- expect(model).to receive(:add_index).
- with(:users, :foo, { algorithm: :concurrently, unique: true })
+ expect(model).to receive(:add_index)
+ .with(:users, :foo, { algorithm: :concurrently, unique: true })
model.add_concurrent_index(:users, :foo, unique: true)
end
@@ -75,8 +75,8 @@ describe Gitlab::Database::MigrationHelpers, lib: true do
it 'creates a regular index' do
expect(Gitlab::Database).to receive(:postgresql?).and_return(false)
- expect(model).to receive(:add_index).
- with(:users, :foo, {})
+ expect(model).to receive(:add_index)
+ .with(:users, :foo, {})
model.add_concurrent_index(:users, :foo)
end
@@ -87,8 +87,8 @@ describe Gitlab::Database::MigrationHelpers, lib: true do
it 'raises RuntimeError' do
expect(model).to receive(:transaction_open?).and_return(true)
- expect { model.add_concurrent_index(:users, :foo) }.
- to raise_error(RuntimeError)
+ expect { model.add_concurrent_index(:users, :foo) }
+ .to raise_error(RuntimeError)
end
end
end
@@ -106,15 +106,15 @@ describe Gitlab::Database::MigrationHelpers, lib: true do
end
it 'removes the index concurrently by column name' do
- expect(model).to receive(:remove_index).
- with(:users, { algorithm: :concurrently, column: :foo })
+ expect(model).to receive(:remove_index)
+ .with(:users, { algorithm: :concurrently, column: :foo })
model.remove_concurrent_index(:users, :foo)
end
it 'removes the index concurrently by index name' do
- expect(model).to receive(:remove_index).
- with(:users, { algorithm: :concurrently, name: "index_x_by_y" })
+ expect(model).to receive(:remove_index)
+ .with(:users, { algorithm: :concurrently, name: "index_x_by_y" })
model.remove_concurrent_index_by_name(:users, "index_x_by_y")
end
@@ -124,8 +124,8 @@ describe Gitlab::Database::MigrationHelpers, lib: true do
it 'removes an index' do
expect(Gitlab::Database).to receive(:postgresql?).and_return(false)
- expect(model).to receive(:remove_index).
- with(:users, { column: :foo })
+ expect(model).to receive(:remove_index)
+ .with(:users, { column: :foo })
model.remove_concurrent_index(:users, :foo)
end
@@ -136,8 +136,8 @@ describe Gitlab::Database::MigrationHelpers, lib: true do
it 'raises RuntimeError' do
expect(model).to receive(:transaction_open?).and_return(true)
- expect { model.remove_concurrent_index(:users, :foo) }.
- to raise_error(RuntimeError)
+ expect { model.remove_concurrent_index(:users, :foo) }
+ .to raise_error(RuntimeError)
end
end
end
@@ -162,8 +162,8 @@ describe Gitlab::Database::MigrationHelpers, lib: true do
it 'creates a regular foreign key' do
allow(Gitlab::Database).to receive(:mysql?).and_return(true)
- expect(model).to receive(:add_foreign_key).
- with(:projects, :users, column: :user_id, on_delete: :cascade)
+ expect(model).to receive(:add_foreign_key)
+ .with(:projects, :users, column: :user_id, on_delete: :cascade)
model.add_concurrent_foreign_key(:projects, :users, column: :user_id)
end
@@ -307,16 +307,16 @@ describe Gitlab::Database::MigrationHelpers, lib: true do
expect(model).to receive(:transaction).and_yield
- expect(model).to receive(:add_column).
- with(:projects, :foo, :integer, default: nil)
+ expect(model).to receive(:add_column)
+ .with(:projects, :foo, :integer, default: nil)
- expect(model).to receive(:change_column_default).
- with(:projects, :foo, 10)
+ expect(model).to receive(:change_column_default)
+ .with(:projects, :foo, 10)
end
it 'adds the column while allowing NULL values' do
- expect(model).to receive(:update_column_in_batches).
- with(:projects, :foo, 10)
+ expect(model).to receive(:update_column_in_batches)
+ .with(:projects, :foo, 10)
expect(model).not_to receive(:change_column_null)
@@ -326,22 +326,22 @@ describe Gitlab::Database::MigrationHelpers, lib: true do
end
it 'adds the column while not allowing NULL values' do
- expect(model).to receive(:update_column_in_batches).
- with(:projects, :foo, 10)
+ expect(model).to receive(:update_column_in_batches)
+ .with(:projects, :foo, 10)
- expect(model).to receive(:change_column_null).
- with(:projects, :foo, false)
+ expect(model).to receive(:change_column_null)
+ .with(:projects, :foo, false)
model.add_column_with_default(:projects, :foo, :integer, default: 10)
end
it 'removes the added column whenever updating the rows fails' do
- expect(model).to receive(:update_column_in_batches).
- with(:projects, :foo, 10).
- and_raise(RuntimeError)
+ expect(model).to receive(:update_column_in_batches)
+ .with(:projects, :foo, 10)
+ .and_raise(RuntimeError)
- expect(model).to receive(:remove_column).
- with(:projects, :foo)
+ expect(model).to receive(:remove_column)
+ .with(:projects, :foo)
expect do
model.add_column_with_default(:projects, :foo, :integer, default: 10)
@@ -349,12 +349,12 @@ describe Gitlab::Database::MigrationHelpers, lib: true do
end
it 'removes the added column whenever changing a column NULL constraint fails' do
- expect(model).to receive(:change_column_null).
- with(:projects, :foo, false).
- and_raise(RuntimeError)
+ expect(model).to receive(:change_column_null)
+ .with(:projects, :foo, false)
+ .and_raise(RuntimeError)
- expect(model).to receive(:remove_column).
- with(:projects, :foo)
+ expect(model).to receive(:remove_column)
+ .with(:projects, :foo)
expect do
model.add_column_with_default(:projects, :foo, :integer, default: 10)
@@ -370,8 +370,8 @@ describe Gitlab::Database::MigrationHelpers, lib: true do
allow(model).to receive(:change_column_null).with(:projects, :foo, false)
allow(model).to receive(:change_column_default).with(:projects, :foo, 10)
- expect(model).to receive(:add_column).
- with(:projects, :foo, :integer, default: nil, limit: 8)
+ expect(model).to receive(:add_column)
+ .with(:projects, :foo, :integer, default: nil, limit: 8)
model.add_column_with_default(:projects, :foo, :integer, default: 10, limit: 8)
end
@@ -394,8 +394,8 @@ describe Gitlab::Database::MigrationHelpers, lib: true do
it 'raises RuntimeError' do
allow(model).to receive(:transaction_open?).and_return(true)
- expect { model.rename_column_concurrently(:users, :old, :new) }.
- to raise_error(RuntimeError)
+ expect { model.rename_column_concurrently(:users, :old, :new) }
+ .to raise_error(RuntimeError)
end
end
@@ -426,17 +426,17 @@ describe Gitlab::Database::MigrationHelpers, lib: true do
it 'renames a column concurrently' do
allow(Gitlab::Database).to receive(:postgresql?).and_return(false)
- expect(model).to receive(:install_rename_triggers_for_mysql).
- with(trigger_name, 'users', 'old', 'new')
+ expect(model).to receive(:install_rename_triggers_for_mysql)
+ .with(trigger_name, 'users', 'old', 'new')
- expect(model).to receive(:add_column).
- with(:users, :new, :integer,
+ expect(model).to receive(:add_column)
+ .with(:users, :new, :integer,
limit: old_column.limit,
precision: old_column.precision,
scale: old_column.scale)
- expect(model).to receive(:change_column_default).
- with(:users, :new, old_column.default)
+ expect(model).to receive(:change_column_default)
+ .with(:users, :new, old_column.default)
expect(model).to receive(:update_column_in_batches)
@@ -453,17 +453,17 @@ describe Gitlab::Database::MigrationHelpers, lib: true do
it 'renames a column concurrently' do
allow(Gitlab::Database).to receive(:postgresql?).and_return(true)
- expect(model).to receive(:install_rename_triggers_for_postgresql).
- with(trigger_name, 'users', 'old', 'new')
+ expect(model).to receive(:install_rename_triggers_for_postgresql)
+ .with(trigger_name, 'users', 'old', 'new')
- expect(model).to receive(:add_column).
- with(:users, :new, :integer,
+ expect(model).to receive(:add_column)
+ .with(:users, :new, :integer,
limit: old_column.limit,
precision: old_column.precision,
scale: old_column.scale)
- expect(model).to receive(:change_column_default).
- with(:users, :new, old_column.default)
+ expect(model).to receive(:change_column_default)
+ .with(:users, :new, old_column.default)
expect(model).to receive(:update_column_in_batches)
@@ -482,8 +482,8 @@ describe Gitlab::Database::MigrationHelpers, lib: true do
it 'cleans up the renaming procedure for PostgreSQL' do
allow(Gitlab::Database).to receive(:postgresql?).and_return(true)
- expect(model).to receive(:remove_rename_triggers_for_postgresql).
- with(:users, /trigger_.{12}/)
+ expect(model).to receive(:remove_rename_triggers_for_postgresql)
+ .with(:users, /trigger_.{12}/)
expect(model).to receive(:remove_column).with(:users, :old)
@@ -493,8 +493,8 @@ describe Gitlab::Database::MigrationHelpers, lib: true do
it 'cleans up the renaming procedure for MySQL' do
allow(Gitlab::Database).to receive(:postgresql?).and_return(false)
- expect(model).to receive(:remove_rename_triggers_for_mysql).
- with(/trigger_.{12}/)
+ expect(model).to receive(:remove_rename_triggers_for_mysql)
+ .with(/trigger_.{12}/)
expect(model).to receive(:remove_column).with(:users, :old)
@@ -504,8 +504,8 @@ describe Gitlab::Database::MigrationHelpers, lib: true do
describe '#change_column_type_concurrently' do
it 'changes the column type' do
- expect(model).to receive(:rename_column_concurrently).
- with('users', 'username', 'username_for_type_change', type: :text)
+ expect(model).to receive(:rename_column_concurrently)
+ .with('users', 'username', 'username_for_type_change', type: :text)
model.change_column_type_concurrently('users', 'username', :text)
end
@@ -513,11 +513,11 @@ describe Gitlab::Database::MigrationHelpers, lib: true do
describe '#cleanup_concurrent_column_type_change' do
it 'cleans up the type changing procedure' do
- expect(model).to receive(:cleanup_concurrent_column_rename).
- with('users', 'username', 'username_for_type_change')
+ expect(model).to receive(:cleanup_concurrent_column_rename)
+ .with('users', 'username', 'username_for_type_change')
- expect(model).to receive(:rename_column).
- with('users', 'username_for_type_change', 'username')
+ expect(model).to receive(:rename_column)
+ .with('users', 'username_for_type_change', 'username')
model.cleanup_concurrent_column_type_change('users', 'username')
end
@@ -525,11 +525,11 @@ describe Gitlab::Database::MigrationHelpers, lib: true do
describe '#install_rename_triggers_for_postgresql' do
it 'installs the triggers for PostgreSQL' do
- expect(model).to receive(:execute).
- with(/CREATE OR REPLACE FUNCTION foo()/m)
+ expect(model).to receive(:execute)
+ .with(/CREATE OR REPLACE FUNCTION foo()/m)
- expect(model).to receive(:execute).
- with(/CREATE TRIGGER foo/m)
+ expect(model).to receive(:execute)
+ .with(/CREATE TRIGGER foo/m)
model.install_rename_triggers_for_postgresql('foo', :users, :old, :new)
end
@@ -537,11 +537,11 @@ describe Gitlab::Database::MigrationHelpers, lib: true do
describe '#install_rename_triggers_for_mysql' do
it 'installs the triggers for MySQL' do
- expect(model).to receive(:execute).
- with(/CREATE TRIGGER foo_insert.+ON users/m)
+ expect(model).to receive(:execute)
+ .with(/CREATE TRIGGER foo_insert.+ON users/m)
- expect(model).to receive(:execute).
- with(/CREATE TRIGGER foo_update.+ON users/m)
+ expect(model).to receive(:execute)
+ .with(/CREATE TRIGGER foo_update.+ON users/m)
model.install_rename_triggers_for_mysql('foo', :users, :old, :new)
end
@@ -567,8 +567,8 @@ describe Gitlab::Database::MigrationHelpers, lib: true do
describe '#rename_trigger_name' do
it 'returns a String' do
- expect(model.rename_trigger_name(:users, :foo, :bar)).
- to match(/trigger_.{12}/)
+ expect(model.rename_trigger_name(:users, :foo, :bar))
+ .to match(/trigger_.{12}/)
end
end
@@ -607,11 +607,11 @@ describe Gitlab::Database::MigrationHelpers, lib: true do
lengths: [],
orders: [])
- allow(model).to receive(:indexes_for).with(:issues, 'project_id').
- and_return([index])
+ allow(model).to receive(:indexes_for).with(:issues, 'project_id')
+ .and_return([index])
- expect(model).to receive(:add_concurrent_index).
- with(:issues,
+ expect(model).to receive(:add_concurrent_index)
+ .with(:issues,
%w(gl_project_id),
unique: false,
name: 'index_on_issues_gl_project_id',
@@ -634,11 +634,11 @@ describe Gitlab::Database::MigrationHelpers, lib: true do
lengths: [],
orders: [])
- allow(model).to receive(:indexes_for).with(:issues, 'project_id').
- and_return([index])
+ allow(model).to receive(:indexes_for).with(:issues, 'project_id')
+ .and_return([index])
- expect(model).to receive(:add_concurrent_index).
- with(:issues,
+ expect(model).to receive(:add_concurrent_index)
+ .with(:issues,
%w(gl_project_id foobar),
unique: false,
name: 'index_on_issues_gl_project_id_foobar',
@@ -661,11 +661,11 @@ describe Gitlab::Database::MigrationHelpers, lib: true do
lengths: [],
orders: [])
- allow(model).to receive(:indexes_for).with(:issues, 'project_id').
- and_return([index])
+ allow(model).to receive(:indexes_for).with(:issues, 'project_id')
+ .and_return([index])
- expect(model).to receive(:add_concurrent_index).
- with(:issues,
+ expect(model).to receive(:add_concurrent_index)
+ .with(:issues,
%w(gl_project_id),
unique: false,
name: 'index_on_issues_gl_project_id',
@@ -689,11 +689,11 @@ describe Gitlab::Database::MigrationHelpers, lib: true do
lengths: [],
orders: [])
- allow(model).to receive(:indexes_for).with(:issues, 'project_id').
- and_return([index])
+ allow(model).to receive(:indexes_for).with(:issues, 'project_id')
+ .and_return([index])
- expect(model).to receive(:add_concurrent_index).
- with(:issues,
+ expect(model).to receive(:add_concurrent_index)
+ .with(:issues,
%w(gl_project_id),
unique: false,
name: 'index_on_issues_gl_project_id',
@@ -717,11 +717,11 @@ describe Gitlab::Database::MigrationHelpers, lib: true do
lengths: [],
orders: [])
- allow(model).to receive(:indexes_for).with(:issues, 'project_id').
- and_return([index])
+ allow(model).to receive(:indexes_for).with(:issues, 'project_id')
+ .and_return([index])
- expect(model).to receive(:add_concurrent_index).
- with(:issues,
+ expect(model).to receive(:add_concurrent_index)
+ .with(:issues,
%w(gl_project_id),
unique: false,
name: 'index_on_issues_gl_project_id',
@@ -745,11 +745,11 @@ describe Gitlab::Database::MigrationHelpers, lib: true do
lengths: [],
orders: [])
- allow(model).to receive(:indexes_for).with(:issues, 'project_id').
- and_return([index])
+ allow(model).to receive(:indexes_for).with(:issues, 'project_id')
+ .and_return([index])
- expect { model.copy_indexes(:issues, :project_id, :gl_project_id) }.
- to raise_error(RuntimeError)
+ expect { model.copy_indexes(:issues, :project_id, :gl_project_id) }
+ .to raise_error(RuntimeError)
end
end
end
@@ -761,11 +761,11 @@ describe Gitlab::Database::MigrationHelpers, lib: true do
to_table: 'projects',
on_delete: :cascade)
- allow(model).to receive(:foreign_keys_for).with(:issues, :project_id).
- and_return([fk])
+ allow(model).to receive(:foreign_keys_for).with(:issues, :project_id)
+ .and_return([fk])
- expect(model).to receive(:add_concurrent_foreign_key).
- with('issues', 'projects', column: :gl_project_id, on_delete: :cascade)
+ expect(model).to receive(:add_concurrent_foreign_key)
+ .with('issues', 'projects', column: :gl_project_id, on_delete: :cascade)
model.copy_foreign_keys(:issues, :project_id, :gl_project_id)
end
@@ -790,8 +790,8 @@ describe Gitlab::Database::MigrationHelpers, lib: true do
end
it 'builds the sql with correct functions' do
- expect(model.replace_sql(Arel::Table.new(:users)[:first_name], "Alice", "Eve").to_s).
- to include('regexp_replace')
+ expect(model.replace_sql(Arel::Table.new(:users)[:first_name], "Alice", "Eve").to_s)
+ .to include('regexp_replace')
end
end
@@ -801,8 +801,8 @@ describe Gitlab::Database::MigrationHelpers, lib: true do
end
it 'builds the sql with the correct functions' do
- expect(model.replace_sql(Arel::Table.new(:users)[:first_name], "Alice", "Eve").to_s).
- to include('locate', 'insert')
+ expect(model.replace_sql(Arel::Table.new(:users)[:first_name], "Alice", "Eve").to_s)
+ .to include('locate', 'insert')
end
end
diff --git a/spec/lib/gitlab/database/rename_reserved_paths_migration/v1/rename_namespaces_spec.rb b/spec/lib/gitlab/database/rename_reserved_paths_migration/v1/rename_namespaces_spec.rb
index ce2b5d620fd..aa63f6f9805 100644
--- a/spec/lib/gitlab/database/rename_reserved_paths_migration/v1/rename_namespaces_spec.rb
+++ b/spec/lib/gitlab/database/rename_reserved_paths_migration/v1/rename_namespaces_spec.rb
@@ -21,8 +21,8 @@ describe Gitlab::Database::RenameReservedPathsMigration::V1::RenameNamespaces do
parent = create(:group, path: 'parent')
child = create(:group, path: 'the-path', parent: parent)
- found_ids = subject.namespaces_for_paths(type: :child).
- map(&:id)
+ found_ids = subject.namespaces_for_paths(type: :child)
+ .map(&:id)
expect(found_ids).to contain_exactly(child.id)
end
@@ -38,8 +38,8 @@ describe Gitlab::Database::RenameReservedPathsMigration::V1::RenameNamespaces do
path: 'the-path',
parent: create(:group))
- found_ids = subject.namespaces_for_paths(type: :child).
- map(&:id)
+ found_ids = subject.namespaces_for_paths(type: :child)
+ .map(&:id)
expect(found_ids).to contain_exactly(namespace.id)
end
@@ -53,8 +53,8 @@ describe Gitlab::Database::RenameReservedPathsMigration::V1::RenameNamespaces do
path: 'the-path',
parent: create(:group))
- found_ids = subject.namespaces_for_paths(type: :child).
- map(&:id)
+ found_ids = subject.namespaces_for_paths(type: :child)
+ .map(&:id)
expect(found_ids).to contain_exactly(namespace.id)
end
@@ -68,8 +68,8 @@ describe Gitlab::Database::RenameReservedPathsMigration::V1::RenameNamespaces do
path: 'the-path',
parent: create(:group))
- found_ids = subject.namespaces_for_paths(type: :top_level).
- map(&:id)
+ found_ids = subject.namespaces_for_paths(type: :top_level)
+ .map(&:id)
expect(found_ids).to contain_exactly(root_namespace.id)
end
@@ -81,8 +81,8 @@ describe Gitlab::Database::RenameReservedPathsMigration::V1::RenameNamespaces do
path: 'the-path',
parent: create(:group))
- found_ids = subject.namespaces_for_paths(type: :top_level).
- map(&:id)
+ found_ids = subject.namespaces_for_paths(type: :top_level)
+ .map(&:id)
expect(found_ids).to contain_exactly(root_namespace.id)
end
@@ -140,9 +140,9 @@ describe Gitlab::Database::RenameReservedPathsMigration::V1::RenameNamespaces do
let(:namespace) { create(:group, name: 'the-path') }
it 'renames paths & routes for the namespace' do
- expect(subject).to receive(:rename_path_for_routable).
- with(namespace).
- and_call_original
+ expect(subject).to receive(:rename_path_for_routable)
+ .with(namespace)
+ .and_call_original
subject.rename_namespace(namespace)
@@ -211,15 +211,15 @@ describe Gitlab::Database::RenameReservedPathsMigration::V1::RenameNamespaces do
end
it 'renames top level namespaces the namespace' do
- expect(subject).to receive(:rename_namespace).
- with(migration_namespace(top_level_namespace))
+ expect(subject).to receive(:rename_namespace)
+ .with(migration_namespace(top_level_namespace))
subject.rename_namespaces(type: :top_level)
end
it 'renames child namespaces' do
- expect(subject).to receive(:rename_namespace).
- with(migration_namespace(child_namespace))
+ expect(subject).to receive(:rename_namespace)
+ .with(migration_namespace(child_namespace))
subject.rename_namespaces(type: :child)
end
diff --git a/spec/lib/gitlab/database/rename_reserved_paths_migration/v1/rename_projects_spec.rb b/spec/lib/gitlab/database/rename_reserved_paths_migration/v1/rename_projects_spec.rb
index 59e8de2712d..9a6ed98898d 100644
--- a/spec/lib/gitlab/database/rename_reserved_paths_migration/v1/rename_projects_spec.rb
+++ b/spec/lib/gitlab/database/rename_reserved_paths_migration/v1/rename_projects_spec.rb
@@ -13,8 +13,8 @@ describe Gitlab::Database::RenameReservedPathsMigration::V1::RenameProjects do
namespace = create(:namespace, path: 'hello')
project = create(:empty_project, path: 'THE-path', namespace: namespace)
- result_ids = described_class.new(['Hello/the-path'], migration).
- projects_for_paths.map(&:id)
+ result_ids = described_class.new(['Hello/the-path'], migration)
+ .projects_for_paths.map(&:id)
expect(result_ids).to contain_exactly(project.id)
end
@@ -39,8 +39,8 @@ describe Gitlab::Database::RenameReservedPathsMigration::V1::RenameProjects do
end
it 'invalidates the markdown cache of related projects' do
- expect(subject).to receive(:remove_cached_html_for_projects).
- with(projects.map(&:id))
+ expect(subject).to receive(:remove_cached_html_for_projects)
+ .with(projects.map(&:id))
subject.rename_projects
end
@@ -54,9 +54,9 @@ describe Gitlab::Database::RenameReservedPathsMigration::V1::RenameProjects do
end
it 'renames path & route for the project' do
- expect(subject).to receive(:rename_path_for_routable).
- with(project).
- and_call_original
+ expect(subject).to receive(:rename_path_for_routable)
+ .with(project)
+ .and_call_original
subject.rename_project(project)
@@ -64,24 +64,24 @@ describe Gitlab::Database::RenameReservedPathsMigration::V1::RenameProjects do
end
it 'moves the wiki & the repo' do
- expect(subject).to receive(:move_repository).
- with(project, 'known-parent/the-path.wiki', 'known-parent/the-path0.wiki')
- expect(subject).to receive(:move_repository).
- with(project, 'known-parent/the-path', 'known-parent/the-path0')
+ expect(subject).to receive(:move_repository)
+ .with(project, 'known-parent/the-path.wiki', 'known-parent/the-path0.wiki')
+ expect(subject).to receive(:move_repository)
+ .with(project, 'known-parent/the-path', 'known-parent/the-path0')
subject.rename_project(project)
end
it 'moves uploads' do
- expect(subject).to receive(:move_uploads).
- with('known-parent/the-path', 'known-parent/the-path0')
+ expect(subject).to receive(:move_uploads)
+ .with('known-parent/the-path', 'known-parent/the-path0')
subject.rename_project(project)
end
it 'moves pages' do
- expect(subject).to receive(:move_pages).
- with('known-parent/the-path', 'known-parent/the-path0')
+ expect(subject).to receive(:move_pages)
+ .with('known-parent/the-path', 'known-parent/the-path0')
subject.rename_project(project)
end
diff --git a/spec/lib/gitlab/database/rename_reserved_paths_migration/v1_spec.rb b/spec/lib/gitlab/database/rename_reserved_paths_migration/v1_spec.rb
index f8cc1eb91ec..bdd3af4ad44 100644
--- a/spec/lib/gitlab/database/rename_reserved_paths_migration/v1_spec.rb
+++ b/spec/lib/gitlab/database/rename_reserved_paths_migration/v1_spec.rb
@@ -3,11 +3,11 @@ require 'spec_helper'
shared_examples 'renames child namespaces' do |type|
it 'renames namespaces' do
rename_namespaces = double
- expect(described_class::RenameNamespaces).
- to receive(:new).with(['first-path', 'second-path'], subject).
- and_return(rename_namespaces)
- expect(rename_namespaces).to receive(:rename_namespaces).
- with(type: :child)
+ expect(described_class::RenameNamespaces)
+ .to receive(:new).with(['first-path', 'second-path'], subject)
+ .and_return(rename_namespaces)
+ expect(rename_namespaces).to receive(:rename_namespaces)
+ .with(type: :child)
subject.rename_wildcard_paths(['first-path', 'second-path'])
end
@@ -29,9 +29,9 @@ describe Gitlab::Database::RenameReservedPathsMigration::V1 do
it 'should rename projects' do
rename_projects = double
- expect(described_class::RenameProjects).
- to receive(:new).with(['the-path'], subject).
- and_return(rename_projects)
+ expect(described_class::RenameProjects)
+ .to receive(:new).with(['the-path'], subject)
+ .and_return(rename_projects)
expect(rename_projects).to receive(:rename_projects)
@@ -42,11 +42,11 @@ describe Gitlab::Database::RenameReservedPathsMigration::V1 do
describe '#rename_root_paths' do
it 'should rename namespaces' do
rename_namespaces = double
- expect(described_class::RenameNamespaces).
- to receive(:new).with(['the-path'], subject).
- and_return(rename_namespaces)
- expect(rename_namespaces).to receive(:rename_namespaces).
- with(type: :top_level)
+ expect(described_class::RenameNamespaces)
+ .to receive(:new).with(['the-path'], subject)
+ .and_return(rename_namespaces)
+ expect(rename_namespaces).to receive(:rename_namespaces)
+ .with(type: :top_level)
subject.rename_root_paths('the-path')
end
diff --git a/spec/lib/gitlab/database_spec.rb b/spec/lib/gitlab/database_spec.rb
index 428b6edb7d6..5e6206b96c7 100644
--- a/spec/lib/gitlab/database_spec.rb
+++ b/spec/lib/gitlab/database_spec.rb
@@ -34,8 +34,8 @@ describe Gitlab::Database, lib: true do
describe '.version' do
context "on mysql" do
it "extracts the version number" do
- allow(described_class).to receive(:database_version).
- and_return("5.7.12-standard")
+ allow(described_class).to receive(:database_version)
+ .and_return("5.7.12-standard")
expect(described_class.version).to eq '5.7.12-standard'
end
@@ -43,8 +43,8 @@ describe Gitlab::Database, lib: true do
context "on postgresql" do
it "extracts the version number" do
- allow(described_class).to receive(:database_version).
- and_return("PostgreSQL 9.4.4 on x86_64-apple-darwin14.3.0")
+ allow(described_class).to receive(:database_version)
+ .and_return("PostgreSQL 9.4.4 on x86_64-apple-darwin14.3.0")
expect(described_class.version).to eq '9.4.4'
end
diff --git a/spec/lib/gitlab/downtime_check_spec.rb b/spec/lib/gitlab/downtime_check_spec.rb
index 42d895e548e..1f1e4e0216c 100644
--- a/spec/lib/gitlab/downtime_check_spec.rb
+++ b/spec/lib/gitlab/downtime_check_spec.rb
@@ -11,12 +11,12 @@ describe Gitlab::DowntimeCheck do
context 'when a migration does not specify if downtime is required' do
it 'raises RuntimeError' do
- expect(subject).to receive(:class_for_migration_file).
- with(path).
- and_return(Class.new)
+ expect(subject).to receive(:class_for_migration_file)
+ .with(path)
+ .and_return(Class.new)
- expect { subject.check([path]) }.
- to raise_error(RuntimeError, /it requires downtime/)
+ expect { subject.check([path]) }
+ .to raise_error(RuntimeError, /it requires downtime/)
end
end
@@ -25,12 +25,12 @@ describe Gitlab::DowntimeCheck do
it 'raises RuntimeError' do
stub_const('TestMigration::DOWNTIME', true)
- expect(subject).to receive(:class_for_migration_file).
- with(path).
- and_return(TestMigration)
+ expect(subject).to receive(:class_for_migration_file)
+ .with(path)
+ .and_return(TestMigration)
- expect { subject.check([path]) }.
- to raise_error(RuntimeError, /no reason was given/)
+ expect { subject.check([path]) }
+ .to raise_error(RuntimeError, /no reason was given/)
end
end
@@ -39,9 +39,9 @@ describe Gitlab::DowntimeCheck do
stub_const('TestMigration::DOWNTIME', true)
stub_const('TestMigration::DOWNTIME_REASON', 'foo')
- expect(subject).to receive(:class_for_migration_file).
- with(path).
- and_return(TestMigration)
+ expect(subject).to receive(:class_for_migration_file)
+ .with(path)
+ .and_return(TestMigration)
messages = subject.check([path])
@@ -65,9 +65,9 @@ describe Gitlab::DowntimeCheck do
expect(subject).to receive(:require).with(path)
- expect(subject).to receive(:class_for_migration_file).
- with(path).
- and_return(TestMigration)
+ expect(subject).to receive(:class_for_migration_file)
+ .with(path)
+ .and_return(TestMigration)
expect(subject).to receive(:puts).with(an_instance_of(String))
diff --git a/spec/lib/gitlab/email/reply_parser_spec.rb b/spec/lib/gitlab/email/reply_parser_spec.rb
index 71659d5e8b0..2ea5e6460a3 100644
--- a/spec/lib/gitlab/email/reply_parser_spec.rb
+++ b/spec/lib/gitlab/email/reply_parser_spec.rb
@@ -20,8 +20,8 @@ describe Gitlab::Email::ReplyParser, lib: true do
end
it "properly renders plaintext-only email" do
- expect(test_parse_body(fixture_file("emails/plaintext_only.eml"))).
- to eq(
+ expect(test_parse_body(fixture_file("emails/plaintext_only.eml")))
+ .to eq(
<<-BODY.strip_heredoc.chomp
### reply from default mail client in Windows 8.1 Metro
@@ -46,8 +46,8 @@ describe Gitlab::Email::ReplyParser, lib: true do
end
it "handles multiple paragraphs" do
- expect(test_parse_body(fixture_file("emails/paragraphs.eml"))).
- to eq(
+ expect(test_parse_body(fixture_file("emails/paragraphs.eml")))
+ .to eq(
<<-BODY.strip_heredoc.chomp
Is there any reason the *old* candy can't be be kept in silos while the new candy
is imported into *new* silos?
@@ -61,8 +61,8 @@ describe Gitlab::Email::ReplyParser, lib: true do
end
it "handles multiple paragraphs when parsing html" do
- expect(test_parse_body(fixture_file("emails/html_paragraphs.eml"))).
- to eq(
+ expect(test_parse_body(fixture_file("emails/html_paragraphs.eml")))
+ .to eq(
<<-BODY.strip_heredoc.chomp
Awesome!
@@ -74,8 +74,8 @@ describe Gitlab::Email::ReplyParser, lib: true do
end
it "handles newlines" do
- expect(test_parse_body(fixture_file("emails/newlines.eml"))).
- to eq(
+ expect(test_parse_body(fixture_file("emails/newlines.eml")))
+ .to eq(
<<-BODY.strip_heredoc.chomp
This is my reply.
It is my best reply.
@@ -85,8 +85,8 @@ describe Gitlab::Email::ReplyParser, lib: true do
end
it "handles inline reply" do
- expect(test_parse_body(fixture_file("emails/inline_reply.eml"))).
- to eq(
+ expect(test_parse_body(fixture_file("emails/inline_reply.eml")))
+ .to eq(
<<-BODY.strip_heredoc.chomp
> techAPJ <https://meta.discourse.org/users/techapj>
> November 28
@@ -132,8 +132,8 @@ describe Gitlab::Email::ReplyParser, lib: true do
end
it "properly renders email reply from gmail web client" do
- expect(test_parse_body(fixture_file("emails/gmail_web.eml"))).
- to eq(
+ expect(test_parse_body(fixture_file("emails/gmail_web.eml")))
+ .to eq(
<<-BODY.strip_heredoc.chomp
### This is a reply from standard GMail in Google Chrome.
@@ -151,8 +151,8 @@ describe Gitlab::Email::ReplyParser, lib: true do
end
it "properly renders email reply from iOS default mail client" do
- expect(test_parse_body(fixture_file("emails/ios_default.eml"))).
- to eq(
+ expect(test_parse_body(fixture_file("emails/ios_default.eml")))
+ .to eq(
<<-BODY.strip_heredoc.chomp
### this is a reply from iOS default mail
@@ -166,8 +166,8 @@ describe Gitlab::Email::ReplyParser, lib: true do
end
it "properly renders email reply from Android 5 gmail client" do
- expect(test_parse_body(fixture_file("emails/android_gmail.eml"))).
- to eq(
+ expect(test_parse_body(fixture_file("emails/android_gmail.eml")))
+ .to eq(
<<-BODY.strip_heredoc.chomp
### this is a reply from Android 5 gmail
@@ -184,8 +184,8 @@ describe Gitlab::Email::ReplyParser, lib: true do
end
it "properly renders email reply from Windows 8.1 Metro default mail client" do
- expect(test_parse_body(fixture_file("emails/windows_8_metro.eml"))).
- to eq(
+ expect(test_parse_body(fixture_file("emails/windows_8_metro.eml")))
+ .to eq(
<<-BODY.strip_heredoc.chomp
### reply from default mail client in Windows 8.1 Metro
diff --git a/spec/lib/gitlab/etag_caching/middleware_spec.rb b/spec/lib/gitlab/etag_caching/middleware_spec.rb
index 4acf4f047f1..4a54d641b4e 100644
--- a/spec/lib/gitlab/etag_caching/middleware_spec.rb
+++ b/spec/lib/gitlab/etag_caching/middleware_spec.rb
@@ -108,8 +108,8 @@ describe Gitlab::EtagCaching::Middleware do
context 'when polling is disabled' do
before do
- allow(Gitlab::PollingInterval).to receive(:polling_enabled?).
- and_return(false)
+ allow(Gitlab::PollingInterval).to receive(:polling_enabled?)
+ .and_return(false)
end
it 'returns status code 429' do
diff --git a/spec/lib/gitlab/file_detector_spec.rb b/spec/lib/gitlab/file_detector_spec.rb
index e5ba13bbaf8..695fd6f8573 100644
--- a/spec/lib/gitlab/file_detector_spec.rb
+++ b/spec/lib/gitlab/file_detector_spec.rb
@@ -3,13 +3,13 @@ require 'spec_helper'
describe Gitlab::FileDetector do
describe '.types_in_paths' do
it 'returns the file types for the given paths' do
- expect(described_class.types_in_paths(%w(README.md CHANGELOG VERSION VERSION))).
- to eq(%i{readme changelog version})
+ expect(described_class.types_in_paths(%w(README.md CHANGELOG VERSION VERSION)))
+ .to eq(%i{readme changelog version})
end
it 'does not include unrecognized file paths' do
- expect(described_class.types_in_paths(%w(README.md foo.txt))).
- to eq(%i{readme})
+ expect(described_class.types_in_paths(%w(README.md foo.txt)))
+ .to eq(%i{readme})
end
end
diff --git a/spec/lib/gitlab/git/attributes_spec.rb b/spec/lib/gitlab/git/attributes_spec.rb
index 1cfd8db09a5..b715fc3410a 100644
--- a/spec/lib/gitlab/git/attributes_spec.rb
+++ b/spec/lib/gitlab/git/attributes_spec.rb
@@ -14,13 +14,13 @@ describe Gitlab::Git::Attributes, seed_helper: true do
end
it 'returns a Hash containing multiple attributes' do
- expect(subject.attributes('test.sh')).
- to eq({ 'eol' => 'lf', 'gitlab-language' => 'shell' })
+ expect(subject.attributes('test.sh'))
+ .to eq({ 'eol' => 'lf', 'gitlab-language' => 'shell' })
end
it 'returns a Hash containing attributes for a file with multiple extensions' do
- expect(subject.attributes('test.haml.html')).
- to eq({ 'gitlab-language' => 'haml' })
+ expect(subject.attributes('test.haml.html'))
+ .to eq({ 'gitlab-language' => 'haml' })
end
it 'returns a Hash containing attributes for a file in a directory' do
@@ -28,8 +28,8 @@ describe Gitlab::Git::Attributes, seed_helper: true do
end
it 'returns a Hash containing attributes with query string parameters' do
- expect(subject.attributes('foo.cgi')).
- to eq({ 'key' => 'value?p1=v1&p2=v2' })
+ expect(subject.attributes('foo.cgi'))
+ .to eq({ 'key' => 'value?p1=v1&p2=v2' })
end
it 'returns a Hash containing the attributes for an absolute path' do
@@ -39,11 +39,11 @@ describe Gitlab::Git::Attributes, seed_helper: true do
it 'returns a Hash containing the attributes when a pattern is defined using an absolute path' do
# When a path is given without a leading slash it should still match
# patterns defined with a leading slash.
- expect(subject.attributes('foo.png')).
- to eq({ 'gitlab-language' => 'png' })
+ expect(subject.attributes('foo.png'))
+ .to eq({ 'gitlab-language' => 'png' })
- expect(subject.attributes('/foo.png')).
- to eq({ 'gitlab-language' => 'png' })
+ expect(subject.attributes('/foo.png'))
+ .to eq({ 'gitlab-language' => 'png' })
end
it 'returns an empty Hash for a defined path without attributes' do
@@ -74,8 +74,8 @@ describe Gitlab::Git::Attributes, seed_helper: true do
end
it 'parses an entry that uses a tab to separate the pattern and attributes' do
- expect(subject.patterns[File.join(path, '*.md')]).
- to eq({ 'gitlab-language' => 'markdown' })
+ expect(subject.patterns[File.join(path, '*.md')])
+ .to eq({ 'gitlab-language' => 'markdown' })
end
it 'stores patterns in reverse order' do
@@ -91,9 +91,9 @@ describe Gitlab::Git::Attributes, seed_helper: true do
end
it 'does not parse anything when the attributes file does not exist' do
- expect(File).to receive(:exist?).
- with(File.join(path, 'info/attributes')).
- and_return(false)
+ expect(File).to receive(:exist?)
+ .with(File.join(path, 'info/attributes'))
+ .and_return(false)
expect(subject.patterns).to eq({})
end
@@ -115,13 +115,13 @@ describe Gitlab::Git::Attributes, seed_helper: true do
it 'parses multiple attributes' do
input = 'boolean key=value -negated'
- expect(subject.parse_attributes(input)).
- to eq({ 'boolean' => true, 'key' => 'value', 'negated' => false })
+ expect(subject.parse_attributes(input))
+ .to eq({ 'boolean' => true, 'key' => 'value', 'negated' => false })
end
it 'parses attributes with query string parameters' do
- expect(subject.parse_attributes('foo=bar?baz=1')).
- to eq({ 'foo' => 'bar?baz=1' })
+ expect(subject.parse_attributes('foo=bar?baz=1'))
+ .to eq({ 'foo' => 'bar?baz=1' })
end
end
@@ -133,9 +133,9 @@ describe Gitlab::Git::Attributes, seed_helper: true do
end
it 'does not yield when the attributes file does not exist' do
- expect(File).to receive(:exist?).
- with(File.join(path, 'info/attributes')).
- and_return(false)
+ expect(File).to receive(:exist?)
+ .with(File.join(path, 'info/attributes'))
+ .and_return(false)
expect { |b| subject.each_line(&b) }.not_to yield_control
end
diff --git a/spec/lib/gitlab/git/blob_spec.rb b/spec/lib/gitlab/git/blob_spec.rb
index e6a07a58d73..58d3ee6b488 100644
--- a/spec/lib/gitlab/git/blob_spec.rb
+++ b/spec/lib/gitlab/git/blob_spec.rb
@@ -15,7 +15,7 @@ describe Gitlab::Git::Blob, seed_helper: true do
end
end
- describe '.find' do
+ shared_examples 'finding blobs' do
context 'file in subdir' do
let(:blob) { Gitlab::Git::Blob.find(repository, SeedRepo::Commit::ID, "files/ruby/popen.rb") }
@@ -92,15 +92,25 @@ describe Gitlab::Git::Blob, seed_helper: true do
end
it 'marks the blob as binary' do
- expect(Gitlab::Git::Blob).to receive(:new).
- with(hash_including(binary: true)).
- and_call_original
+ expect(Gitlab::Git::Blob).to receive(:new)
+ .with(hash_including(binary: true))
+ .and_call_original
expect(blob).to be_binary
end
end
end
+ describe '.find' do
+ context 'when project_raw_show Gitaly feature is enabled' do
+ it_behaves_like 'finding blobs'
+ end
+
+ context 'when project_raw_show Gitaly feature is disabled', skip_gitaly_mock: true do
+ it_behaves_like 'finding blobs'
+ end
+ end
+
describe '.raw' do
let(:raw_blob) { Gitlab::Git::Blob.raw(repository, SeedRepo::RubyBlob::ID) }
it { expect(raw_blob.id).to eq(SeedRepo::RubyBlob::ID) }
diff --git a/spec/lib/gitlab/git/branch_spec.rb b/spec/lib/gitlab/git/branch_spec.rb
index 9eac7660cd1..9dba4397e79 100644
--- a/spec/lib/gitlab/git/branch_spec.rb
+++ b/spec/lib/gitlab/git/branch_spec.rb
@@ -45,8 +45,8 @@ describe Gitlab::Git::Branch, seed_helper: true do
let(:branch) { described_class.new(repository, 'foo', gitaly_branch) }
it 'parses Gitaly::FindLocalBranchResponse correctly' do
- expect(Gitlab::Git::Commit).to receive(:decorate).
- with(hash_including(attributes)).and_call_original
+ expect(Gitlab::Git::Commit).to receive(:decorate)
+ .with(hash_including(attributes)).and_call_original
expect(branch.dereferenced_target.message.encoding).to be(Encoding::UTF_8)
end
diff --git a/spec/lib/gitlab/git/diff_collection_spec.rb b/spec/lib/gitlab/git/diff_collection_spec.rb
index a9a7bba2c05..d20298fa139 100644
--- a/spec/lib/gitlab/git/diff_collection_spec.rb
+++ b/spec/lib/gitlab/git/diff_collection_spec.rb
@@ -325,8 +325,8 @@ describe Gitlab::Git::DiffCollection, seed_helper: true do
end
it 'yields Diff instances even when they are too large' do
- expect { |b| collection.each(&b) }.
- to yield_with_args(an_instance_of(Gitlab::Git::Diff))
+ expect { |b| collection.each(&b) }
+ .to yield_with_args(an_instance_of(Gitlab::Git::Diff))
end
it 'prunes diffs that are too large' do
@@ -348,8 +348,8 @@ describe Gitlab::Git::DiffCollection, seed_helper: true do
let(:expanded) { true }
it 'yields Diff instances even when they are quite big' do
- expect { |b| subject.each(&b) }.
- to yield_with_args(an_instance_of(Gitlab::Git::Diff))
+ expect { |b| subject.each(&b) }
+ .to yield_with_args(an_instance_of(Gitlab::Git::Diff))
end
it 'does not prune diffs' do
@@ -367,8 +367,8 @@ describe Gitlab::Git::DiffCollection, seed_helper: true do
let(:expanded) { false }
it 'yields Diff instances even when they are quite big' do
- expect { |b| subject.each(&b) }.
- to yield_with_args(an_instance_of(Gitlab::Git::Diff))
+ expect { |b| subject.each(&b) }
+ .to yield_with_args(an_instance_of(Gitlab::Git::Diff))
end
it 'prunes diffs that are quite big' do
@@ -454,8 +454,8 @@ describe Gitlab::Git::DiffCollection, seed_helper: true do
let(:limits) { false }
it 'yields Diff instances even when they are quite big' do
- expect { |b| subject.each(&b) }.
- to yield_with_args(an_instance_of(Gitlab::Git::Diff))
+ expect { |b| subject.each(&b) }
+ .to yield_with_args(an_instance_of(Gitlab::Git::Diff))
end
it 'does not prune diffs' do
diff --git a/spec/lib/gitlab/git/diff_spec.rb b/spec/lib/gitlab/git/diff_spec.rb
index 78d741f0110..d50ccb0df30 100644
--- a/spec/lib/gitlab/git/diff_spec.rb
+++ b/spec/lib/gitlab/git/diff_spec.rb
@@ -100,8 +100,8 @@ EOT
context 'using a diff that is too large' do
it 'prunes the diff' do
- expect_any_instance_of(String).to receive(:bytesize).
- and_return(1024 * 1024 * 1024)
+ expect_any_instance_of(String).to receive(:bytesize)
+ .and_return(1024 * 1024 * 1024)
diff = described_class.new(@rugged_diff)
@@ -130,8 +130,8 @@ EOT
context 'using a large binary diff' do
it 'does not prune the diff' do
- expect_any_instance_of(Rugged::Diff::Delta).to receive(:binary?).
- and_return(true)
+ expect_any_instance_of(Rugged::Diff::Delta).to receive(:binary?)
+ .and_return(true)
diff = described_class.new(@rugged_diff)
@@ -175,6 +175,14 @@ EOT
expect(diff).to be_too_large
end
end
+
+ context 'when the patch passed is not UTF-8-encoded' do
+ let(:raw_patch) { @raw_diff_hash[:diff].encode(Encoding::ASCII_8BIT) }
+
+ it 'encodes diff patch to UTF-8' do
+ expect(diff.diff.encoding).to eq(Encoding::UTF_8)
+ end
+ end
end
end
diff --git a/spec/lib/gitlab/git/repository_spec.rb b/spec/lib/gitlab/git/repository_spec.rb
index 02b3f167250..703b0c2c202 100644
--- a/spec/lib/gitlab/git/repository_spec.rb
+++ b/spec/lib/gitlab/git/repository_spec.rb
@@ -41,14 +41,14 @@ describe Gitlab::Git::Repository, seed_helper: true do
end
it 'wraps GRPC not found' do
- expect_any_instance_of(Gitlab::GitalyClient::Ref).to receive(:default_branch_name).
- and_raise(GRPC::NotFound)
+ expect_any_instance_of(Gitlab::GitalyClient::Ref).to receive(:default_branch_name)
+ .and_raise(GRPC::NotFound)
expect { repository.root_ref }.to raise_error(Gitlab::Git::Repository::NoRepository)
end
it 'wraps GRPC exceptions' do
- expect_any_instance_of(Gitlab::GitalyClient::Ref).to receive(:default_branch_name).
- and_raise(GRPC::Unknown)
+ expect_any_instance_of(Gitlab::GitalyClient::Ref).to receive(:default_branch_name)
+ .and_raise(GRPC::Unknown)
expect { repository.root_ref }.to raise_error(Gitlab::Git::CommandError)
end
end
@@ -141,14 +141,14 @@ describe Gitlab::Git::Repository, seed_helper: true do
end
it 'wraps GRPC not found' do
- expect_any_instance_of(Gitlab::GitalyClient::Ref).to receive(:branch_names).
- and_raise(GRPC::NotFound)
+ expect_any_instance_of(Gitlab::GitalyClient::Ref).to receive(:branch_names)
+ .and_raise(GRPC::NotFound)
expect { subject }.to raise_error(Gitlab::Git::Repository::NoRepository)
end
it 'wraps GRPC other exceptions' do
- expect_any_instance_of(Gitlab::GitalyClient::Ref).to receive(:branch_names).
- and_raise(GRPC::Unknown)
+ expect_any_instance_of(Gitlab::GitalyClient::Ref).to receive(:branch_names)
+ .and_raise(GRPC::Unknown)
expect { subject }.to raise_error(Gitlab::Git::CommandError)
end
end
@@ -184,14 +184,14 @@ describe Gitlab::Git::Repository, seed_helper: true do
end
it 'wraps GRPC not found' do
- expect_any_instance_of(Gitlab::GitalyClient::Ref).to receive(:tag_names).
- and_raise(GRPC::NotFound)
+ expect_any_instance_of(Gitlab::GitalyClient::Ref).to receive(:tag_names)
+ .and_raise(GRPC::NotFound)
expect { subject }.to raise_error(Gitlab::Git::Repository::NoRepository)
end
it 'wraps GRPC exceptions' do
- expect_any_instance_of(Gitlab::GitalyClient::Ref).to receive(:tag_names).
- and_raise(GRPC::Unknown)
+ expect_any_instance_of(Gitlab::GitalyClient::Ref).to receive(:tag_names)
+ .and_raise(GRPC::Unknown)
expect { subject }.to raise_error(Gitlab::Git::CommandError)
end
end
@@ -472,8 +472,8 @@ describe Gitlab::Git::Repository, seed_helper: true do
end
it "should move the tip of the master branch to the correct commit" do
- new_tip = @normal_repo.rugged.references["refs/heads/master"].
- target.oid
+ new_tip = @normal_repo.rugged.references["refs/heads/master"]
+ .target.oid
expect(new_tip).to eq(reset_commit)
end
@@ -1306,20 +1306,20 @@ describe Gitlab::Git::Repository, seed_helper: true do
end
it 'gets the branches from GitalyClient' do
- expect_any_instance_of(Gitlab::GitalyClient::Ref).to receive(:local_branches).
- and_return([])
+ expect_any_instance_of(Gitlab::GitalyClient::Ref).to receive(:local_branches)
+ .and_return([])
@repo.local_branches
end
it 'wraps GRPC not found' do
- expect_any_instance_of(Gitlab::GitalyClient::Ref).to receive(:local_branches).
- and_raise(GRPC::NotFound)
+ expect_any_instance_of(Gitlab::GitalyClient::Ref).to receive(:local_branches)
+ .and_raise(GRPC::NotFound)
expect { @repo.local_branches }.to raise_error(Gitlab::Git::Repository::NoRepository)
end
it 'wraps GRPC exceptions' do
- expect_any_instance_of(Gitlab::GitalyClient::Ref).to receive(:local_branches).
- and_raise(GRPC::Unknown)
+ expect_any_instance_of(Gitlab::GitalyClient::Ref).to receive(:local_branches)
+ .and_raise(GRPC::Unknown)
expect { @repo.local_branches }.to raise_error(Gitlab::Git::CommandError)
end
end
diff --git a/spec/lib/gitlab/gitaly_client/notifications_spec.rb b/spec/lib/gitlab/gitaly_client/notifications_spec.rb
index c2b8ca9f501..7404ffe0f06 100644
--- a/spec/lib/gitlab/gitaly_client/notifications_spec.rb
+++ b/spec/lib/gitlab/gitaly_client/notifications_spec.rb
@@ -8,8 +8,8 @@ describe Gitlab::GitalyClient::Notifications do
subject { described_class.new(project.repository) }
it 'sends a post_receive message' do
- expect_any_instance_of(Gitaly::Notifications::Stub).
- to receive(:post_receive).with(gitaly_request_with_path(storage_name, relative_path), kind_of(Hash))
+ expect_any_instance_of(Gitaly::Notifications::Stub)
+ .to receive(:post_receive).with(gitaly_request_with_path(storage_name, relative_path), kind_of(Hash))
subject.post_receive
end
diff --git a/spec/lib/gitlab/gitaly_client/ref_spec.rb b/spec/lib/gitlab/gitaly_client/ref_spec.rb
index 3272333bb33..42dba2ff874 100644
--- a/spec/lib/gitlab/gitaly_client/ref_spec.rb
+++ b/spec/lib/gitlab/gitaly_client/ref_spec.rb
@@ -19,10 +19,10 @@ describe Gitlab::GitalyClient::Ref do
describe '#branch_names' do
it 'sends a find_all_branch_names message' do
- expect_any_instance_of(Gitaly::Ref::Stub).
- to receive(:find_all_branch_names).
- with(gitaly_request_with_path(storage_name, relative_path), kind_of(Hash)).
- and_return([])
+ expect_any_instance_of(Gitaly::Ref::Stub)
+ .to receive(:find_all_branch_names)
+ .with(gitaly_request_with_path(storage_name, relative_path), kind_of(Hash))
+ .and_return([])
client.branch_names
end
@@ -30,10 +30,10 @@ describe Gitlab::GitalyClient::Ref do
describe '#tag_names' do
it 'sends a find_all_tag_names message' do
- expect_any_instance_of(Gitaly::Ref::Stub).
- to receive(:find_all_tag_names).
- with(gitaly_request_with_path(storage_name, relative_path), kind_of(Hash)).
- and_return([])
+ expect_any_instance_of(Gitaly::Ref::Stub)
+ .to receive(:find_all_tag_names)
+ .with(gitaly_request_with_path(storage_name, relative_path), kind_of(Hash))
+ .and_return([])
client.tag_names
end
@@ -41,10 +41,10 @@ describe Gitlab::GitalyClient::Ref do
describe '#default_branch_name' do
it 'sends a find_default_branch_name message' do
- expect_any_instance_of(Gitaly::Ref::Stub).
- to receive(:find_default_branch_name).
- with(gitaly_request_with_path(storage_name, relative_path), kind_of(Hash)).
- and_return(double(name: 'foo'))
+ expect_any_instance_of(Gitaly::Ref::Stub)
+ .to receive(:find_default_branch_name)
+ .with(gitaly_request_with_path(storage_name, relative_path), kind_of(Hash))
+ .and_return(double(name: 'foo'))
client.default_branch_name
end
@@ -52,19 +52,19 @@ describe Gitlab::GitalyClient::Ref do
describe '#local_branches' do
it 'sends a find_local_branches message' do
- expect_any_instance_of(Gitaly::Ref::Stub).
- to receive(:find_local_branches).
- with(gitaly_request_with_path(storage_name, relative_path), kind_of(Hash)).
- and_return([])
+ expect_any_instance_of(Gitaly::Ref::Stub)
+ .to receive(:find_local_branches)
+ .with(gitaly_request_with_path(storage_name, relative_path), kind_of(Hash))
+ .and_return([])
client.local_branches
end
it 'parses and sends the sort parameter' do
- expect_any_instance_of(Gitaly::Ref::Stub).
- to receive(:find_local_branches).
- with(gitaly_request_with_params(sort_by: :UPDATED_DESC), kind_of(Hash)).
- and_return([])
+ expect_any_instance_of(Gitaly::Ref::Stub)
+ .to receive(:find_local_branches)
+ .with(gitaly_request_with_params(sort_by: :UPDATED_DESC), kind_of(Hash))
+ .and_return([])
client.local_branches(sort_by: 'updated_desc')
end
diff --git a/spec/lib/gitlab/gitlab_import/importer_spec.rb b/spec/lib/gitlab/gitlab_import/importer_spec.rb
index 9b499b593d3..4f588da0a83 100644
--- a/spec/lib/gitlab/gitlab_import/importer_spec.rb
+++ b/spec/lib/gitlab/gitlab_import/importer_spec.rb
@@ -45,8 +45,8 @@ describe Gitlab::GitlabImport::Importer, lib: true do
def stub_request(path, body)
url = "https://gitlab.com/api/v3/projects/asd%2Fvim/#{path}?page=1&per_page=100"
- WebMock.stub_request(:get, url).
- to_return(
+ WebMock.stub_request(:get, url)
+ .to_return(
headers: { 'Content-Type' => 'application/json' },
body: body
)
diff --git a/spec/lib/gitlab/highlight_spec.rb b/spec/lib/gitlab/highlight_spec.rb
index fdc5b484ef1..07687b470c5 100644
--- a/spec/lib/gitlab/highlight_spec.rb
+++ b/spec/lib/gitlab/highlight_spec.rb
@@ -51,8 +51,8 @@ describe Gitlab::Highlight, lib: true do
end
it 'links dependencies via DependencyLinker' do
- expect(Gitlab::DependencyLinker).to receive(:link).
- with('file.name', 'Contents', anything).and_call_original
+ expect(Gitlab::DependencyLinker).to receive(:link)
+ .with('file.name', 'Contents', anything).and_call_original
described_class.highlight('file.name', 'Contents')
end
diff --git a/spec/lib/gitlab/identifier_spec.rb b/spec/lib/gitlab/identifier_spec.rb
index bb758a8a202..29912da2e25 100644
--- a/spec/lib/gitlab/identifier_spec.rb
+++ b/spec/lib/gitlab/identifier_spec.rb
@@ -12,8 +12,8 @@ describe Gitlab::Identifier do
describe '#identify' do
context 'without an identifier' do
it 'identifies the user using a commit' do
- expect(identifier).to receive(:identify_using_commit).
- with(project, '123')
+ expect(identifier).to receive(:identify_using_commit)
+ .with(project, '123')
identifier.identify('', project, '123')
end
@@ -21,8 +21,8 @@ describe Gitlab::Identifier do
context 'with a user identifier' do
it 'identifies the user using a user ID' do
- expect(identifier).to receive(:identify_using_user).
- with("user-#{user.id}")
+ expect(identifier).to receive(:identify_using_user)
+ .with("user-#{user.id}")
identifier.identify("user-#{user.id}", project, '123')
end
@@ -30,8 +30,8 @@ describe Gitlab::Identifier do
context 'with an SSH key identifier' do
it 'identifies the user using an SSH key ID' do
- expect(identifier).to receive(:identify_using_ssh_key).
- with("key-#{key.id}")
+ expect(identifier).to receive(:identify_using_ssh_key)
+ .with("key-#{key.id}")
identifier.identify("key-#{key.id}", project, '123')
end
diff --git a/spec/lib/gitlab/job_waiter_spec.rb b/spec/lib/gitlab/job_waiter_spec.rb
index 780f5b1f8d7..6186cec2689 100644
--- a/spec/lib/gitlab/job_waiter_spec.rb
+++ b/spec/lib/gitlab/job_waiter_spec.rb
@@ -4,8 +4,8 @@ describe Gitlab::JobWaiter do
describe '#wait' do
let(:waiter) { described_class.new(%w(a)) }
it 'returns when all jobs have been completed' do
- expect(Gitlab::SidekiqStatus).to receive(:all_completed?).with(%w(a)).
- and_return(true)
+ expect(Gitlab::SidekiqStatus).to receive(:all_completed?).with(%w(a))
+ .and_return(true)
expect(waiter).not_to receive(:sleep)
@@ -13,9 +13,9 @@ describe Gitlab::JobWaiter do
end
it 'sleeps between checking the job statuses' do
- expect(Gitlab::SidekiqStatus).to receive(:all_completed?).
- with(%w(a)).
- and_return(false, true)
+ expect(Gitlab::SidekiqStatus).to receive(:all_completed?)
+ .with(%w(a))
+ .and_return(false, true)
expect(waiter).to receive(:sleep).with(described_class::INTERVAL)
diff --git a/spec/lib/gitlab/ldap/authentication_spec.rb b/spec/lib/gitlab/ldap/authentication_spec.rb
index b8f3290e84c..f689b47fec4 100644
--- a/spec/lib/gitlab/ldap/authentication_spec.rb
+++ b/spec/lib/gitlab/ldap/authentication_spec.rb
@@ -16,8 +16,8 @@ describe Gitlab::LDAP::Authentication, lib: true do
# try only to fake the LDAP call
adapter = double('adapter', dn: dn).as_null_object
- allow_any_instance_of(described_class).
- to receive(:adapter).and_return(adapter)
+ allow_any_instance_of(described_class)
+ .to receive(:adapter).and_return(adapter)
expect(described_class.login(login, password)).to be_truthy
end
@@ -25,8 +25,8 @@ describe Gitlab::LDAP::Authentication, lib: true do
it "is false if the user does not exist" do
# try only to fake the LDAP call
adapter = double('adapter', dn: dn).as_null_object
- allow_any_instance_of(described_class).
- to receive(:adapter).and_return(adapter)
+ allow_any_instance_of(described_class)
+ .to receive(:adapter).and_return(adapter)
expect(described_class.login(login, password)).to be_falsey
end
@@ -36,8 +36,8 @@ describe Gitlab::LDAP::Authentication, lib: true do
# try only to fake the LDAP call
adapter = double('adapter', bind_as: nil).as_null_object
- allow_any_instance_of(described_class).
- to receive(:adapter).and_return(adapter)
+ allow_any_instance_of(described_class)
+ .to receive(:adapter).and_return(adapter)
expect(described_class.login(login, password)).to be_falsey
end
diff --git a/spec/lib/gitlab/ldap/user_spec.rb b/spec/lib/gitlab/ldap/user_spec.rb
index f0a1dd22fee..b796d8bf076 100644
--- a/spec/lib/gitlab/ldap/user_spec.rb
+++ b/spec/lib/gitlab/ldap/user_spec.rb
@@ -167,8 +167,8 @@ describe Gitlab::LDAP::User, lib: true do
describe 'blocking' do
def configure_block(value)
- allow_any_instance_of(Gitlab::LDAP::Config).
- to receive(:block_auto_created_users).and_return(value)
+ allow_any_instance_of(Gitlab::LDAP::Config)
+ .to receive(:block_auto_created_users).and_return(value)
end
context 'signup' do
diff --git a/spec/lib/gitlab/metrics/instrumentation_spec.rb b/spec/lib/gitlab/metrics/instrumentation_spec.rb
index a986cb520fb..4b19ee19103 100644
--- a/spec/lib/gitlab/metrics/instrumentation_spec.rb
+++ b/spec/lib/gitlab/metrics/instrumentation_spec.rb
@@ -78,11 +78,11 @@ describe Gitlab::Metrics::Instrumentation do
end
it 'tracks the call duration upon calling the method' do
- allow(Gitlab::Metrics).to receive(:method_call_threshold).
- and_return(0)
+ allow(Gitlab::Metrics).to receive(:method_call_threshold)
+ .and_return(0)
- allow(described_class).to receive(:transaction).
- and_return(transaction)
+ allow(described_class).to receive(:transaction)
+ .and_return(transaction)
expect_any_instance_of(Gitlab::Metrics::MethodCall).to receive(:measure)
@@ -90,8 +90,8 @@ describe Gitlab::Metrics::Instrumentation do
end
it 'does not track method calls below a given duration threshold' do
- allow(Gitlab::Metrics).to receive(:method_call_threshold).
- and_return(100)
+ allow(Gitlab::Metrics).to receive(:method_call_threshold)
+ .and_return(100)
expect(transaction).not_to receive(:add_metric)
@@ -137,8 +137,8 @@ describe Gitlab::Metrics::Instrumentation do
before do
allow(Gitlab::Metrics).to receive(:enabled?).and_return(true)
- described_class.
- instrument_instance_method(@dummy, :bar)
+ described_class
+ .instrument_instance_method(@dummy, :bar)
end
it 'instruments instances of the Class' do
@@ -156,11 +156,11 @@ describe Gitlab::Metrics::Instrumentation do
end
it 'tracks the call duration upon calling the method' do
- allow(Gitlab::Metrics).to receive(:method_call_threshold).
- and_return(0)
+ allow(Gitlab::Metrics).to receive(:method_call_threshold)
+ .and_return(0)
- allow(described_class).to receive(:transaction).
- and_return(transaction)
+ allow(described_class).to receive(:transaction)
+ .and_return(transaction)
expect_any_instance_of(Gitlab::Metrics::MethodCall).to receive(:measure)
@@ -168,8 +168,8 @@ describe Gitlab::Metrics::Instrumentation do
end
it 'does not track method calls below a given duration threshold' do
- allow(Gitlab::Metrics).to receive(:method_call_threshold).
- and_return(100)
+ allow(Gitlab::Metrics).to receive(:method_call_threshold)
+ .and_return(100)
expect(transaction).not_to receive(:add_metric)
@@ -183,8 +183,8 @@ describe Gitlab::Metrics::Instrumentation do
end
it 'does not instrument the method' do
- described_class.
- instrument_instance_method(@dummy, :bar)
+ described_class
+ .instrument_instance_method(@dummy, :bar)
expect(described_class.instrumented?(@dummy)).to eq(false)
end
diff --git a/spec/lib/gitlab/metrics/rack_middleware_spec.rb b/spec/lib/gitlab/metrics/rack_middleware_spec.rb
index fb470ea7568..ec415f2bd85 100644
--- a/spec/lib/gitlab/metrics/rack_middleware_spec.rb
+++ b/spec/lib/gitlab/metrics/rack_middleware_spec.rb
@@ -26,8 +26,8 @@ describe Gitlab::Metrics::RackMiddleware do
allow(app).to receive(:call).with(env)
- expect(middleware).to receive(:tag_controller).
- with(an_instance_of(Gitlab::Metrics::Transaction), env)
+ expect(middleware).to receive(:tag_controller)
+ .with(an_instance_of(Gitlab::Metrics::Transaction), env)
middleware.call(env)
end
@@ -40,8 +40,8 @@ describe Gitlab::Metrics::RackMiddleware do
allow(app).to receive(:call).with(env)
- expect(middleware).to receive(:tag_endpoint).
- with(an_instance_of(Gitlab::Metrics::Transaction), env)
+ expect(middleware).to receive(:tag_endpoint)
+ .with(an_instance_of(Gitlab::Metrics::Transaction), env)
middleware.call(env)
end
@@ -49,8 +49,8 @@ describe Gitlab::Metrics::RackMiddleware do
it 'tracks any raised exceptions' do
expect(app).to receive(:call).with(env).and_raise(RuntimeError)
- expect_any_instance_of(Gitlab::Metrics::Transaction).
- to receive(:add_event).with(:rails_exception)
+ expect_any_instance_of(Gitlab::Metrics::Transaction)
+ .to receive(:add_event).with(:rails_exception)
expect { middleware.call(env) }.to raise_error(RuntimeError)
end
diff --git a/spec/lib/gitlab/metrics/sampler_spec.rb b/spec/lib/gitlab/metrics/sampler_spec.rb
index 1ab923b58cf..d07ce6f81af 100644
--- a/spec/lib/gitlab/metrics/sampler_spec.rb
+++ b/spec/lib/gitlab/metrics/sampler_spec.rb
@@ -38,8 +38,8 @@ describe Gitlab::Metrics::Sampler do
describe '#flush' do
it 'schedules the metrics using Sidekiq' do
- expect(Gitlab::Metrics).to receive(:submit_metrics).
- with([an_instance_of(Hash)])
+ expect(Gitlab::Metrics).to receive(:submit_metrics)
+ .with([an_instance_of(Hash)])
sampler.sample_memory_usage
sampler.flush
@@ -48,12 +48,12 @@ describe Gitlab::Metrics::Sampler do
describe '#sample_memory_usage' do
it 'adds a metric containing the memory usage' do
- expect(Gitlab::Metrics::System).to receive(:memory_usage).
- and_return(9000)
+ expect(Gitlab::Metrics::System).to receive(:memory_usage)
+ .and_return(9000)
- expect(sampler).to receive(:add_metric).
- with(/memory_usage/, value: 9000).
- and_call_original
+ expect(sampler).to receive(:add_metric)
+ .with(/memory_usage/, value: 9000)
+ .and_call_original
sampler.sample_memory_usage
end
@@ -61,12 +61,12 @@ describe Gitlab::Metrics::Sampler do
describe '#sample_file_descriptors' do
it 'adds a metric containing the amount of open file descriptors' do
- expect(Gitlab::Metrics::System).to receive(:file_descriptor_count).
- and_return(4)
+ expect(Gitlab::Metrics::System).to receive(:file_descriptor_count)
+ .and_return(4)
- expect(sampler).to receive(:add_metric).
- with(/file_descriptors/, value: 4).
- and_call_original
+ expect(sampler).to receive(:add_metric)
+ .with(/file_descriptors/, value: 4)
+ .and_call_original
sampler.sample_file_descriptors
end
@@ -75,10 +75,10 @@ describe Gitlab::Metrics::Sampler do
if Gitlab::Metrics.mri?
describe '#sample_objects' do
it 'adds a metric containing the amount of allocated objects' do
- expect(sampler).to receive(:add_metric).
- with(/object_counts/, an_instance_of(Hash), an_instance_of(Hash)).
- at_least(:once).
- and_call_original
+ expect(sampler).to receive(:add_metric)
+ .with(/object_counts/, an_instance_of(Hash), an_instance_of(Hash))
+ .at_least(:once)
+ .and_call_original
sampler.sample_objects
end
@@ -86,8 +86,8 @@ describe Gitlab::Metrics::Sampler do
it 'ignores classes without a name' do
expect(Allocations).to receive(:to_hash).and_return({ Class.new => 4 })
- expect(sampler).not_to receive(:add_metric).
- with('object_counts', an_instance_of(Hash), type: nil)
+ expect(sampler).not_to receive(:add_metric)
+ .with('object_counts', an_instance_of(Hash), type: nil)
sampler.sample_objects
end
@@ -98,9 +98,9 @@ describe Gitlab::Metrics::Sampler do
it 'adds a metric containing garbage collection statistics' do
expect(GC::Profiler).to receive(:total_time).and_return(0.24)
- expect(sampler).to receive(:add_metric).
- with(/gc_statistics/, an_instance_of(Hash)).
- and_call_original
+ expect(sampler).to receive(:add_metric)
+ .with(/gc_statistics/, an_instance_of(Hash))
+ .and_call_original
sampler.sample_gc
end
@@ -110,9 +110,9 @@ describe Gitlab::Metrics::Sampler do
it 'prefixes the series name for a Rails process' do
expect(sampler).to receive(:sidekiq?).and_return(false)
- expect(Gitlab::Metrics::Metric).to receive(:new).
- with('rails_cats', { value: 10 }, {}).
- and_call_original
+ expect(Gitlab::Metrics::Metric).to receive(:new)
+ .with('rails_cats', { value: 10 }, {})
+ .and_call_original
sampler.add_metric('cats', value: 10)
end
@@ -120,9 +120,9 @@ describe Gitlab::Metrics::Sampler do
it 'prefixes the series name for a Sidekiq process' do
expect(sampler).to receive(:sidekiq?).and_return(true)
- expect(Gitlab::Metrics::Metric).to receive(:new).
- with('sidekiq_cats', { value: 10 }, {}).
- and_call_original
+ expect(Gitlab::Metrics::Metric).to receive(:new)
+ .with('sidekiq_cats', { value: 10 }, {})
+ .and_call_original
sampler.add_metric('cats', value: 10)
end
diff --git a/spec/lib/gitlab/metrics/sidekiq_middleware_spec.rb b/spec/lib/gitlab/metrics/sidekiq_middleware_spec.rb
index acaba785606..b576d7173f5 100644
--- a/spec/lib/gitlab/metrics/sidekiq_middleware_spec.rb
+++ b/spec/lib/gitlab/metrics/sidekiq_middleware_spec.rb
@@ -8,12 +8,12 @@ describe Gitlab::Metrics::SidekiqMiddleware do
it 'tracks the transaction' do
worker = double(:worker, class: double(:class, name: 'TestWorker'))
- expect(Gitlab::Metrics::Transaction).to receive(:new).
- with('TestWorker#perform').
- and_call_original
+ expect(Gitlab::Metrics::Transaction).to receive(:new)
+ .with('TestWorker#perform')
+ .and_call_original
- expect_any_instance_of(Gitlab::Metrics::Transaction).to receive(:set).
- with(:sidekiq_queue_duration, instance_of(Float))
+ expect_any_instance_of(Gitlab::Metrics::Transaction).to receive(:set)
+ .with(:sidekiq_queue_duration, instance_of(Float))
expect_any_instance_of(Gitlab::Metrics::Transaction).to receive(:finish)
@@ -23,12 +23,12 @@ describe Gitlab::Metrics::SidekiqMiddleware do
it 'tracks the transaction (for messages without `enqueued_at`)' do
worker = double(:worker, class: double(:class, name: 'TestWorker'))
- expect(Gitlab::Metrics::Transaction).to receive(:new).
- with('TestWorker#perform').
- and_call_original
+ expect(Gitlab::Metrics::Transaction).to receive(:new)
+ .with('TestWorker#perform')
+ .and_call_original
- expect_any_instance_of(Gitlab::Metrics::Transaction).to receive(:set).
- with(:sidekiq_queue_duration, instance_of(Float))
+ expect_any_instance_of(Gitlab::Metrics::Transaction).to receive(:set)
+ .with(:sidekiq_queue_duration, instance_of(Float))
expect_any_instance_of(Gitlab::Metrics::Transaction).to receive(:finish)
@@ -38,17 +38,17 @@ describe Gitlab::Metrics::SidekiqMiddleware do
it 'tracks any raised exceptions' do
worker = double(:worker, class: double(:class, name: 'TestWorker'))
- expect_any_instance_of(Gitlab::Metrics::Transaction).
- to receive(:run).and_raise(RuntimeError)
+ expect_any_instance_of(Gitlab::Metrics::Transaction)
+ .to receive(:run).and_raise(RuntimeError)
- expect_any_instance_of(Gitlab::Metrics::Transaction).
- to receive(:add_event).with(:sidekiq_exception)
+ expect_any_instance_of(Gitlab::Metrics::Transaction)
+ .to receive(:add_event).with(:sidekiq_exception)
- expect_any_instance_of(Gitlab::Metrics::Transaction).
- to receive(:finish)
+ expect_any_instance_of(Gitlab::Metrics::Transaction)
+ .to receive(:finish)
- expect { middleware.call(worker, message, :test) }.
- to raise_error(RuntimeError)
+ expect { middleware.call(worker, message, :test) }
+ .to raise_error(RuntimeError)
end
end
end
diff --git a/spec/lib/gitlab/metrics/subscribers/action_view_spec.rb b/spec/lib/gitlab/metrics/subscribers/action_view_spec.rb
index 0695c5ce096..e7b595405a8 100644
--- a/spec/lib/gitlab/metrics/subscribers/action_view_spec.rb
+++ b/spec/lib/gitlab/metrics/subscribers/action_view_spec.rb
@@ -21,11 +21,11 @@ describe Gitlab::Metrics::Subscribers::ActionView do
values = { duration: 2.1 }
tags = { view: 'app/views/x.html.haml' }
- expect(transaction).to receive(:increment).
- with(:view_duration, 2.1)
+ expect(transaction).to receive(:increment)
+ .with(:view_duration, 2.1)
- expect(transaction).to receive(:add_metric).
- with(described_class::SERIES, values, tags)
+ expect(transaction).to receive(:add_metric)
+ .with(described_class::SERIES, values, tags)
subscriber.render_template(event)
end
diff --git a/spec/lib/gitlab/metrics/subscribers/active_record_spec.rb b/spec/lib/gitlab/metrics/subscribers/active_record_spec.rb
index 49699ffe28f..ce6587e993f 100644
--- a/spec/lib/gitlab/metrics/subscribers/active_record_spec.rb
+++ b/spec/lib/gitlab/metrics/subscribers/active_record_spec.rb
@@ -12,8 +12,8 @@ describe Gitlab::Metrics::Subscribers::ActiveRecord do
describe '#sql' do
describe 'without a current transaction' do
it 'simply returns' do
- expect_any_instance_of(Gitlab::Metrics::Transaction).
- not_to receive(:increment)
+ expect_any_instance_of(Gitlab::Metrics::Transaction)
+ .not_to receive(:increment)
subscriber.sql(event)
end
@@ -21,15 +21,15 @@ describe Gitlab::Metrics::Subscribers::ActiveRecord do
describe 'with a current transaction' do
it 'increments the :sql_duration value' do
- expect(subscriber).to receive(:current_transaction).
- at_least(:once).
- and_return(transaction)
+ expect(subscriber).to receive(:current_transaction)
+ .at_least(:once)
+ .and_return(transaction)
- expect(transaction).to receive(:increment).
- with(:sql_duration, 0.2)
+ expect(transaction).to receive(:increment)
+ .with(:sql_duration, 0.2)
- expect(transaction).to receive(:increment).
- with(:sql_count, 1)
+ expect(transaction).to receive(:increment)
+ .with(:sql_count, 1)
subscriber.sql(event)
end
diff --git a/spec/lib/gitlab/metrics/subscribers/rails_cache_spec.rb b/spec/lib/gitlab/metrics/subscribers/rails_cache_spec.rb
index d986c6fac43..f04dc8dcc02 100644
--- a/spec/lib/gitlab/metrics/subscribers/rails_cache_spec.rb
+++ b/spec/lib/gitlab/metrics/subscribers/rails_cache_spec.rb
@@ -8,26 +8,26 @@ describe Gitlab::Metrics::Subscribers::RailsCache do
describe '#cache_read' do
it 'increments the cache_read duration' do
- expect(subscriber).to receive(:increment).
- with(:cache_read, event.duration)
+ expect(subscriber).to receive(:increment)
+ .with(:cache_read, event.duration)
subscriber.cache_read(event)
end
context 'with a transaction' do
before do
- allow(subscriber).to receive(:current_transaction).
- and_return(transaction)
+ allow(subscriber).to receive(:current_transaction)
+ .and_return(transaction)
end
context 'with hit event' do
let(:event) { double(:event, duration: 15.2, payload: { hit: true }) }
it 'increments the cache_read_hit count' do
- expect(transaction).to receive(:increment).
- with(:cache_read_hit_count, 1)
- expect(transaction).to receive(:increment).
- with(any_args).at_least(1) # Other calls
+ expect(transaction).to receive(:increment)
+ .with(:cache_read_hit_count, 1)
+ expect(transaction).to receive(:increment)
+ .with(any_args).at_least(1) # Other calls
subscriber.cache_read(event)
end
@@ -36,8 +36,8 @@ describe Gitlab::Metrics::Subscribers::RailsCache do
let(:event) { double(:event, duration: 15.2, payload: { hit: true, super_operation: :fetch }) }
it 'does not increment cache read miss' do
- expect(transaction).not_to receive(:increment).
- with(:cache_read_hit_count, 1)
+ expect(transaction).not_to receive(:increment)
+ .with(:cache_read_hit_count, 1)
subscriber.cache_read(event)
end
@@ -48,10 +48,10 @@ describe Gitlab::Metrics::Subscribers::RailsCache do
let(:event) { double(:event, duration: 15.2, payload: { hit: false }) }
it 'increments the cache_read_miss count' do
- expect(transaction).to receive(:increment).
- with(:cache_read_miss_count, 1)
- expect(transaction).to receive(:increment).
- with(any_args).at_least(1) # Other calls
+ expect(transaction).to receive(:increment)
+ .with(:cache_read_miss_count, 1)
+ expect(transaction).to receive(:increment)
+ .with(any_args).at_least(1) # Other calls
subscriber.cache_read(event)
end
@@ -60,8 +60,8 @@ describe Gitlab::Metrics::Subscribers::RailsCache do
let(:event) { double(:event, duration: 15.2, payload: { hit: false, super_operation: :fetch }) }
it 'does not increment cache read miss' do
- expect(transaction).not_to receive(:increment).
- with(:cache_read_miss_count, 1)
+ expect(transaction).not_to receive(:increment)
+ .with(:cache_read_miss_count, 1)
subscriber.cache_read(event)
end
@@ -72,8 +72,8 @@ describe Gitlab::Metrics::Subscribers::RailsCache do
describe '#cache_write' do
it 'increments the cache_write duration' do
- expect(subscriber).to receive(:increment).
- with(:cache_write, event.duration)
+ expect(subscriber).to receive(:increment)
+ .with(:cache_write, event.duration)
subscriber.cache_write(event)
end
@@ -81,8 +81,8 @@ describe Gitlab::Metrics::Subscribers::RailsCache do
describe '#cache_delete' do
it 'increments the cache_delete duration' do
- expect(subscriber).to receive(:increment).
- with(:cache_delete, event.duration)
+ expect(subscriber).to receive(:increment)
+ .with(:cache_delete, event.duration)
subscriber.cache_delete(event)
end
@@ -90,8 +90,8 @@ describe Gitlab::Metrics::Subscribers::RailsCache do
describe '#cache_exist?' do
it 'increments the cache_exists duration' do
- expect(subscriber).to receive(:increment).
- with(:cache_exists, event.duration)
+ expect(subscriber).to receive(:increment)
+ .with(:cache_exists, event.duration)
subscriber.cache_exist?(event)
end
@@ -108,13 +108,13 @@ describe Gitlab::Metrics::Subscribers::RailsCache do
context 'with a transaction' do
before do
- allow(subscriber).to receive(:current_transaction).
- and_return(transaction)
+ allow(subscriber).to receive(:current_transaction)
+ .and_return(transaction)
end
it 'increments the cache_read_hit count' do
- expect(transaction).to receive(:increment).
- with(:cache_read_hit_count, 1)
+ expect(transaction).to receive(:increment)
+ .with(:cache_read_hit_count, 1)
subscriber.cache_fetch_hit(event)
end
@@ -132,13 +132,13 @@ describe Gitlab::Metrics::Subscribers::RailsCache do
context 'with a transaction' do
before do
- allow(subscriber).to receive(:current_transaction).
- and_return(transaction)
+ allow(subscriber).to receive(:current_transaction)
+ .and_return(transaction)
end
it 'increments the cache_fetch_miss count' do
- expect(transaction).to receive(:increment).
- with(:cache_read_miss_count, 1)
+ expect(transaction).to receive(:increment)
+ .with(:cache_read_miss_count, 1)
subscriber.cache_generate(event)
end
@@ -156,22 +156,22 @@ describe Gitlab::Metrics::Subscribers::RailsCache do
context 'with a transaction' do
before do
- allow(subscriber).to receive(:current_transaction).
- and_return(transaction)
+ allow(subscriber).to receive(:current_transaction)
+ .and_return(transaction)
end
it 'increments the total and specific cache duration' do
- expect(transaction).to receive(:increment).
- with(:cache_duration, event.duration)
+ expect(transaction).to receive(:increment)
+ .with(:cache_duration, event.duration)
- expect(transaction).to receive(:increment).
- with(:cache_count, 1)
+ expect(transaction).to receive(:increment)
+ .with(:cache_count, 1)
- expect(transaction).to receive(:increment).
- with(:cache_delete_duration, event.duration)
+ expect(transaction).to receive(:increment)
+ .with(:cache_delete_duration, event.duration)
- expect(transaction).to receive(:increment).
- with(:cache_delete_count, 1)
+ expect(transaction).to receive(:increment)
+ .with(:cache_delete_count, 1)
subscriber.increment(:cache_delete, event.duration)
end
diff --git a/spec/lib/gitlab/metrics/transaction_spec.rb b/spec/lib/gitlab/metrics/transaction_spec.rb
index 0c5a6246d85..3779af81512 100644
--- a/spec/lib/gitlab/metrics/transaction_spec.rb
+++ b/spec/lib/gitlab/metrics/transaction_spec.rb
@@ -39,8 +39,8 @@ describe Gitlab::Metrics::Transaction do
describe '#add_metric' do
it 'adds a metric to the transaction' do
- expect(Gitlab::Metrics::Metric).to receive(:new).
- with('rails_foo', { number: 10 }, {})
+ expect(Gitlab::Metrics::Metric).to receive(:new)
+ .with('rails_foo', { number: 10 }, {})
transaction.add_metric('foo', number: 10)
end
@@ -61,8 +61,8 @@ describe Gitlab::Metrics::Transaction do
values = { duration: 0.0, time: 3, allocated_memory: a_kind_of(Numeric) }
- expect(transaction).to receive(:add_metric).
- with('transactions', values, {})
+ expect(transaction).to receive(:add_metric)
+ .with('transactions', values, {})
transaction.track_self
end
@@ -78,8 +78,8 @@ describe Gitlab::Metrics::Transaction do
allocated_memory: a_kind_of(Numeric)
}
- expect(transaction).to receive(:add_metric).
- with('transactions', values, {})
+ expect(transaction).to receive(:add_metric)
+ .with('transactions', values, {})
transaction.track_self
end
@@ -109,8 +109,8 @@ describe Gitlab::Metrics::Transaction do
allocated_memory: a_kind_of(Numeric)
}
- expect(transaction).to receive(:add_metric).
- with('transactions', values, {})
+ expect(transaction).to receive(:add_metric)
+ .with('transactions', values, {})
transaction.track_self
end
@@ -120,8 +120,8 @@ describe Gitlab::Metrics::Transaction do
it 'submits the metrics to Sidekiq' do
transaction.track_self
- expect(Gitlab::Metrics).to receive(:submit_metrics).
- with([an_instance_of(Hash)])
+ expect(Gitlab::Metrics).to receive(:submit_metrics)
+ .with([an_instance_of(Hash)])
transaction.submit
end
@@ -137,8 +137,8 @@ describe Gitlab::Metrics::Transaction do
timestamp: a_kind_of(Integer)
}
- expect(Gitlab::Metrics).to receive(:submit_metrics).
- with([hash])
+ expect(Gitlab::Metrics).to receive(:submit_metrics)
+ .with([hash])
transaction.submit
end
@@ -154,8 +154,8 @@ describe Gitlab::Metrics::Transaction do
timestamp: a_kind_of(Integer)
}
- expect(Gitlab::Metrics).to receive(:submit_metrics).
- with([hash])
+ expect(Gitlab::Metrics).to receive(:submit_metrics)
+ .with([hash])
transaction.submit
end
diff --git a/spec/lib/gitlab/metrics_spec.rb b/spec/lib/gitlab/metrics_spec.rb
index 58a84cd3fe1..599b8807d8d 100644
--- a/spec/lib/gitlab/metrics_spec.rb
+++ b/spec/lib/gitlab/metrics_spec.rb
@@ -72,8 +72,8 @@ describe Gitlab::Metrics do
describe '.prepare_metrics' do
it 'returns a Hash with the keys as Symbols' do
- metrics = described_class.
- prepare_metrics([{ 'values' => {}, 'tags' => {} }])
+ metrics = described_class
+ .prepare_metrics([{ 'values' => {}, 'tags' => {} }])
expect(metrics).to eq([{ values: {}, tags: {} }])
end
@@ -118,19 +118,19 @@ describe Gitlab::Metrics do
let(:transaction) { Gitlab::Metrics::Transaction.new }
before do
- allow(described_class).to receive(:current_transaction).
- and_return(transaction)
+ allow(described_class).to receive(:current_transaction)
+ .and_return(transaction)
end
it 'adds a metric to the current transaction' do
- expect(transaction).to receive(:increment).
- with('foo_real_time', a_kind_of(Numeric))
+ expect(transaction).to receive(:increment)
+ .with('foo_real_time', a_kind_of(Numeric))
- expect(transaction).to receive(:increment).
- with('foo_cpu_time', a_kind_of(Numeric))
+ expect(transaction).to receive(:increment)
+ .with('foo_cpu_time', a_kind_of(Numeric))
- expect(transaction).to receive(:increment).
- with('foo_call_count', 1)
+ expect(transaction).to receive(:increment)
+ .with('foo_call_count', 1)
described_class.measure(:foo) { 10 }
end
@@ -146,8 +146,8 @@ describe Gitlab::Metrics do
describe '.tag_transaction' do
context 'without a transaction' do
it 'does nothing' do
- expect_any_instance_of(Gitlab::Metrics::Transaction).
- not_to receive(:add_tag)
+ expect_any_instance_of(Gitlab::Metrics::Transaction)
+ .not_to receive(:add_tag)
described_class.tag_transaction(:foo, 'bar')
end
@@ -157,11 +157,11 @@ describe Gitlab::Metrics do
let(:transaction) { Gitlab::Metrics::Transaction.new }
it 'adds the tag to the transaction' do
- expect(described_class).to receive(:current_transaction).
- and_return(transaction)
+ expect(described_class).to receive(:current_transaction)
+ .and_return(transaction)
- expect(transaction).to receive(:add_tag).
- with(:foo, 'bar')
+ expect(transaction).to receive(:add_tag)
+ .with(:foo, 'bar')
described_class.tag_transaction(:foo, 'bar')
end
@@ -171,8 +171,8 @@ describe Gitlab::Metrics do
describe '.action=' do
context 'without a transaction' do
it 'does nothing' do
- expect_any_instance_of(Gitlab::Metrics::Transaction).
- not_to receive(:action=)
+ expect_any_instance_of(Gitlab::Metrics::Transaction)
+ .not_to receive(:action=)
described_class.action = 'foo'
end
@@ -182,8 +182,8 @@ describe Gitlab::Metrics do
it 'sets the action of a transaction' do
trans = Gitlab::Metrics::Transaction.new
- expect(described_class).to receive(:current_transaction).
- and_return(trans)
+ expect(described_class).to receive(:current_transaction)
+ .and_return(trans)
expect(trans).to receive(:action=).with('foo')
@@ -201,8 +201,8 @@ describe Gitlab::Metrics do
describe '.add_event' do
context 'without a transaction' do
it 'does nothing' do
- expect_any_instance_of(Gitlab::Metrics::Transaction).
- not_to receive(:add_event)
+ expect_any_instance_of(Gitlab::Metrics::Transaction)
+ .not_to receive(:add_event)
described_class.add_event(:meow)
end
@@ -214,8 +214,8 @@ describe Gitlab::Metrics do
expect(transaction).to receive(:add_event).with(:meow)
- expect(described_class).to receive(:current_transaction).
- and_return(transaction)
+ expect(described_class).to receive(:current_transaction)
+ .and_return(transaction)
described_class.add_event(:meow)
end
diff --git a/spec/lib/gitlab/project_authorizations_spec.rb b/spec/lib/gitlab/project_authorizations_spec.rb
index 67321f43710..9ce33685697 100644
--- a/spec/lib/gitlab/project_authorizations_spec.rb
+++ b/spec/lib/gitlab/project_authorizations_spec.rb
@@ -34,8 +34,8 @@ describe Gitlab::ProjectAuthorizations do
end
it 'includes the correct projects' do
- expect(authorizations.pluck(:project_id)).
- to include(owned_project.id, other_project.id, group_project.id)
+ expect(authorizations.pluck(:project_id))
+ .to include(owned_project.id, other_project.id, group_project.id)
end
it 'includes the correct access levels' do
diff --git a/spec/lib/gitlab/prometheus/additional_metrics_parser_spec.rb b/spec/lib/gitlab/prometheus/additional_metrics_parser_spec.rb
new file mode 100644
index 00000000000..61d48b05454
--- /dev/null
+++ b/spec/lib/gitlab/prometheus/additional_metrics_parser_spec.rb
@@ -0,0 +1,246 @@
+require 'spec_helper'
+
+describe Gitlab::Prometheus::AdditionalMetricsParser, lib: true do
+ include Prometheus::MetricBuilders
+
+ let(:parser_error_class) { Gitlab::Prometheus::ParsingError }
+
+ describe '#load_groups_from_yaml' do
+ subject { described_class.load_groups_from_yaml }
+
+ describe 'parsing sample yaml' do
+ let(:sample_yaml) do
+ <<-EOF.strip_heredoc
+ - group: group_a
+ priority: 1
+ metrics:
+ - title: "title"
+ required_metrics: [ metric_a, metric_b ]
+ weight: 1
+ queries: [{ query_range: 'query_range_a', label: label, unit: unit }]
+ - title: "title"
+ required_metrics: [metric_a]
+ weight: 1
+ queries: [{ query_range: 'query_range_empty' }]
+ - group: group_b
+ priority: 1
+ metrics:
+ - title: title
+ required_metrics: ['metric_a']
+ weight: 1
+ queries: [{query_range: query_range_a}]
+ EOF
+ end
+
+ before do
+ allow(described_class).to receive(:load_yaml_file) { YAML.load(sample_yaml) }
+ end
+
+ it 'parses to two metric groups with 2 and 1 metric respectively' do
+ expect(subject.count).to eq(2)
+ expect(subject[0].metrics.count).to eq(2)
+ expect(subject[1].metrics.count).to eq(1)
+ end
+
+ it 'provide group data' do
+ expect(subject[0]).to have_attributes(name: 'group_a', priority: 1)
+ expect(subject[1]).to have_attributes(name: 'group_b', priority: 1)
+ end
+
+ it 'provides metrics data' do
+ metrics = subject.flat_map(&:metrics)
+
+ expect(metrics.count).to eq(3)
+ expect(metrics[0]).to have_attributes(title: 'title', required_metrics: %w(metric_a metric_b), weight: 1)
+ expect(metrics[1]).to have_attributes(title: 'title', required_metrics: %w(metric_a), weight: 1)
+ expect(metrics[2]).to have_attributes(title: 'title', required_metrics: %w{metric_a}, weight: 1)
+ end
+
+ it 'provides query data' do
+ queries = subject.flat_map(&:metrics).flat_map(&:queries)
+
+ expect(queries.count).to eq(3)
+ expect(queries[0]).to eq(query_range: 'query_range_a', label: 'label', unit: 'unit')
+ expect(queries[1]).to eq(query_range: 'query_range_empty')
+ expect(queries[2]).to eq(query_range: 'query_range_a')
+ end
+ end
+
+ shared_examples 'required field' do |field_name|
+ context "when #{field_name} is nil" do
+ before do
+ allow(described_class).to receive(:load_yaml_file) { YAML.load(field_missing) }
+ end
+
+ it 'throws parsing error' do
+ expect { subject }.to raise_error(parser_error_class, /#{field_name} can't be blank/i)
+ end
+ end
+
+ context "when #{field_name} are not specified" do
+ before do
+ allow(described_class).to receive(:load_yaml_file) { YAML.load(field_nil) }
+ end
+
+ it 'throws parsing error' do
+ expect { subject }.to raise_error(parser_error_class, /#{field_name} can't be blank/i)
+ end
+ end
+ end
+
+ describe 'group required fields' do
+ it_behaves_like 'required field', 'metrics' do
+ let(:field_nil) do
+ <<-EOF.strip_heredoc
+ - group: group_a
+ priority: 1
+ metrics:
+ EOF
+ end
+
+ let(:field_missing) do
+ <<-EOF.strip_heredoc
+ - group: group_a
+ priority: 1
+ EOF
+ end
+ end
+
+ it_behaves_like 'required field', 'name' do
+ let(:field_nil) do
+ <<-EOF.strip_heredoc
+ - group:
+ priority: 1
+ metrics: []
+ EOF
+ end
+
+ let(:field_missing) do
+ <<-EOF.strip_heredoc
+ - priority: 1
+ metrics: []
+ EOF
+ end
+ end
+
+ it_behaves_like 'required field', 'priority' do
+ let(:field_nil) do
+ <<-EOF.strip_heredoc
+ - group: group_a
+ priority:
+ metrics: []
+ EOF
+ end
+
+ let(:field_missing) do
+ <<-EOF.strip_heredoc
+ - group: group_a
+ metrics: []
+ EOF
+ end
+ end
+ end
+
+ describe 'metrics fields parsing' do
+ it_behaves_like 'required field', 'title' do
+ let(:field_nil) do
+ <<-EOF.strip_heredoc
+ - group: group_a
+ priority: 1
+ metrics:
+ - title:
+ required_metrics: []
+ weight: 1
+ queries: []
+ EOF
+ end
+
+ let(:field_missing) do
+ <<-EOF.strip_heredoc
+ - group: group_a
+ priority: 1
+ metrics:
+ - required_metrics: []
+ weight: 1
+ queries: []
+ EOF
+ end
+ end
+
+ it_behaves_like 'required field', 'required metrics' do
+ let(:field_nil) do
+ <<-EOF.strip_heredoc
+ - group: group_a
+ priority: 1
+ metrics:
+ - title: title
+ required_metrics:
+ weight: 1
+ queries: []
+ EOF
+ end
+
+ let(:field_missing) do
+ <<-EOF.strip_heredoc
+ - group: group_a
+ priority: 1
+ metrics:
+ - title: title
+ weight: 1
+ queries: []
+ EOF
+ end
+ end
+
+ it_behaves_like 'required field', 'weight' do
+ let(:field_nil) do
+ <<-EOF.strip_heredoc
+ - group: group_a
+ priority: 1
+ metrics:
+ - title: title
+ required_metrics: []
+ weight:
+ queries: []
+ EOF
+ end
+
+ let(:field_missing) do
+ <<-EOF.strip_heredoc
+ - group: group_a
+ priority: 1
+ metrics:
+ - title: title
+ required_metrics: []
+ queries: []
+ EOF
+ end
+ end
+
+ it_behaves_like 'required field', :queries do
+ let(:field_nil) do
+ <<-EOF.strip_heredoc
+ - group: group_a
+ priority: 1
+ metrics:
+ - title: title
+ required_metrics: []
+ weight: 1
+ queries:
+ EOF
+ end
+
+ let(:field_missing) do
+ <<-EOF.strip_heredoc
+ - group: group_a
+ priority: 1
+ metrics:
+ - title: title
+ required_metrics: []
+ weight: 1
+ EOF
+ end
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/prometheus/queries/additional_metrics_deployment_query_spec.rb b/spec/lib/gitlab/prometheus/queries/additional_metrics_deployment_query_spec.rb
new file mode 100644
index 00000000000..4909aec5a4d
--- /dev/null
+++ b/spec/lib/gitlab/prometheus/queries/additional_metrics_deployment_query_spec.rb
@@ -0,0 +1,25 @@
+require 'spec_helper'
+
+describe Gitlab::Prometheus::Queries::AdditionalMetricsDeploymentQuery, lib: true do
+ include Prometheus::MetricBuilders
+
+ let(:client) { double('prometheus_client') }
+ let(:environment) { create(:environment, slug: 'environment-slug') }
+ let(:deployment) { create(:deployment, environment: environment) }
+
+ subject(:query_result) { described_class.new(client).query(deployment.id) }
+
+ around do |example|
+ Timecop.freeze(Time.local(2008, 9, 1, 12, 0, 0)) { example.run }
+ end
+
+ include_examples 'additional metrics query' do
+ it 'queries using specific time' do
+ expect(client).to receive(:query_range).with(anything,
+ start: (deployment.created_at - 30.minutes).to_f,
+ stop: (deployment.created_at + 30.minutes).to_f)
+
+ expect(query_result).not_to be_nil
+ end
+ end
+end
diff --git a/spec/lib/gitlab/prometheus/queries/additional_metrics_environment_query_spec.rb b/spec/lib/gitlab/prometheus/queries/additional_metrics_environment_query_spec.rb
new file mode 100644
index 00000000000..8e6e3bb5946
--- /dev/null
+++ b/spec/lib/gitlab/prometheus/queries/additional_metrics_environment_query_spec.rb
@@ -0,0 +1,21 @@
+require 'spec_helper'
+
+describe Gitlab::Prometheus::Queries::AdditionalMetricsEnvironmentQuery, lib: true do
+ include Prometheus::MetricBuilders
+
+ let(:client) { double('prometheus_client') }
+ let(:environment) { create(:environment, slug: 'environment-slug') }
+
+ subject(:query_result) { described_class.new(client).query(environment.id) }
+
+ around do |example|
+ Timecop.freeze { example.run }
+ end
+
+ include_examples 'additional metrics query' do
+ it 'queries using specific time' do
+ expect(client).to receive(:query_range).with(anything, start: 8.hours.ago.to_f, stop: Time.now.to_f)
+ expect(query_result).not_to be_nil
+ end
+ end
+end
diff --git a/spec/lib/gitlab/prometheus/queries/matched_metrics_query_spec.rb b/spec/lib/gitlab/prometheus/queries/matched_metrics_query_spec.rb
new file mode 100644
index 00000000000..d2796ab72da
--- /dev/null
+++ b/spec/lib/gitlab/prometheus/queries/matched_metrics_query_spec.rb
@@ -0,0 +1,134 @@
+require 'spec_helper'
+
+describe Gitlab::Prometheus::Queries::MatchedMetricsQuery, lib: true do
+ include Prometheus::MetricBuilders
+
+ let(:metric_group_class) { Gitlab::Prometheus::MetricGroup }
+ let(:metric_class) { Gitlab::Prometheus::Metric }
+
+ def series_info_with_environment(*more_metrics)
+ %w{metric_a metric_b}.concat(more_metrics).map { |metric_name| { '__name__' => metric_name, 'environment' => '' } }
+ end
+
+ let(:metric_names) { %w{metric_a metric_b} }
+ let(:series_info_without_environment) do
+ [{ '__name__' => 'metric_a' },
+ { '__name__' => 'metric_b' }]
+ end
+ let(:partialy_empty_series_info) { [{ '__name__' => 'metric_a', 'environment' => '' }] }
+ let(:empty_series_info) { [] }
+
+ let(:client) { double('prometheus_client') }
+
+ subject { described_class.new(client) }
+
+ context 'with one group where two metrics is found' do
+ before do
+ allow(metric_group_class).to receive(:all).and_return([simple_metric_group])
+ allow(client).to receive(:label_values).and_return(metric_names)
+ end
+
+ context 'both metrics in the group pass requirements' do
+ before do
+ allow(client).to receive(:series).and_return(series_info_with_environment)
+ end
+
+ it 'responds with both metrics as actve' do
+ expect(subject.query).to eq([{ group: 'name', priority: 1, active_metrics: 2, metrics_missing_requirements: 0 }])
+ end
+ end
+
+ context 'none of the metrics pass requirements' do
+ before do
+ allow(client).to receive(:series).and_return(series_info_without_environment)
+ end
+
+ it 'responds with both metrics missing requirements' do
+ expect(subject.query).to eq([{ group: 'name', priority: 1, active_metrics: 0, metrics_missing_requirements: 2 }])
+ end
+ end
+
+ context 'no series information found about the metrics' do
+ before do
+ allow(client).to receive(:series).and_return(empty_series_info)
+ end
+
+ it 'responds with both metrics missing requirements' do
+ expect(subject.query).to eq([{ group: 'name', priority: 1, active_metrics: 0, metrics_missing_requirements: 2 }])
+ end
+ end
+
+ context 'one of the series info was not found' do
+ before do
+ allow(client).to receive(:series).and_return(partialy_empty_series_info)
+ end
+ it 'responds with one active and one missing metric' do
+ expect(subject.query).to eq([{ group: 'name', priority: 1, active_metrics: 1, metrics_missing_requirements: 1 }])
+ end
+ end
+ end
+
+ context 'with one group where only one metric is found' do
+ before do
+ allow(metric_group_class).to receive(:all).and_return([simple_metric_group])
+ allow(client).to receive(:label_values).and_return('metric_a')
+ end
+
+ context 'both metrics in the group pass requirements' do
+ before do
+ allow(client).to receive(:series).and_return(series_info_with_environment)
+ end
+
+ it 'responds with one metrics as active and no missing requiremens' do
+ expect(subject.query).to eq([{ group: 'name', priority: 1, active_metrics: 1, metrics_missing_requirements: 0 }])
+ end
+ end
+
+ context 'no metrics in group pass requirements' do
+ before do
+ allow(client).to receive(:series).and_return(series_info_without_environment)
+ end
+
+ it 'responds with one metrics as active and no missing requiremens' do
+ expect(subject.query).to eq([{ group: 'name', priority: 1, active_metrics: 0, metrics_missing_requirements: 1 }])
+ end
+ end
+ end
+
+ context 'with two groups where metrics are found in each group' do
+ let(:second_metric_group) { simple_metric_group(name: 'nameb', metrics: simple_metrics(added_metric_name: 'metric_c')) }
+
+ before do
+ allow(metric_group_class).to receive(:all).and_return([simple_metric_group, second_metric_group])
+ allow(client).to receive(:label_values).and_return('metric_c')
+ end
+
+ context 'all metrics in both groups pass requirements' do
+ before do
+ allow(client).to receive(:series).and_return(series_info_with_environment('metric_c'))
+ end
+
+ it 'responds with one metrics as active and no missing requiremens' do
+ expect(subject.query).to eq([
+ { group: 'name', priority: 1, active_metrics: 1, metrics_missing_requirements: 0 },
+ { group: 'nameb', priority: 1, active_metrics: 2, metrics_missing_requirements: 0 }
+ ]
+ )
+ end
+ end
+
+ context 'no metrics in groups pass requirements' do
+ before do
+ allow(client).to receive(:series).and_return(series_info_without_environment)
+ end
+
+ it 'responds with one metrics as active and no missing requiremens' do
+ expect(subject.query).to eq([
+ { group: 'name', priority: 1, active_metrics: 0, metrics_missing_requirements: 1 },
+ { group: 'nameb', priority: 1, active_metrics: 0, metrics_missing_requirements: 2 }
+ ]
+ )
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/prometheus_client_spec.rb b/spec/lib/gitlab/prometheus_client_spec.rb
index 2d8bd2f6b97..46eaadae206 100644
--- a/spec/lib/gitlab/prometheus_client_spec.rb
+++ b/spec/lib/gitlab/prometheus_client_spec.rb
@@ -119,6 +119,36 @@ describe Gitlab::PrometheusClient, lib: true do
end
end
+ describe '#series' do
+ let(:query_url) { prometheus_series_url('series_name', 'other_service') }
+
+ around do |example|
+ Timecop.freeze { example.run }
+ end
+
+ it 'calls endpoint and returns list of series' do
+ req_stub = stub_prometheus_request(query_url, body: prometheus_series('series_name'))
+ expected = prometheus_series('series_name').deep_stringify_keys['data']
+
+ expect(subject.series('series_name', 'other_service')).to eq(expected)
+
+ expect(req_stub).to have_been_requested
+ end
+ end
+
+ describe '#label_values' do
+ let(:query_url) { prometheus_label_values_url('__name__') }
+
+ it 'calls endpoint and returns label values' do
+ req_stub = stub_prometheus_request(query_url, body: prometheus_label_values)
+ expected = prometheus_label_values.deep_stringify_keys['data']
+
+ expect(subject.label_values('__name__')).to eq(expected)
+
+ expect(req_stub).to have_been_requested
+ end
+ end
+
describe '#query_range' do
let(:prometheus_query) { prometheus_memory_query('env-slug') }
let(:query_url) { prometheus_query_range_url(prometheus_query) }
diff --git a/spec/lib/gitlab/route_map_spec.rb b/spec/lib/gitlab/route_map_spec.rb
index 2370f56a613..21c00c6e5b8 100644
--- a/spec/lib/gitlab/route_map_spec.rb
+++ b/spec/lib/gitlab/route_map_spec.rb
@@ -4,43 +4,43 @@ describe Gitlab::RouteMap, lib: true do
describe '#initialize' do
context 'when the data is not YAML' do
it 'raises an error' do
- expect { described_class.new('"') }.
- to raise_error(Gitlab::RouteMap::FormatError, /valid YAML/)
+ expect { described_class.new('"') }
+ .to raise_error(Gitlab::RouteMap::FormatError, /valid YAML/)
end
end
context 'when the data is not a YAML array' do
it 'raises an error' do
- expect { described_class.new(YAML.dump('foo')) }.
- to raise_error(Gitlab::RouteMap::FormatError, /an array/)
+ expect { described_class.new(YAML.dump('foo')) }
+ .to raise_error(Gitlab::RouteMap::FormatError, /an array/)
end
end
context 'when an entry is not a hash' do
it 'raises an error' do
- expect { described_class.new(YAML.dump(['foo'])) }.
- to raise_error(Gitlab::RouteMap::FormatError, /a hash/)
+ expect { described_class.new(YAML.dump(['foo'])) }
+ .to raise_error(Gitlab::RouteMap::FormatError, /a hash/)
end
end
context 'when an entry does not have a source key' do
it 'raises an error' do
- expect { described_class.new(YAML.dump([{ 'public' => 'index.html' }])) }.
- to raise_error(Gitlab::RouteMap::FormatError, /source key/)
+ expect { described_class.new(YAML.dump([{ 'public' => 'index.html' }])) }
+ .to raise_error(Gitlab::RouteMap::FormatError, /source key/)
end
end
context 'when an entry does not have a public key' do
it 'raises an error' do
- expect { described_class.new(YAML.dump([{ 'source' => '/index\.html/' }])) }.
- to raise_error(Gitlab::RouteMap::FormatError, /public key/)
+ expect { described_class.new(YAML.dump([{ 'source' => '/index\.html/' }])) }
+ .to raise_error(Gitlab::RouteMap::FormatError, /public key/)
end
end
context 'when an entry source is not a valid regex' do
it 'raises an error' do
- expect { described_class.new(YAML.dump([{ 'source' => '/[/', 'public' => 'index.html' }])) }.
- to raise_error(Gitlab::RouteMap::FormatError, /regular expression/)
+ expect { described_class.new(YAML.dump([{ 'source' => '/[/', 'public' => 'index.html' }])) }
+ .to raise_error(Gitlab::RouteMap::FormatError, /regular expression/)
end
end
diff --git a/spec/lib/gitlab/sherlock/file_sample_spec.rb b/spec/lib/gitlab/sherlock/file_sample_spec.rb
index cadf8bbce78..4989d14def3 100644
--- a/spec/lib/gitlab/sherlock/file_sample_spec.rb
+++ b/spec/lib/gitlab/sherlock/file_sample_spec.rb
@@ -35,8 +35,8 @@ describe Gitlab::Sherlock::FileSample, lib: true do
describe '#relative_path' do
it 'returns the relative path' do
- expect(sample.relative_path).
- to eq('spec/lib/gitlab/sherlock/file_sample_spec.rb')
+ expect(sample.relative_path)
+ .to eq('spec/lib/gitlab/sherlock/file_sample_spec.rb')
end
end
diff --git a/spec/lib/gitlab/sherlock/line_profiler_spec.rb b/spec/lib/gitlab/sherlock/line_profiler_spec.rb
index d57627bba2b..39c6b2a4844 100644
--- a/spec/lib/gitlab/sherlock/line_profiler_spec.rb
+++ b/spec/lib/gitlab/sherlock/line_profiler_spec.rb
@@ -20,9 +20,9 @@ describe Gitlab::Sherlock::LineProfiler, lib: true do
describe '#profile_mri' do
it 'returns an Array containing the return value and profiling samples' do
- allow(profiler).to receive(:lineprof).
- and_yield.
- and_return({ __FILE__ => [[0, 0, 0, 0]] })
+ allow(profiler).to receive(:lineprof)
+ .and_yield
+ .and_return({ __FILE__ => [[0, 0, 0, 0]] })
retval, samples = profiler.profile_mri { 42 }
diff --git a/spec/lib/gitlab/sherlock/middleware_spec.rb b/spec/lib/gitlab/sherlock/middleware_spec.rb
index 2bbeb25ce98..b98ab0b14a2 100644
--- a/spec/lib/gitlab/sherlock/middleware_spec.rb
+++ b/spec/lib/gitlab/sherlock/middleware_spec.rb
@@ -72,8 +72,8 @@ describe Gitlab::Sherlock::Middleware, lib: true do
'REQUEST_URI' => '/cats'
}
- expect(middleware.transaction_from_env(env)).
- to be_an_instance_of(Gitlab::Sherlock::Transaction)
+ expect(middleware.transaction_from_env(env))
+ .to be_an_instance_of(Gitlab::Sherlock::Transaction)
end
end
end
diff --git a/spec/lib/gitlab/sherlock/query_spec.rb b/spec/lib/gitlab/sherlock/query_spec.rb
index 0a620428138..d97b5eef573 100644
--- a/spec/lib/gitlab/sherlock/query_spec.rb
+++ b/spec/lib/gitlab/sherlock/query_spec.rb
@@ -13,8 +13,8 @@ describe Gitlab::Sherlock::Query, lib: true do
sql = 'SELECT COUNT(*) FROM users WHERE id = $1'
bindings = [[double(:column), 10]]
- query = described_class.
- new_with_bindings(sql, bindings, started_at, finished_at)
+ query = described_class
+ .new_with_bindings(sql, bindings, started_at, finished_at)
expect(query.query).to eq('SELECT COUNT(*) FROM users WHERE id = 10;')
end
diff --git a/spec/lib/gitlab/sherlock/transaction_spec.rb b/spec/lib/gitlab/sherlock/transaction_spec.rb
index 9fe18f253f0..6ae1aa20ea7 100644
--- a/spec/lib/gitlab/sherlock/transaction_spec.rb
+++ b/spec/lib/gitlab/sherlock/transaction_spec.rb
@@ -109,8 +109,8 @@ describe Gitlab::Sherlock::Transaction, lib: true do
query1 = Gitlab::Sherlock::Query.new('SELECT 1', start_time, start_time)
- query2 = Gitlab::Sherlock::Query.
- new('SELECT 2', start_time, start_time + 5)
+ query2 = Gitlab::Sherlock::Query
+ .new('SELECT 2', start_time, start_time + 5)
transaction.queries << query1
transaction.queries << query2
@@ -162,11 +162,11 @@ describe Gitlab::Sherlock::Transaction, lib: true do
describe '#profile_lines' do
describe 'when line profiling is enabled' do
it 'yields the block using the line profiler' do
- allow(Gitlab::Sherlock).to receive(:enable_line_profiler?).
- and_return(true)
+ allow(Gitlab::Sherlock).to receive(:enable_line_profiler?)
+ .and_return(true)
- allow_any_instance_of(Gitlab::Sherlock::LineProfiler).
- to receive(:profile).and_return('cats are amazing', [])
+ allow_any_instance_of(Gitlab::Sherlock::LineProfiler)
+ .to receive(:profile).and_return('cats are amazing', [])
retval = transaction.profile_lines { 'cats are amazing' }
@@ -176,8 +176,8 @@ describe Gitlab::Sherlock::Transaction, lib: true do
describe 'when line profiling is disabled' do
it 'yields the block' do
- allow(Gitlab::Sherlock).to receive(:enable_line_profiler?).
- and_return(false)
+ allow(Gitlab::Sherlock).to receive(:enable_line_profiler?)
+ .and_return(false)
retval = transaction.profile_lines { 'cats are amazing' }
@@ -196,8 +196,8 @@ describe Gitlab::Sherlock::Transaction, lib: true do
end
it 'tracks executed queries' do
- expect(transaction).to receive(:track_query).
- with('SELECT 1', [], time, time)
+ expect(transaction).to receive(:track_query)
+ .with('SELECT 1', [], time, time)
subscription.publish('test', time, time, nil, query_data)
end
@@ -205,8 +205,8 @@ describe Gitlab::Sherlock::Transaction, lib: true do
it 'only tracks queries triggered from the transaction thread' do
expect(transaction).not_to receive(:track_query)
- Thread.new { subscription.publish('test', time, time, nil, query_data) }.
- join
+ Thread.new { subscription.publish('test', time, time, nil, query_data) }
+ .join
end
end
@@ -228,8 +228,8 @@ describe Gitlab::Sherlock::Transaction, lib: true do
it 'only tracks views rendered from the transaction thread' do
expect(transaction).not_to receive(:track_view)
- Thread.new { subscription.publish('test', time, time, nil, view_data) }.
- join
+ Thread.new { subscription.publish('test', time, time, nil, view_data) }
+ .join
end
end
end
diff --git a/spec/lib/gitlab/sidekiq_status/client_middleware_spec.rb b/spec/lib/gitlab/sidekiq_status/client_middleware_spec.rb
index 6307f8c16a3..37d9e1d3e6b 100644
--- a/spec/lib/gitlab/sidekiq_status/client_middleware_spec.rb
+++ b/spec/lib/gitlab/sidekiq_status/client_middleware_spec.rb
@@ -5,8 +5,8 @@ describe Gitlab::SidekiqStatus::ClientMiddleware do
it 'tracks the job in Redis' do
expect(Gitlab::SidekiqStatus).to receive(:set).with('123', Gitlab::SidekiqStatus::DEFAULT_EXPIRATION)
- described_class.new.
- call('Foo', { 'jid' => '123' }, double(:queue), double(:pool)) { nil }
+ described_class.new
+ .call('Foo', { 'jid' => '123' }, double(:queue), double(:pool)) { nil }
end
end
end
diff --git a/spec/lib/gitlab/sidekiq_status/server_middleware_spec.rb b/spec/lib/gitlab/sidekiq_status/server_middleware_spec.rb
index 80728197b8c..04e09d3dec8 100644
--- a/spec/lib/gitlab/sidekiq_status/server_middleware_spec.rb
+++ b/spec/lib/gitlab/sidekiq_status/server_middleware_spec.rb
@@ -5,8 +5,8 @@ describe Gitlab::SidekiqStatus::ServerMiddleware do
it 'stops tracking of a job upon completion' do
expect(Gitlab::SidekiqStatus).to receive(:unset).with('123')
- ret = described_class.new.
- call(double(:worker), { 'jid' => '123' }, double(:queue)) { 10 }
+ ret = described_class.new
+ .call(double(:worker), { 'jid' => '123' }, double(:queue)) { 10 }
expect(ret).to eq(10)
end
diff --git a/spec/lib/gitlab/url_builder_spec.rb b/spec/lib/gitlab/url_builder_spec.rb
index e8a37e8d77b..e9a6e273516 100644
--- a/spec/lib/gitlab/url_builder_spec.rb
+++ b/spec/lib/gitlab/url_builder_spec.rb
@@ -112,8 +112,8 @@ describe Gitlab::UrlBuilder, lib: true do
it 'returns a proper URL' do
project = build_stubbed(:empty_project)
- expect { described_class.build(project) }.
- to raise_error(NotImplementedError, 'No URL builder defined for Project')
+ expect { described_class.build(project) }
+ .to raise_error(NotImplementedError, 'No URL builder defined for Project')
end
end
end
diff --git a/spec/lib/gitlab/view/presenter/delegated_spec.rb b/spec/lib/gitlab/view/presenter/delegated_spec.rb
index e9d4af54389..940a2ce6ebd 100644
--- a/spec/lib/gitlab/view/presenter/delegated_spec.rb
+++ b/spec/lib/gitlab/view/presenter/delegated_spec.rb
@@ -18,8 +18,8 @@ describe Gitlab::View::Presenter::Delegated do
end
it 'raise an error if the presentee already respond to method' do
- expect { presenter_class.new(project, user: 'Jane Doe') }.
- to raise_error Gitlab::View::Presenter::CannotOverrideMethodError
+ expect { presenter_class.new(project, user: 'Jane Doe') }
+ .to raise_error Gitlab::View::Presenter::CannotOverrideMethodError
end
end
diff --git a/spec/lib/gitlab/visibility_level_spec.rb b/spec/lib/gitlab/visibility_level_spec.rb
index a8f21803ec7..84d2484cc8a 100644
--- a/spec/lib/gitlab/visibility_level_spec.rb
+++ b/spec/lib/gitlab/visibility_level_spec.rb
@@ -23,30 +23,30 @@ describe Gitlab::VisibilityLevel, lib: true do
it 'returns all levels for an admin' do
user = double(:user, admin?: true)
- expect(described_class.levels_for_user(user)).
- to eq([Gitlab::VisibilityLevel::PRIVATE,
- Gitlab::VisibilityLevel::INTERNAL,
- Gitlab::VisibilityLevel::PUBLIC])
+ expect(described_class.levels_for_user(user))
+ .to eq([Gitlab::VisibilityLevel::PRIVATE,
+ Gitlab::VisibilityLevel::INTERNAL,
+ Gitlab::VisibilityLevel::PUBLIC])
end
it 'returns INTERNAL and PUBLIC for internal users' do
user = double(:user, admin?: false, external?: false)
- expect(described_class.levels_for_user(user)).
- to eq([Gitlab::VisibilityLevel::INTERNAL,
- Gitlab::VisibilityLevel::PUBLIC])
+ expect(described_class.levels_for_user(user))
+ .to eq([Gitlab::VisibilityLevel::INTERNAL,
+ Gitlab::VisibilityLevel::PUBLIC])
end
it 'returns PUBLIC for external users' do
user = double(:user, admin?: false, external?: true)
- expect(described_class.levels_for_user(user)).
- to eq([Gitlab::VisibilityLevel::PUBLIC])
+ expect(described_class.levels_for_user(user))
+ .to eq([Gitlab::VisibilityLevel::PUBLIC])
end
it 'returns PUBLIC when no user is given' do
- expect(described_class.levels_for_user).
- to eq([Gitlab::VisibilityLevel::PUBLIC])
+ expect(described_class.levels_for_user)
+ .to eq([Gitlab::VisibilityLevel::PUBLIC])
end
end
end
diff --git a/spec/lib/gitlab/workhorse_spec.rb b/spec/lib/gitlab/workhorse_spec.rb
index a3e8166cb70..493ff3bb5fb 100644
--- a/spec/lib/gitlab/workhorse_spec.rb
+++ b/spec/lib/gitlab/workhorse_spec.rb
@@ -216,7 +216,6 @@ describe Gitlab::Workhorse, lib: true do
it 'includes a Repository param' do
repo_param = { Repository: {
- path: '', # deprecated field; grpc automatically creates it anyway
storage_name: 'default',
relative_path: project.full_path + '.git'
} }
diff --git a/spec/lib/mattermost/command_spec.rb b/spec/lib/mattermost/command_spec.rb
index 4b5938edeb9..369e7b181b9 100644
--- a/spec/lib/mattermost/command_spec.rb
+++ b/spec/lib/mattermost/command_spec.rb
@@ -6,8 +6,8 @@ describe Mattermost::Command do
before do
Mattermost::Session.base_uri('http://mattermost.example.com')
- allow_any_instance_of(Mattermost::Client).to receive(:with_session).
- and_yield(Mattermost::Session.new(nil))
+ allow_any_instance_of(Mattermost::Client).to receive(:with_session)
+ .and_yield(Mattermost::Session.new(nil))
end
describe '#create' do
@@ -20,12 +20,12 @@ describe Mattermost::Command do
context 'for valid trigger word' do
before do
- stub_request(:post, 'http://mattermost.example.com/api/v3/teams/abc/commands/create').
- with(body: {
+ stub_request(:post, 'http://mattermost.example.com/api/v3/teams/abc/commands/create')
+ .with(body: {
team_id: 'abc',
trigger: 'gitlab'
- }.to_json).
- to_return(
+ }.to_json)
+ .to_return(
status: 200,
headers: { 'Content-Type' => 'application/json' },
body: { token: 'token' }.to_json
@@ -39,8 +39,8 @@ describe Mattermost::Command do
context 'for error message' do
before do
- stub_request(:post, 'http://mattermost.example.com/api/v3/teams/abc/commands/create').
- to_return(
+ stub_request(:post, 'http://mattermost.example.com/api/v3/teams/abc/commands/create')
+ .to_return(
status: 500,
headers: { 'Content-Type' => 'application/json' },
body: {
diff --git a/spec/lib/mattermost/session_spec.rb b/spec/lib/mattermost/session_spec.rb
index 74d12e37181..be3908e8f6a 100644
--- a/spec/lib/mattermost/session_spec.rb
+++ b/spec/lib/mattermost/session_spec.rb
@@ -21,8 +21,8 @@ describe Mattermost::Session, type: :request do
describe '#with session' do
let(:location) { 'http://location.tld' }
let!(:stub) do
- WebMock.stub_request(:get, "#{mattermost_url}/api/v3/oauth/gitlab/login").
- to_return(headers: { 'location' => location }, status: 307)
+ WebMock.stub_request(:get, "#{mattermost_url}/api/v3/oauth/gitlab/login")
+ .to_return(headers: { 'location' => location }, status: 307)
end
context 'without oauth uri' do
@@ -60,9 +60,9 @@ describe Mattermost::Session, type: :request do
end
before do
- WebMock.stub_request(:get, "#{mattermost_url}/signup/gitlab/complete").
- with(query: hash_including({ 'state' => state })).
- to_return do |request|
+ WebMock.stub_request(:get, "#{mattermost_url}/signup/gitlab/complete")
+ .with(query: hash_including({ 'state' => state }))
+ .to_return do |request|
post "/oauth/token",
client_id: doorkeeper.uid,
client_secret: doorkeeper.secret,
@@ -75,8 +75,8 @@ describe Mattermost::Session, type: :request do
end
end
- WebMock.stub_request(:post, "#{mattermost_url}/api/v3/users/logout").
- to_return(headers: { Authorization: 'token thisworksnow' }, status: 200)
+ WebMock.stub_request(:post, "#{mattermost_url}/api/v3/users/logout")
+ .to_return(headers: { Authorization: 'token thisworksnow' }, status: 200)
end
it 'can setup a session' do
diff --git a/spec/lib/mattermost/team_spec.rb b/spec/lib/mattermost/team_spec.rb
index ac493fdb20f..e638ad7a2c9 100644
--- a/spec/lib/mattermost/team_spec.rb
+++ b/spec/lib/mattermost/team_spec.rb
@@ -4,8 +4,8 @@ describe Mattermost::Team do
before do
Mattermost::Session.base_uri('http://mattermost.example.com')
- allow_any_instance_of(Mattermost::Client).to receive(:with_session).
- and_yield(Mattermost::Session.new(nil))
+ allow_any_instance_of(Mattermost::Client).to receive(:with_session)
+ .and_yield(Mattermost::Session.new(nil))
end
describe '#all' do
@@ -30,8 +30,8 @@ describe Mattermost::Team do
end
before do
- stub_request(:get, 'http://mattermost.example.com/api/v3/teams/all').
- to_return(
+ stub_request(:get, 'http://mattermost.example.com/api/v3/teams/all')
+ .to_return(
status: 200,
headers: { 'Content-Type' => 'application/json' },
body: response.to_json
@@ -45,8 +45,8 @@ describe Mattermost::Team do
context 'for error message' do
before do
- stub_request(:get, 'http://mattermost.example.com/api/v3/teams/all').
- to_return(
+ stub_request(:get, 'http://mattermost.example.com/api/v3/teams/all')
+ .to_return(
status: 500,
headers: { 'Content-Type' => 'application/json' },
body: {
diff --git a/spec/mailers/abuse_report_mailer_spec.rb b/spec/mailers/abuse_report_mailer_spec.rb
index eb433c38873..bda892083b3 100644
--- a/spec/mailers/abuse_report_mailer_spec.rb
+++ b/spec/mailers/abuse_report_mailer_spec.rb
@@ -30,8 +30,8 @@ describe AbuseReportMailer do
it 'returns early' do
stub_application_setting(admin_notification_email: nil)
- expect { described_class.notify(spy).deliver_now }.
- not_to change { ActionMailer::Base.deliveries.count }
+ expect { described_class.notify(spy).deliver_now }
+ .not_to change { ActionMailer::Base.deliveries.count }
end
end
end
diff --git a/spec/migrations/migrate_build_stage_reference_spec.rb b/spec/migrations/migrate_build_stage_reference_again_spec.rb
index 80b321860c2..6be480ce58e 100644
--- a/spec/migrations/migrate_build_stage_reference_spec.rb
+++ b/spec/migrations/migrate_build_stage_reference_again_spec.rb
@@ -1,7 +1,7 @@
require 'spec_helper'
-require Rails.root.join('db', 'post_migrate', '20170526185921_migrate_build_stage_reference.rb')
+require Rails.root.join('db', 'post_migrate', '20170526190000_migrate_build_stage_reference_again.rb')
-describe MigrateBuildStageReference, :migration do
+describe MigrateBuildStageReferenceAgain, :migration do
##
# Create test data - pipeline and CI/CD jobs.
#
diff --git a/spec/migrations/migrate_process_commit_worker_jobs_spec.rb b/spec/migrations/migrate_process_commit_worker_jobs_spec.rb
index 3db57595fa6..4223d2337a8 100644
--- a/spec/migrations/migrate_process_commit_worker_jobs_spec.rb
+++ b/spec/migrations/migrate_process_commit_worker_jobs_spec.rb
@@ -11,33 +11,33 @@ describe MigrateProcessCommitWorkerJobs do
describe 'Project' do
describe 'find_including_path' do
it 'returns Project instances' do
- expect(described_class::Project.find_including_path(project.id)).
- to be_an_instance_of(described_class::Project)
+ expect(described_class::Project.find_including_path(project.id))
+ .to be_an_instance_of(described_class::Project)
end
it 'selects the full path for every Project' do
- migration_project = described_class::Project.
- find_including_path(project.id)
+ migration_project = described_class::Project
+ .find_including_path(project.id)
- expect(migration_project[:path_with_namespace]).
- to eq(project.path_with_namespace)
+ expect(migration_project[:path_with_namespace])
+ .to eq(project.path_with_namespace)
end
end
describe '#repository_storage_path' do
it 'returns the storage path for the repository' do
- migration_project = described_class::Project.
- find_including_path(project.id)
+ migration_project = described_class::Project
+ .find_including_path(project.id)
- expect(File.directory?(migration_project.repository_storage_path)).
- to eq(true)
+ expect(File.directory?(migration_project.repository_storage_path))
+ .to eq(true)
end
end
describe '#repository_path' do
it 'returns the path to the repository' do
- migration_project = described_class::Project.
- find_including_path(project.id)
+ migration_project = described_class::Project
+ .find_including_path(project.id)
expect(File.directory?(migration_project.repository_path)).to eq(true)
end
@@ -45,11 +45,11 @@ describe MigrateProcessCommitWorkerJobs do
describe '#repository' do
it 'returns a Rugged::Repository' do
- migration_project = described_class::Project.
- find_including_path(project.id)
+ migration_project = described_class::Project
+ .find_including_path(project.id)
- expect(migration_project.repository).
- to be_an_instance_of(Rugged::Repository)
+ expect(migration_project.repository)
+ .to be_an_instance_of(Rugged::Repository)
end
end
end
@@ -73,9 +73,9 @@ describe MigrateProcessCommitWorkerJobs do
end
it 'skips jobs using a project that no longer exists' do
- allow(described_class::Project).to receive(:find_including_path).
- with(project.id).
- and_return(nil)
+ allow(described_class::Project).to receive(:find_including_path)
+ .with(project.id)
+ .and_return(nil)
migration.up
@@ -83,9 +83,9 @@ describe MigrateProcessCommitWorkerJobs do
end
it 'skips jobs using commits that no longer exist' do
- allow_any_instance_of(Rugged::Repository).to receive(:lookup).
- with(commit.oid).
- and_raise(Rugged::OdbError)
+ allow_any_instance_of(Rugged::Repository).to receive(:lookup)
+ .with(commit.oid)
+ .and_raise(Rugged::OdbError)
migration.up
@@ -99,12 +99,12 @@ describe MigrateProcessCommitWorkerJobs do
end
it 'encodes data to UTF-8' do
- allow_any_instance_of(Rugged::Repository).to receive(:lookup).
- with(commit.oid).
- and_return(commit)
+ allow_any_instance_of(Rugged::Repository).to receive(:lookup)
+ .with(commit.oid)
+ .and_return(commit)
- allow(commit).to receive(:message).
- and_return('김치'.force_encoding('BINARY'))
+ allow(commit).to receive(:message)
+ .and_return('김치'.force_encoding('BINARY'))
migration.up
diff --git a/spec/migrations/turn_nested_groups_into_regular_groups_for_mysql_spec.rb b/spec/migrations/turn_nested_groups_into_regular_groups_for_mysql_spec.rb
index 175bf1876b2..42109fd0743 100644
--- a/spec/migrations/turn_nested_groups_into_regular_groups_for_mysql_spec.rb
+++ b/spec/migrations/turn_nested_groups_into_regular_groups_for_mysql_spec.rb
@@ -31,8 +31,8 @@ describe TurnNestedGroupsIntoRegularGroupsForMysql do
end
it 'adds members of parent groups as members to the migrated group' do
- is_member = child_group.members.
- where(user_id: member, access_level: Gitlab::Access::DEVELOPER).any?
+ is_member = child_group.members
+ .where(user_id: member, access_level: Gitlab::Access::DEVELOPER).any?
expect(is_member).to eq(true)
end
@@ -44,21 +44,21 @@ describe TurnNestedGroupsIntoRegularGroupsForMysql do
end
it 'renames projects of the nested group' do
- expect(updated_project.path_with_namespace).
- to eq("#{parent_group.name}-#{child_group.name}/#{updated_project.path}")
+ expect(updated_project.path_with_namespace)
+ .to eq("#{parent_group.name}-#{child_group.name}/#{updated_project.path}")
end
it 'renames the repository of any projects' do
- expect(updated_project.repository.path).
- to end_with("#{parent_group.name}-#{child_group.name}/#{updated_project.path}.git")
+ expect(updated_project.repository.path)
+ .to end_with("#{parent_group.name}-#{child_group.name}/#{updated_project.path}.git")
expect(File.directory?(updated_project.repository.path)).to eq(true)
end
it 'creates a redirect route for renamed projects' do
- exists = RedirectRoute.
- where(source_type: 'Project', source_id: project.id).
- any?
+ exists = RedirectRoute
+ .where(source_type: 'Project', source_id: project.id)
+ .any?
expect(exists).to eq(true)
end
diff --git a/spec/models/ability_spec.rb b/spec/models/ability_spec.rb
index 92d70cfc64c..090f9e70c50 100644
--- a/spec/models/ability_spec.rb
+++ b/spec/models/ability_spec.rb
@@ -69,8 +69,8 @@ describe Ability, lib: true do
project = create(:empty_project, :public)
user = build(:user)
- expect(described_class.users_that_can_read_project([user], project)).
- to eq([user])
+ expect(described_class.users_that_can_read_project([user], project))
+ .to eq([user])
end
end
@@ -80,8 +80,8 @@ describe Ability, lib: true do
it 'returns users that are administrators' do
user = build(:user, admin: true)
- expect(described_class.users_that_can_read_project([user], project)).
- to eq([user])
+ expect(described_class.users_that_can_read_project([user], project))
+ .to eq([user])
end
it 'returns internal users while skipping external users' do
@@ -89,8 +89,8 @@ describe Ability, lib: true do
user2 = build(:user, external: true)
users = [user1, user2]
- expect(described_class.users_that_can_read_project(users, project)).
- to eq([user1])
+ expect(described_class.users_that_can_read_project(users, project))
+ .to eq([user1])
end
it 'returns external users if they are the project owner' do
@@ -100,8 +100,8 @@ describe Ability, lib: true do
expect(project).to receive(:owner).twice.and_return(user1)
- expect(described_class.users_that_can_read_project(users, project)).
- to eq([user1])
+ expect(described_class.users_that_can_read_project(users, project))
+ .to eq([user1])
end
it 'returns external users if they are project members' do
@@ -111,8 +111,8 @@ describe Ability, lib: true do
expect(project.team).to receive(:members).twice.and_return([user1])
- expect(described_class.users_that_can_read_project(users, project)).
- to eq([user1])
+ expect(described_class.users_that_can_read_project(users, project))
+ .to eq([user1])
end
it 'returns an empty Array if all users are external users without access' do
@@ -120,8 +120,8 @@ describe Ability, lib: true do
user2 = build(:user, external: true)
users = [user1, user2]
- expect(described_class.users_that_can_read_project(users, project)).
- to eq([])
+ expect(described_class.users_that_can_read_project(users, project))
+ .to eq([])
end
end
@@ -131,8 +131,8 @@ describe Ability, lib: true do
it 'returns users that are administrators' do
user = build(:user, admin: true)
- expect(described_class.users_that_can_read_project([user], project)).
- to eq([user])
+ expect(described_class.users_that_can_read_project([user], project))
+ .to eq([user])
end
it 'returns external users if they are the project owner' do
@@ -142,8 +142,8 @@ describe Ability, lib: true do
expect(project).to receive(:owner).twice.and_return(user1)
- expect(described_class.users_that_can_read_project(users, project)).
- to eq([user1])
+ expect(described_class.users_that_can_read_project(users, project))
+ .to eq([user1])
end
it 'returns external users if they are project members' do
@@ -153,8 +153,8 @@ describe Ability, lib: true do
expect(project.team).to receive(:members).twice.and_return([user1])
- expect(described_class.users_that_can_read_project(users, project)).
- to eq([user1])
+ expect(described_class.users_that_can_read_project(users, project))
+ .to eq([user1])
end
it 'returns an empty Array if all users are internal users without access' do
@@ -162,8 +162,8 @@ describe Ability, lib: true do
user2 = build(:user)
users = [user1, user2]
- expect(described_class.users_that_can_read_project(users, project)).
- to eq([])
+ expect(described_class.users_that_can_read_project(users, project))
+ .to eq([])
end
it 'returns an empty Array if all users are external users without access' do
@@ -171,8 +171,8 @@ describe Ability, lib: true do
user2 = build(:user, external: true)
users = [user1, user2]
- expect(described_class.users_that_can_read_project(users, project)).
- to eq([])
+ expect(described_class.users_that_can_read_project(users, project))
+ .to eq([])
end
end
end
@@ -210,8 +210,8 @@ describe Ability, lib: true do
user = build(:user, admin: true)
issue = build(:issue)
- expect(described_class.issues_readable_by_user([issue], user)).
- to eq([issue])
+ expect(described_class.issues_readable_by_user([issue], user))
+ .to eq([issue])
end
end
@@ -222,8 +222,8 @@ describe Ability, lib: true do
expect(issue).to receive(:readable_by?).with(user).and_return(true)
- expect(described_class.issues_readable_by_user([issue], user)).
- to eq([issue])
+ expect(described_class.issues_readable_by_user([issue], user))
+ .to eq([issue])
end
it 'returns an empty Array when no issues are readable' do
@@ -244,8 +244,8 @@ describe Ability, lib: true do
expect(hidden_issue).to receive(:publicly_visible?).and_return(false)
expect(visible_issue).to receive(:publicly_visible?).and_return(true)
- issues = described_class.
- issues_readable_by_user([hidden_issue, visible_issue])
+ issues = described_class
+ .issues_readable_by_user([hidden_issue, visible_issue])
expect(issues).to eq([visible_issue])
end
diff --git a/spec/models/abuse_report_spec.rb b/spec/models/abuse_report_spec.rb
index 90aec2b45e6..c1bf5551fe0 100644
--- a/spec/models/abuse_report_spec.rb
+++ b/spec/models/abuse_report_spec.rb
@@ -36,8 +36,8 @@ RSpec.describe AbuseReport, type: :model do
describe '#notify' do
it 'delivers' do
- expect(AbuseReportMailer).to receive(:notify).with(subject.id).
- and_return(spy)
+ expect(AbuseReportMailer).to receive(:notify).with(subject.id)
+ .and_return(spy)
subject.notify
end
diff --git a/spec/models/ci/build_spec.rb b/spec/models/ci/build_spec.rb
index 3816422fec6..488697f74eb 100644
--- a/spec/models/ci/build_spec.rb
+++ b/spec/models/ci/build_spec.rb
@@ -451,42 +451,6 @@ describe Ci::Build, :models do
end
end
- describe '#environment_url' do
- subject { job.environment_url }
-
- context 'when yaml environment uses $CI_COMMIT_REF_NAME' do
- let(:job) do
- create(:ci_build,
- ref: 'master',
- options: { environment: { url: 'http://review/$CI_COMMIT_REF_NAME' } })
- end
-
- it { is_expected.to eq('http://review/master') }
- end
-
- context 'when yaml environment uses yaml_variables containing symbol keys' do
- let(:job) do
- create(:ci_build,
- yaml_variables: [{ key: :APP_HOST, value: 'host' }],
- options: { environment: { url: 'http://review/$APP_HOST' } })
- end
-
- it { is_expected.to eq('http://review/host') }
- end
-
- context 'when yaml environment does not have url' do
- let(:job) { create(:ci_build, environment: 'staging') }
-
- let!(:environment) do
- create(:environment, project: job.project, name: job.environment)
- end
-
- it 'returns the external_url from persisted environment' do
- is_expected.to eq(environment.external_url)
- end
- end
- end
-
describe '#starts_environment?' do
subject { build.starts_environment? }
@@ -899,8 +863,8 @@ describe Ci::Build, :models do
pipeline2 = create(:ci_pipeline, project: project)
@build2 = create(:ci_build, pipeline: pipeline2)
- allow(@merge_request).to receive(:commits_sha).
- and_return([pipeline.sha, pipeline2.sha])
+ allow(@merge_request).to receive(:commits_sha)
+ .and_return([pipeline.sha, pipeline2.sha])
allow(MergeRequest).to receive_message_chain(:includes, :where, :reorder).and_return([@merge_request])
end
@@ -1292,10 +1256,20 @@ describe Ci::Build, :models do
context 'when the URL was set from the job' do
before do
- build.update(options: { environment: { url: 'http://host/$CI_JOB_NAME' } })
+ build.update(options: { environment: { url: url } })
end
it_behaves_like 'containing environment variables'
+
+ context 'when variables are used in the URL, it does not expand' do
+ let(:url) { 'http://$CI_PROJECT_NAME-$CI_ENVIRONMENT_SLUG' }
+
+ it_behaves_like 'containing environment variables'
+
+ it 'puts $CI_ENVIRONMENT_URL in the last so all other variables are available to be used when runners are trying to expand it' do
+ expect(subject.last).to eq(environment_variables.last)
+ end
+ end
end
context 'when the URL was not set from the job, but environment' do
diff --git a/spec/models/ci/pipeline_schedule_spec.rb b/spec/models/ci/pipeline_schedule_spec.rb
index b00e7a73571..56817baf79d 100644
--- a/spec/models/ci/pipeline_schedule_spec.rb
+++ b/spec/models/ci/pipeline_schedule_spec.rb
@@ -40,8 +40,8 @@ describe Ci::PipelineSchedule, models: true do
context 'when creates new pipeline schedule' do
let(:expected_next_run_at) do
- Gitlab::Ci::CronParser.new(pipeline_schedule.cron, pipeline_schedule.cron_timezone).
- next_time_from(Time.now)
+ Gitlab::Ci::CronParser.new(pipeline_schedule.cron, pipeline_schedule.cron_timezone)
+ .next_time_from(Time.now)
end
it 'updates next_run_at automatically' do
@@ -53,8 +53,8 @@ describe Ci::PipelineSchedule, models: true do
let(:new_cron) { '0 0 1 1 *' }
let(:expected_next_run_at) do
- Gitlab::Ci::CronParser.new(new_cron, pipeline_schedule.cron_timezone).
- next_time_from(Time.now)
+ Gitlab::Ci::CronParser.new(new_cron, pipeline_schedule.cron_timezone)
+ .next_time_from(Time.now)
end
it 'updates next_run_at automatically' do
@@ -72,8 +72,8 @@ describe Ci::PipelineSchedule, models: true do
let(:future_time) { 10.days.from_now }
let(:expected_next_run_at) do
- Gitlab::Ci::CronParser.new(pipeline_schedule.cron, pipeline_schedule.cron_timezone).
- next_time_from(future_time)
+ Gitlab::Ci::CronParser.new(pipeline_schedule.cron, pipeline_schedule.cron_timezone)
+ .next_time_from(future_time)
end
it 'points to proper next_run_at' do
diff --git a/spec/models/ci/pipeline_spec.rb b/spec/models/ci/pipeline_spec.rb
index e86cbe8498a..dab8e8ca432 100644
--- a/spec/models/ci/pipeline_spec.rb
+++ b/spec/models/ci/pipeline_spec.rb
@@ -608,8 +608,8 @@ describe Ci::Pipeline, models: true do
it 'returns the latest pipeline for the same ref and different sha' do
expect(pipelines.map(&:sha)).to contain_exactly('A', 'B', 'C')
- expect(pipelines.map(&:status)).
- to contain_exactly('success', 'failed', 'skipped')
+ expect(pipelines.map(&:status))
+ .to contain_exactly('success', 'failed', 'skipped')
end
end
@@ -618,8 +618,8 @@ describe Ci::Pipeline, models: true do
it 'returns the latest pipeline for ref and different sha' do
expect(pipelines.map(&:sha)).to contain_exactly('A', 'B')
- expect(pipelines.map(&:status)).
- to contain_exactly('success', 'failed')
+ expect(pipelines.map(&:status))
+ .to contain_exactly('success', 'failed')
end
end
end
@@ -654,8 +654,8 @@ describe Ci::Pipeline, models: true do
end
it 'returns the latest successful pipeline' do
- expect(described_class.latest_successful_for('ref')).
- to eq(latest_successful_pipeline)
+ expect(described_class.latest_successful_for('ref'))
+ .to eq(latest_successful_pipeline)
end
end
@@ -1201,8 +1201,8 @@ describe Ci::Pipeline, models: true do
before do
project.team << [pipeline.user, Gitlab::Access::DEVELOPER]
- pipeline.user.global_notification_setting.
- update(level: 'custom', failed_pipeline: true, success_pipeline: true)
+ pipeline.user.global_notification_setting
+ .update(level: 'custom', failed_pipeline: true, success_pipeline: true)
reset_delivered_emails!
diff --git a/spec/models/ci/variable_spec.rb b/spec/models/ci/variable_spec.rb
index 077b10227d7..83494af24ba 100644
--- a/spec/models/ci/variable_spec.rb
+++ b/spec/models/ci/variable_spec.rb
@@ -54,8 +54,8 @@ describe Ci::Variable, models: true do
it 'fails to decrypt if iv is incorrect' do
subject.encrypted_value_iv = SecureRandom.hex
subject.instance_variable_set(:@value, nil)
- expect { subject.value }.
- to raise_error(OpenSSL::Cipher::CipherError, 'bad decrypt')
+ expect { subject.value }
+ .to raise_error(OpenSSL::Cipher::CipherError, 'bad decrypt')
end
end
diff --git a/spec/models/commit_range_spec.rb b/spec/models/commit_range_spec.rb
index e4bddf67096..ba9c3f66d21 100644
--- a/spec/models/commit_range_spec.rb
+++ b/spec/models/commit_range_spec.rb
@@ -147,9 +147,9 @@ describe CommitRange, models: true do
note: commit1.revert_description(user),
project: issue.project)
- expect_any_instance_of(Commit).to receive(:reverts_commit?).
- with(commit1, user).
- and_return(true)
+ expect_any_instance_of(Commit).to receive(:reverts_commit?)
+ .with(commit1, user)
+ .and_return(true)
expect(commit1.has_been_reverted?(user, issue)).to eq(true)
end
diff --git a/spec/models/concerns/case_sensitivity_spec.rb b/spec/models/concerns/case_sensitivity_spec.rb
index 92fdc5cd65d..a6fccb668e3 100644
--- a/spec/models/concerns/case_sensitivity_spec.rb
+++ b/spec/models/concerns/case_sensitivity_spec.rb
@@ -15,13 +15,13 @@ describe CaseSensitivity, models: true do
it 'returns the criteria for a column and a value' do
criteria = double(:criteria)
- expect(connection).to receive(:quote_table_name).
- with(:foo).
- and_return('"foo"')
+ expect(connection).to receive(:quote_table_name)
+ .with(:foo)
+ .and_return('"foo"')
- expect(model).to receive(:where).
- with(%q{LOWER("foo") = LOWER(:value)}, value: 'bar').
- and_return(criteria)
+ expect(model).to receive(:where)
+ .with(%q{LOWER("foo") = LOWER(:value)}, value: 'bar')
+ .and_return(criteria)
expect(model.iwhere(foo: 'bar')).to eq(criteria)
end
@@ -29,13 +29,13 @@ describe CaseSensitivity, models: true do
it 'returns the criteria for a column with a table, and a value' do
criteria = double(:criteria)
- expect(connection).to receive(:quote_table_name).
- with(:'foo.bar').
- and_return('"foo"."bar"')
+ expect(connection).to receive(:quote_table_name)
+ .with(:'foo.bar')
+ .and_return('"foo"."bar"')
- expect(model).to receive(:where).
- with(%q{LOWER("foo"."bar") = LOWER(:value)}, value: 'bar').
- and_return(criteria)
+ expect(model).to receive(:where)
+ .with(%q{LOWER("foo"."bar") = LOWER(:value)}, value: 'bar')
+ .and_return(criteria)
expect(model.iwhere('foo.bar'.to_sym => 'bar')).to eq(criteria)
end
@@ -46,21 +46,21 @@ describe CaseSensitivity, models: true do
initial = double(:criteria)
final = double(:criteria)
- expect(connection).to receive(:quote_table_name).
- with(:foo).
- and_return('"foo"')
+ expect(connection).to receive(:quote_table_name)
+ .with(:foo)
+ .and_return('"foo"')
- expect(connection).to receive(:quote_table_name).
- with(:bar).
- and_return('"bar"')
+ expect(connection).to receive(:quote_table_name)
+ .with(:bar)
+ .and_return('"bar"')
- expect(model).to receive(:where).
- with(%q{LOWER("foo") = LOWER(:value)}, value: 'bar').
- and_return(initial)
+ expect(model).to receive(:where)
+ .with(%q{LOWER("foo") = LOWER(:value)}, value: 'bar')
+ .and_return(initial)
- expect(initial).to receive(:where).
- with(%q{LOWER("bar") = LOWER(:value)}, value: 'baz').
- and_return(final)
+ expect(initial).to receive(:where)
+ .with(%q{LOWER("bar") = LOWER(:value)}, value: 'baz')
+ .and_return(final)
got = model.iwhere(foo: 'bar', bar: 'baz')
@@ -71,21 +71,21 @@ describe CaseSensitivity, models: true do
initial = double(:criteria)
final = double(:criteria)
- expect(connection).to receive(:quote_table_name).
- with(:'foo.bar').
- and_return('"foo"."bar"')
+ expect(connection).to receive(:quote_table_name)
+ .with(:'foo.bar')
+ .and_return('"foo"."bar"')
- expect(connection).to receive(:quote_table_name).
- with(:'foo.baz').
- and_return('"foo"."baz"')
+ expect(connection).to receive(:quote_table_name)
+ .with(:'foo.baz')
+ .and_return('"foo"."baz"')
- expect(model).to receive(:where).
- with(%q{LOWER("foo"."bar") = LOWER(:value)}, value: 'bar').
- and_return(initial)
+ expect(model).to receive(:where)
+ .with(%q{LOWER("foo"."bar") = LOWER(:value)}, value: 'bar')
+ .and_return(initial)
- expect(initial).to receive(:where).
- with(%q{LOWER("foo"."baz") = LOWER(:value)}, value: 'baz').
- and_return(final)
+ expect(initial).to receive(:where)
+ .with(%q{LOWER("foo"."baz") = LOWER(:value)}, value: 'baz')
+ .and_return(final)
got = model.iwhere('foo.bar'.to_sym => 'bar',
'foo.baz'.to_sym => 'baz')
@@ -105,13 +105,13 @@ describe CaseSensitivity, models: true do
it 'returns the criteria for a column and a value' do
criteria = double(:criteria)
- expect(connection).to receive(:quote_table_name).
- with(:foo).
- and_return('`foo`')
+ expect(connection).to receive(:quote_table_name)
+ .with(:foo)
+ .and_return('`foo`')
- expect(model).to receive(:where).
- with(%q{`foo` = :value}, value: 'bar').
- and_return(criteria)
+ expect(model).to receive(:where)
+ .with(%q{`foo` = :value}, value: 'bar')
+ .and_return(criteria)
expect(model.iwhere(foo: 'bar')).to eq(criteria)
end
@@ -119,16 +119,16 @@ describe CaseSensitivity, models: true do
it 'returns the criteria for a column with a table, and a value' do
criteria = double(:criteria)
- expect(connection).to receive(:quote_table_name).
- with(:'foo.bar').
- and_return('`foo`.`bar`')
+ expect(connection).to receive(:quote_table_name)
+ .with(:'foo.bar')
+ .and_return('`foo`.`bar`')
- expect(model).to receive(:where).
- with(%q{`foo`.`bar` = :value}, value: 'bar').
- and_return(criteria)
+ expect(model).to receive(:where)
+ .with(%q{`foo`.`bar` = :value}, value: 'bar')
+ .and_return(criteria)
- expect(model.iwhere('foo.bar'.to_sym => 'bar')).
- to eq(criteria)
+ expect(model.iwhere('foo.bar'.to_sym => 'bar'))
+ .to eq(criteria)
end
end
@@ -137,21 +137,21 @@ describe CaseSensitivity, models: true do
initial = double(:criteria)
final = double(:criteria)
- expect(connection).to receive(:quote_table_name).
- with(:foo).
- and_return('`foo`')
+ expect(connection).to receive(:quote_table_name)
+ .with(:foo)
+ .and_return('`foo`')
- expect(connection).to receive(:quote_table_name).
- with(:bar).
- and_return('`bar`')
+ expect(connection).to receive(:quote_table_name)
+ .with(:bar)
+ .and_return('`bar`')
- expect(model).to receive(:where).
- with(%q{`foo` = :value}, value: 'bar').
- and_return(initial)
+ expect(model).to receive(:where)
+ .with(%q{`foo` = :value}, value: 'bar')
+ .and_return(initial)
- expect(initial).to receive(:where).
- with(%q{`bar` = :value}, value: 'baz').
- and_return(final)
+ expect(initial).to receive(:where)
+ .with(%q{`bar` = :value}, value: 'baz')
+ .and_return(final)
got = model.iwhere(foo: 'bar', bar: 'baz')
@@ -162,21 +162,21 @@ describe CaseSensitivity, models: true do
initial = double(:criteria)
final = double(:criteria)
- expect(connection).to receive(:quote_table_name).
- with(:'foo.bar').
- and_return('`foo`.`bar`')
+ expect(connection).to receive(:quote_table_name)
+ .with(:'foo.bar')
+ .and_return('`foo`.`bar`')
- expect(connection).to receive(:quote_table_name).
- with(:'foo.baz').
- and_return('`foo`.`baz`')
+ expect(connection).to receive(:quote_table_name)
+ .with(:'foo.baz')
+ .and_return('`foo`.`baz`')
- expect(model).to receive(:where).
- with(%q{`foo`.`bar` = :value}, value: 'bar').
- and_return(initial)
+ expect(model).to receive(:where)
+ .with(%q{`foo`.`bar` = :value}, value: 'bar')
+ .and_return(initial)
- expect(initial).to receive(:where).
- with(%q{`foo`.`baz` = :value}, value: 'baz').
- and_return(final)
+ expect(initial).to receive(:where)
+ .with(%q{`foo`.`baz` = :value}, value: 'baz')
+ .and_return(final)
got = model.iwhere('foo.bar'.to_sym => 'bar',
'foo.baz'.to_sym => 'baz')
diff --git a/spec/models/concerns/has_status_spec.rb b/spec/models/concerns/has_status_spec.rb
index 67dae7cf4c0..101567998c9 100644
--- a/spec/models/concerns/has_status_spec.rb
+++ b/spec/models/concerns/has_status_spec.rb
@@ -168,8 +168,8 @@ describe HasStatus do
describe ".#{status}" do
it 'contains the job' do
- expect(CommitStatus.public_send(status).all).
- to contain_exactly(job)
+ expect(CommitStatus.public_send(status).all)
+ .to contain_exactly(job)
end
end
diff --git a/spec/models/concerns/issuable_spec.rb b/spec/models/concerns/issuable_spec.rb
index 1a9bda64191..ac9303370ab 100644
--- a/spec/models/concerns/issuable_spec.rb
+++ b/spec/models/concerns/issuable_spec.rb
@@ -69,8 +69,8 @@ describe Issuable do
let!(:searchable_issue) { create(:issue, title: "Searchable issue") }
it 'returns notes with a matching title' do
- expect(issuable_class.search(searchable_issue.title)).
- to eq([searchable_issue])
+ expect(issuable_class.search(searchable_issue.title))
+ .to eq([searchable_issue])
end
it 'returns notes with a partially matching title' do
@@ -78,8 +78,8 @@ describe Issuable do
end
it 'returns notes with a matching title regardless of the casing' do
- expect(issuable_class.search(searchable_issue.title.upcase)).
- to eq([searchable_issue])
+ expect(issuable_class.search(searchable_issue.title.upcase))
+ .to eq([searchable_issue])
end
end
@@ -89,8 +89,8 @@ describe Issuable do
end
it 'returns notes with a matching title' do
- expect(issuable_class.full_search(searchable_issue.title)).
- to eq([searchable_issue])
+ expect(issuable_class.full_search(searchable_issue.title))
+ .to eq([searchable_issue])
end
it 'returns notes with a partially matching title' do
@@ -98,23 +98,23 @@ describe Issuable do
end
it 'returns notes with a matching title regardless of the casing' do
- expect(issuable_class.full_search(searchable_issue.title.upcase)).
- to eq([searchable_issue])
+ expect(issuable_class.full_search(searchable_issue.title.upcase))
+ .to eq([searchable_issue])
end
it 'returns notes with a matching description' do
- expect(issuable_class.full_search(searchable_issue.description)).
- to eq([searchable_issue])
+ expect(issuable_class.full_search(searchable_issue.description))
+ .to eq([searchable_issue])
end
it 'returns notes with a partially matching description' do
- expect(issuable_class.full_search(searchable_issue.description)).
- to eq([searchable_issue])
+ expect(issuable_class.full_search(searchable_issue.description))
+ .to eq([searchable_issue])
end
it 'returns notes with a matching description regardless of the casing' do
- expect(issuable_class.full_search(searchable_issue.description.upcase)).
- to eq([searchable_issue])
+ expect(issuable_class.full_search(searchable_issue.description.upcase))
+ .to eq([searchable_issue])
end
end
diff --git a/spec/models/concerns/resolvable_discussion_spec.rb b/spec/models/concerns/resolvable_discussion_spec.rb
index 18327fe262d..3934992c143 100644
--- a/spec/models/concerns/resolvable_discussion_spec.rb
+++ b/spec/models/concerns/resolvable_discussion_spec.rb
@@ -306,22 +306,22 @@ describe Discussion, ResolvableDiscussion, models: true do
it "doesn't change resolved_at on the resolved note" do
expect(first_note.resolved_at).not_to be_nil
- expect { subject.resolve!(current_user) }.
- not_to change { first_note.reload.resolved_at }
+ expect { subject.resolve!(current_user) }
+ .not_to change { first_note.reload.resolved_at }
end
it "doesn't change resolved_by on the resolved note" do
expect(first_note.resolved_by).to eq(user)
- expect { subject.resolve!(current_user) }.
- not_to change { first_note.reload && first_note.resolved_by }
+ expect { subject.resolve!(current_user) }
+ .not_to change { first_note.reload && first_note.resolved_by }
end
it "doesn't change the resolved state on the resolved note" do
expect(first_note.resolved?).to be true
- expect { subject.resolve!(current_user) }.
- not_to change { first_note.reload && first_note.resolved? }
+ expect { subject.resolve!(current_user) }
+ .not_to change { first_note.reload && first_note.resolved? }
end
it "sets resolved_at on the unresolved note" do
diff --git a/spec/models/deployment_spec.rb b/spec/models/deployment_spec.rb
index aad215d5f41..bb84d3fc13d 100644
--- a/spec/models/deployment_spec.rb
+++ b/spec/models/deployment_spec.rb
@@ -30,7 +30,7 @@ describe Deployment, models: true do
end
describe '#includes_commit?' do
- let(:project) { create(:project, :repository) }
+ let(:project) { create(:project, :repository) }
let(:environment) { create(:environment, project: project) }
let(:deployment) do
create(:deployment, environment: environment, sha: project.commit.id)
@@ -90,6 +90,36 @@ describe Deployment, models: true do
end
end
+ describe '#additional_metrics' do
+ let(:project) { create(:project) }
+ let(:deployment) { create(:deployment, project: project) }
+
+ subject { deployment.additional_metrics }
+
+ context 'metrics are disabled' do
+ it { is_expected.to eq({}) }
+ end
+
+ context 'metrics are enabled' do
+ let(:simple_metrics) do
+ {
+ success: true,
+ metrics: {},
+ last_update: 42
+ }
+ end
+
+ let(:prometheus_service) { double('prometheus_service') }
+
+ before do
+ allow(project).to receive(:prometheus_service).and_return(prometheus_service)
+ allow(prometheus_service).to receive(:additional_deployment_metrics).and_return(simple_metrics)
+ end
+
+ it { is_expected.to eq(simple_metrics.merge({ deployment_time: deployment.created_at.to_i })) }
+ end
+ end
+
describe '#stop_action' do
let(:build) { create(:ci_build) }
diff --git a/spec/models/environment_spec.rb b/spec/models/environment_spec.rb
index f8123cb518e..b0635c6a90a 100644
--- a/spec/models/environment_spec.rb
+++ b/spec/models/environment_spec.rb
@@ -432,6 +432,99 @@ describe Environment, models: true do
end
end
+ describe '#has_metrics?' do
+ subject { environment.has_metrics? }
+
+ context 'when the enviroment is available' do
+ context 'with a deployment service' do
+ let(:project) { create(:prometheus_project) }
+
+ context 'and a deployment' do
+ let!(:deployment) { create(:deployment, environment: environment) }
+ it { is_expected.to be_truthy }
+ end
+
+ context 'but no deployments' do
+ it { is_expected.to be_falsy }
+ end
+ end
+
+ context 'without a monitoring service' do
+ it { is_expected.to be_falsy }
+ end
+ end
+
+ context 'when the environment is unavailable' do
+ let(:project) { create(:prometheus_project) }
+
+ before do
+ environment.stop
+ end
+
+ it { is_expected.to be_falsy }
+ end
+ end
+
+ describe '#additional_metrics' do
+ let(:project) { create(:prometheus_project) }
+ subject { environment.additional_metrics }
+
+ context 'when the environment has additional metrics' do
+ before do
+ allow(environment).to receive(:has_additional_metrics?).and_return(true)
+ end
+
+ it 'returns the additional metrics from the deployment service' do
+ expect(project.prometheus_service).to receive(:additional_environment_metrics)
+ .with(environment)
+ .and_return(:fake_metrics)
+
+ is_expected.to eq(:fake_metrics)
+ end
+ end
+
+ context 'when the environment does not have metrics' do
+ before do
+ allow(environment).to receive(:has_additional_metrics?).and_return(false)
+ end
+
+ it { is_expected.to be_nil }
+ end
+ end
+
+ describe '#has_additional_metrics??' do
+ subject { environment.has_additional_metrics? }
+
+ context 'when the enviroment is available' do
+ context 'with a deployment service' do
+ let(:project) { create(:prometheus_project) }
+
+ context 'and a deployment' do
+ let!(:deployment) { create(:deployment, environment: environment) }
+ it { is_expected.to be_truthy }
+ end
+
+ context 'but no deployments' do
+ it { is_expected.to be_falsy }
+ end
+ end
+
+ context 'without a monitoring service' do
+ it { is_expected.to be_falsy }
+ end
+ end
+
+ context 'when the environment is unavailable' do
+ let(:project) { create(:prometheus_project) }
+
+ before do
+ environment.stop
+ end
+
+ it { is_expected.to be_falsy }
+ end
+ end
+
describe '#slug' do
it "is automatically generated" do
expect(environment.slug).not_to be_nil
diff --git a/spec/models/event_spec.rb b/spec/models/event_spec.rb
index b8cb967c4cc..10b9bf9f43a 100644
--- a/spec/models/event_spec.rb
+++ b/spec/models/event_spec.rb
@@ -266,8 +266,8 @@ describe Event, models: true do
it 'does not update the project' do
project.update(last_activity_at: Time.now)
- expect(project).not_to receive(:update_column).
- with(:last_activity_at, a_kind_of(Time))
+ expect(project).not_to receive(:update_column)
+ .with(:last_activity_at, a_kind_of(Time))
create_push_event(project, project.owner)
end
diff --git a/spec/models/group_spec.rb b/spec/models/group_spec.rb
index 449b7c2f7d7..4de1683b21c 100644
--- a/spec/models/group_spec.rb
+++ b/spec/models/group_spec.rb
@@ -374,8 +374,8 @@ describe Group, models: true do
group.add_user(master, GroupMember::MASTER)
group.add_user(developer, GroupMember::DEVELOPER)
- expect(group.user_ids_for_project_authorizations).
- to include(master.id, developer.id)
+ expect(group.user_ids_for_project_authorizations)
+ .to include(master.id, developer.id)
end
end
diff --git a/spec/models/issue_collection_spec.rb b/spec/models/issue_collection_spec.rb
index 93c2c538e10..04d23d4c4fd 100644
--- a/spec/models/issue_collection_spec.rb
+++ b/spec/models/issue_collection_spec.rb
@@ -50,8 +50,8 @@ describe IssueCollection do
context 'using a user that is the owner of a project' do
it 'returns the issues of the project' do
- expect(collection.updatable_by_user(project.namespace.owner)).
- to eq([issue1, issue2])
+ expect(collection.updatable_by_user(project.namespace.owner))
+ .to eq([issue1, issue2])
end
end
end
diff --git a/spec/models/issue_spec.rb b/spec/models/issue_spec.rb
index 12e7d646382..bf97c6ececd 100644
--- a/spec/models/issue_spec.rb
+++ b/spec/models/issue_spec.rb
@@ -33,8 +33,8 @@ describe Issue, models: true do
let!(:issue4) { create(:issue, project: project, relative_position: 200) }
it 'returns ordered list' do
- expect(project.issues.order_by_position_and_priority).
- to match [issue3, issue4, issue1, issue2]
+ expect(project.issues.order_by_position_and_priority)
+ .to match [issue3, issue4, issue1, issue2]
end
end
@@ -43,16 +43,16 @@ describe Issue, models: true do
allow(subject).to receive(:author).and_return(double(name: 'Robert'))
allow(subject).to receive(:assignees).and_return([])
- expect(subject.card_attributes).
- to eq({ 'Author' => 'Robert', 'Assignee' => '' })
+ expect(subject.card_attributes)
+ .to eq({ 'Author' => 'Robert', 'Assignee' => '' })
end
it 'includes the assignee name' do
allow(subject).to receive(:author).and_return(double(name: 'Robert'))
allow(subject).to receive(:assignees).and_return([double(name: 'Douwe')])
- expect(subject.card_attributes).
- to eq({ 'Author' => 'Robert', 'Assignee' => 'Douwe' })
+ expect(subject.card_attributes)
+ .to eq({ 'Author' => 'Robert', 'Assignee' => 'Douwe' })
end
end
@@ -299,8 +299,8 @@ describe Issue, models: true do
let(:user) { build(:admin) }
before do
- allow(subject.project.repository).to receive(:branch_names).
- and_return(["mpempe", "#{subject.iid}mepmep", subject.to_branch_name, "#{subject.iid}-branch"])
+ allow(subject.project.repository).to receive(:branch_names)
+ .and_return(["mpempe", "#{subject.iid}mepmep", subject.to_branch_name, "#{subject.iid}-branch"])
# Without this stub, the `create(:merge_request)` above fails because it can't find
# the source branch. This seems like a reasonable compromise, in comparison with
@@ -322,8 +322,8 @@ describe Issue, models: true do
end
it 'excludes stable branches from the related branches' do
- allow(subject.project.repository).to receive(:branch_names).
- and_return(["#{subject.iid}-0-stable"])
+ allow(subject.project.repository).to receive(:branch_names)
+ .and_return(["#{subject.iid}-0-stable"])
expect(subject.related_branches(user)).to eq []
end
diff --git a/spec/models/key_spec.rb b/spec/models/key_spec.rb
index f1e2a2cc518..f27920f9feb 100644
--- a/spec/models/key_spec.rb
+++ b/spec/models/key_spec.rb
@@ -34,8 +34,8 @@ describe Key, models: true do
context 'when key was not updated during the last day' do
before do
- allow_any_instance_of(Gitlab::ExclusiveLease).to receive(:try_obtain).
- and_return('000000')
+ allow_any_instance_of(Gitlab::ExclusiveLease).to receive(:try_obtain)
+ .and_return('000000')
end
it 'enqueues a UseKeyWorker job' do
@@ -46,8 +46,8 @@ describe Key, models: true do
context 'when key was updated during the last day' do
before do
- allow_any_instance_of(Gitlab::ExclusiveLease).to receive(:try_obtain).
- and_return(false)
+ allow_any_instance_of(Gitlab::ExclusiveLease).to receive(:try_obtain)
+ .and_return(false)
end
it 'does not enqueue a UseKeyWorker job' do
diff --git a/spec/models/label_spec.rb b/spec/models/label_spec.rb
index 84867e3d96b..31190fe5685 100644
--- a/spec/models/label_spec.rb
+++ b/spec/models/label_spec.rb
@@ -59,8 +59,8 @@ describe Label, models: true do
describe '#text_color' do
it 'uses default color if color is missing' do
- expect(LabelsHelper).to receive(:text_color_for_bg).with(Label::DEFAULT_COLOR).
- and_return(spy)
+ expect(LabelsHelper).to receive(:text_color_for_bg).with(Label::DEFAULT_COLOR)
+ .and_return(spy)
label = described_class.new(color: nil)
diff --git a/spec/models/member_spec.rb b/spec/models/member_spec.rb
index ccc3deac199..494a88368ba 100644
--- a/spec/models/member_spec.rb
+++ b/spec/models/member_spec.rb
@@ -83,8 +83,8 @@ describe Member, models: true do
@accepted_invite_member = create(:project_member, :developer,
project: project,
invite_token: '1234',
- invite_email: 'toto2@example.com').
- tap { |u| u.accept_invite!(accepted_invite_user) }
+ invite_email: 'toto2@example.com')
+ .tap { |u| u.accept_invite!(accepted_invite_user) }
requested_user = create(:user).tap { |u| project.request_access(u) }
@requested_member = project.requesters.find_by(user_id: requested_user.id)
@@ -265,8 +265,8 @@ describe Member, models: true do
expect(source.users).not_to include(user)
expect(source.requesters.exists?(user_id: user)).to be_truthy
- expect { described_class.add_user(source, user, :master) }.
- to raise_error(Gitlab::Access::AccessDeniedError)
+ expect { described_class.add_user(source, user, :master) }
+ .to raise_error(Gitlab::Access::AccessDeniedError)
expect(source.users.reload).not_to include(user)
expect(source.requesters.reload.exists?(user_id: user)).to be_truthy
diff --git a/spec/models/members/group_member_spec.rb b/spec/models/members/group_member_spec.rb
index 17765b25856..37014268a70 100644
--- a/spec/models/members/group_member_spec.rb
+++ b/spec/models/members/group_member_spec.rb
@@ -33,8 +33,8 @@ describe GroupMember, models: true do
it "sends email to user" do
membership = build(:group_member)
- allow(membership).to receive(:notification_service).
- and_return(double('NotificationService').as_null_object)
+ allow(membership).to receive(:notification_service)
+ .and_return(double('NotificationService').as_null_object)
expect(membership).to receive(:notification_service)
membership.save
@@ -44,8 +44,8 @@ describe GroupMember, models: true do
describe "#after_update" do
before do
@group_member = create :group_member
- allow(@group_member).to receive(:notification_service).
- and_return(double('NotificationService').as_null_object)
+ allow(@group_member).to receive(:notification_service)
+ .and_return(double('NotificationService').as_null_object)
end
it "sends email to user" do
diff --git a/spec/models/merge_request_spec.rb b/spec/models/merge_request_spec.rb
index a56bc524a98..1240c9745e2 100644
--- a/spec/models/merge_request_spec.rb
+++ b/spec/models/merge_request_spec.rb
@@ -92,16 +92,16 @@ describe MergeRequest, models: true do
allow(subject).to receive(:author).and_return(double(name: 'Robert'))
allow(subject).to receive(:assignee).and_return(nil)
- expect(subject.card_attributes).
- to eq({ 'Author' => 'Robert', 'Assignee' => nil })
+ expect(subject.card_attributes)
+ .to eq({ 'Author' => 'Robert', 'Assignee' => nil })
end
it 'includes the assignee name' do
allow(subject).to receive(:author).and_return(double(name: 'Robert'))
allow(subject).to receive(:assignee).and_return(double(name: 'Douwe'))
- expect(subject.card_attributes).
- to eq({ 'Author' => 'Robert', 'Assignee' => 'Douwe' })
+ expect(subject.card_attributes)
+ .to eq({ 'Author' => 'Robert', 'Assignee' => 'Douwe' })
end
end
@@ -361,8 +361,8 @@ describe MergeRequest, models: true do
end
it 'accesses the set of issues that will be closed on acceptance' do
- allow(subject.project).to receive(:default_branch).
- and_return(subject.target_branch)
+ allow(subject.project).to receive(:default_branch)
+ .and_return(subject.target_branch)
closed = subject.closes_issues
@@ -388,8 +388,8 @@ describe MergeRequest, models: true do
subject.description = "Is related to #{mentioned_issue.to_reference} and #{closing_issue.to_reference}"
allow(subject).to receive(:commits).and_return([commit])
- allow(subject.project).to receive(:default_branch).
- and_return(subject.target_branch)
+ allow(subject.project).to receive(:default_branch)
+ .and_return(subject.target_branch)
expect(subject.issues_mentioned_but_not_closing(subject.author)).to match_array([mentioned_issue])
end
@@ -537,8 +537,8 @@ describe MergeRequest, models: true do
subject.project.team << [subject.author, :developer]
subject.description = "This issue Closes #{issue.to_reference}"
- allow(subject.project).to receive(:default_branch).
- and_return(subject.target_branch)
+ allow(subject.project).to receive(:default_branch)
+ .and_return(subject.target_branch)
expect(subject.merge_commit_message)
.to match("Closes #{issue.to_reference}")
@@ -663,18 +663,18 @@ describe MergeRequest, models: true do
end
it 'caches the output' do
- expect(subject).to receive(:compute_diverged_commits_count).
- once.
- and_return(2)
+ expect(subject).to receive(:compute_diverged_commits_count)
+ .once
+ .and_return(2)
subject.diverged_commits_count
subject.diverged_commits_count
end
it 'invalidates the cache when the source sha changes' do
- expect(subject).to receive(:compute_diverged_commits_count).
- twice.
- and_return(2)
+ expect(subject).to receive(:compute_diverged_commits_count)
+ .twice
+ .and_return(2)
subject.diverged_commits_count
allow(subject).to receive(:source_branch_sha).and_return('123abc')
@@ -682,9 +682,9 @@ describe MergeRequest, models: true do
end
it 'invalidates the cache when the target sha changes' do
- expect(subject).to receive(:compute_diverged_commits_count).
- twice.
- and_return(2)
+ expect(subject).to receive(:compute_diverged_commits_count)
+ .twice
+ .and_return(2)
subject.diverged_commits_count
allow(subject).to receive(:target_branch_sha).and_return('123abc')
@@ -706,8 +706,8 @@ describe MergeRequest, models: true do
describe '#commits_sha' do
before do
- allow(subject.merge_request_diff).to receive(:commits_sha).
- and_return(['sha1'])
+ allow(subject.merge_request_diff).to receive(:commits_sha)
+ .and_return(['sha1'])
end
it 'delegates to merge request diff' do
@@ -1504,8 +1504,8 @@ describe MergeRequest, models: true do
describe '#has_commits?' do
before do
- allow(subject.merge_request_diff).to receive(:commits_count).
- and_return(2)
+ allow(subject.merge_request_diff).to receive(:commits_count)
+ .and_return(2)
end
it 'returns true when merge request diff has commits' do
@@ -1515,8 +1515,8 @@ describe MergeRequest, models: true do
describe '#has_no_commits?' do
before do
- allow(subject.merge_request_diff).to receive(:commits_count).
- and_return(0)
+ allow(subject.merge_request_diff).to receive(:commits_count)
+ .and_return(0)
end
it 'returns true when merge request diff has 0 commits' do
diff --git a/spec/models/milestone_spec.rb b/spec/models/milestone_spec.rb
index 20b96c08a8f..45953023a36 100644
--- a/spec/models/milestone_spec.rb
+++ b/spec/models/milestone_spec.rb
@@ -164,13 +164,13 @@ describe Milestone, models: true do
end
it 'returns milestones with a partially matching description' do
- expect(described_class.search(milestone.description[0..2])).
- to eq([milestone])
+ expect(described_class.search(milestone.description[0..2]))
+ .to eq([milestone])
end
it 'returns milestones with a matching description regardless of the casing' do
- expect(described_class.search(milestone.description.upcase)).
- to eq([milestone])
+ expect(described_class.search(milestone.description.upcase))
+ .to eq([milestone])
end
end
diff --git a/spec/models/namespace_spec.rb b/spec/models/namespace_spec.rb
index 145c7ad5770..e7c3acf19eb 100644
--- a/spec/models/namespace_spec.rb
+++ b/spec/models/namespace_spec.rb
@@ -325,8 +325,8 @@ describe Namespace, models: true do
describe '#user_ids_for_project_authorizations' do
it 'returns the user IDs for which to refresh authorizations' do
- expect(namespace.user_ids_for_project_authorizations).
- to eq([namespace.owner_id])
+ expect(namespace.user_ids_for_project_authorizations)
+ .to eq([namespace.owner_id])
end
end
diff --git a/spec/models/note_spec.rb b/spec/models/note_spec.rb
index d4d4fc86343..e2b80cb6e61 100644
--- a/spec/models/note_spec.rb
+++ b/spec/models/note_spec.rb
@@ -152,8 +152,8 @@ describe Note, models: true do
let!(:note2) { create(:note_on_issue) }
it "reads the rendered note body from the cache" do
- expect(Banzai::Renderer).to receive(:cache_collection_render).
- with([{
+ expect(Banzai::Renderer).to receive(:cache_collection_render)
+ .with([{
text: note1.note,
context: {
skip_project_check: false,
@@ -164,8 +164,8 @@ describe Note, models: true do
}
}]).and_call_original
- expect(Banzai::Renderer).to receive(:cache_collection_render).
- with([{
+ expect(Banzai::Renderer).to receive(:cache_collection_render)
+ .with([{
text: note2.note,
context: {
skip_project_check: false,
@@ -406,8 +406,8 @@ describe Note, models: true do
let(:note) { build(:note_on_project_snippet) }
before do
- expect(Banzai::Renderer).to receive(:cacheless_render_field).
- with(note, :note, { skip_project_check: false }).and_return(html)
+ expect(Banzai::Renderer).to receive(:cacheless_render_field)
+ .with(note, :note, { skip_project_check: false }).and_return(html)
note.save
end
@@ -421,8 +421,8 @@ describe Note, models: true do
let(:note) { build(:note_on_personal_snippet) }
before do
- expect(Banzai::Renderer).to receive(:cacheless_render_field).
- with(note, :note, { skip_project_check: true }).and_return(html)
+ expect(Banzai::Renderer).to receive(:cacheless_render_field)
+ .with(note, :note, { skip_project_check: true }).and_return(html)
note.save
end
diff --git a/spec/models/project_authorization_spec.rb b/spec/models/project_authorization_spec.rb
index cd0a4a94809..ee6bdc39c8c 100644
--- a/spec/models/project_authorization_spec.rb
+++ b/spec/models/project_authorization_spec.rb
@@ -7,8 +7,8 @@ describe ProjectAuthorization do
describe '.insert_authorizations' do
it 'inserts the authorizations' do
- described_class.
- insert_authorizations([[user.id, project1.id, Gitlab::Access::MASTER]])
+ described_class
+ .insert_authorizations([[user.id, project1.id, Gitlab::Access::MASTER]])
expect(user.project_authorizations.count).to eq(1)
end
diff --git a/spec/models/project_services/mattermost_slash_commands_service_spec.rb b/spec/models/project_services/mattermost_slash_commands_service_spec.rb
index f9531be5d25..fa38d23e82f 100644
--- a/spec/models/project_services/mattermost_slash_commands_service_spec.rb
+++ b/spec/models/project_services/mattermost_slash_commands_service_spec.rb
@@ -11,8 +11,8 @@ describe MattermostSlashCommandsService, :models do
before do
Mattermost::Session.base_uri("http://mattermost.example.com")
- allow_any_instance_of(Mattermost::Client).to receive(:with_session).
- and_yield(Mattermost::Session.new(nil))
+ allow_any_instance_of(Mattermost::Client).to receive(:with_session)
+ .and_yield(Mattermost::Session.new(nil))
end
describe '#configure' do
@@ -24,8 +24,8 @@ describe MattermostSlashCommandsService, :models do
context 'the requests succeeds' do
before do
- stub_request(:post, 'http://mattermost.example.com/api/v3/teams/abc/commands/create').
- with(body: {
+ stub_request(:post, 'http://mattermost.example.com/api/v3/teams/abc/commands/create')
+ .with(body: {
team_id: 'abc',
trigger: 'gitlab',
url: 'http://trigger.url',
@@ -37,8 +37,8 @@ describe MattermostSlashCommandsService, :models do
display_name: "GitLab / #{project.name_with_namespace}",
method: 'P',
username: 'GitLab'
- }.to_json).
- to_return(
+ }.to_json)
+ .to_return(
status: 200,
headers: { 'Content-Type' => 'application/json' },
body: { token: 'token' }.to_json
@@ -58,8 +58,8 @@ describe MattermostSlashCommandsService, :models do
context 'an error is received' do
before do
- stub_request(:post, 'http://mattermost.example.com/api/v3/teams/abc/commands/create').
- to_return(
+ stub_request(:post, 'http://mattermost.example.com/api/v3/teams/abc/commands/create')
+ .to_return(
status: 500,
headers: { 'Content-Type' => 'application/json' },
body: {
@@ -88,8 +88,8 @@ describe MattermostSlashCommandsService, :models do
context 'the requests succeeds' do
before do
- stub_request(:get, 'http://mattermost.example.com/api/v3/teams/all').
- to_return(
+ stub_request(:get, 'http://mattermost.example.com/api/v3/teams/all')
+ .to_return(
status: 200,
headers: { 'Content-Type' => 'application/json' },
body: { 'list' => true }.to_json
@@ -103,8 +103,8 @@ describe MattermostSlashCommandsService, :models do
context 'an error is received' do
before do
- stub_request(:get, 'http://mattermost.example.com/api/v3/teams/all').
- to_return(
+ stub_request(:get, 'http://mattermost.example.com/api/v3/teams/all')
+ .to_return(
status: 500,
headers: { 'Content-Type' => 'application/json' },
body: {
diff --git a/spec/models/project_services/prometheus_service_spec.rb b/spec/models/project_services/prometheus_service_spec.rb
index 71b53732164..c914b32f7a4 100644
--- a/spec/models/project_services/prometheus_service_spec.rb
+++ b/spec/models/project_services/prometheus_service_spec.rb
@@ -65,7 +65,7 @@ describe PrometheusService, models: true, caching: true do
end
it 'returns reactive data' do
- is_expected.to eq(prometheus_data)
+ is_expected.to eq(prometheus_metrics_data)
end
end
end
@@ -86,7 +86,7 @@ describe PrometheusService, models: true, caching: true do
end
it 'returns reactive data' do
- is_expected.to eq(prometheus_data.merge(deployment_time: deployment.created_at.to_i))
+ is_expected.to eq(prometheus_metrics_data.merge(deployment_time: deployment.created_at.to_i))
end
end
end
@@ -116,6 +116,7 @@ describe PrometheusService, models: true, caching: true do
end
it { expect(subject.to_json).to eq(prometheus_data.to_json) }
+ it { expect(subject.to_json).to eq(prometheus_data.to_json) }
end
[404, 500].each do |status|
diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb
index c7ba3ae903d..d7fcadb895e 100644
--- a/spec/models/project_spec.rb
+++ b/spec/models/project_spec.rb
@@ -1195,23 +1195,23 @@ describe Project, models: true do
it 'renames a repository' do
stub_container_registry_config(enabled: false)
- expect(gitlab_shell).to receive(:mv_repository).
- ordered.
- with(project.repository_storage_path, "#{project.namespace.full_path}/foo", "#{project.full_path}").
- and_return(true)
+ expect(gitlab_shell).to receive(:mv_repository)
+ .ordered
+ .with(project.repository_storage_path, "#{project.namespace.full_path}/foo", "#{project.full_path}")
+ .and_return(true)
- expect(gitlab_shell).to receive(:mv_repository).
- ordered.
- with(project.repository_storage_path, "#{project.namespace.full_path}/foo.wiki", "#{project.full_path}.wiki").
- and_return(true)
+ expect(gitlab_shell).to receive(:mv_repository)
+ .ordered
+ .with(project.repository_storage_path, "#{project.namespace.full_path}/foo.wiki", "#{project.full_path}.wiki")
+ .and_return(true)
- expect_any_instance_of(SystemHooksService).
- to receive(:execute_hooks_for).
- with(project, :rename)
+ expect_any_instance_of(SystemHooksService)
+ .to receive(:execute_hooks_for)
+ .with(project, :rename)
- expect_any_instance_of(Gitlab::UploadsTransfer).
- to receive(:rename_project).
- with('foo', project.path, project.namespace.full_path)
+ expect_any_instance_of(Gitlab::UploadsTransfer)
+ .to receive(:rename_project)
+ .with('foo', project.path, project.namespace.full_path)
expect(project).to receive(:expire_caches_before_rename)
@@ -1239,13 +1239,13 @@ describe Project, models: true do
let(:wiki) { double(:wiki, exists?: true) }
it 'expires the caches of the repository and wiki' do
- allow(Repository).to receive(:new).
- with('foo', project).
- and_return(repo)
+ allow(Repository).to receive(:new)
+ .with('foo', project)
+ .and_return(repo)
- allow(Repository).to receive(:new).
- with('foo.wiki', project).
- and_return(wiki)
+ allow(Repository).to receive(:new)
+ .with('foo.wiki', project)
+ .and_return(wiki)
expect(repo).to receive(:before_delete)
expect(wiki).to receive(:before_delete)
@@ -1296,9 +1296,9 @@ describe Project, models: true do
context 'using a regular repository' do
it 'creates the repository' do
- expect(shell).to receive(:add_repository).
- with(project.repository_storage_path, project.path_with_namespace).
- and_return(true)
+ expect(shell).to receive(:add_repository)
+ .with(project.repository_storage_path, project.path_with_namespace)
+ .and_return(true)
expect(project.repository).to receive(:after_create)
@@ -1306,9 +1306,9 @@ describe Project, models: true do
end
it 'adds an error if the repository could not be created' do
- expect(shell).to receive(:add_repository).
- with(project.repository_storage_path, project.path_with_namespace).
- and_return(false)
+ expect(shell).to receive(:add_repository)
+ .with(project.repository_storage_path, project.path_with_namespace)
+ .and_return(false)
expect(project.repository).not_to receive(:after_create)
@@ -1564,8 +1564,8 @@ describe Project, models: true do
let(:project) { forked_project_link.forked_to_project }
it 'schedules a RepositoryForkWorker job' do
- expect(RepositoryForkWorker).to receive(:perform_async).
- with(project.id, forked_from_project.repository_storage_path,
+ expect(RepositoryForkWorker).to receive(:perform_async)
+ .with(project.id, forked_from_project.repository_storage_path,
forked_from_project.path_with_namespace, project.namespace.full_path)
project.add_import_job
@@ -2041,15 +2041,15 @@ describe Project, models: true do
error_message = 'Failed to replace merge_requests because one or more of the new records could not be saved.'\
' Validate fork Source project is not a fork of the target project'
- expect { project.append_or_update_attribute(:merge_requests, [create(:merge_request)]) }.
- to raise_error(ActiveRecord::RecordNotSaved, error_message)
+ expect { project.append_or_update_attribute(:merge_requests, [create(:merge_request)]) }
+ .to raise_error(ActiveRecord::RecordNotSaved, error_message)
end
it 'updates the project succesfully' do
merge_request = create(:merge_request, target_project: project, source_project: project)
- expect { project.append_or_update_attribute(:merge_requests, [merge_request]) }.
- not_to raise_error
+ expect { project.append_or_update_attribute(:merge_requests, [merge_request]) }
+ .not_to raise_error
end
end
diff --git a/spec/models/project_team_spec.rb b/spec/models/project_team_spec.rb
index ea3cd5fe10a..49f2f8c0ad1 100644
--- a/spec/models/project_team_spec.rb
+++ b/spec/models/project_team_spec.rb
@@ -100,8 +100,8 @@ describe ProjectTeam, models: true do
group_access: Gitlab::Access::GUEST
)
- expect(project.team.members).
- to contain_exactly(group_member.user, project.owner)
+ expect(project.team.members)
+ .to contain_exactly(group_member.user, project.owner)
end
it 'returns invited members of a group of a specified level' do
diff --git a/spec/models/project_wiki_spec.rb b/spec/models/project_wiki_spec.rb
index 3f5f4eea4a1..bf74ac5ea25 100644
--- a/spec/models/project_wiki_spec.rb
+++ b/spec/models/project_wiki_spec.rb
@@ -149,15 +149,15 @@ describe ProjectWiki, models: true do
describe '#find_file' do
before do
file = Gollum::File.new(subject.wiki)
- allow_any_instance_of(Gollum::Wiki).
- to receive(:file).with('image.jpg', 'master', true).
- and_return(file)
- allow_any_instance_of(Gollum::File).
- to receive(:mime_type).
- and_return('image/jpeg')
- allow_any_instance_of(Gollum::Wiki).
- to receive(:file).with('non-existant', 'master', true).
- and_return(nil)
+ allow_any_instance_of(Gollum::Wiki)
+ .to receive(:file).with('image.jpg', 'master', true)
+ .and_return(file)
+ allow_any_instance_of(Gollum::File)
+ .to receive(:mime_type)
+ .and_return('image/jpeg')
+ allow_any_instance_of(Gollum::Wiki)
+ .to receive(:file).with('non-existant', 'master', true)
+ .and_return(nil)
end
after do
@@ -268,9 +268,9 @@ describe ProjectWiki, models: true do
describe '#create_repo!' do
it 'creates a repository' do
- expect(subject).to receive(:init_repo).
- with(subject.path_with_namespace).
- and_return(true)
+ expect(subject).to receive(:init_repo)
+ .with(subject.path_with_namespace)
+ .and_return(true)
expect(subject.repository).to receive(:after_create)
diff --git a/spec/models/repository_spec.rb b/spec/models/repository_spec.rb
index a6d4d92c450..3e984ec7588 100644
--- a/spec/models/repository_spec.rb
+++ b/spec/models/repository_spec.rb
@@ -111,8 +111,8 @@ describe Repository, models: true do
describe '#ref_name_for_sha' do
it 'returns the ref' do
- allow(repository.raw_repository).to receive(:ref_name_for_sha).
- and_return('refs/environments/production/77')
+ allow(repository.raw_repository).to receive(:ref_name_for_sha)
+ .and_return('refs/environments/production/77')
expect(repository.ref_name_for_sha('bla', '0' * 40)).to eq 'refs/environments/production/77'
end
@@ -593,8 +593,8 @@ describe Repository, models: true do
user, 'LICENSE', 'Copyright!',
message: 'Add LICENSE', branch_name: 'master')
- allow(repository).to receive(:file_on_head).
- and_raise(Rugged::ReferenceError)
+ allow(repository).to receive(:file_on_head)
+ .and_raise(Rugged::ReferenceError)
expect(repository.license_blob).to be_nil
end
@@ -779,8 +779,8 @@ describe Repository, models: true do
context 'when pre hooks were successful' do
it 'runs without errors' do
- expect_any_instance_of(GitHooksService).to receive(:execute).
- with(user, project.repository.path_to_repo, old_rev, blank_sha, 'refs/heads/feature')
+ expect_any_instance_of(GitHooksService).to receive(:execute)
+ .with(user, project.repository.path_to_repo, old_rev, blank_sha, 'refs/heads/feature')
expect { repository.rm_branch(user, 'feature') }.not_to raise_error
end
@@ -822,14 +822,14 @@ describe Repository, models: true do
before do
service = GitHooksService.new
expect(GitHooksService).to receive(:new).and_return(service)
- expect(service).to receive(:execute).
- with(
+ expect(service).to receive(:execute)
+ .with(
user,
repository.path_to_repo,
old_rev,
new_rev,
- 'refs/heads/feature').
- and_yield(service).and_return(true)
+ 'refs/heads/feature')
+ .and_yield(service).and_return(true)
end
it 'runs without errors' do
@@ -923,8 +923,8 @@ describe Repository, models: true do
expect(repository).not_to receive(:expire_emptiness_caches)
expect(repository).to receive(:expire_branches_cache)
- GitOperationService.new(user, repository).
- with_branch('new-feature') do
+ GitOperationService.new(user, repository)
+ .with_branch('new-feature') do
new_rev
end
end
@@ -1007,8 +1007,8 @@ describe Repository, models: true do
end
it 'does nothing' do
- expect(repository.raw_repository).not_to receive(:autocrlf=).
- with(:input)
+ expect(repository.raw_repository).not_to receive(:autocrlf=)
+ .with(:input)
GitOperationService.new(nil, repository).send(:update_autocrlf_option)
end
@@ -1027,9 +1027,9 @@ describe Repository, models: true do
end
it 'caches the output' do
- expect(repository.raw_repository).to receive(:empty?).
- once.
- and_return(false)
+ expect(repository.raw_repository).to receive(:empty?)
+ .once
+ .and_return(false)
repository.empty?
repository.empty?
@@ -1042,9 +1042,9 @@ describe Repository, models: true do
end
it 'caches the output' do
- expect(repository.raw_repository).to receive(:root_ref).
- once.
- and_return('master')
+ expect(repository.raw_repository).to receive(:root_ref)
+ .once
+ .and_return('master')
repository.root_ref
repository.root_ref
@@ -1055,9 +1055,9 @@ describe Repository, models: true do
it 'expires the root reference cache' do
repository.root_ref
- expect(repository.raw_repository).to receive(:root_ref).
- once.
- and_return('foo')
+ expect(repository.raw_repository).to receive(:root_ref)
+ .once
+ .and_return('foo')
repository.expire_root_ref_cache
@@ -1071,17 +1071,17 @@ describe Repository, models: true do
let(:cache) { repository.send(:cache) }
it 'expires the cache for all branches' do
- expect(cache).to receive(:expire).
- at_least(repository.branches.length * 2).
- times
+ expect(cache).to receive(:expire)
+ .at_least(repository.branches.length * 2)
+ .times
repository.expire_branch_cache
end
it 'expires the cache for all branches when the root branch is given' do
- expect(cache).to receive(:expire).
- at_least(repository.branches.length * 2).
- times
+ expect(cache).to receive(:expire)
+ .at_least(repository.branches.length * 2)
+ .times
repository.expire_branch_cache(repository.root_ref)
end
@@ -1344,12 +1344,12 @@ describe Repository, models: true do
describe '#after_push_commit' do
it 'expires statistics caches' do
- expect(repository).to receive(:expire_statistics_caches).
- and_call_original
+ expect(repository).to receive(:expire_statistics_caches)
+ .and_call_original
- expect(repository).to receive(:expire_branch_cache).
- with('master').
- and_call_original
+ expect(repository).to receive(:expire_branch_cache)
+ .with('master')
+ .and_call_original
repository.after_push_commit('master')
end
@@ -1434,9 +1434,9 @@ describe Repository, models: true do
describe '#expire_branches_cache' do
it 'expires the cache' do
- expect(repository).to receive(:expire_method_caches).
- with(%i(branch_names branch_count)).
- and_call_original
+ expect(repository).to receive(:expire_method_caches)
+ .with(%i(branch_names branch_count))
+ .and_call_original
repository.expire_branches_cache
end
@@ -1444,9 +1444,9 @@ describe Repository, models: true do
describe '#expire_tags_cache' do
it 'expires the cache' do
- expect(repository).to receive(:expire_method_caches).
- with(%i(tag_names tag_count)).
- and_call_original
+ expect(repository).to receive(:expire_method_caches)
+ .with(%i(tag_names tag_count))
+ .and_call_original
repository.expire_tags_cache
end
@@ -1457,11 +1457,11 @@ describe Repository, models: true do
let(:user) { build_stubbed(:user) }
it 'creates the tag using rugged' do
- expect(repository.rugged.tags).to receive(:create).
- with('8.5', repository.commit('master').id,
+ expect(repository.rugged.tags).to receive(:create)
+ .with('8.5', repository.commit('master').id,
hash_including(message: 'foo',
- tagger: hash_including(name: user.name, email: user.email))).
- and_call_original
+ tagger: hash_including(name: user.name, email: user.email)))
+ .and_call_original
repository.add_tag(user, '8.5', 'master', 'foo')
end
@@ -1478,8 +1478,8 @@ describe Repository, models: true do
update_hook = Gitlab::Git::Hook.new('update', repository.path_to_repo)
post_receive_hook = Gitlab::Git::Hook.new('post-receive', repository.path_to_repo)
- allow(Gitlab::Git::Hook).to receive(:new).
- and_return(pre_receive_hook, update_hook, post_receive_hook)
+ allow(Gitlab::Git::Hook).to receive(:new)
+ .and_return(pre_receive_hook, update_hook, post_receive_hook)
allow(pre_receive_hook).to receive(:trigger).and_call_original
allow(update_hook).to receive(:trigger).and_call_original
@@ -1490,12 +1490,12 @@ describe Repository, models: true do
commit_sha = repository.commit('master').id
tag_sha = tag.target
- expect(pre_receive_hook).to have_received(:trigger).
- with(anything, anything, commit_sha, anything)
- expect(update_hook).to have_received(:trigger).
- with(anything, anything, commit_sha, anything)
- expect(post_receive_hook).to have_received(:trigger).
- with(anything, anything, tag_sha, anything)
+ expect(pre_receive_hook).to have_received(:trigger)
+ .with(anything, anything, commit_sha, anything)
+ expect(update_hook).to have_received(:trigger)
+ .with(anything, anything, commit_sha, anything)
+ expect(post_receive_hook).to have_received(:trigger)
+ .with(anything, anything, tag_sha, anything)
end
end
@@ -1529,25 +1529,25 @@ describe Repository, models: true do
describe '#avatar' do
it 'returns nil if repo does not exist' do
- expect(repository).to receive(:file_on_head).
- and_raise(Rugged::ReferenceError)
+ expect(repository).to receive(:file_on_head)
+ .and_raise(Rugged::ReferenceError)
expect(repository.avatar).to eq(nil)
end
it 'returns the first avatar file found in the repository' do
- expect(repository).to receive(:file_on_head).
- with(:avatar).
- and_return(double(:tree, path: 'logo.png'))
+ expect(repository).to receive(:file_on_head)
+ .with(:avatar)
+ .and_return(double(:tree, path: 'logo.png'))
expect(repository.avatar).to eq('logo.png')
end
it 'caches the output' do
- expect(repository).to receive(:file_on_head).
- with(:avatar).
- once.
- and_return(double(:tree, path: 'logo.png'))
+ expect(repository).to receive(:file_on_head)
+ .with(:avatar)
+ .once
+ .and_return(double(:tree, path: 'logo.png'))
2.times { expect(repository.avatar).to eq('logo.png') }
end
@@ -1607,24 +1607,24 @@ describe Repository, models: true do
describe '#contribution_guide', caching: true do
it 'returns and caches the output' do
- expect(repository).to receive(:file_on_head).
- with(:contributing).
- and_return(Gitlab::Git::Tree.new(path: 'CONTRIBUTING.md')).
- once
+ expect(repository).to receive(:file_on_head)
+ .with(:contributing)
+ .and_return(Gitlab::Git::Tree.new(path: 'CONTRIBUTING.md'))
+ .once
2.times do
- expect(repository.contribution_guide).
- to be_an_instance_of(Gitlab::Git::Tree)
+ expect(repository.contribution_guide)
+ .to be_an_instance_of(Gitlab::Git::Tree)
end
end
end
describe '#gitignore', caching: true do
it 'returns and caches the output' do
- expect(repository).to receive(:file_on_head).
- with(:gitignore).
- and_return(Gitlab::Git::Tree.new(path: '.gitignore')).
- once
+ expect(repository).to receive(:file_on_head)
+ .with(:gitignore)
+ .and_return(Gitlab::Git::Tree.new(path: '.gitignore'))
+ .once
2.times do
expect(repository.gitignore).to be_an_instance_of(Gitlab::Git::Tree)
@@ -1634,10 +1634,10 @@ describe Repository, models: true do
describe '#koding_yml', caching: true do
it 'returns and caches the output' do
- expect(repository).to receive(:file_on_head).
- with(:koding).
- and_return(Gitlab::Git::Tree.new(path: '.koding.yml')).
- once
+ expect(repository).to receive(:file_on_head)
+ .with(:koding)
+ .and_return(Gitlab::Git::Tree.new(path: '.koding.yml'))
+ .once
2.times do
expect(repository.koding_yml).to be_an_instance_of(Gitlab::Git::Tree)
@@ -1673,8 +1673,8 @@ describe Repository, models: true do
describe '#expire_statistics_caches' do
it 'expires the caches' do
- expect(repository).to receive(:expire_method_caches).
- with(%i(size commit_count))
+ expect(repository).to receive(:expire_method_caches)
+ .with(%i(size commit_count))
repository.expire_statistics_caches
end
@@ -1691,8 +1691,8 @@ describe Repository, models: true do
describe '#expire_all_method_caches' do
it 'expires the caches of all methods' do
- expect(repository).to receive(:expire_method_caches).
- with(Repository::CACHED_METHODS)
+ expect(repository).to receive(:expire_method_caches)
+ .with(Repository::CACHED_METHODS)
repository.expire_all_method_caches
end
@@ -1717,8 +1717,8 @@ describe Repository, models: true do
context 'with an existing repository' do
it 'returns a Gitlab::Git::Tree' do
- expect(repository.file_on_head(:readme)).
- to be_an_instance_of(Gitlab::Git::Tree)
+ expect(repository.file_on_head(:readme))
+ .to be_an_instance_of(Gitlab::Git::Tree)
end
end
end
@@ -1856,8 +1856,8 @@ describe Repository, models: true do
describe '#refresh_method_caches' do
it 'refreshes the caches of the given types' do
- expect(repository).to receive(:expire_method_caches).
- with(%i(rendered_readme license_blob license_key license))
+ expect(repository).to receive(:expire_method_caches)
+ .with(%i(rendered_readme license_blob license_key license))
expect(repository).to receive(:rendered_readme)
expect(repository).to receive(:license_blob)
diff --git a/spec/models/upload_spec.rb b/spec/models/upload_spec.rb
index 4c832c87d6a..2dea2c6015f 100644
--- a/spec/models/upload_spec.rb
+++ b/spec/models/upload_spec.rb
@@ -54,8 +54,8 @@ describe Upload, type: :model do
uploader: 'AvatarUploader'
)
- expect { described_class.remove_path(__FILE__) }.
- to change { described_class.count }.from(1).to(0)
+ expect { described_class.remove_path(__FILE__) }
+ .to change { described_class.count }.from(1).to(0)
end
end
diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb
index f6b7120e2c9..314f8781867 100644
--- a/spec/models/user_spec.rb
+++ b/spec/models/user_spec.rb
@@ -912,8 +912,8 @@ describe User, models: true do
describe '.find_by_username!' do
it 'raises RecordNotFound' do
- expect { described_class.find_by_username!('JohnDoe') }.
- to raise_error(ActiveRecord::RecordNotFound)
+ expect { described_class.find_by_username!('JohnDoe') }
+ .to raise_error(ActiveRecord::RecordNotFound)
end
it 'is case-insensitive' do
@@ -1557,8 +1557,8 @@ describe User, models: true do
end
it 'returns the projects when using an ActiveRecord relation' do
- projects = user.
- projects_with_reporter_access_limited_to(Project.select(:id))
+ projects = user
+ .projects_with_reporter_access_limited_to(Project.select(:id))
expect(projects).to eq([project1])
end
diff --git a/spec/models/wiki_page_spec.rb b/spec/models/wiki_page_spec.rb
index 753dc938c52..4a73552b8a6 100644
--- a/spec/models/wiki_page_spec.rb
+++ b/spec/models/wiki_page_spec.rb
@@ -61,8 +61,8 @@ describe WikiPage, models: true do
actual_order =
grouped_entries.map do |page_or_dir|
get_slugs(page_or_dir)
- end.
- flatten
+ end
+ .flatten
expect(actual_order).to eq(expected_order)
end
end
diff --git a/spec/policies/project_policy_spec.rb b/spec/policies/project_policy_spec.rb
index 848fd547e10..d70e15f006b 100644
--- a/spec/policies/project_policy_spec.rb
+++ b/spec/policies/project_policy_spec.rb
@@ -80,8 +80,8 @@ describe ProjectPolicy, models: true do
expect(project.team.member?(issue.author)).to eq(false)
- expect(BasePolicy.class_for(project).abilities(user, project).can_set).
- not_to include(:read_issue)
+ expect(BasePolicy.class_for(project).abilities(user, project).can_set)
+ .not_to include(:read_issue)
expect(Ability.allowed?(user, :read_issue, project)).to be_falsy
end
diff --git a/spec/presenters/ci/build_presenter_spec.rb b/spec/presenters/ci/build_presenter_spec.rb
index 2190ab0e82e..518e97d17a1 100644
--- a/spec/presenters/ci/build_presenter_spec.rb
+++ b/spec/presenters/ci/build_presenter_spec.rb
@@ -47,8 +47,8 @@ describe Ci::BuildPresenter do
context 'when build is erased' do
before do
expect(presenter).to receive(:erased_by_user?).and_return(true)
- expect(build).to receive(:erased_by).
- and_return(double(:user, name: 'John Doe'))
+ expect(build).to receive(:erased_by)
+ .and_return(double(:user, name: 'John Doe'))
end
it 'returns the name of the eraser' do
diff --git a/spec/requests/api/files_spec.rb b/spec/requests/api/files_spec.rb
index c5ec8be4f21..9e268adf950 100644
--- a/spec/requests/api/files_spec.rb
+++ b/spec/requests/api/files_spec.rb
@@ -205,8 +205,8 @@ describe API::Files do
end
it "returns a 400 if editor fails to create file" do
- allow_any_instance_of(Repository).to receive(:create_file).
- and_raise(Repository::CommitError, 'Cannot create file')
+ allow_any_instance_of(Repository).to receive(:create_file)
+ .and_raise(Repository::CommitError, 'Cannot create file')
post api(route("any%2Etxt"), user), valid_params
diff --git a/spec/requests/api/groups_spec.rb b/spec/requests/api/groups_spec.rb
index bb53796cbd7..656f098aea8 100644
--- a/spec/requests/api/groups_spec.rb
+++ b/spec/requests/api/groups_spec.rb
@@ -513,8 +513,8 @@ describe API::Groups do
let(:project_path) { project.full_path.gsub('/', '%2F') }
before(:each) do
- allow_any_instance_of(Projects::TransferService).
- to receive(:execute).and_return(true)
+ allow_any_instance_of(Projects::TransferService)
+ .to receive(:execute).and_return(true)
end
context "when authenticated as user" do
diff --git a/spec/requests/api/merge_requests_spec.rb b/spec/requests/api/merge_requests_spec.rb
index 452ff4aba8e..4d0bd67c571 100644
--- a/spec/requests/api/merge_requests_spec.rb
+++ b/spec/requests/api/merge_requests_spec.rb
@@ -560,8 +560,8 @@ describe API::MergeRequests do
end
it "returns 406 if branch can't be merged" do
- allow_any_instance_of(MergeRequest).
- to receive(:can_be_merged?).and_return(false)
+ allow_any_instance_of(MergeRequest)
+ .to receive(:can_be_merged?).and_return(false)
put api("/projects/#{project.id}/merge_requests/#{merge_request.iid}/merge", user)
diff --git a/spec/requests/api/notes_spec.rb b/spec/requests/api/notes_spec.rb
index 03f2b5950ee..4701ad585c9 100644
--- a/spec/requests/api/notes_spec.rb
+++ b/spec/requests/api/notes_spec.rb
@@ -13,8 +13,8 @@ describe API::Notes do
# For testing the cross-reference of a private issue in a public issue
let(:private_user) { create(:user) }
let(:private_project) do
- create(:empty_project, namespace: private_user.namespace).
- tap { |p| p.team << [private_user, :master] }
+ create(:empty_project, namespace: private_user.namespace)
+ .tap { |p| p.team << [private_user, :master] }
end
let(:private_issue) { create(:issue, project: private_project) }
diff --git a/spec/requests/api/project_snippets_spec.rb b/spec/requests/api/project_snippets_spec.rb
index 4d4631322b1..518639f45a2 100644
--- a/spec/requests/api/project_snippets_spec.rb
+++ b/spec/requests/api/project_snippets_spec.rb
@@ -102,23 +102,23 @@ describe API::ProjectSnippets do
context 'when the snippet is private' do
it 'creates the snippet' do
- expect { create_snippet(project, visibility: 'private') }.
- to change { Snippet.count }.by(1)
+ expect { create_snippet(project, visibility: 'private') }
+ .to change { Snippet.count }.by(1)
end
end
context 'when the snippet is public' do
it 'rejects the snippet' do
- expect { create_snippet(project, visibility: 'public') }.
- not_to change { Snippet.count }
+ expect { create_snippet(project, visibility: 'public') }
+ .not_to change { Snippet.count }
expect(response).to have_http_status(400)
expect(json_response['message']).to eq({ "error" => "Spam detected" })
end
it 'creates a spam log' do
- expect { create_snippet(project, visibility: 'public') }.
- to change { SpamLog.count }.by(1)
+ expect { create_snippet(project, visibility: 'public') }
+ .to change { SpamLog.count }.by(1)
end
end
end
@@ -166,8 +166,8 @@ describe API::ProjectSnippets do
let(:visibility_level) { Snippet::PRIVATE }
it 'creates the snippet' do
- expect { update_snippet(title: 'Foo') }.
- to change { snippet.reload.title }.to('Foo')
+ expect { update_snippet(title: 'Foo') }
+ .to change { snippet.reload.title }.to('Foo')
end
end
@@ -175,13 +175,13 @@ describe API::ProjectSnippets do
let(:visibility_level) { Snippet::PUBLIC }
it 'rejects the snippet' do
- expect { update_snippet(title: 'Foo') }.
- not_to change { snippet.reload.title }
+ expect { update_snippet(title: 'Foo') }
+ .not_to change { snippet.reload.title }
end
it 'creates a spam log' do
- expect { update_snippet(title: 'Foo') }.
- to change { SpamLog.count }.by(1)
+ expect { update_snippet(title: 'Foo') }
+ .to change { SpamLog.count }.by(1)
end
end
@@ -189,16 +189,16 @@ describe API::ProjectSnippets do
let(:visibility_level) { Snippet::PRIVATE }
it 'rejects the snippet' do
- expect { update_snippet(title: 'Foo', visibility: 'public') }.
- not_to change { snippet.reload.title }
+ expect { update_snippet(title: 'Foo', visibility: 'public') }
+ .not_to change { snippet.reload.title }
expect(response).to have_http_status(400)
expect(json_response['message']).to eq({ "error" => "Spam detected" })
end
it 'creates a spam log' do
- expect { update_snippet(title: 'Foo', visibility: 'public') }.
- to change { SpamLog.count }.by(1)
+ expect { update_snippet(title: 'Foo', visibility: 'public') }
+ .to change { SpamLog.count }.by(1)
end
end
end
diff --git a/spec/requests/api/projects_spec.rb b/spec/requests/api/projects_spec.rb
index d92262a4c99..fd7ff0b9cff 100644
--- a/spec/requests/api/projects_spec.rb
+++ b/spec/requests/api/projects_spec.rb
@@ -288,15 +288,15 @@ describe API::Projects do
context 'maximum number of projects reached' do
it 'does not create new project and respond with 403' do
allow_any_instance_of(User).to receive(:projects_limit_left).and_return(0)
- expect { post api('/projects', user2), name: 'foo' }.
- to change {Project.count}.by(0)
+ expect { post api('/projects', user2), name: 'foo' }
+ .to change {Project.count}.by(0)
expect(response).to have_http_status(403)
end
end
it 'creates new project without path but with name and returns 201' do
- expect { post api('/projects', user), name: 'Foo Project' }.
- to change { Project.count }.by(1)
+ expect { post api('/projects', user), name: 'Foo Project' }
+ .to change { Project.count }.by(1)
expect(response).to have_http_status(201)
project = Project.first
@@ -306,8 +306,8 @@ describe API::Projects do
end
it 'creates new project without name but with path and returns 201' do
- expect { post api('/projects', user), path: 'foo_project' }.
- to change { Project.count }.by(1)
+ expect { post api('/projects', user), path: 'foo_project' }
+ .to change { Project.count }.by(1)
expect(response).to have_http_status(201)
project = Project.first
@@ -317,8 +317,8 @@ describe API::Projects do
end
it 'creates new project with name and path and returns 201' do
- expect { post api('/projects', user), path: 'path-project-Foo', name: 'Foo Project' }.
- to change { Project.count }.by(1)
+ expect { post api('/projects', user), path: 'path-project-Foo', name: 'Foo Project' }
+ .to change { Project.count }.by(1)
expect(response).to have_http_status(201)
project = Project.first
@@ -491,8 +491,8 @@ describe API::Projects do
end
it 'creates new project with name and path and returns 201' do
- expect { post api("/projects/user/#{user.id}", admin), path: 'path-project-Foo', name: 'Foo Project' }.
- to change { Project.count }.by(1)
+ expect { post api("/projects/user/#{user.id}", admin), path: 'path-project-Foo', name: 'Foo Project' }
+ .to change { Project.count }.by(1)
expect(response).to have_http_status(201)
project = Project.first
@@ -502,8 +502,8 @@ describe API::Projects do
end
it 'responds with 400 on failure and not project' do
- expect { post api("/projects/user/#{user.id}", admin) }.
- not_to change { Project.count }
+ expect { post api("/projects/user/#{user.id}", admin) }
+ .not_to change { Project.count }
expect(response).to have_http_status(400)
expect(json_response['error']).to eq('name is missing')
@@ -740,8 +740,8 @@ describe API::Projects do
get api("/projects", user)
expect(response).to have_http_status(200)
- expect(json_response.first['permissions']['project_access']['access_level']).
- to eq(Gitlab::Access::MASTER)
+ expect(json_response.first['permissions']['project_access']['access_level'])
+ .to eq(Gitlab::Access::MASTER)
expect(json_response.first['permissions']['group_access']).to be_nil
end
end
@@ -752,8 +752,8 @@ describe API::Projects do
get api("/projects/#{project.id}", user)
expect(response).to have_http_status(200)
- expect(json_response['permissions']['project_access']['access_level']).
- to eq(Gitlab::Access::MASTER)
+ expect(json_response['permissions']['project_access']['access_level'])
+ .to eq(Gitlab::Access::MASTER)
expect(json_response['permissions']['group_access']).to be_nil
end
end
@@ -770,8 +770,8 @@ describe API::Projects do
expect(response).to have_http_status(200)
expect(json_response['permissions']['project_access']).to be_nil
- expect(json_response['permissions']['group_access']['access_level']).
- to eq(Gitlab::Access::OWNER)
+ expect(json_response['permissions']['group_access']['access_level'])
+ .to eq(Gitlab::Access::OWNER)
end
end
end
diff --git a/spec/requests/api/runner_spec.rb b/spec/requests/api/runner_spec.rb
index d554c242916..339a57a1f20 100644
--- a/spec/requests/api/runner_spec.rb
+++ b/spec/requests/api/runner_spec.rb
@@ -414,8 +414,8 @@ describe API::Runner do
context 'when concurrently updating a job' do
before do
- expect_any_instance_of(Ci::Build).to receive(:run!).
- and_raise(ActiveRecord::StaleObjectError.new(nil, nil))
+ expect_any_instance_of(Ci::Build).to receive(:run!)
+ .and_raise(ActiveRecord::StaleObjectError.new(nil, nil))
end
it 'returns a conflict' do
diff --git a/spec/requests/api/snippets_spec.rb b/spec/requests/api/snippets_spec.rb
index 8741cbd4e80..b20a187acfe 100644
--- a/spec/requests/api/snippets_spec.rb
+++ b/spec/requests/api/snippets_spec.rb
@@ -142,23 +142,23 @@ describe API::Snippets do
context 'when the snippet is private' do
it 'creates the snippet' do
- expect { create_snippet(visibility: 'private') }.
- to change { Snippet.count }.by(1)
+ expect { create_snippet(visibility: 'private') }
+ .to change { Snippet.count }.by(1)
end
end
context 'when the snippet is public' do
it 'rejects the shippet' do
- expect { create_snippet(visibility: 'public') }.
- not_to change { Snippet.count }
+ expect { create_snippet(visibility: 'public') }
+ .not_to change { Snippet.count }
expect(response).to have_http_status(400)
expect(json_response['message']).to eq({ "error" => "Spam detected" })
end
it 'creates a spam log' do
- expect { create_snippet(visibility: 'public') }.
- to change { SpamLog.count }.by(1)
+ expect { create_snippet(visibility: 'public') }
+ .to change { SpamLog.count }.by(1)
end
end
end
@@ -216,8 +216,8 @@ describe API::Snippets do
let(:visibility_level) { Snippet::PRIVATE }
it 'updates the snippet' do
- expect { update_snippet(title: 'Foo') }.
- to change { snippet.reload.title }.to('Foo')
+ expect { update_snippet(title: 'Foo') }
+ .to change { snippet.reload.title }.to('Foo')
end
end
@@ -225,16 +225,16 @@ describe API::Snippets do
let(:visibility_level) { Snippet::PUBLIC }
it 'rejects the shippet' do
- expect { update_snippet(title: 'Foo') }.
- not_to change { snippet.reload.title }
+ expect { update_snippet(title: 'Foo') }
+ .not_to change { snippet.reload.title }
expect(response).to have_http_status(400)
expect(json_response['message']).to eq({ "error" => "Spam detected" })
end
it 'creates a spam log' do
- expect { update_snippet(title: 'Foo') }.
- to change { SpamLog.count }.by(1)
+ expect { update_snippet(title: 'Foo') }
+ .to change { SpamLog.count }.by(1)
end
end
@@ -242,13 +242,13 @@ describe API::Snippets do
let(:visibility_level) { Snippet::PRIVATE }
it 'rejects the snippet' do
- expect { update_snippet(title: 'Foo', visibility: 'public') }.
- not_to change { snippet.reload.title }
+ expect { update_snippet(title: 'Foo', visibility: 'public') }
+ .not_to change { snippet.reload.title }
end
it 'creates a spam log' do
- expect { update_snippet(title: 'Foo', visibility: 'public') }.
- to change { SpamLog.count }.by(1)
+ expect { update_snippet(title: 'Foo', visibility: 'public') }
+ .to change { SpamLog.count }.by(1)
end
end
end
diff --git a/spec/requests/api/users_spec.rb b/spec/requests/api/users_spec.rb
index 750682bde52..18000d91795 100644
--- a/spec/requests/api/users_spec.rb
+++ b/spec/requests/api/users_spec.rb
@@ -289,14 +289,14 @@ describe API::Users do
bio: 'g' * 256,
projects_limit: -1
expect(response).to have_http_status(400)
- expect(json_response['message']['password']).
- to eq(['is too short (minimum is 8 characters)'])
- expect(json_response['message']['bio']).
- to eq(['is too long (maximum is 255 characters)'])
- expect(json_response['message']['projects_limit']).
- to eq(['must be greater than or equal to 0'])
- expect(json_response['message']['username']).
- to eq([Gitlab::PathRegex.namespace_format_message])
+ expect(json_response['message']['password'])
+ .to eq(['is too short (minimum is 8 characters)'])
+ expect(json_response['message']['bio'])
+ .to eq(['is too long (maximum is 255 characters)'])
+ expect(json_response['message']['projects_limit'])
+ .to eq(['must be greater than or equal to 0'])
+ expect(json_response['message']['username'])
+ .to eq([Gitlab::PathRegex.namespace_format_message])
end
it "is not available for non admin users" do
@@ -478,14 +478,14 @@ describe API::Users do
bio: 'g' * 256,
projects_limit: -1
expect(response).to have_http_status(400)
- expect(json_response['message']['password']).
- to eq(['is too short (minimum is 8 characters)'])
- expect(json_response['message']['bio']).
- to eq(['is too long (maximum is 255 characters)'])
- expect(json_response['message']['projects_limit']).
- to eq(['must be greater than or equal to 0'])
- expect(json_response['message']['username']).
- to eq([Gitlab::PathRegex.namespace_format_message])
+ expect(json_response['message']['password'])
+ .to eq(['is too short (minimum is 8 characters)'])
+ expect(json_response['message']['bio'])
+ .to eq(['is too long (maximum is 255 characters)'])
+ expect(json_response['message']['projects_limit'])
+ .to eq(['must be greater than or equal to 0'])
+ expect(json_response['message']['username'])
+ .to eq([Gitlab::PathRegex.namespace_format_message])
end
it 'returns 400 if provider is missing for identity update' do
diff --git a/spec/requests/api/v3/files_spec.rb b/spec/requests/api/v3/files_spec.rb
index 378ca1720ff..8b2d165c763 100644
--- a/spec/requests/api/v3/files_spec.rb
+++ b/spec/requests/api/v3/files_spec.rb
@@ -126,8 +126,8 @@ describe API::V3::Files do
end
it "returns a 400 if editor fails to create file" do
- allow_any_instance_of(Repository).to receive(:create_file).
- and_raise(Repository::CommitError, 'Cannot create file')
+ allow_any_instance_of(Repository).to receive(:create_file)
+ .and_raise(Repository::CommitError, 'Cannot create file')
post v3_api("/projects/#{project.id}/repository/files", user), valid_params
diff --git a/spec/requests/api/v3/groups_spec.rb b/spec/requests/api/v3/groups_spec.rb
index 98e8c954909..63c5707b2e4 100644
--- a/spec/requests/api/v3/groups_spec.rb
+++ b/spec/requests/api/v3/groups_spec.rb
@@ -505,8 +505,8 @@ describe API::V3::Groups do
let(:project_path) { "#{project.namespace.path}%2F#{project.path}" }
before(:each) do
- allow_any_instance_of(Projects::TransferService).
- to receive(:execute).and_return(true)
+ allow_any_instance_of(Projects::TransferService)
+ .to receive(:execute).and_return(true)
end
context "when authenticated as user" do
diff --git a/spec/requests/api/v3/merge_requests_spec.rb b/spec/requests/api/v3/merge_requests_spec.rb
index f6ff96be566..4f9e63f2ace 100644
--- a/spec/requests/api/v3/merge_requests_spec.rb
+++ b/spec/requests/api/v3/merge_requests_spec.rb
@@ -432,8 +432,8 @@ describe API::MergeRequests do
end
it "returns 406 if branch can't be merged" do
- allow_any_instance_of(MergeRequest).
- to receive(:can_be_merged?).and_return(false)
+ allow_any_instance_of(MergeRequest)
+ .to receive(:can_be_merged?).and_return(false)
put v3_api("/projects/#{project.id}/merge_requests/#{merge_request.id}/merge", user)
diff --git a/spec/requests/api/v3/notes_spec.rb b/spec/requests/api/v3/notes_spec.rb
index 2bae4a60931..b5f98a9a545 100644
--- a/spec/requests/api/v3/notes_spec.rb
+++ b/spec/requests/api/v3/notes_spec.rb
@@ -13,8 +13,8 @@ describe API::V3::Notes do
# For testing the cross-reference of a private issue in a public issue
let(:private_user) { create(:user) }
let(:private_project) do
- create(:empty_project, namespace: private_user.namespace).
- tap { |p| p.team << [private_user, :master] }
+ create(:empty_project, namespace: private_user.namespace)
+ .tap { |p| p.team << [private_user, :master] }
end
let(:private_issue) { create(:issue, project: private_project) }
diff --git a/spec/requests/api/v3/project_snippets_spec.rb b/spec/requests/api/v3/project_snippets_spec.rb
index 365e7365fda..1950c64c690 100644
--- a/spec/requests/api/v3/project_snippets_spec.rb
+++ b/spec/requests/api/v3/project_snippets_spec.rb
@@ -85,23 +85,23 @@ describe API::ProjectSnippets do
context 'when the snippet is private' do
it 'creates the snippet' do
- expect { create_snippet(project, visibility_level: Snippet::PRIVATE) }.
- to change { Snippet.count }.by(1)
+ expect { create_snippet(project, visibility_level: Snippet::PRIVATE) }
+ .to change { Snippet.count }.by(1)
end
end
context 'when the snippet is public' do
it 'rejects the shippet' do
- expect { create_snippet(project, visibility_level: Snippet::PUBLIC) }.
- not_to change { Snippet.count }
+ expect { create_snippet(project, visibility_level: Snippet::PUBLIC) }
+ .not_to change { Snippet.count }
expect(response).to have_http_status(400)
expect(json_response['message']).to eq({ "error" => "Spam detected" })
end
it 'creates a spam log' do
- expect { create_snippet(project, visibility_level: Snippet::PUBLIC) }.
- to change { SpamLog.count }.by(1)
+ expect { create_snippet(project, visibility_level: Snippet::PUBLIC) }
+ .to change { SpamLog.count }.by(1)
end
end
end
@@ -147,8 +147,8 @@ describe API::ProjectSnippets do
let(:visibility_level) { Snippet::PRIVATE }
it 'creates the snippet' do
- expect { update_snippet(title: 'Foo') }.
- to change { snippet.reload.title }.to('Foo')
+ expect { update_snippet(title: 'Foo') }
+ .to change { snippet.reload.title }.to('Foo')
end
end
@@ -156,13 +156,13 @@ describe API::ProjectSnippets do
let(:visibility_level) { Snippet::PUBLIC }
it 'rejects the snippet' do
- expect { update_snippet(title: 'Foo') }.
- not_to change { snippet.reload.title }
+ expect { update_snippet(title: 'Foo') }
+ .not_to change { snippet.reload.title }
end
it 'creates a spam log' do
- expect { update_snippet(title: 'Foo') }.
- to change { SpamLog.count }.by(1)
+ expect { update_snippet(title: 'Foo') }
+ .to change { SpamLog.count }.by(1)
end
end
@@ -170,16 +170,16 @@ describe API::ProjectSnippets do
let(:visibility_level) { Snippet::PRIVATE }
it 'rejects the snippet' do
- expect { update_snippet(title: 'Foo', visibility_level: Snippet::PUBLIC) }.
- not_to change { snippet.reload.title }
+ expect { update_snippet(title: 'Foo', visibility_level: Snippet::PUBLIC) }
+ .not_to change { snippet.reload.title }
expect(response).to have_http_status(400)
expect(json_response['message']).to eq({ "error" => "Spam detected" })
end
it 'creates a spam log' do
- expect { update_snippet(title: 'Foo', visibility_level: Snippet::PUBLIC) }.
- to change { SpamLog.count }.by(1)
+ expect { update_snippet(title: 'Foo', visibility_level: Snippet::PUBLIC) }
+ .to change { SpamLog.count }.by(1)
end
end
end
diff --git a/spec/requests/api/v3/projects_spec.rb b/spec/requests/api/v3/projects_spec.rb
index 47cca4275af..cb74868324c 100644
--- a/spec/requests/api/v3/projects_spec.rb
+++ b/spec/requests/api/v3/projects_spec.rb
@@ -124,6 +124,36 @@ describe API::V3::Projects do
end
end
+ context 'and using archived' do
+ let!(:archived_project) { create(:empty_project, creator_id: user.id, namespace: user.namespace, archived: true) }
+
+ it 'returns archived project' do
+ get v3_api('/projects?archived=true', user)
+
+ expect(response).to have_http_status(200)
+ expect(json_response).to be_an Array
+ expect(json_response.length).to eq(1)
+ expect(json_response.first['id']).to eq(archived_project.id)
+ end
+
+ it 'returns non-archived project' do
+ get v3_api('/projects?archived=false', user)
+
+ expect(response).to have_http_status(200)
+ expect(json_response).to be_an Array
+ expect(json_response.length).to eq(1)
+ expect(json_response.first['id']).to eq(project.id)
+ end
+
+ it 'returns all project' do
+ get v3_api('/projects', user)
+
+ expect(response).to have_http_status(200)
+ expect(json_response).to be_an Array
+ expect(json_response.length).to eq(2)
+ end
+ end
+
context 'and using sorting' do
before do
project2
@@ -301,15 +331,15 @@ describe API::V3::Projects do
context 'maximum number of projects reached' do
it 'does not create new project and respond with 403' do
allow_any_instance_of(User).to receive(:projects_limit_left).and_return(0)
- expect { post v3_api('/projects', user2), name: 'foo' }.
- to change {Project.count}.by(0)
+ expect { post v3_api('/projects', user2), name: 'foo' }
+ .to change {Project.count}.by(0)
expect(response).to have_http_status(403)
end
end
it 'creates new project without path but with name and returns 201' do
- expect { post v3_api('/projects', user), name: 'Foo Project' }.
- to change { Project.count }.by(1)
+ expect { post v3_api('/projects', user), name: 'Foo Project' }
+ .to change { Project.count }.by(1)
expect(response).to have_http_status(201)
project = Project.first
@@ -319,8 +349,8 @@ describe API::V3::Projects do
end
it 'creates new project without name but with path and returns 201' do
- expect { post v3_api('/projects', user), path: 'foo_project' }.
- to change { Project.count }.by(1)
+ expect { post v3_api('/projects', user), path: 'foo_project' }
+ .to change { Project.count }.by(1)
expect(response).to have_http_status(201)
project = Project.first
@@ -330,8 +360,8 @@ describe API::V3::Projects do
end
it 'creates new project name and path and returns 201' do
- expect { post v3_api('/projects', user), path: 'foo-Project', name: 'Foo Project' }.
- to change { Project.count }.by(1)
+ expect { post v3_api('/projects', user), path: 'foo-Project', name: 'Foo Project' }
+ .to change { Project.count }.by(1)
expect(response).to have_http_status(201)
project = Project.first
@@ -489,8 +519,8 @@ describe API::V3::Projects do
end
it 'responds with 400 on failure and not project' do
- expect { post v3_api("/projects/user/#{user.id}", admin) }.
- not_to change { Project.count }
+ expect { post v3_api("/projects/user/#{user.id}", admin) }
+ .not_to change { Project.count }
expect(response).to have_http_status(400)
expect(json_response['error']).to eq('name is missing')
@@ -716,8 +746,8 @@ describe API::V3::Projects do
get v3_api("/projects", user)
expect(response).to have_http_status(200)
- expect(json_response.first['permissions']['project_access']['access_level']).
- to eq(Gitlab::Access::MASTER)
+ expect(json_response.first['permissions']['project_access']['access_level'])
+ .to eq(Gitlab::Access::MASTER)
expect(json_response.first['permissions']['group_access']).to be_nil
end
end
@@ -728,8 +758,8 @@ describe API::V3::Projects do
get v3_api("/projects/#{project.id}", user)
expect(response).to have_http_status(200)
- expect(json_response['permissions']['project_access']['access_level']).
- to eq(Gitlab::Access::MASTER)
+ expect(json_response['permissions']['project_access']['access_level'])
+ .to eq(Gitlab::Access::MASTER)
expect(json_response['permissions']['group_access']).to be_nil
end
end
@@ -744,8 +774,8 @@ describe API::V3::Projects do
expect(response).to have_http_status(200)
expect(json_response['permissions']['project_access']).to be_nil
- expect(json_response['permissions']['group_access']['access_level']).
- to eq(Gitlab::Access::OWNER)
+ expect(json_response['permissions']['group_access']['access_level'])
+ .to eq(Gitlab::Access::OWNER)
end
end
end
diff --git a/spec/requests/api/v3/snippets_spec.rb b/spec/requests/api/v3/snippets_spec.rb
index 4f02b7b1a54..1bc2258ebd3 100644
--- a/spec/requests/api/v3/snippets_spec.rb
+++ b/spec/requests/api/v3/snippets_spec.rb
@@ -112,21 +112,21 @@ describe API::V3::Snippets do
context 'when the snippet is private' do
it 'creates the snippet' do
- expect { create_snippet(visibility_level: Snippet::PRIVATE) }.
- to change { Snippet.count }.by(1)
+ expect { create_snippet(visibility_level: Snippet::PRIVATE) }
+ .to change { Snippet.count }.by(1)
end
end
context 'when the snippet is public' do
it 'rejects the shippet' do
- expect { create_snippet(visibility_level: Snippet::PUBLIC) }.
- not_to change { Snippet.count }
+ expect { create_snippet(visibility_level: Snippet::PUBLIC) }
+ .not_to change { Snippet.count }
expect(response).to have_http_status(400)
end
it 'creates a spam log' do
- expect { create_snippet(visibility_level: Snippet::PUBLIC) }.
- to change { SpamLog.count }.by(1)
+ expect { create_snippet(visibility_level: Snippet::PUBLIC) }
+ .to change { SpamLog.count }.by(1)
end
end
end
diff --git a/spec/requests/ci/api/builds_spec.rb b/spec/requests/ci/api/builds_spec.rb
index 83c675792f4..c969d08d0dd 100644
--- a/spec/requests/ci/api/builds_spec.rb
+++ b/spec/requests/ci/api/builds_spec.rb
@@ -91,8 +91,8 @@ describe Ci::API::Builds do
context 'when concurrently updating build' do
before do
- expect_any_instance_of(Ci::Build).to receive(:run!).
- and_raise(ActiveRecord::StaleObjectError.new(nil, nil))
+ expect_any_instance_of(Ci::Build).to receive(:run!)
+ .and_raise(ActiveRecord::StaleObjectError.new(nil, nil))
end
it 'returns a conflict' do
@@ -670,8 +670,8 @@ describe Ci::API::Builds do
build.reload
expect(response).to have_http_status(201)
expect(json_response['artifacts_expire_at']).not_to be_empty
- expect(build.artifacts_expire_at).
- to be_within(5.minutes).of(7.days.from_now)
+ expect(build.artifacts_expire_at)
+ .to be_within(5.minutes).of(7.days.from_now)
end
end
diff --git a/spec/requests/git_http_spec.rb b/spec/requests/git_http_spec.rb
index b08148eca3c..185679e1a0f 100644
--- a/spec/requests/git_http_spec.rb
+++ b/spec/requests/git_http_spec.rb
@@ -483,8 +483,8 @@ describe 'Git HTTP requests', lib: true do
context 'when LDAP is configured' do
before do
allow(Gitlab::LDAP::Config).to receive(:enabled?).and_return(true)
- allow_any_instance_of(Gitlab::LDAP::Authentication).
- to receive(:login).and_return(nil)
+ allow_any_instance_of(Gitlab::LDAP::Authentication)
+ .to receive(:login).and_return(nil)
end
it 'does not display the personal access token error message' do
diff --git a/spec/rubocop/cop/migration/update_column_in_batches_spec.rb b/spec/rubocop/cop/migration/update_column_in_batches_spec.rb
index 968dcd6232e..38b8f439a55 100644
--- a/spec/rubocop/cop/migration/update_column_in_batches_spec.rb
+++ b/spec/rubocop/cop/migration/update_column_in_batches_spec.rb
@@ -54,8 +54,8 @@ describe RuboCop::Cop::Migration::UpdateColumnInBatches do
aggregate_failures do
expect(cop.offenses.size).to eq(1)
expect(cop.offenses.map(&:line)).to eq([2])
- expect(cop.offenses.first.message).
- to include("`#{relative_spec_filepath}`")
+ expect(cop.offenses.first.message)
+ .to include("`#{relative_spec_filepath}`")
end
end
end
diff --git a/spec/services/create_deployment_service_spec.rb b/spec/services/create_deployment_service_spec.rb
index 6cf4342ad4c..dfab6ebf372 100644
--- a/spec/services/create_deployment_service_spec.rb
+++ b/spec/services/create_deployment_service_spec.rb
@@ -122,6 +122,61 @@ describe CreateDeploymentService, services: true do
end
end
+ describe '#expanded_environment_url' do
+ subject { service.send(:expanded_environment_url) }
+
+ context 'when yaml environment uses $CI_COMMIT_REF_NAME' do
+ let(:job) do
+ create(:ci_build,
+ ref: 'master',
+ options: { environment: { url: 'http://review/$CI_COMMIT_REF_NAME' } })
+ end
+
+ it { is_expected.to eq('http://review/master') }
+ end
+
+ context 'when yaml environment uses $CI_ENVIRONMENT_SLUG' do
+ let(:job) do
+ create(:ci_build,
+ ref: 'master',
+ environment: 'production',
+ options: { environment: { url: 'http://review/$CI_ENVIRONMENT_SLUG' } })
+ end
+
+ let!(:environment) do
+ create(:environment,
+ project: job.project,
+ name: 'production',
+ slug: 'prod-slug',
+ external_url: 'http://review/old')
+ end
+
+ it { is_expected.to eq('http://review/prod-slug') }
+ end
+
+ context 'when yaml environment uses yaml_variables containing symbol keys' do
+ let(:job) do
+ create(:ci_build,
+ yaml_variables: [{ key: :APP_HOST, value: 'host' }],
+ options: { environment: { url: 'http://review/$APP_HOST' } })
+ end
+
+ it { is_expected.to eq('http://review/host') }
+ end
+
+ context 'when yaml environment does not have url' do
+ let(:job) { create(:ci_build, environment: 'staging') }
+
+ let!(:environment) do
+ create(:environment, project: job.project, name: job.environment)
+ end
+
+ it 'returns the external_url from persisted environment' do
+ is_expected.to be_nil
+ end
+ end
+ end
+
describe 'processing of builds' do
shared_examples 'does not create deployment' do
it 'does not create a new deployment' do
diff --git a/spec/services/files/update_service_spec.rb b/spec/services/files/update_service_spec.rb
index 16bca66766a..cc950ae6bb3 100644
--- a/spec/services/files/update_service_spec.rb
+++ b/spec/services/files/update_service_spec.rb
@@ -32,8 +32,8 @@ describe Files::UpdateService do
let(:last_commit_sha) { "foo" }
it "returns a hash with the correct error message and a :error status " do
- expect { subject.execute }.
- to raise_error(Files::UpdateService::FileChangedError,
+ expect { subject.execute }
+ .to raise_error(Files::UpdateService::FileChangedError,
"You are attempting to update a file that has changed since you started editing it.")
end
end
diff --git a/spec/services/git_push_service_spec.rb b/spec/services/git_push_service_spec.rb
index bcd1fb64ab9..ca827fc0f39 100644
--- a/spec/services/git_push_service_spec.rb
+++ b/spec/services/git_push_service_spec.rb
@@ -158,8 +158,8 @@ describe GitPushService, services: true do
context "Updates merge requests" do
it "when pushing a new branch for the first time" do
- expect(UpdateMergeRequestsWorker).to receive(:perform_async).
- with(project.id, user.id, @blankrev, 'newrev', 'refs/heads/master')
+ expect(UpdateMergeRequestsWorker).to receive(:perform_async)
+ .with(project.id, user.id, @blankrev, 'newrev', 'refs/heads/master')
execute_service(project, user, @blankrev, 'newrev', 'refs/heads/master' )
end
end
@@ -283,8 +283,8 @@ describe GitPushService, services: true do
author_email: commit_author.email
)
- allow_any_instance_of(ProcessCommitWorker).to receive(:build_commit).
- and_return(commit)
+ allow_any_instance_of(ProcessCommitWorker).to receive(:build_commit)
+ .and_return(commit)
allow(project.repository).to receive(:commits_between).and_return([commit])
end
@@ -341,8 +341,8 @@ describe GitPushService, services: true do
committed_date: commit_time
)
- allow_any_instance_of(ProcessCommitWorker).to receive(:build_commit).
- and_return(commit)
+ allow_any_instance_of(ProcessCommitWorker).to receive(:build_commit)
+ .and_return(commit)
allow(project.repository).to receive(:commits_between).and_return([commit])
end
@@ -377,11 +377,11 @@ describe GitPushService, services: true do
author_email: commit_author.email
)
- allow(project.repository).to receive(:commits_between).
- and_return([closing_commit])
+ allow(project.repository).to receive(:commits_between)
+ .and_return([closing_commit])
- allow_any_instance_of(ProcessCommitWorker).to receive(:build_commit).
- and_return(closing_commit)
+ allow_any_instance_of(ProcessCommitWorker).to receive(:build_commit)
+ .and_return(closing_commit)
project.team << [commit_author, :master]
end
@@ -403,8 +403,8 @@ describe GitPushService, services: true do
end
it "doesn't close issues when external issue tracker is in use" do
- allow_any_instance_of(Project).to receive(:default_issues_tracker?).
- and_return(false)
+ allow_any_instance_of(Project).to receive(:default_issues_tracker?)
+ .and_return(false)
external_issue_tracker = double(title: 'My Tracker', issue_path: issue.iid, reference_pattern: project.issue_reference_pattern)
allow_any_instance_of(Project).to receive(:external_issue_tracker).and_return(external_issue_tracker)
@@ -598,13 +598,13 @@ describe GitPushService, services: true do
commit = double(:commit)
diff = double(:diff, new_path: 'README.md')
- expect(commit).to receive(:raw_deltas).
- and_return([diff])
+ expect(commit).to receive(:raw_deltas)
+ .and_return([diff])
service.push_commits = [commit]
- expect(ProjectCacheWorker).to receive(:perform_async).
- with(project.id, %i(readme), %i(commit_count repository_size))
+ expect(ProjectCacheWorker).to receive(:perform_async)
+ .with(project.id, %i(readme), %i(commit_count repository_size))
service.update_caches
end
@@ -616,9 +616,9 @@ describe GitPushService, services: true do
end
it 'does not flush any conditional caches' do
- expect(ProjectCacheWorker).to receive(:perform_async).
- with(project.id, [], %i(commit_count repository_size)).
- and_call_original
+ expect(ProjectCacheWorker).to receive(:perform_async)
+ .with(project.id, [], %i(commit_count repository_size))
+ .and_call_original
service.update_caches
end
@@ -635,8 +635,8 @@ describe GitPushService, services: true do
end
it 'only schedules a limited number of commits' do
- allow(service).to receive(:push_commits).
- and_return(Array.new(1000, double(:commit, to_hash: {}, matches_cross_reference_regex?: true)))
+ allow(service).to receive(:push_commits)
+ .and_return(Array.new(1000, double(:commit, to_hash: {}, matches_cross_reference_regex?: true)))
expect(ProcessCommitWorker).to receive(:perform_async).exactly(100).times
@@ -644,8 +644,8 @@ describe GitPushService, services: true do
end
it "skips commits which don't include cross-references" do
- allow(service).to receive(:push_commits).
- and_return([double(:commit, to_hash: {}, matches_cross_reference_regex?: false)])
+ allow(service).to receive(:push_commits)
+ .and_return([double(:commit, to_hash: {}, matches_cross_reference_regex?: false)])
expect(ProcessCommitWorker).not_to receive(:perform_async)
diff --git a/spec/services/issues/close_service_spec.rb b/spec/services/issues/close_service_spec.rb
index be0e829880e..d6f4c694069 100644
--- a/spec/services/issues/close_service_spec.rb
+++ b/spec/services/issues/close_service_spec.rb
@@ -18,26 +18,26 @@ describe Issues::CloseService, services: true do
let(:service) { described_class.new(project, user) }
it 'checks if the user is authorized to update the issue' do
- expect(service).to receive(:can?).with(user, :update_issue, issue).
- and_call_original
+ expect(service).to receive(:can?).with(user, :update_issue, issue)
+ .and_call_original
service.execute(issue)
end
it 'does not close the issue when the user is not authorized to do so' do
- allow(service).to receive(:can?).with(user, :update_issue, issue).
- and_return(false)
+ allow(service).to receive(:can?).with(user, :update_issue, issue)
+ .and_return(false)
expect(service).not_to receive(:close_issue)
expect(service.execute(issue)).to eq(issue)
end
it 'closes the issue when the user is authorized to do so' do
- allow(service).to receive(:can?).with(user, :update_issue, issue).
- and_return(true)
+ allow(service).to receive(:can?).with(user, :update_issue, issue)
+ .and_return(true)
- expect(service).to receive(:close_issue).
- with(issue, commit: nil, notifications: true, system_note: true)
+ expect(service).to receive(:close_issue)
+ .with(issue, commit: nil, notifications: true, system_note: true)
service.execute(issue)
end
diff --git a/spec/services/labels/promote_service_spec.rb b/spec/services/labels/promote_service_spec.rb
index 4b90ad19640..500afdfb916 100644
--- a/spec/services/labels/promote_service_spec.rb
+++ b/spec/services/labels/promote_service_spec.rb
@@ -66,9 +66,9 @@ describe Labels::PromoteService, services: true do
end
it 'recreates the label as a group label' do
- expect { service.execute(project_label_1_1) }.
- to change(project_1.labels, :count).by(-1).
- and change(group_1.labels, :count).by(1)
+ expect { service.execute(project_label_1_1) }
+ .to change(project_1.labels, :count).by(-1)
+ .and change(group_1.labels, :count).by(1)
expect(new_label).not_to be_nil
end
diff --git a/spec/services/members/destroy_service_spec.rb b/spec/services/members/destroy_service_spec.rb
index 41450c67d7e..9ab7839430c 100644
--- a/spec/services/members/destroy_service_spec.rb
+++ b/spec/services/members/destroy_service_spec.rb
@@ -104,8 +104,8 @@ describe Members::DestroyService, services: true do
let(:params) { { id: project.members.find_by!(user_id: user.id).id } }
it 'destroys the member' do
- expect { described_class.new(project, user, params).execute }.
- to change { project.members.count }.by(-1)
+ expect { described_class.new(project, user, params).execute }
+ .to change { project.members.count }.by(-1)
end
end
end
diff --git a/spec/services/merge_requests/close_service_spec.rb b/spec/services/merge_requests/close_service_spec.rb
index 154f30aac3b..074d4672b06 100644
--- a/spec/services/merge_requests/close_service_spec.rb
+++ b/spec/services/merge_requests/close_service_spec.rb
@@ -32,8 +32,8 @@ describe MergeRequests::CloseService, services: true do
it { expect(@merge_request).to be_closed }
it 'executes hooks with close action' do
- expect(service).to have_received(:execute_hooks).
- with(@merge_request, 'close')
+ expect(service).to have_received(:execute_hooks)
+ .with(@merge_request, 'close')
end
it 'sends email to user2 about assign of new merge_request' do
diff --git a/spec/services/merge_requests/conflicts/resolve_service_spec.rb b/spec/services/merge_requests/conflicts/resolve_service_spec.rb
index c77e6e9cd50..6f49a65d795 100644
--- a/spec/services/merge_requests/conflicts/resolve_service_spec.rb
+++ b/spec/services/merge_requests/conflicts/resolve_service_spec.rb
@@ -64,9 +64,9 @@ describe MergeRequests::Conflicts::ResolveService do
end
it 'creates a commit with the correct parents' do
- expect(merge_request.source_branch_head.parents.map(&:id)).
- to eq(%w(1450cd639e0bc6721eb02800169e464f212cde06
- 824be604a34828eb682305f0d963056cfac87b2d))
+ expect(merge_request.source_branch_head.parents.map(&:id))
+ .to eq(%w(1450cd639e0bc6721eb02800169e464f212cde06
+ 824be604a34828eb682305f0d963056cfac87b2d))
end
end
@@ -129,9 +129,8 @@ describe MergeRequests::Conflicts::ResolveService do
it 'creates a commit with the correct parents' do
resolve_conflicts
- expect(merge_request_from_fork.source_branch_head.parents.map(&:id)).
- to eq(['404fa3fc7c2c9b5dacff102f353bdf55b1be2813',
- target_head])
+ expect(merge_request_from_fork.source_branch_head.parents.map(&:id))
+ .to eq(['404fa3fc7c2c9b5dacff102f353bdf55b1be2813', target_head])
end
end
end
@@ -169,9 +168,9 @@ describe MergeRequests::Conflicts::ResolveService do
end
it 'creates a commit with the correct parents' do
- expect(merge_request.source_branch_head.parents.map(&:id)).
- to eq(%w(1450cd639e0bc6721eb02800169e464f212cde06
- 824be604a34828eb682305f0d963056cfac87b2d))
+ expect(merge_request.source_branch_head.parents.map(&:id))
+ .to eq(%w(1450cd639e0bc6721eb02800169e464f212cde06
+ 824be604a34828eb682305f0d963056cfac87b2d))
end
it 'sets the content to the content given' do
@@ -204,8 +203,8 @@ describe MergeRequests::Conflicts::ResolveService do
end
it 'raises a MissingResolution error' do
- expect { service.execute(user, invalid_params) }.
- to raise_error(Gitlab::Conflict::File::MissingResolution)
+ expect { service.execute(user, invalid_params) }
+ .to raise_error(Gitlab::Conflict::File::MissingResolution)
end
end
@@ -230,8 +229,8 @@ describe MergeRequests::Conflicts::ResolveService do
end
it 'raises a MissingResolution error' do
- expect { service.execute(user, invalid_params) }.
- to raise_error(Gitlab::Conflict::File::MissingResolution)
+ expect { service.execute(user, invalid_params) }
+ .to raise_error(Gitlab::Conflict::File::MissingResolution)
end
end
@@ -250,8 +249,8 @@ describe MergeRequests::Conflicts::ResolveService do
end
it 'raises a MissingFiles error' do
- expect { service.execute(user, invalid_params) }.
- to raise_error(described_class::MissingFiles)
+ expect { service.execute(user, invalid_params) }
+ .to raise_error(described_class::MissingFiles)
end
end
end
diff --git a/spec/services/merge_requests/create_service_spec.rb b/spec/services/merge_requests/create_service_spec.rb
index 2026b7f6510..36a2b672473 100644
--- a/spec/services/merge_requests/create_service_spec.rb
+++ b/spec/services/merge_requests/create_service_spec.rb
@@ -83,9 +83,9 @@ describe MergeRequests::CreateService, services: true do
let!(:pipeline_3) { create(:ci_pipeline, project: project, ref: "other_branch", project_id: project.id) }
before do
- project.merge_requests.
- where(source_branch: opts[:source_branch], target_branch: opts[:target_branch]).
- destroy_all
+ project.merge_requests
+ .where(source_branch: opts[:source_branch], target_branch: opts[:target_branch])
+ .destroy_all
end
it 'sets head pipeline' do
diff --git a/spec/services/merge_requests/merge_service_spec.rb b/spec/services/merge_requests/merge_service_spec.rb
index b3b188a805f..711059208c1 100644
--- a/spec/services/merge_requests/merge_service_spec.rb
+++ b/spec/services/merge_requests/merge_service_spec.rb
@@ -141,9 +141,9 @@ describe MergeRequests::MergeService, services: true do
end
it 'removes the source branch' do
- expect(DeleteBranchService).to receive(:new).
- with(merge_request.source_project, merge_request.author).
- and_call_original
+ expect(DeleteBranchService).to receive(:new)
+ .with(merge_request.source_project, merge_request.author)
+ .and_call_original
service.execute(merge_request)
end
end
diff --git a/spec/services/merge_requests/refresh_service_spec.rb b/spec/services/merge_requests/refresh_service_spec.rb
index 1f109eab268..671a932441e 100644
--- a/spec/services/merge_requests/refresh_service_spec.rb
+++ b/spec/services/merge_requests/refresh_service_spec.rb
@@ -57,8 +57,8 @@ describe MergeRequests::RefreshService, services: true do
end
it 'executes hooks with update action' do
- expect(refresh_service).to have_received(:execute_hooks).
- with(@merge_request, 'update', @oldrev)
+ expect(refresh_service).to have_received(:execute_hooks)
+ .with(@merge_request, 'update', @oldrev)
expect(@merge_request.notes).not_to be_empty
expect(@merge_request).to be_open
@@ -83,8 +83,8 @@ describe MergeRequests::RefreshService, services: true do
end
it 'executes hooks with update action' do
- expect(refresh_service).to have_received(:execute_hooks).
- with(@merge_request, 'update', @oldrev)
+ expect(refresh_service).to have_received(:execute_hooks)
+ .with(@merge_request, 'update', @oldrev)
expect(@merge_request.notes).not_to be_empty
expect(@merge_request).to be_open
@@ -146,8 +146,8 @@ describe MergeRequests::RefreshService, services: true do
end
it 'executes hooks with update action' do
- expect(refresh_service).to have_received(:execute_hooks).
- with(@fork_merge_request, 'update', @oldrev)
+ expect(refresh_service).to have_received(:execute_hooks)
+ .with(@fork_merge_request, 'update', @oldrev)
expect(@merge_request.notes).to be_empty
expect(@merge_request).to be_open
@@ -228,8 +228,8 @@ describe MergeRequests::RefreshService, services: true do
let(:refresh_service) { service.new(@fork_project, @user) }
it 'refreshes the merge request' do
- expect(refresh_service).to receive(:execute_hooks).
- with(@fork_merge_request, 'update', Gitlab::Git::BLANK_SHA)
+ expect(refresh_service).to receive(:execute_hooks)
+ .with(@fork_merge_request, 'update', Gitlab::Git::BLANK_SHA)
allow_any_instance_of(Repository).to receive(:merge_base).and_return(@oldrev)
refresh_service.execute(Gitlab::Git::BLANK_SHA, @newrev, 'refs/heads/master')
diff --git a/spec/services/merge_requests/reopen_service_spec.rb b/spec/services/merge_requests/reopen_service_spec.rb
index b6d4db2f922..6cc403bdb7f 100644
--- a/spec/services/merge_requests/reopen_service_spec.rb
+++ b/spec/services/merge_requests/reopen_service_spec.rb
@@ -31,8 +31,8 @@ describe MergeRequests::ReopenService, services: true do
it { expect(merge_request).to be_reopened }
it 'executes hooks with reopen action' do
- expect(service).to have_received(:execute_hooks).
- with(merge_request, 'reopen')
+ expect(service).to have_received(:execute_hooks)
+ .with(merge_request, 'reopen')
end
it 'sends email to user2 about reopen of merge_request' do
diff --git a/spec/services/merge_requests/update_service_spec.rb b/spec/services/merge_requests/update_service_spec.rb
index fd46020bbdb..ec15b5cac14 100644
--- a/spec/services/merge_requests/update_service_spec.rb
+++ b/spec/services/merge_requests/update_service_spec.rb
@@ -78,8 +78,8 @@ describe MergeRequests::UpdateService, services: true do
end
it 'executes hooks with update action' do
- expect(service).to have_received(:execute_hooks).
- with(@merge_request, 'update')
+ expect(service).to have_received(:execute_hooks)
+ .with(@merge_request, 'update')
end
it 'sends email to user2 about assign of new merge request and email to user3 about merge request unassignment' do
@@ -195,8 +195,8 @@ describe MergeRequests::UpdateService, services: true do
head_pipeline_of: merge_request
)
- expect(MergeRequests::MergeWhenPipelineSucceedsService).to receive(:new).with(project, user).
- and_return(service_mock)
+ expect(MergeRequests::MergeWhenPipelineSucceedsService).to receive(:new).with(project, user)
+ .and_return(service_mock)
expect(service_mock).to receive(:execute).with(merge_request)
end
diff --git a/spec/services/projects/housekeeping_service_spec.rb b/spec/services/projects/housekeeping_service_spec.rb
index fff12beed71..ebed802708d 100644
--- a/spec/services/projects/housekeeping_service_spec.rb
+++ b/spec/services/projects/housekeeping_service_spec.rb
@@ -66,14 +66,14 @@ describe Projects::HousekeepingService do
allow(subject).to receive(:lease_key).and_return(:the_lease_key)
# At push 200
- expect(GitGarbageCollectWorker).to receive(:perform_async).with(project.id, :gc, :the_lease_key, :the_uuid).
- exactly(1).times
+ expect(GitGarbageCollectWorker).to receive(:perform_async).with(project.id, :gc, :the_lease_key, :the_uuid)
+ .exactly(1).times
# At push 50, 100, 150
- expect(GitGarbageCollectWorker).to receive(:perform_async).with(project.id, :full_repack, :the_lease_key, :the_uuid).
- exactly(3).times
+ expect(GitGarbageCollectWorker).to receive(:perform_async).with(project.id, :full_repack, :the_lease_key, :the_uuid)
+ .exactly(3).times
# At push 10, 20, ... (except those above)
- expect(GitGarbageCollectWorker).to receive(:perform_async).with(project.id, :incremental_repack, :the_lease_key, :the_uuid).
- exactly(16).times
+ expect(GitGarbageCollectWorker).to receive(:perform_async).with(project.id, :incremental_repack, :the_lease_key, :the_uuid)
+ .exactly(16).times
201.times do
subject.increment!
diff --git a/spec/services/projects/import_service_spec.rb b/spec/services/projects/import_service_spec.rb
index 44db299812f..e855de38037 100644
--- a/spec/services/projects/import_service_spec.rb
+++ b/spec/services/projects/import_service_spec.rb
@@ -111,11 +111,11 @@ describe Projects::ImportService, services: true do
end
it 'flushes various caches' do
- allow_any_instance_of(Repository).to receive(:fetch_remote).
- and_return(true)
+ allow_any_instance_of(Repository).to receive(:fetch_remote)
+ .and_return(true)
- allow_any_instance_of(Gitlab::GithubImport::Importer).to receive(:execute).
- and_return(true)
+ allow_any_instance_of(Gitlab::GithubImport::Importer).to receive(:execute)
+ .and_return(true)
expect_any_instance_of(Repository).to receive(:expire_content_cache)
diff --git a/spec/services/projects/propagate_service_template_spec.rb b/spec/services/projects/propagate_service_template_spec.rb
index 8a6a9f09f74..a6d43c4f0f1 100644
--- a/spec/services/projects/propagate_service_template_spec.rb
+++ b/spec/services/projects/propagate_service_template_spec.rb
@@ -60,8 +60,8 @@ describe Projects::PropagateServiceTemplate, services: true do
Service.build_from_template(project.id, service_template).save!
Service.build_from_template(project.id, other_service).save!
- expect { described_class.propagate(service_template) }.
- not_to change { Service.count }
+ expect { described_class.propagate(service_template) }
+ .not_to change { Service.count }
end
it 'creates the service containing the template attributes' do
@@ -90,8 +90,8 @@ describe Projects::PropagateServiceTemplate, services: true do
it 'updates the project external tracker' do
service_template.update!(category: 'issue_tracker', default: false)
- expect { described_class.propagate(service_template) }.
- to change { project.reload.has_external_issue_tracker }.to(true)
+ expect { described_class.propagate(service_template) }
+ .to change { project.reload.has_external_issue_tracker }.to(true)
end
end
@@ -99,8 +99,8 @@ describe Projects::PropagateServiceTemplate, services: true do
it 'updates the project external tracker' do
service_template.update!(type: 'ExternalWikiService')
- expect { described_class.propagate(service_template) }.
- to change { project.reload.has_external_wiki }.to(true)
+ expect { described_class.propagate(service_template) }
+ .to change { project.reload.has_external_wiki }.to(true)
end
end
end
diff --git a/spec/services/projects/transfer_service_spec.rb b/spec/services/projects/transfer_service_spec.rb
index 63bd0353d50..76c52d55ae5 100644
--- a/spec/services/projects/transfer_service_spec.rb
+++ b/spec/services/projects/transfer_service_spec.rb
@@ -7,10 +7,10 @@ describe Projects::TransferService, services: true do
context 'namespace -> namespace' do
before do
- allow_any_instance_of(Gitlab::UploadsTransfer).
- to receive(:move_project).and_return(true)
- allow_any_instance_of(Gitlab::PagesTransfer).
- to receive(:move_project).and_return(true)
+ allow_any_instance_of(Gitlab::UploadsTransfer)
+ .to receive(:move_project).and_return(true)
+ allow_any_instance_of(Gitlab::PagesTransfer)
+ .to receive(:move_project).and_return(true)
group.add_owner(user)
@result = transfer_project(project, user, group)
end
@@ -173,9 +173,9 @@ describe Projects::TransferService, services: true do
end
it 'only schedules a single job for every user' do
- expect(UserProjectAccessChangedService).to receive(:new).
- with([owner.id, group_member.id]).
- and_call_original
+ expect(UserProjectAccessChangedService).to receive(:new)
+ .with([owner.id, group_member.id])
+ .and_call_original
transfer_project(project, owner, group)
end
diff --git a/spec/services/projects/unlink_fork_service_spec.rb b/spec/services/projects/unlink_fork_service_spec.rb
index 23f5555d3e0..d34652bd7ac 100644
--- a/spec/services/projects/unlink_fork_service_spec.rb
+++ b/spec/services/projects/unlink_fork_service_spec.rb
@@ -12,9 +12,9 @@ describe Projects::UnlinkForkService, services: true do
let(:mr_close_service) { MergeRequests::CloseService.new(fork_project, user) }
before do
- allow(MergeRequests::CloseService).to receive(:new).
- with(fork_project, user).
- and_return(mr_close_service)
+ allow(MergeRequests::CloseService).to receive(:new)
+ .with(fork_project, user)
+ .and_return(mr_close_service)
end
it 'close all pending merge requests' do
diff --git a/spec/services/submit_usage_ping_service_spec.rb b/spec/services/submit_usage_ping_service_spec.rb
index 63a1e78f274..817fa4262d5 100644
--- a/spec/services/submit_usage_ping_service_spec.rb
+++ b/spec/services/submit_usage_ping_service_spec.rb
@@ -92,8 +92,8 @@ describe SubmitUsagePingService do
end
def stub_response(body)
- stub_request(:post, 'https://version.gitlab.com/usage_data').
- to_return(
+ stub_request(:post, 'https://version.gitlab.com/usage_data')
+ .to_return(
headers: { 'Content-Type' => 'application/json' },
body: body.to_json
)
diff --git a/spec/services/system_note_service_spec.rb b/spec/services/system_note_service_spec.rb
index 9295c09aefc..8d3dafafab2 100644
--- a/spec/services/system_note_service_spec.rb
+++ b/spec/services/system_note_service_spec.rb
@@ -333,8 +333,8 @@ describe SystemNoteService, services: true do
end
it 'sets the note text' do
- expect(subject.note).
- to eq "changed title from **{-Old title-}** to **{+Lorem ipsum+}**"
+ expect(subject.note)
+ .to eq "changed title from **{-Old title-}** to **{+Lorem ipsum+}**"
end
end
end
@@ -521,8 +521,8 @@ describe SystemNoteService, services: true do
context 'when mentioner is not a MergeRequest' do
it 'is falsey' do
mentioner = noteable.dup
- expect(described_class.cross_reference_disallowed?(noteable, mentioner)).
- to be_falsey
+ expect(described_class.cross_reference_disallowed?(noteable, mentioner))
+ .to be_falsey
end
end
@@ -533,14 +533,14 @@ describe SystemNoteService, services: true do
it 'is truthy when noteable is in commits' do
expect(mentioner).to receive(:commits).and_return([noteable])
- expect(described_class.cross_reference_disallowed?(noteable, mentioner)).
- to be_truthy
+ expect(described_class.cross_reference_disallowed?(noteable, mentioner))
+ .to be_truthy
end
it 'is falsey when noteable is not in commits' do
expect(mentioner).to receive(:commits).and_return([])
- expect(described_class.cross_reference_disallowed?(noteable, mentioner)).
- to be_falsey
+ expect(described_class.cross_reference_disallowed?(noteable, mentioner))
+ .to be_falsey
end
end
@@ -548,8 +548,8 @@ describe SystemNoteService, services: true do
let(:noteable) { ExternalIssue.new('EXT-1234', project) }
it 'is truthy' do
mentioner = noteable.dup
- expect(described_class.cross_reference_disallowed?(noteable, mentioner)).
- to be_truthy
+ expect(described_class.cross_reference_disallowed?(noteable, mentioner))
+ .to be_truthy
end
end
end
@@ -566,13 +566,13 @@ describe SystemNoteService, services: true do
end
it 'is truthy when already mentioned' do
- expect(described_class.cross_reference_exists?(noteable, commit0)).
- to be_truthy
+ expect(described_class.cross_reference_exists?(noteable, commit0))
+ .to be_truthy
end
it 'is falsey when not already mentioned' do
- expect(described_class.cross_reference_exists?(noteable, commit1)).
- to be_falsey
+ expect(described_class.cross_reference_exists?(noteable, commit1))
+ .to be_falsey
end
context 'legacy capitalized cross reference' do
@@ -583,8 +583,8 @@ describe SystemNoteService, services: true do
end
it 'is truthy when already mentioned' do
- expect(described_class.cross_reference_exists?(noteable, commit0)).
- to be_truthy
+ expect(described_class.cross_reference_exists?(noteable, commit0))
+ .to be_truthy
end
end
end
@@ -596,13 +596,13 @@ describe SystemNoteService, services: true do
end
it 'is truthy when already mentioned' do
- expect(described_class.cross_reference_exists?(commit0, commit1)).
- to be_truthy
+ expect(described_class.cross_reference_exists?(commit0, commit1))
+ .to be_truthy
end
it 'is falsey when not already mentioned' do
- expect(described_class.cross_reference_exists?(commit1, commit0)).
- to be_falsey
+ expect(described_class.cross_reference_exists?(commit1, commit0))
+ .to be_falsey
end
context 'legacy capitalized cross reference' do
@@ -613,8 +613,8 @@ describe SystemNoteService, services: true do
end
it 'is truthy when already mentioned' do
- expect(described_class.cross_reference_exists?(commit0, commit1)).
- to be_truthy
+ expect(described_class.cross_reference_exists?(commit0, commit1))
+ .to be_truthy
end
end
end
@@ -629,8 +629,8 @@ describe SystemNoteService, services: true do
end
it 'is true when a fork mentions an external issue' do
- expect(described_class.cross_reference_exists?(noteable, commit2)).
- to be true
+ expect(described_class.cross_reference_exists?(noteable, commit2))
+ .to be true
end
context 'legacy capitalized cross reference' do
@@ -640,8 +640,8 @@ describe SystemNoteService, services: true do
end
it 'is true when a fork mentions an external issue' do
- expect(described_class.cross_reference_exists?(noteable, commit2)).
- to be true
+ expect(described_class.cross_reference_exists?(noteable, commit2))
+ .to be true
end
end
end
diff --git a/spec/services/tags/create_service_spec.rb b/spec/services/tags/create_service_spec.rb
index b9121b1de49..9f143cc5667 100644
--- a/spec/services/tags/create_service_spec.rb
+++ b/spec/services/tags/create_service_spec.rb
@@ -26,9 +26,9 @@ describe Tags::CreateService, services: true do
context 'when tag already exists' do
it 'returns an error' do
- expect(repository).to receive(:add_tag).
- with(user, 'v1.1.0', 'master', 'Foo').
- and_raise(Rugged::TagError)
+ expect(repository).to receive(:add_tag)
+ .with(user, 'v1.1.0', 'master', 'Foo')
+ .and_raise(Rugged::TagError)
response = service.execute('v1.1.0', 'master', 'Foo')
@@ -39,9 +39,9 @@ describe Tags::CreateService, services: true do
context 'when pre-receive hook fails' do
it 'returns an error' do
- expect(repository).to receive(:add_tag).
- with(user, 'v1.1.0', 'master', 'Foo').
- and_raise(GitHooksService::PreReceiveError, 'something went wrong')
+ expect(repository).to receive(:add_tag)
+ .with(user, 'v1.1.0', 'master', 'Foo')
+ .and_raise(GitHooksService::PreReceiveError, 'something went wrong')
response = service.execute('v1.1.0', 'master', 'Foo')
diff --git a/spec/services/user_project_access_changed_service_spec.rb b/spec/services/user_project_access_changed_service_spec.rb
index b4efe7de431..14a5e40350a 100644
--- a/spec/services/user_project_access_changed_service_spec.rb
+++ b/spec/services/user_project_access_changed_service_spec.rb
@@ -3,8 +3,8 @@ require 'spec_helper'
describe UserProjectAccessChangedService do
describe '#execute' do
it 'schedules the user IDs' do
- expect(AuthorizedProjectsWorker).to receive(:bulk_perform_and_wait).
- with([[1], [2]])
+ expect(AuthorizedProjectsWorker).to receive(:bulk_perform_and_wait)
+ .with([[1], [2]])
described_class.new([1, 2]).execute
end
diff --git a/spec/services/users/activity_service_spec.rb b/spec/services/users/activity_service_spec.rb
index 8d67ebe3231..2e009d4ce1c 100644
--- a/spec/services/users/activity_service_spec.rb
+++ b/spec/services/users/activity_service_spec.rb
@@ -41,8 +41,8 @@ describe Users::ActivityService, services: true do
end
def last_hour_user_ids
- Gitlab::UserActivities.new.
- select { |k, v| v >= 1.hour.ago.to_i.to_s }.
- map { |k, _| k.to_i }
+ Gitlab::UserActivities.new
+ .select { |k, v| v >= 1.hour.ago.to_i.to_s }
+ .map { |k, _| k.to_i }
end
end
diff --git a/spec/services/users/refresh_authorized_projects_service_spec.rb b/spec/services/users/refresh_authorized_projects_service_spec.rb
index 8c40d25e00c..b65cadbb2f5 100644
--- a/spec/services/users/refresh_authorized_projects_service_spec.rb
+++ b/spec/services/users/refresh_authorized_projects_service_spec.rb
@@ -10,11 +10,11 @@ describe Users::RefreshAuthorizedProjectsService do
describe '#execute', :redis do
it 'refreshes the authorizations using a lease' do
- expect_any_instance_of(Gitlab::ExclusiveLease).to receive(:try_obtain).
- and_return('foo')
+ expect_any_instance_of(Gitlab::ExclusiveLease).to receive(:try_obtain)
+ .and_return('foo')
- expect(Gitlab::ExclusiveLease).to receive(:cancel).
- with(an_instance_of(String), 'foo')
+ expect(Gitlab::ExclusiveLease).to receive(:cancel)
+ .with(an_instance_of(String), 'foo')
expect(service).to receive(:execute_without_lease)
@@ -29,11 +29,11 @@ describe Users::RefreshAuthorizedProjectsService do
it 'updates the authorized projects of the user' do
project2 = create(:empty_project)
- to_remove = user.project_authorizations.
- create!(project: project2, access_level: Gitlab::Access::MASTER)
+ to_remove = user.project_authorizations
+ .create!(project: project2, access_level: Gitlab::Access::MASTER)
- expect(service).to receive(:update_authorizations).
- with([to_remove.project_id], [[user.id, project.id, Gitlab::Access::MASTER]])
+ expect(service).to receive(:update_authorizations)
+ .with([to_remove.project_id], [[user.id, project.id, Gitlab::Access::MASTER]])
service.execute_without_lease
end
@@ -41,11 +41,11 @@ describe Users::RefreshAuthorizedProjectsService do
it 'sets the access level of a project to the highest available level' do
user.project_authorizations.delete_all
- to_remove = user.project_authorizations.
- create!(project: project, access_level: Gitlab::Access::DEVELOPER)
+ to_remove = user.project_authorizations
+ .create!(project: project, access_level: Gitlab::Access::DEVELOPER)
- expect(service).to receive(:update_authorizations).
- with([to_remove.project_id], [[user.id, project.id, Gitlab::Access::MASTER]])
+ expect(service).to receive(:update_authorizations)
+ .with([to_remove.project_id], [[user.id, project.id, Gitlab::Access::MASTER]])
service.execute_without_lease
end
diff --git a/spec/support/api/schema_matcher.rb b/spec/support/api/schema_matcher.rb
index e42d727672b..dff0dfba675 100644
--- a/spec/support/api/schema_matcher.rb
+++ b/spec/support/api/schema_matcher.rb
@@ -1,8 +1,16 @@
+def schema_path(schema)
+ schema_directory = "#{Dir.pwd}/spec/fixtures/api/schemas"
+ "#{schema_directory}/#{schema}.json"
+end
+
RSpec::Matchers.define :match_response_schema do |schema, **options|
match do |response|
- schema_directory = "#{Dir.pwd}/spec/fixtures/api/schemas"
- schema_path = "#{schema_directory}/#{schema}.json"
+ JSON::Validator.validate!(schema_path(schema), response.body, options)
+ end
+end
- JSON::Validator.validate!(schema_path, response.body, options)
+RSpec::Matchers.define :match_schema do |schema, **options|
+ match do |data|
+ JSON::Validator.validate!(schema_path(schema), data, options)
end
end
diff --git a/spec/support/capybara.rb b/spec/support/capybara.rb
index 4aa81a03558..3e5d6cf1364 100644
--- a/spec/support/capybara.rb
+++ b/spec/support/capybara.rb
@@ -36,12 +36,13 @@ RSpec.configure do |config|
$capybara_server_already_started = true
end
- config.after(:each, :js) do
+ config.after(:each, :js) do |example|
# capybara/rspec already calls Capybara.reset_sessions! in an `after` hook,
# but `block_and_wait_for_requests_complete` is called before it so by
# calling it explicitely here, we prevent any new requests from being fired
# See https://github.com/teamcapybara/capybara/blob/ffb41cfad620de1961bb49b1562a9fa9b28c0903/lib/capybara/rspec.rb#L20-L25
- Capybara.reset_sessions!
+ # We don't reset the session when the example failed, because we need capybara-screenshot to have access to it.
+ Capybara.reset_sessions! unless example.exception
block_and_wait_for_requests_complete
end
end
diff --git a/spec/support/controllers/githubish_import_controller_shared_examples.rb b/spec/support/controllers/githubish_import_controller_shared_examples.rb
index d6b40db09ce..a8d9566b4e4 100644
--- a/spec/support/controllers/githubish_import_controller_shared_examples.rb
+++ b/spec/support/controllers/githubish_import_controller_shared_examples.rb
@@ -14,8 +14,8 @@ shared_examples 'a GitHub-ish import controller: POST personal_access_token' do
it "updates access token" do
token = 'asdfasdf9876'
- allow_any_instance_of(Gitlab::GithubImport::Client).
- to receive(:user).and_return(true)
+ allow_any_instance_of(Gitlab::GithubImport::Client)
+ .to receive(:user).and_return(true)
post :personal_access_token, personal_access_token: token
@@ -79,8 +79,8 @@ shared_examples 'a GitHub-ish import controller: GET status' do
end
it "handles an invalid access token" do
- allow_any_instance_of(Gitlab::GithubImport::Client).
- to receive(:repos).and_raise(Octokit::Unauthorized)
+ allow_any_instance_of(Gitlab::GithubImport::Client)
+ .to receive(:repos).and_raise(Octokit::Unauthorized)
get :status
@@ -110,9 +110,9 @@ shared_examples 'a GitHub-ish import controller: POST create' do
context "when the repository owner is the provider user" do
context "when the provider user and GitLab user's usernames match" do
it "takes the current user's namespace" do
- expect(Gitlab::GithubImport::ProjectCreator).
- to receive(:new).with(provider_repo, provider_repo.name, user.namespace, user, access_params, type: provider).
- and_return(double(execute: true))
+ expect(Gitlab::GithubImport::ProjectCreator)
+ .to receive(:new).with(provider_repo, provider_repo.name, user.namespace, user, access_params, type: provider)
+ .and_return(double(execute: true))
post :create, format: :js
end
@@ -122,9 +122,9 @@ shared_examples 'a GitHub-ish import controller: POST create' do
let(:provider_username) { "someone_else" }
it "takes the current user's namespace" do
- expect(Gitlab::GithubImport::ProjectCreator).
- to receive(:new).with(provider_repo, provider_repo.name, user.namespace, user, access_params, type: provider).
- and_return(double(execute: true))
+ expect(Gitlab::GithubImport::ProjectCreator)
+ .to receive(:new).with(provider_repo, provider_repo.name, user.namespace, user, access_params, type: provider)
+ .and_return(double(execute: true))
post :create, format: :js
end
@@ -144,9 +144,9 @@ shared_examples 'a GitHub-ish import controller: POST create' do
context "when the namespace is owned by the GitLab user" do
it "takes the existing namespace" do
- expect(Gitlab::GithubImport::ProjectCreator).
- to receive(:new).with(provider_repo, provider_repo.name, existing_namespace, user, access_params, type: provider).
- and_return(double(execute: true))
+ expect(Gitlab::GithubImport::ProjectCreator)
+ .to receive(:new).with(provider_repo, provider_repo.name, existing_namespace, user, access_params, type: provider)
+ .and_return(double(execute: true))
post :create, format: :js
end
@@ -159,9 +159,9 @@ shared_examples 'a GitHub-ish import controller: POST create' do
end
it "creates a project using user's namespace" do
- expect(Gitlab::GithubImport::ProjectCreator).
- to receive(:new).with(provider_repo, provider_repo.name, user.namespace, user, access_params, type: provider).
- and_return(double(execute: true))
+ expect(Gitlab::GithubImport::ProjectCreator)
+ .to receive(:new).with(provider_repo, provider_repo.name, user.namespace, user, access_params, type: provider)
+ .and_return(double(execute: true))
post :create, format: :js
end
@@ -171,16 +171,16 @@ shared_examples 'a GitHub-ish import controller: POST create' do
context "when a namespace with the provider user's username doesn't exist" do
context "when current user can create namespaces" do
it "creates the namespace" do
- expect(Gitlab::GithubImport::ProjectCreator).
- to receive(:new).and_return(double(execute: true))
+ expect(Gitlab::GithubImport::ProjectCreator)
+ .to receive(:new).and_return(double(execute: true))
expect { post :create, target_namespace: provider_repo.name, format: :js }.to change(Namespace, :count).by(1)
end
it "takes the new namespace" do
- expect(Gitlab::GithubImport::ProjectCreator).
- to receive(:new).with(provider_repo, provider_repo.name, an_instance_of(Group), user, access_params, type: provider).
- and_return(double(execute: true))
+ expect(Gitlab::GithubImport::ProjectCreator)
+ .to receive(:new).with(provider_repo, provider_repo.name, an_instance_of(Group), user, access_params, type: provider)
+ .and_return(double(execute: true))
post :create, target_namespace: provider_repo.name, format: :js
end
@@ -192,16 +192,16 @@ shared_examples 'a GitHub-ish import controller: POST create' do
end
it "doesn't create the namespace" do
- expect(Gitlab::GithubImport::ProjectCreator).
- to receive(:new).and_return(double(execute: true))
+ expect(Gitlab::GithubImport::ProjectCreator)
+ .to receive(:new).and_return(double(execute: true))
expect { post :create, format: :js }.not_to change(Namespace, :count)
end
it "takes the current user's namespace" do
- expect(Gitlab::GithubImport::ProjectCreator).
- to receive(:new).with(provider_repo, provider_repo.name, user.namespace, user, access_params, type: provider).
- and_return(double(execute: true))
+ expect(Gitlab::GithubImport::ProjectCreator)
+ .to receive(:new).with(provider_repo, provider_repo.name, user.namespace, user, access_params, type: provider)
+ .and_return(double(execute: true))
post :create, format: :js
end
@@ -217,17 +217,17 @@ shared_examples 'a GitHub-ish import controller: POST create' do
end
it 'takes the selected namespace and name' do
- expect(Gitlab::GithubImport::ProjectCreator).
- to receive(:new).with(provider_repo, test_name, test_namespace, user, access_params, type: provider).
- and_return(double(execute: true))
+ expect(Gitlab::GithubImport::ProjectCreator)
+ .to receive(:new).with(provider_repo, test_name, test_namespace, user, access_params, type: provider)
+ .and_return(double(execute: true))
post :create, { target_namespace: test_namespace.name, new_name: test_name, format: :js }
end
it 'takes the selected name and default namespace' do
- expect(Gitlab::GithubImport::ProjectCreator).
- to receive(:new).with(provider_repo, test_name, user.namespace, user, access_params, type: provider).
- and_return(double(execute: true))
+ expect(Gitlab::GithubImport::ProjectCreator)
+ .to receive(:new).with(provider_repo, test_name, user.namespace, user, access_params, type: provider)
+ .and_return(double(execute: true))
post :create, { new_name: test_name, format: :js }
end
@@ -243,9 +243,9 @@ shared_examples 'a GitHub-ish import controller: POST create' do
end
it 'takes the selected namespace and name' do
- expect(Gitlab::GithubImport::ProjectCreator).
- to receive(:new).with(provider_repo, test_name, nested_namespace, user, access_params, type: provider).
- and_return(double(execute: true))
+ expect(Gitlab::GithubImport::ProjectCreator)
+ .to receive(:new).with(provider_repo, test_name, nested_namespace, user, access_params, type: provider)
+ .and_return(double(execute: true))
post :create, { target_namespace: nested_namespace.full_path, new_name: test_name, format: :js }
end
@@ -255,26 +255,26 @@ shared_examples 'a GitHub-ish import controller: POST create' do
let(:test_name) { 'test_name' }
it 'takes the selected namespace and name' do
- expect(Gitlab::GithubImport::ProjectCreator).
- to receive(:new).with(provider_repo, test_name, kind_of(Namespace), user, access_params, type: provider).
- and_return(double(execute: true))
+ expect(Gitlab::GithubImport::ProjectCreator)
+ .to receive(:new).with(provider_repo, test_name, kind_of(Namespace), user, access_params, type: provider)
+ .and_return(double(execute: true))
post :create, { target_namespace: 'foo/bar', new_name: test_name, format: :js }
end
it 'creates the namespaces' do
- allow(Gitlab::GithubImport::ProjectCreator).
- to receive(:new).with(provider_repo, test_name, kind_of(Namespace), user, access_params, type: provider).
- and_return(double(execute: true))
+ allow(Gitlab::GithubImport::ProjectCreator)
+ .to receive(:new).with(provider_repo, test_name, kind_of(Namespace), user, access_params, type: provider)
+ .and_return(double(execute: true))
expect { post :create, { target_namespace: 'foo/bar', new_name: test_name, format: :js } }
.to change { Namespace.count }.by(2)
end
it 'new namespace has the right parent' do
- allow(Gitlab::GithubImport::ProjectCreator).
- to receive(:new).with(provider_repo, test_name, kind_of(Namespace), user, access_params, type: provider).
- and_return(double(execute: true))
+ allow(Gitlab::GithubImport::ProjectCreator)
+ .to receive(:new).with(provider_repo, test_name, kind_of(Namespace), user, access_params, type: provider)
+ .and_return(double(execute: true))
post :create, { target_namespace: 'foo/bar', new_name: test_name, format: :js }
@@ -287,17 +287,17 @@ shared_examples 'a GitHub-ish import controller: POST create' do
let!(:parent_namespace) { create(:group, name: 'foo', owner: user) }
it 'takes the selected namespace and name' do
- expect(Gitlab::GithubImport::ProjectCreator).
- to receive(:new).with(provider_repo, test_name, kind_of(Namespace), user, access_params, type: provider).
- and_return(double(execute: true))
+ expect(Gitlab::GithubImport::ProjectCreator)
+ .to receive(:new).with(provider_repo, test_name, kind_of(Namespace), user, access_params, type: provider)
+ .and_return(double(execute: true))
post :create, { target_namespace: 'foo/foobar/bar', new_name: test_name, format: :js }
end
it 'creates the namespaces' do
- allow(Gitlab::GithubImport::ProjectCreator).
- to receive(:new).with(provider_repo, test_name, kind_of(Namespace), user, access_params, type: provider).
- and_return(double(execute: true))
+ allow(Gitlab::GithubImport::ProjectCreator)
+ .to receive(:new).with(provider_repo, test_name, kind_of(Namespace), user, access_params, type: provider)
+ .and_return(double(execute: true))
expect { post :create, { target_namespace: 'foo/foobar/bar', new_name: test_name, format: :js } }
.to change { Namespace.count }.by(2)
diff --git a/spec/support/filtered_search_helpers.rb b/spec/support/filtered_search_helpers.rb
index 37cc308e613..d21c4324d9e 100644
--- a/spec/support/filtered_search_helpers.rb
+++ b/spec/support/filtered_search_helpers.rb
@@ -14,6 +14,9 @@ module FilteredSearchHelpers
filtered_search.set(search)
if submit
+ # Wait for the lazy author/assignee tokens that
+ # swap out the username with an avatar and name
+ wait_for_requests
filtered_search.send_keys(:enter)
end
end
diff --git a/spec/support/mentionable_shared_examples.rb b/spec/support/mentionable_shared_examples.rb
index 87936bb4859..3ac201f1fb1 100644
--- a/spec/support/mentionable_shared_examples.rb
+++ b/spec/support/mentionable_shared_examples.rb
@@ -81,8 +81,8 @@ shared_examples 'a mentionable' do
ext_issue, ext_mr, ext_commit]
mentioned_objects.each do |referenced|
- expect(SystemNoteService).to receive(:cross_reference).
- with(referenced, subject.local_reference, author)
+ expect(SystemNoteService).to receive(:cross_reference)
+ .with(referenced, subject.local_reference, author)
end
subject.create_cross_references!
@@ -127,15 +127,15 @@ shared_examples 'an editable mentionable' do
# These three objects were already referenced, and should not receive new
# notes
[mentioned_issue, mentioned_commit, ext_issue].each do |oldref|
- expect(SystemNoteService).not_to receive(:cross_reference).
- with(oldref, any_args)
+ expect(SystemNoteService).not_to receive(:cross_reference)
+ .with(oldref, any_args)
end
# These two issues are new and should receive reference notes
# In the case of MergeRequests remember that cannot mention commits included in the MergeRequest
new_issues.each do |newref|
- expect(SystemNoteService).to receive(:cross_reference).
- with(newref, subject.local_reference, author)
+ expect(SystemNoteService).to receive(:cross_reference)
+ .with(newref, subject.local_reference, author)
end
set_mentionable_text.call(new_text)
diff --git a/spec/support/prometheus/additional_metrics_shared_examples.rb b/spec/support/prometheus/additional_metrics_shared_examples.rb
new file mode 100644
index 00000000000..016e16fc8d4
--- /dev/null
+++ b/spec/support/prometheus/additional_metrics_shared_examples.rb
@@ -0,0 +1,101 @@
+RSpec.shared_examples 'additional metrics query' do
+ include Prometheus::MetricBuilders
+
+ let(:metric_group_class) { Gitlab::Prometheus::MetricGroup }
+ let(:metric_class) { Gitlab::Prometheus::Metric }
+
+ let(:metric_names) { %w{metric_a metric_b} }
+
+ let(:query_range_result) do
+ [{ 'metric': {}, 'values': [[1488758662.506, '0.00002996364761904785'], [1488758722.506, '0.00003090239047619091']] }]
+ end
+
+ before do
+ allow(client).to receive(:label_values).and_return(metric_names)
+ allow(metric_group_class).to receive(:all).and_return([simple_metric_group(metrics: [simple_metric])])
+ end
+
+ context 'with one group where two metrics is found' do
+ before do
+ allow(metric_group_class).to receive(:all).and_return([simple_metric_group])
+ end
+
+ context 'some queries return results' do
+ before do
+ allow(client).to receive(:query_range).with('query_range_a', any_args).and_return(query_range_result)
+ allow(client).to receive(:query_range).with('query_range_b', any_args).and_return(query_range_result)
+ allow(client).to receive(:query_range).with('query_range_empty', any_args).and_return([])
+ end
+
+ it 'return group data only for queries with results' do
+ expected = [
+ {
+ group: 'name',
+ priority: 1,
+ metrics: [
+ {
+ title: 'title', weight: 1, y_label: 'Values', queries: [
+ { query_range: 'query_range_a', result: query_range_result },
+ { query_range: 'query_range_b', label: 'label', unit: 'unit', result: query_range_result }
+ ]
+ }
+ ]
+ }
+ ]
+
+ expect(query_result).to match_schema('prometheus/additional_metrics_query_result')
+ expect(query_result).to eq(expected)
+ end
+ end
+ end
+
+ context 'with two groups with one metric each' do
+ let(:metrics) { [simple_metric(queries: [simple_query])] }
+ before do
+ allow(metric_group_class).to receive(:all).and_return(
+ [
+ simple_metric_group(name: 'group_a', metrics: [simple_metric(queries: [simple_query])]),
+ simple_metric_group(name: 'group_b', metrics: [simple_metric(title: 'title_b', queries: [simple_query('b')])])
+ ])
+ allow(client).to receive(:label_values).and_return(metric_names)
+ end
+
+ context 'both queries return results' do
+ before do
+ allow(client).to receive(:query_range).with('query_range_a', any_args).and_return(query_range_result)
+ allow(client).to receive(:query_range).with('query_range_b', any_args).and_return(query_range_result)
+ end
+
+ it 'return group data both queries' do
+ queries_with_result_a = { queries: [{ query_range: 'query_range_a', result: query_range_result }] }
+ queries_with_result_b = { queries: [{ query_range: 'query_range_b', result: query_range_result }] }
+
+ expect(query_result).to match_schema('prometheus/additional_metrics_query_result')
+
+ expect(query_result.count).to eq(2)
+ expect(query_result).to all(satisfy { |r| r[:metrics].count == 1 })
+
+ expect(query_result[0][:metrics].first).to include(queries_with_result_a)
+ expect(query_result[1][:metrics].first).to include(queries_with_result_b)
+ end
+ end
+
+ context 'one query returns result' do
+ before do
+ allow(client).to receive(:query_range).with('query_range_a', any_args).and_return(query_range_result)
+ allow(client).to receive(:query_range).with('query_range_b', any_args).and_return([])
+ end
+
+ it 'return group data only for query with results' do
+ queries_with_result = { queries: [{ query_range: 'query_range_a', result: query_range_result }] }
+
+ expect(query_result).to match_schema('prometheus/additional_metrics_query_result')
+
+ expect(query_result.count).to eq(1)
+ expect(query_result).to all(satisfy { |r| r[:metrics].count == 1 })
+
+ expect(query_result.first[:metrics].first).to include(queries_with_result)
+ end
+ end
+ end
+end
diff --git a/spec/support/prometheus/metric_builders.rb b/spec/support/prometheus/metric_builders.rb
new file mode 100644
index 00000000000..c8d056d3fc8
--- /dev/null
+++ b/spec/support/prometheus/metric_builders.rb
@@ -0,0 +1,27 @@
+module Prometheus
+ module MetricBuilders
+ def simple_query(suffix = 'a', **opts)
+ { query_range: "query_range_#{suffix}" }.merge(opts)
+ end
+
+ def simple_queries
+ [simple_query, simple_query('b', label: 'label', unit: 'unit')]
+ end
+
+ def simple_metric(title: 'title', required_metrics: [], queries: [simple_query])
+ Gitlab::Prometheus::Metric.new(title: title, required_metrics: required_metrics, weight: 1, queries: queries)
+ end
+
+ def simple_metrics(added_metric_name: 'metric_a')
+ [
+ simple_metric(required_metrics: %W(#{added_metric_name} metric_b), queries: simple_queries),
+ simple_metric(required_metrics: [added_metric_name], queries: [simple_query('empty')]),
+ simple_metric(required_metrics: %w{metric_c})
+ ]
+ end
+
+ def simple_metric_group(name: 'name', metrics: simple_metrics)
+ Gitlab::Prometheus::MetricGroup.new(name: name, priority: 1, metrics: metrics)
+ end
+ end
+end
diff --git a/spec/support/prometheus_helpers.rb b/spec/support/prometheus_helpers.rb
index 6b9ebcf2bb3..4212be2cc88 100644
--- a/spec/support/prometheus_helpers.rb
+++ b/spec/support/prometheus_helpers.rb
@@ -36,6 +36,19 @@ module PrometheusHelpers
"https://prometheus.example.com/api/v1/query_range?#{query}"
end
+ def prometheus_label_values_url(name)
+ "https://prometheus.example.com/api/v1/label/#{name}/values"
+ end
+
+ def prometheus_series_url(*matches, start: 8.hours.ago, stop: Time.now)
+ query = {
+ match: matches,
+ start: start.to_f,
+ end: stop.to_f
+ }.to_query
+ "https://prometheus.example.com/api/v1/series?#{query}"
+ end
+
def stub_prometheus_request(url, body: {}, status: 200)
WebMock.stub_request(:get, url)
.to_return({
@@ -85,6 +98,19 @@ module PrometheusHelpers
def prometheus_data(last_update: Time.now.utc)
{
success: true,
+ data: {
+ memory_values: prometheus_values_body('matrix').dig(:data, :result),
+ memory_current: prometheus_value_body('vector').dig(:data, :result),
+ cpu_values: prometheus_values_body('matrix').dig(:data, :result),
+ cpu_current: prometheus_value_body('vector').dig(:data, :result)
+ },
+ last_update: last_update
+ }
+ end
+
+ def prometheus_metrics_data(last_update: Time.now.utc)
+ {
+ success: true,
metrics: {
memory_values: prometheus_values_body('matrix').dig(:data, :result),
memory_current: prometheus_value_body('vector').dig(:data, :result),
@@ -140,4 +166,37 @@ module PrometheusHelpers
}
}
end
+
+ def prometheus_label_values
+ {
+ 'status': 'success',
+ 'data': %w(job_adds job_controller_rate_limiter_use job_depth job_queue_latency job_work_duration_sum up)
+ }
+ end
+
+ def prometheus_series(name)
+ {
+ 'status': 'success',
+ 'data': [
+ {
+ '__name__': name,
+ 'container_name': 'gitlab',
+ 'environment': 'mattermost',
+ 'id': '/docker/9953982f95cf5010dfc59d7864564d5f188aaecddeda343699783009f89db667',
+ 'image': 'gitlab/gitlab-ce:8.15.4-ce.1',
+ 'instance': 'minikube',
+ 'job': 'kubernetes-nodes',
+ 'name': 'k8s_gitlab.e6611886_mattermost-4210310111-77z8r_gitlab_2298ae6b-da24-11e6-baee-8e7f67d0eb3a_43536cb6',
+ 'namespace': 'gitlab',
+ 'pod_name': 'mattermost-4210310111-77z8r'
+ },
+ {
+ '__name__': name,
+ 'id': '/docker',
+ 'instance': 'minikube',
+ 'job': 'kubernetes-nodes'
+ }
+ ]
+ }
+ end
end
diff --git a/spec/support/reactive_caching_helpers.rb b/spec/support/reactive_caching_helpers.rb
index 98eb57f8b54..34124f02133 100644
--- a/spec/support/reactive_caching_helpers.rb
+++ b/spec/support/reactive_caching_helpers.rb
@@ -35,8 +35,8 @@ module ReactiveCachingHelpers
end
def expect_reactive_cache_update_queued(subject)
- expect(ReactiveCachingWorker).
- to receive(:perform_in).
- with(subject.class.reactive_cache_refresh_interval, subject.class, subject.id)
+ expect(ReactiveCachingWorker)
+ .to receive(:perform_in)
+ .with(subject.class.reactive_cache_refresh_interval, subject.class, subject.id)
end
end
diff --git a/spec/support/services_shared_context.rb b/spec/support/services_shared_context.rb
index 66c93890e31..7457484a932 100644
--- a/spec/support/services_shared_context.rb
+++ b/spec/support/services_shared_context.rb
@@ -6,9 +6,9 @@ Service.available_services_names.each do |service|
let(:service_fields) { service_klass.new.fields }
let(:service_attrs_list) { service_fields.inject([]) {|arr, hash| arr << hash[:name].to_sym } }
let(:service_attrs_list_without_passwords) do
- service_fields.
- select { |field| field[:type] != 'password' }.
- map { |field| field[:name].to_sym}
+ service_fields
+ .select { |field| field[:type] != 'password' }
+ .map { |field| field[:name].to_sym}
end
let(:service_attrs) do
service_attrs_list.inject({}) do |hash, k|
diff --git a/spec/support/slack_mattermost_notifications_shared_examples.rb b/spec/support/slack_mattermost_notifications_shared_examples.rb
index a7deb038703..044c09d5fde 100644
--- a/spec/support/slack_mattermost_notifications_shared_examples.rb
+++ b/spec/support/slack_mattermost_notifications_shared_examples.rb
@@ -108,9 +108,9 @@ RSpec.shared_examples 'slack or mattermost notifications' do
it 'uses the username as an option for slack when configured' do
allow(chat_service).to receive(:username).and_return(username)
- expect(Slack::Notifier).to receive(:new).
- with(webhook_url, username: username).
- and_return(
+ expect(Slack::Notifier).to receive(:new)
+ .with(webhook_url, username: username)
+ .and_return(
double(:slack_service).as_null_object
)
@@ -119,9 +119,9 @@ RSpec.shared_examples 'slack or mattermost notifications' do
it 'uses the channel as an option when it is configured' do
allow(chat_service).to receive(:channel).and_return(channel)
- expect(Slack::Notifier).to receive(:new).
- with(webhook_url, channel: channel).
- and_return(
+ expect(Slack::Notifier).to receive(:new)
+ .with(webhook_url, channel: channel)
+ .and_return(
double(:slack_service).as_null_object
)
chat_service.execute(push_sample_data)
@@ -131,9 +131,9 @@ RSpec.shared_examples 'slack or mattermost notifications' do
it "uses the right channel for push event" do
chat_service.update_attributes(push_channel: "random")
- expect(Slack::Notifier).to receive(:new).
- with(webhook_url, channel: "random").
- and_return(
+ expect(Slack::Notifier).to receive(:new)
+ .with(webhook_url, channel: "random")
+ .and_return(
double(:slack_service).as_null_object
)
@@ -143,9 +143,9 @@ RSpec.shared_examples 'slack or mattermost notifications' do
it "uses the right channel for merge request event" do
chat_service.update_attributes(merge_request_channel: "random")
- expect(Slack::Notifier).to receive(:new).
- with(webhook_url, channel: "random").
- and_return(
+ expect(Slack::Notifier).to receive(:new)
+ .with(webhook_url, channel: "random")
+ .and_return(
double(:slack_service).as_null_object
)
@@ -155,9 +155,9 @@ RSpec.shared_examples 'slack or mattermost notifications' do
it "uses the right channel for issue event" do
chat_service.update_attributes(issue_channel: "random")
- expect(Slack::Notifier).to receive(:new).
- with(webhook_url, channel: "random").
- and_return(
+ expect(Slack::Notifier).to receive(:new)
+ .with(webhook_url, channel: "random")
+ .and_return(
double(:slack_service).as_null_object
)
@@ -167,9 +167,9 @@ RSpec.shared_examples 'slack or mattermost notifications' do
it "uses the right channel for wiki event" do
chat_service.update_attributes(wiki_page_channel: "random")
- expect(Slack::Notifier).to receive(:new).
- with(webhook_url, channel: "random").
- and_return(
+ expect(Slack::Notifier).to receive(:new)
+ .with(webhook_url, channel: "random")
+ .and_return(
double(:slack_service).as_null_object
)
@@ -186,9 +186,9 @@ RSpec.shared_examples 'slack or mattermost notifications' do
note_data = Gitlab::DataBuilder::Note.build(issue_note, user)
- expect(Slack::Notifier).to receive(:new).
- with(webhook_url, channel: "random").
- and_return(
+ expect(Slack::Notifier).to receive(:new)
+ .with(webhook_url, channel: "random")
+ .and_return(
double(:slack_service).as_null_object
)
diff --git a/spec/support/stub_configuration.rb b/spec/support/stub_configuration.rb
index b39a23bd18a..48f454c7187 100644
--- a/spec/support/stub_configuration.rb
+++ b/spec/support/stub_configuration.rb
@@ -5,8 +5,8 @@ module StubConfiguration
# Stubbing both of these because we're not yet consistent with how we access
# current application settings
allow_any_instance_of(ApplicationSetting).to receive_messages(messages)
- allow(Gitlab::CurrentSettings.current_application_settings).
- to receive_messages(messages)
+ allow(Gitlab::CurrentSettings.current_application_settings)
+ .to receive_messages(messages)
end
def stub_config_setting(messages)
diff --git a/spec/support/stub_gitlab_calls.rb b/spec/support/stub_gitlab_calls.rb
index ded2d593059..78a2ff73746 100644
--- a/spec/support/stub_gitlab_calls.rb
+++ b/spec/support/stub_gitlab_calls.rb
@@ -68,22 +68,22 @@ module StubGitlabCalls
def stub_session
f = File.read(Rails.root.join('spec/support/gitlab_stubs/session.json'))
- stub_request(:post, "#{gitlab_url}api/v3/session.json").
- with(body: "{\"email\":\"test@test.com\",\"password\":\"123456\"}",
- headers: { 'Content-Type' => 'application/json' }).
- to_return(status: 201, body: f, headers: { 'Content-Type' => 'application/json' })
+ stub_request(:post, "#{gitlab_url}api/v3/session.json")
+ .with(body: "{\"email\":\"test@test.com\",\"password\":\"123456\"}",
+ headers: { 'Content-Type' => 'application/json' })
+ .to_return(status: 201, body: f, headers: { 'Content-Type' => 'application/json' })
end
def stub_user
f = File.read(Rails.root.join('spec/support/gitlab_stubs/user.json'))
- stub_request(:get, "#{gitlab_url}api/v3/user?private_token=Wvjy2Krpb7y8xi93owUz").
- with(headers: { 'Content-Type' => 'application/json' }).
- to_return(status: 200, body: f, headers: { 'Content-Type' => 'application/json' })
+ stub_request(:get, "#{gitlab_url}api/v3/user?private_token=Wvjy2Krpb7y8xi93owUz")
+ .with(headers: { 'Content-Type' => 'application/json' })
+ .to_return(status: 200, body: f, headers: { 'Content-Type' => 'application/json' })
- stub_request(:get, "#{gitlab_url}api/v3/user?access_token=some_token").
- with(headers: { 'Content-Type' => 'application/json' }).
- to_return(status: 200, body: f, headers: { 'Content-Type' => 'application/json' })
+ stub_request(:get, "#{gitlab_url}api/v3/user?access_token=some_token")
+ .with(headers: { 'Content-Type' => 'application/json' })
+ .to_return(status: 200, body: f, headers: { 'Content-Type' => 'application/json' })
end
def stub_project_8
@@ -99,21 +99,21 @@ module StubGitlabCalls
def stub_projects
f = File.read(Rails.root.join('spec/support/gitlab_stubs/projects.json'))
- stub_request(:get, "#{gitlab_url}api/v3/projects.json?archived=false&ci_enabled_first=true&private_token=Wvjy2Krpb7y8xi93owUz").
- with(headers: { 'Content-Type' => 'application/json' }).
- to_return(status: 200, body: f, headers: { 'Content-Type' => 'application/json' })
+ stub_request(:get, "#{gitlab_url}api/v3/projects.json?archived=false&ci_enabled_first=true&private_token=Wvjy2Krpb7y8xi93owUz")
+ .with(headers: { 'Content-Type' => 'application/json' })
+ .to_return(status: 200, body: f, headers: { 'Content-Type' => 'application/json' })
end
def stub_projects_owned
- stub_request(:get, "#{gitlab_url}api/v3/projects/owned.json?archived=false&ci_enabled_first=true&private_token=Wvjy2Krpb7y8xi93owUz").
- with(headers: { 'Content-Type' => 'application/json' }).
- to_return(status: 200, body: "", headers: {})
+ stub_request(:get, "#{gitlab_url}api/v3/projects/owned.json?archived=false&ci_enabled_first=true&private_token=Wvjy2Krpb7y8xi93owUz")
+ .with(headers: { 'Content-Type' => 'application/json' })
+ .to_return(status: 200, body: "", headers: {})
end
def stub_ci_enable
- stub_request(:put, "#{gitlab_url}api/v3/projects/2/services/gitlab-ci.json?private_token=Wvjy2Krpb7y8xi93owUz").
- with(headers: { 'Content-Type' => 'application/json' }).
- to_return(status: 200, body: "", headers: {})
+ stub_request(:put, "#{gitlab_url}api/v3/projects/2/services/gitlab-ci.json?private_token=Wvjy2Krpb7y8xi93owUz")
+ .with(headers: { 'Content-Type' => 'application/json' })
+ .to_return(status: 200, body: "", headers: {})
end
def project_hash_array
diff --git a/spec/support/test_env.rb b/spec/support/test_env.rb
index 3f472e59c49..1c5267c290b 100644
--- a/spec/support/test_env.rb
+++ b/spec/support/test_env.rb
@@ -83,13 +83,13 @@ module TestEnv
end
def disable_mailer
- allow_any_instance_of(NotificationService).to receive(:mailer).
- and_return(double.as_null_object)
+ allow_any_instance_of(NotificationService).to receive(:mailer)
+ .and_return(double.as_null_object)
end
def enable_mailer
- allow_any_instance_of(NotificationService).to receive(:mailer).
- and_call_original
+ allow_any_instance_of(NotificationService).to receive(:mailer)
+ .and_call_original
end
def disable_pre_receive
diff --git a/spec/support/update_invalid_issuable.rb b/spec/support/update_invalid_issuable.rb
index 365c34448ac..1490287681b 100644
--- a/spec/support/update_invalid_issuable.rb
+++ b/spec/support/update_invalid_issuable.rb
@@ -21,8 +21,8 @@ shared_examples 'update invalid issuable' do |klass|
context 'when updating causes conflicts' do
before do
- allow_any_instance_of(issuable.class).to receive(:save).
- and_raise(ActiveRecord::StaleObjectError.new(issuable, :save))
+ allow_any_instance_of(issuable.class).to receive(:save)
+ .and_raise(ActiveRecord::StaleObjectError.new(issuable, :save))
end
it 'renders edit when format is html' do
diff --git a/spec/support/user_activities_helpers.rb b/spec/support/user_activities_helpers.rb
index f7ca9a31edd..44feb104644 100644
--- a/spec/support/user_activities_helpers.rb
+++ b/spec/support/user_activities_helpers.rb
@@ -1,7 +1,7 @@
module UserActivitiesHelpers
def user_activity(user)
- Gitlab::UserActivities.new.
- find { |k, _| k == user.id.to_s }&.
+ Gitlab::UserActivities.new
+ .find { |k, _| k == user.id.to_s }&.
second
end
end
diff --git a/spec/tasks/gitlab/backup_rake_spec.rb b/spec/tasks/gitlab/backup_rake_spec.rb
index 1e5f55a738a..71580a788d0 100644
--- a/spec/tasks/gitlab/backup_rake_spec.rb
+++ b/spec/tasks/gitlab/backup_rake_spec.rb
@@ -47,24 +47,24 @@ describe 'gitlab:app namespace rake task' do
allow(Kernel).to receive(:system).and_return(true)
allow(FileUtils).to receive(:cp_r).and_return(true)
allow(FileUtils).to receive(:mv).and_return(true)
- allow(Rake::Task["gitlab:shell:setup"]).
- to receive(:invoke).and_return(true)
+ allow(Rake::Task["gitlab:shell:setup"])
+ .to receive(:invoke).and_return(true)
ENV['force'] = 'yes'
end
let(:gitlab_version) { Gitlab::VERSION }
it 'fails on mismatch' do
- allow(YAML).to receive(:load_file).
- and_return({ gitlab_version: "not #{gitlab_version}" })
+ allow(YAML).to receive(:load_file)
+ .and_return({ gitlab_version: "not #{gitlab_version}" })
- expect { run_rake_task('gitlab:backup:restore') }.
- to raise_error(SystemExit)
+ expect { run_rake_task('gitlab:backup:restore') }
+ .to raise_error(SystemExit)
end
it 'invokes restoration on match' do
- allow(YAML).to receive(:load_file).
- and_return({ gitlab_version: gitlab_version })
+ allow(YAML).to receive(:load_file)
+ .and_return({ gitlab_version: gitlab_version })
expect(Rake::Task['gitlab:db:drop_tables']).to receive(:invoke)
expect(Rake::Task['gitlab:backup:db:restore']).to receive(:invoke)
expect(Rake::Task['gitlab:backup:repo:restore']).to receive(:invoke)
@@ -310,8 +310,8 @@ describe 'gitlab:app namespace rake task' do
end
it 'does not invoke repositories restore' do
- allow(Rake::Task['gitlab:shell:setup']).
- to receive(:invoke).and_return(true)
+ allow(Rake::Task['gitlab:shell:setup'])
+ .to receive(:invoke).and_return(true)
allow($stdout).to receive :write
expect(Rake::Task['gitlab:db:drop_tables']).to receive :invoke
diff --git a/spec/tasks/gitlab/gitaly_rake_spec.rb b/spec/tasks/gitlab/gitaly_rake_spec.rb
index c9a0f1cb144..d42d2423f15 100644
--- a/spec/tasks/gitlab/gitaly_rake_spec.rb
+++ b/spec/tasks/gitlab/gitaly_rake_spec.rb
@@ -20,8 +20,8 @@ describe 'gitlab:gitaly namespace rake task' do
context 'when an underlying Git command fail' do
it 'aborts and display a help message' do
- expect_any_instance_of(Object).
- to receive(:checkout_or_clone_version).and_raise 'Git error'
+ expect_any_instance_of(Object)
+ .to receive(:checkout_or_clone_version).and_raise 'Git error'
expect { run_rake_task('gitlab:gitaly:install', clone_path) }.to raise_error 'Git error'
end
@@ -33,8 +33,8 @@ describe 'gitlab:gitaly namespace rake task' do
end
it 'calls checkout_or_clone_version with the right arguments' do
- expect_any_instance_of(Object).
- to receive(:checkout_or_clone_version).with(version: version, repo: repo, target_dir: clone_path)
+ expect_any_instance_of(Object)
+ .to receive(:checkout_or_clone_version).with(version: version, repo: repo, target_dir: clone_path)
run_rake_task('gitlab:gitaly:install', clone_path)
end
@@ -106,8 +106,8 @@ describe 'gitlab:gitaly namespace rake task' do
TOML
end
- expect { run_rake_task('gitlab:gitaly:storage_config')}.
- to output(expected_output).to_stdout
+ expect { run_rake_task('gitlab:gitaly:storage_config')}
+ .to output(expected_output).to_stdout
parsed_output = TOML.parse(expected_output)
config.each do |name, params|
diff --git a/spec/tasks/gitlab/task_helpers_spec.rb b/spec/tasks/gitlab/task_helpers_spec.rb
index 3d9ba7cdc6f..91cc684d032 100644
--- a/spec/tasks/gitlab/task_helpers_spec.rb
+++ b/spec/tasks/gitlab/task_helpers_spec.rb
@@ -60,8 +60,8 @@ describe Gitlab::TaskHelpers do
describe '#clone_repo' do
it 'clones the repo in the target dir' do
- expect(subject).
- to receive(:run_command!).with(%W[#{Gitlab.config.git.bin_path} clone -- #{repo} #{clone_path}])
+ expect(subject)
+ .to receive(:run_command!).with(%W[#{Gitlab.config.git.bin_path} clone -- #{repo} #{clone_path}])
subject.clone_repo(repo, clone_path)
end
@@ -69,10 +69,10 @@ describe Gitlab::TaskHelpers do
describe '#checkout_version' do
it 'clones the repo in the target dir' do
- expect(subject).
- to receive(:run_command!).with(%W[#{Gitlab.config.git.bin_path} -C #{clone_path} fetch --quiet])
- expect(subject).
- to receive(:run_command!).with(%W[#{Gitlab.config.git.bin_path} -C #{clone_path} checkout --quiet #{tag}])
+ expect(subject)
+ .to receive(:run_command!).with(%W[#{Gitlab.config.git.bin_path} -C #{clone_path} fetch --quiet])
+ expect(subject)
+ .to receive(:run_command!).with(%W[#{Gitlab.config.git.bin_path} -C #{clone_path} checkout --quiet #{tag}])
subject.checkout_version(tag, clone_path)
end
@@ -80,8 +80,8 @@ describe Gitlab::TaskHelpers do
describe '#reset_to_version' do
it 'resets --hard to the given version' do
- expect(subject).
- to receive(:run_command!).with(%W[#{Gitlab.config.git.bin_path} -C #{clone_path} reset --hard #{tag}])
+ expect(subject)
+ .to receive(:run_command!).with(%W[#{Gitlab.config.git.bin_path} -C #{clone_path} reset --hard #{tag}])
subject.reset_to_version(tag, clone_path)
end
diff --git a/spec/tasks/gitlab/workhorse_rake_spec.rb b/spec/tasks/gitlab/workhorse_rake_spec.rb
index 63d1cf2bbe5..1b68f3044a4 100644
--- a/spec/tasks/gitlab/workhorse_rake_spec.rb
+++ b/spec/tasks/gitlab/workhorse_rake_spec.rb
@@ -20,8 +20,8 @@ describe 'gitlab:workhorse namespace rake task' do
context 'when an underlying Git command fail' do
it 'aborts and display a help message' do
- expect_any_instance_of(Object).
- to receive(:checkout_or_clone_version).and_raise 'Git error'
+ expect_any_instance_of(Object)
+ .to receive(:checkout_or_clone_version).and_raise 'Git error'
expect { run_rake_task('gitlab:workhorse:install', clone_path) }.to raise_error 'Git error'
end
@@ -33,8 +33,8 @@ describe 'gitlab:workhorse namespace rake task' do
end
it 'calls checkout_or_clone_version with the right arguments' do
- expect_any_instance_of(Object).
- to receive(:checkout_or_clone_version).with(version: version, repo: repo, target_dir: clone_path)
+ expect_any_instance_of(Object)
+ .to receive(:checkout_or_clone_version).with(version: version, repo: repo, target_dir: clone_path)
run_rake_task('gitlab:workhorse:install', clone_path)
end
diff --git a/spec/validators/dynamic_path_validator_spec.rb b/spec/validators/dynamic_path_validator_spec.rb
index 8dbf3eecd23..8bd5306ff98 100644
--- a/spec/validators/dynamic_path_validator_spec.rb
+++ b/spec/validators/dynamic_path_validator_spec.rb
@@ -84,5 +84,14 @@ describe DynamicPathValidator do
expect(group.errors[:path]).to include('users is a reserved name')
end
+
+ it 'updating to an invalid path is not allowed' do
+ project = create(:empty_project)
+ project.path = 'update'
+
+ validator.validate_each(project, :path, 'update')
+
+ expect(project.errors[:path]).to include('update is a reserved name')
+ end
end
end
diff --git a/spec/views/devise/shared/_signin_box.html.haml_spec.rb b/spec/views/devise/shared/_signin_box.html.haml_spec.rb
index 1397bfa5864..9adbb0476be 100644
--- a/spec/views/devise/shared/_signin_box.html.haml_spec.rb
+++ b/spec/views/devise/shared/_signin_box.html.haml_spec.rb
@@ -31,7 +31,7 @@ describe 'devise/shared/_signin_box' do
def enable_crowd
allow(view).to receive(:form_based_providers).and_return([:crowd])
allow(view).to receive(:crowd_enabled?).and_return(true)
- allow(view).to receive(:omniauth_authorize_path).with(:user, :crowd).
- and_return('/crowd')
+ allow(view).to receive(:omniauth_authorize_path).with(:user, :crowd)
+ .and_return('/crowd')
end
end
diff --git a/spec/workers/background_migration_worker_spec.rb b/spec/workers/background_migration_worker_spec.rb
index 0d742ae9dc7..85939429feb 100644
--- a/spec/workers/background_migration_worker_spec.rb
+++ b/spec/workers/background_migration_worker_spec.rb
@@ -3,9 +3,9 @@ require 'spec_helper'
describe BackgroundMigrationWorker do
describe '.perform' do
it 'performs a background migration' do
- expect(Gitlab::BackgroundMigration).
- to receive(:perform).
- with('Foo', [10, 20])
+ expect(Gitlab::BackgroundMigration)
+ .to receive(:perform)
+ .with('Foo', [10, 20])
described_class.new.perform('Foo', [10, 20])
end
diff --git a/spec/workers/delete_user_worker_spec.rb b/spec/workers/delete_user_worker_spec.rb
index 5912dd76262..36594515005 100644
--- a/spec/workers/delete_user_worker_spec.rb
+++ b/spec/workers/delete_user_worker_spec.rb
@@ -5,15 +5,15 @@ describe DeleteUserWorker do
let!(:current_user) { create(:user) }
it "calls the DeleteUserWorker with the params it was given" do
- expect_any_instance_of(Users::DestroyService).to receive(:execute).
- with(user, {})
+ expect_any_instance_of(Users::DestroyService).to receive(:execute)
+ .with(user, {})
described_class.new.perform(current_user.id, user.id)
end
it "uses symbolized keys" do
- expect_any_instance_of(Users::DestroyService).to receive(:execute).
- with(user, test: "test")
+ expect_any_instance_of(Users::DestroyService).to receive(:execute)
+ .with(user, test: "test")
described_class.new.perform(current_user.id, user.id, "test" => "test")
end
diff --git a/spec/workers/every_sidekiq_worker_spec.rb b/spec/workers/every_sidekiq_worker_spec.rb
index fc9adf47c1e..30908534eb3 100644
--- a/spec/workers/every_sidekiq_worker_spec.rb
+++ b/spec/workers/every_sidekiq_worker_spec.rb
@@ -5,8 +5,8 @@ describe 'Every Sidekiq worker' do
root = Rails.root.join('app', 'workers')
concerns = root.join('concerns').to_s
- workers = Dir[root.join('**', '*.rb')].
- reject { |path| path.start_with?(concerns) }
+ workers = Dir[root.join('**', '*.rb')]
+ .reject { |path| path.start_with?(concerns) }
workers.map do |path|
ns = Pathname.new(path).relative_path_from(root).to_s.gsub('.rb', '')
@@ -22,9 +22,9 @@ describe 'Every Sidekiq worker' do
end
it 'uses the cronjob queue when the worker runs as a cronjob' do
- cron_workers = Settings.cron_jobs.
- map { |job_name, options| options['job_class'].constantize }.
- to_set
+ cron_workers = Settings.cron_jobs
+ .map { |job_name, options| options['job_class'].constantize }
+ .to_set
workers.each do |worker|
next unless cron_workers.include?(worker)
diff --git a/spec/workers/expire_pipeline_cache_worker_spec.rb b/spec/workers/expire_pipeline_cache_worker_spec.rb
index 28e5b706803..e4f78999489 100644
--- a/spec/workers/expire_pipeline_cache_worker_spec.rb
+++ b/spec/workers/expire_pipeline_cache_worker_spec.rb
@@ -37,8 +37,8 @@ describe ExpirePipelineCacheWorker do
end
it 'updates the cached status for a project' do
- expect(Gitlab::Cache::Ci::ProjectPipelineStatus).to receive(:update_for_pipeline).
- with(pipeline)
+ expect(Gitlab::Cache::Ci::ProjectPipelineStatus).to receive(:update_for_pipeline)
+ .with(pipeline)
subject.perform(pipeline.id)
end
diff --git a/spec/workers/git_garbage_collect_worker_spec.rb b/spec/workers/git_garbage_collect_worker_spec.rb
index f443bb2c9b4..309b3172da1 100644
--- a/spec/workers/git_garbage_collect_worker_spec.rb
+++ b/spec/workers/git_garbage_collect_worker_spec.rb
@@ -11,8 +11,8 @@ describe GitGarbageCollectWorker do
describe "#perform" do
it "flushes ref caches when the task is 'gc'" do
expect(subject).to receive(:command).with(:gc).and_return([:the, :command])
- expect(Gitlab::Popen).to receive(:popen).
- with([:the, :command], project.repository.path_to_repo).and_return(["", 0])
+ expect(Gitlab::Popen).to receive(:popen)
+ .with([:the, :command], project.repository.path_to_repo).and_return(["", 0])
expect_any_instance_of(Repository).to receive(:after_create_branch).and_call_original
expect_any_instance_of(Repository).to receive(:branch_names).and_call_original
diff --git a/spec/workers/new_note_worker_spec.rb b/spec/workers/new_note_worker_spec.rb
index 8fdbb35afd0..575361c93d4 100644
--- a/spec/workers/new_note_worker_spec.rb
+++ b/spec/workers/new_note_worker_spec.rb
@@ -24,8 +24,8 @@ describe NewNoteWorker do
let(:unexistent_note_id) { 999 }
it 'logs NewNoteWorker process skipping' do
- expect(Rails.logger).to receive(:error).
- with("NewNoteWorker: couldn't find note with ID=999, skipping job")
+ expect(Rails.logger).to receive(:error)
+ .with("NewNoteWorker: couldn't find note with ID=999, skipping job")
described_class.new.perform(unexistent_note_id)
end
diff --git a/spec/workers/post_receive_spec.rb b/spec/workers/post_receive_spec.rb
index 6c4df7350ea..cc9bc29c6cc 100644
--- a/spec/workers/post_receive_spec.rb
+++ b/spec/workers/post_receive_spec.rb
@@ -123,9 +123,9 @@ describe PostReceive do
end
it "does not run if the author is not in the project" do
- allow_any_instance_of(Gitlab::GitPostReceive).
- to receive(:identify_using_ssh_key).
- and_return(nil)
+ allow_any_instance_of(Gitlab::GitPostReceive)
+ .to receive(:identify_using_ssh_key)
+ .and_return(nil)
expect(project).not_to receive(:execute_hooks)
diff --git a/spec/workers/process_commit_worker_spec.rb b/spec/workers/process_commit_worker_spec.rb
index 4e036285e8c..6ebc94bb544 100644
--- a/spec/workers/process_commit_worker_spec.rb
+++ b/spec/workers/process_commit_worker_spec.rb
@@ -48,11 +48,11 @@ describe ProcessCommitWorker do
describe '#process_commit_message' do
context 'when pushing to the default branch' do
it 'closes issues that should be closed per the commit message' do
- allow(commit).to receive(:safe_message).
- and_return("Closes #{issue.to_reference}")
+ allow(commit).to receive(:safe_message)
+ .and_return("Closes #{issue.to_reference}")
- expect(worker).to receive(:close_issues).
- with(project, user, user, commit, [issue])
+ expect(worker).to receive(:close_issues)
+ .with(project, user, user, commit, [issue])
worker.process_commit_message(project, commit, user, user, true)
end
@@ -60,8 +60,8 @@ describe ProcessCommitWorker do
context 'when pushing to a non-default branch' do
it 'does not close any issues' do
- allow(commit).to receive(:safe_message).
- and_return("Closes #{issue.to_reference}")
+ allow(commit).to receive(:safe_message)
+ .and_return("Closes #{issue.to_reference}")
expect(worker).not_to receive(:close_issues)
@@ -102,8 +102,8 @@ describe ProcessCommitWorker do
describe '#update_issue_metrics' do
it 'updates any existing issue metrics' do
- allow(commit).to receive(:safe_message).
- and_return("Closes #{issue.to_reference}")
+ allow(commit).to receive(:safe_message)
+ .and_return("Closes #{issue.to_reference}")
worker.update_issue_metrics(commit, user)
@@ -113,8 +113,8 @@ describe ProcessCommitWorker do
end
it "doesn't execute any queries with false conditions" do
- allow(commit).to receive(:safe_message).
- and_return("Lorem Ipsum")
+ allow(commit).to receive(:safe_message)
+ .and_return("Lorem Ipsum")
expect { worker.update_issue_metrics(commit, user) }.not_to make_queries_matching(/WHERE (?:1=0|0=1)/)
end
@@ -128,8 +128,8 @@ describe ProcessCommitWorker do
end
it 'parses date strings into Time instances' do
- commit = worker.
- build_commit(project, id: '123', authored_date: Time.now.to_s)
+ commit = worker
+ .build_commit(project, id: '123', authored_date: Time.now.to_s)
expect(commit.authored_date).to be_an_instance_of(Time)
end
diff --git a/spec/workers/project_cache_worker_spec.rb b/spec/workers/project_cache_worker_spec.rb
index a4ba5f7c943..6b1f2ff3227 100644
--- a/spec/workers/project_cache_worker_spec.rb
+++ b/spec/workers/project_cache_worker_spec.rb
@@ -7,8 +7,8 @@ describe ProjectCacheWorker do
describe '#perform' do
before do
- allow_any_instance_of(Gitlab::ExclusiveLease).to receive(:try_obtain).
- and_return(true)
+ allow_any_instance_of(Gitlab::ExclusiveLease).to receive(:try_obtain)
+ .and_return(true)
end
context 'with a non-existing project' do
@@ -39,9 +39,9 @@ describe ProjectCacheWorker do
end
it 'refreshes the method caches' do
- expect_any_instance_of(Repository).to receive(:refresh_method_caches).
- with(%i(readme)).
- and_call_original
+ expect_any_instance_of(Repository).to receive(:refresh_method_caches)
+ .with(%i(readme))
+ .and_call_original
worker.perform(project.id, %w(readme))
end
@@ -51,9 +51,9 @@ describe ProjectCacheWorker do
allow(MarkupHelper).to receive(:gitlab_markdown?).and_return(false)
allow(MarkupHelper).to receive(:plain?).and_return(true)
- expect_any_instance_of(Repository).to receive(:refresh_method_caches).
- with(%i(readme)).
- and_call_original
+ expect_any_instance_of(Repository).to receive(:refresh_method_caches)
+ .with(%i(readme))
+ .and_call_original
worker.perform(project.id, %w(readme))
end
end
@@ -63,9 +63,9 @@ describe ProjectCacheWorker do
describe '#update_statistics' do
context 'when a lease could not be obtained' do
it 'does not update the repository size' do
- allow(worker).to receive(:try_obtain_lease_for).
- with(project.id, :update_statistics).
- and_return(false)
+ allow(worker).to receive(:try_obtain_lease_for)
+ .with(project.id, :update_statistics)
+ .and_return(false)
expect(statistics).not_to receive(:refresh!)
@@ -75,9 +75,9 @@ describe ProjectCacheWorker do
context 'when a lease could be obtained' do
it 'updates the project statistics' do
- allow(worker).to receive(:try_obtain_lease_for).
- with(project.id, :update_statistics).
- and_return(true)
+ allow(worker).to receive(:try_obtain_lease_for)
+ .with(project.id, :update_statistics)
+ .and_return(true)
expect(statistics).to receive(:refresh!)
.with(only: %i(repository_size))
diff --git a/spec/workers/propagate_service_template_worker_spec.rb b/spec/workers/propagate_service_template_worker_spec.rb
index 7040d5ef81c..b8b65ead9b3 100644
--- a/spec/workers/propagate_service_template_worker_spec.rb
+++ b/spec/workers/propagate_service_template_worker_spec.rb
@@ -15,8 +15,8 @@ describe PropagateServiceTemplateWorker do
end
before do
- allow_any_instance_of(Gitlab::ExclusiveLease).to receive(:try_obtain).
- and_return(true)
+ allow_any_instance_of(Gitlab::ExclusiveLease).to receive(:try_obtain)
+ .and_return(true)
end
describe '#perform' do
diff --git a/spec/workers/repository_fork_worker_spec.rb b/spec/workers/repository_fork_worker_spec.rb
index 6ea5569b438..d9e9409840f 100644
--- a/spec/workers/repository_fork_worker_spec.rb
+++ b/spec/workers/repository_fork_worker_spec.rb
@@ -35,11 +35,11 @@ describe RepositoryForkWorker do
fork_project.namespace.full_path
).and_return(true)
- expect_any_instance_of(Repository).to receive(:expire_emptiness_caches).
- and_call_original
+ expect_any_instance_of(Repository).to receive(:expire_emptiness_caches)
+ .and_call_original
- expect_any_instance_of(Repository).to receive(:expire_exists_cache).
- and_call_original
+ expect_any_instance_of(Repository).to receive(:expire_exists_cache)
+ .and_call_original
subject.perform(project.id, '/test/path', project.full_path,
fork_project.namespace.full_path)
diff --git a/spec/workers/repository_import_worker_spec.rb b/spec/workers/repository_import_worker_spec.rb
index 9c277c501f1..6b30dabc80e 100644
--- a/spec/workers/repository_import_worker_spec.rb
+++ b/spec/workers/repository_import_worker_spec.rb
@@ -8,8 +8,8 @@ describe RepositoryImportWorker do
describe '#perform' do
context 'when the import was successful' do
it 'imports a project' do
- expect_any_instance_of(Projects::ImportService).to receive(:execute).
- and_return({ status: :ok })
+ expect_any_instance_of(Projects::ImportService).to receive(:execute)
+ .and_return({ status: :ok })
expect_any_instance_of(Repository).to receive(:expire_emptiness_caches)
expect_any_instance_of(Project).to receive(:import_finish)