summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitlab-ci.yml4
-rw-r--r--CHANGELOG.md5
-rw-r--r--GITLAB_SHELL_VERSION2
-rw-r--r--app/assets/javascripts/copy_as_gfm.js128
-rw-r--r--app/assets/javascripts/dispatcher.js4
-rw-r--r--app/assets/javascripts/droplab/plugins/ajax_filter.js47
-rw-r--r--app/assets/javascripts/filtered_search/components/recent_searches_dropdown_content.js6
-rw-r--r--app/assets/javascripts/filtered_search/dropdown_hint.js29
-rw-r--r--app/assets/javascripts/filtered_search/dropdown_non_user.js2
-rw-r--r--app/assets/javascripts/filtered_search/dropdown_user.js5
-rw-r--r--app/assets/javascripts/filtered_search/dropdown_utils.js6
-rw-r--r--app/assets/javascripts/filtered_search/filtered_search_dropdown_manager.js10
-rw-r--r--app/assets/javascripts/filtered_search/filtered_search_manager.js7
-rw-r--r--app/assets/javascripts/filtered_search/filtered_search_token_keys.js8
-rw-r--r--app/assets/javascripts/filtered_search/filtered_search_tokenizer.js3
-rw-r--r--app/assets/javascripts/filtered_search/recent_searches_root.js1
-rw-r--r--app/assets/javascripts/filtered_search/stores/recent_searches_store.js3
-rw-r--r--app/assets/javascripts/group_name.js6
-rw-r--r--app/assets/javascripts/lib/utils/ajax_cache.js18
-rw-r--r--app/assets/javascripts/lib/utils/cache.js19
-rw-r--r--app/assets/javascripts/lib/utils/common_utils.js6
-rw-r--r--app/assets/javascripts/lib/utils/users_cache.js28
-rw-r--r--app/assets/javascripts/project_edit.js9
-rw-r--r--app/assets/javascripts/raven/index.js4
-rw-r--r--app/assets/javascripts/raven/raven_config.js5
-rw-r--r--app/assets/javascripts/shortcuts_issuable.js8
-rw-r--r--app/assets/javascripts/sidebar/components/assignees/sidebar_assignees.js3
-rw-r--r--app/assets/javascripts/sidebar/stores/sidebar_store.js4
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/stores/get_state_key.js8
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/stores/mr_widget_store.js5
-rw-r--r--app/assets/javascripts/vue_shared/vue_resource_interceptor.js2
-rw-r--r--app/assets/stylesheets/framework/awards.scss9
-rw-r--r--app/assets/stylesheets/framework/filters.scss7
-rw-r--r--app/assets/stylesheets/framework/header.scss22
-rw-r--r--app/assets/stylesheets/framework/sidebar.scss4
-rw-r--r--app/assets/stylesheets/framework/timeline.scss3
-rw-r--r--app/assets/stylesheets/pages/detail_page.scss6
-rw-r--r--app/assets/stylesheets/pages/note_form.scss4
-rw-r--r--app/assets/stylesheets/pages/notes.scss32
-rw-r--r--app/controllers/application_controller.rb32
-rw-r--r--app/controllers/profiles_controller.rb8
-rw-r--r--app/controllers/projects/environments_controller.rb1
-rw-r--r--app/controllers/projects_controller.rb2
-rw-r--r--app/helpers/preferences_helper.rb2
-rw-r--r--app/helpers/rss_helper.rb2
-rw-r--r--app/helpers/system_note_helper.rb3
-rw-r--r--app/models/commit_status.rb1
-rw-r--r--app/models/concerns/discussion_on_diff.rb8
-rw-r--r--app/models/diff_discussion.rb16
-rw-r--r--app/models/diff_note.rb7
-rw-r--r--app/models/merge_request.rb20
-rw-r--r--app/models/merge_request_diff.rb5
-rw-r--r--app/models/note.rb11
-rw-r--r--app/models/project.rb2
-rw-r--r--app/models/project_services/issue_tracker_service.rb2
-rw-r--r--app/models/project_services/jira_service.rb2
-rw-r--r--app/models/system_note_metadata.rb1
-rw-r--r--app/models/user.rb8
-rw-r--r--app/services/merge_requests/refresh_service.rb4
-rw-r--r--app/services/merge_requests/reopen_service.rb2
-rw-r--r--app/services/notes/diff_position_update_service.rb25
-rw-r--r--app/services/projects/transfer_service.rb11
-rw-r--r--app/services/system_note_service.rb24
-rw-r--r--app/validators/dynamic_path_validator.rb203
-rw-r--r--app/views/admin/requests_profiles/index.html.haml2
-rw-r--r--app/views/discussions/_discussion.html.haml7
-rw-r--r--app/views/profiles/accounts/_reset_token.html.haml11
-rw-r--r--app/views/profiles/accounts/show.html.haml34
-rw-r--r--app/views/profiles/preferences/show.html.haml4
-rw-r--r--app/views/projects/_home_panel.html.haml2
-rw-r--r--app/views/projects/ci/builds/_build.html.haml2
-rw-r--r--app/views/projects/commit/show.html.haml2
-rw-r--r--app/views/projects/edit.html.haml10
-rw-r--r--app/views/projects/issues/_discussion.html.haml6
-rw-r--r--app/views/projects/issues/show.html.haml30
-rw-r--r--app/views/projects/merge_requests/_discussion.html.haml2
-rw-r--r--app/views/projects/merge_requests/show/_mr_title.html.haml2
-rw-r--r--app/views/projects/merge_requests/show/_versions.html.haml2
-rw-r--r--app/views/projects/pipeline_schedules/_pipeline_schedule.html.haml2
-rw-r--r--app/views/projects/snippets/show.html.haml2
-rwxr-xr-xapp/views/shared/icons/_icon_status_skipped.svg2
-rw-r--r--app/views/shared/icons/_icon_status_skipped_borderless.svg2
-rw-r--r--app/views/shared/issuable/_search_bar.html.haml8
-rw-r--r--app/views/shared/issuable/_sidebar_assignees.html.haml3
-rw-r--r--app/views/shared/notes/_notes_with_form.html.haml2
-rw-r--r--app/views/snippets/show.html.haml2
-rw-r--r--app/views/users/show.html.haml2
-rw-r--r--app/workers/expire_job_cache_worker.rb35
-rw-r--r--app/workers/expire_pipeline_cache_worker.rb9
-rw-r--r--changelogs/unreleased/17489-hide-code-from-guests.yml4
-rw-r--r--changelogs/unreleased/18927-reorder-issue-action-buttons.yml4
-rw-r--r--changelogs/unreleased/29852-latex-formatting.yml4
-rw-r--r--changelogs/unreleased/32418-make-link-to-self-less-obvious.yml4
-rw-r--r--changelogs/unreleased/32682-skipped-ci-icon.yml4
-rw-r--r--changelogs/unreleased/32715-fix-note-padding.yml4
-rw-r--r--changelogs/unreleased/32799-remove-no_turbolink-attribute-from-haml.yml4
-rw-r--r--changelogs/unreleased/32807-company-icon.yml4
-rw-r--r--changelogs/unreleased/add-unicode-trace-feature-test.yml4
-rw-r--r--changelogs/unreleased/dm-copy-as-gfm-without-empty-elements.yml4
-rw-r--r--changelogs/unreleased/dm-copy-gfm-when-parts-of-other-elements-are-selected.yml4
-rw-r--r--changelogs/unreleased/dm-outdated-system-note.yml4
-rw-r--r--changelogs/unreleased/dm-paste-code-inside-gfm-code.yml4
-rw-r--r--changelogs/unreleased/feature-rss-scoped-token.yml4
-rw-r--r--changelogs/unreleased/mrchrisw-catch-openssl.yml4
-rw-r--r--changelogs/unreleased/prevent-project-transfer.yml4
-rw-r--r--changelogs/unreleased/wait-for-ajax-handling-all-js-requests.yml4
-rw-r--r--changelogs/unreleased/zj-fix-pipeline-etag.yml4
-rw-r--r--changelogs/unreleased/zj-sort-env-folders.yml4
-rw-r--r--config/application.rb1
-rw-r--r--config/gitlab.yml.example2
-rw-r--r--config/routes/admin.rb4
-rw-r--r--config/routes/git_http.rb4
-rw-r--r--config/routes/profile.rb1
-rw-r--r--config/routes/project.rb17
-rw-r--r--config/routes/user.rb6
-rw-r--r--config/webpack.config.js6
-rw-r--r--db/migrate/20170521184006_add_change_position_to_notes.rb13
-rw-r--r--db/migrate/20170523091700_add_rss_token_to_users.rb19
-rw-r--r--db/post_migrate/20170503004427_update_retried_for_ci_build.rb (renamed from db/post_migrate/20170503004427_upate_retried_for_ci_build.rb)10
-rw-r--r--db/schema.rb5
-rw-r--r--doc/README.md11
-rw-r--r--doc/install/kubernetes/gitlab_chart.md42
-rw-r--r--doc/install/kubernetes/gitlab_runner_chart.md3
-rw-r--r--doc/install/kubernetes/index.md3
-rw-r--r--doc/integration/github.md21
-rw-r--r--doc/update/9.0-to-9.1.md1
-rw-r--r--doc/user/group/subgroups/index.md6
-rw-r--r--doc/user/profile/preferences.md8
-rw-r--r--doc/user/project/milestones/img/progress.pngbin0 -> 23491 bytes
-rw-r--r--doc/user/project/milestones/index.md8
-rw-r--r--doc/workflow/lfs/lfs_administration.md2
-rw-r--r--features/steps/dashboard/event_filters.rb14
-rw-r--r--features/steps/dashboard/todos.rb6
-rw-r--r--features/steps/group/members.rb4
-rw-r--r--features/steps/group/milestones.rb4
-rw-r--r--features/steps/project/builds/artifacts.rb4
-rw-r--r--features/steps/project/forked_merge_requests.rb7
-rw-r--r--features/steps/project/merge_requests.rb55
-rw-r--r--features/steps/project/merge_requests/acceptance.rb6
-rw-r--r--features/steps/project/merge_requests/revert.rb4
-rw-r--r--features/steps/project/project.rb4
-rw-r--r--features/steps/project/project_milestone.rb4
-rw-r--r--features/steps/project/snippets.rb6
-rw-r--r--features/steps/project/source/browse_files.rb6
-rw-r--r--features/steps/project/source/markdown_render.rb22
-rw-r--r--features/steps/shared/active_tab.rb4
-rw-r--r--features/steps/shared/diff_note.rb4
-rw-r--r--features/steps/shared/note.rb10
-rw-r--r--features/steps/shared/paths.rb12
-rw-r--r--features/steps/snippets/snippets.rb4
-rw-r--r--features/support/env.rb4
-rw-r--r--lib/api/internal.rb17
-rw-r--r--lib/backup/manager.rb6
-rw-r--r--lib/banzai/filter/ascii_doc_post_processing_filter.rb13
-rw-r--r--lib/banzai/filter/sanitization_filter.rb4
-rw-r--r--lib/banzai/pipeline/ascii_doc_pipeline.rb14
-rw-r--r--lib/constraints/group_url_constrainer.rb4
-rw-r--r--lib/constraints/project_url_constrainer.rb2
-rw-r--r--lib/gitlab/asciidoc.rb13
-rw-r--r--lib/gitlab/diff/file_collection/base.rb8
-rw-r--r--lib/gitlab/diff/position.rb30
-rw-r--r--lib/gitlab/diff/position_tracer.rb216
-rw-r--r--lib/gitlab/etag_caching/router.rb4
-rw-r--r--lib/gitlab/gon_helper.rb1
-rw-r--r--lib/gitlab/regex.rb195
-rw-r--r--lib/tasks/tokens.rake10
-rw-r--r--spec/controllers/application_controller_spec.rb36
-rw-r--r--spec/controllers/import/gitlab_controller_spec.rb2
-rw-r--r--spec/controllers/projects/builds_controller_spec.rb57
-rw-r--r--spec/controllers/projects/environments_controller_spec.rb11
-rw-r--r--spec/controllers/projects/merge_requests_controller_spec.rb9
-rw-r--r--spec/controllers/projects_controller_spec.rb44
-rw-r--r--spec/factories/ci/builds.rb10
-rw-r--r--spec/factories/ci/pipelines.rb9
-rw-r--r--spec/factories/users.rb4
-rw-r--r--spec/features/admin/admin_labels_spec.rb4
-rw-r--r--spec/features/admin/admin_users_spec.rb2
-rw-r--r--spec/features/atom/dashboard_issues_spec.rb15
-rw-r--r--spec/features/atom/dashboard_spec.rb9
-rw-r--r--spec/features/atom/issues_spec.rb23
-rw-r--r--spec/features/atom/users_spec.rb9
-rw-r--r--spec/features/auto_deploy_spec.rb2
-rw-r--r--spec/features/boards/add_issues_modal_spec.rb14
-rw-r--r--spec/features/boards/boards_spec.rb77
-rw-r--r--spec/features/boards/issue_ordering_spec.rb27
-rw-r--r--spec/features/boards/keyboard_shortcut_spec.rb4
-rw-r--r--spec/features/boards/modal_filter_spec.rb28
-rw-r--r--spec/features/boards/new_issue_spec.rb10
-rw-r--r--spec/features/boards/sidebar_spec.rb46
-rw-r--r--spec/features/boards/sub_group_project_spec.rb6
-rw-r--r--spec/features/calendar_spec.rb10
-rw-r--r--spec/features/copy_as_gfm_spec.rb39
-rw-r--r--spec/features/cycle_analytics_spec.rb11
-rw-r--r--spec/features/dashboard/activity_spec.rb6
-rw-r--r--spec/features/dashboard/datetime_on_tooltips_spec.rb4
-rw-r--r--spec/features/dashboard/groups_list_spec.rb6
-rw-r--r--spec/features/dashboard/issues_spec.rb4
-rw-r--r--spec/features/dashboard/milestone_filter_spec.rb10
-rw-r--r--spec/features/dashboard/project_member_activity_index_spec.rb2
-rw-r--r--spec/features/dashboard/projects_spec.rb2
-rw-r--r--spec/features/dashboard_issues_spec.rb4
-rw-r--r--spec/features/expand_collapse_diffs_spec.rb16
-rw-r--r--spec/features/explore/groups_list_spec.rb6
-rw-r--r--spec/features/gitlab_flavored_markdown_spec.rb2
-rw-r--r--spec/features/groups/activity_spec.rb8
-rw-r--r--spec/features/groups/issues_spec.rb10
-rw-r--r--spec/features/groups/show_spec.rb4
-rw-r--r--spec/features/issues/award_emoji_spec.rb16
-rw-r--r--spec/features/issues/award_spec.rb8
-rw-r--r--spec/features/issues/bulk_assignment_labels_spec.rb8
-rw-r--r--spec/features/issues/create_branch_merge_request_spec.rb4
-rw-r--r--spec/features/issues/filtered_search/dropdown_author_spec.rb2
-rw-r--r--spec/features/issues/filtered_search/filter_issues_spec.rb18
-rw-r--r--spec/features/issues/form_spec.rb7
-rw-r--r--spec/features/issues/gfm_autocomplete_spec.rb10
-rw-r--r--spec/features/issues/issue_sidebar_spec.rb6
-rw-r--r--spec/features/issues/notes_on_issues_spec.rb2
-rw-r--r--spec/features/issues/update_issues_spec.rb4
-rw-r--r--spec/features/issues/user_uses_slash_commands_spec.rb2
-rw-r--r--spec/features/issues_spec.rb12
-rw-r--r--spec/features/merge_requests/closes_issues_spec.rb4
-rw-r--r--spec/features/merge_requests/conflicts_spec.rb22
-rw-r--r--spec/features/merge_requests/create_new_mr_spec.rb4
-rw-r--r--spec/features/merge_requests/deleted_source_branch_spec.rb2
-rw-r--r--spec/features/merge_requests/diff_notes_avatars_spec.rb12
-rw-r--r--spec/features/merge_requests/discussion_spec.rb2
-rw-r--r--spec/features/merge_requests/filter_merge_requests_spec.rb2
-rw-r--r--spec/features/merge_requests/form_spec.rb2
-rw-r--r--spec/features/merge_requests/merge_immediately_with_pipeline_spec.rb2
-rw-r--r--spec/features/merge_requests/merge_when_pipeline_succeeds_spec.rb4
-rw-r--r--spec/features/merge_requests/mini_pipeline_graph_spec.rb2
-rw-r--r--spec/features/merge_requests/only_allow_merge_if_build_succeeds_spec.rb24
-rw-r--r--spec/features/merge_requests/pipelines_spec.rb2
-rw-r--r--spec/features/merge_requests/update_merge_requests_spec.rb4
-rw-r--r--spec/features/merge_requests/user_posts_diff_notes_spec.rb6
-rw-r--r--spec/features/merge_requests/user_posts_notes_spec.rb4
-rw-r--r--spec/features/merge_requests/user_uses_slash_commands_spec.rb4
-rw-r--r--spec/features/merge_requests/versions_spec.rb8
-rw-r--r--spec/features/merge_requests/widget_deployments_spec.rb4
-rw-r--r--spec/features/merge_requests/widget_spec.rb34
-rw-r--r--spec/features/milestones/milestones_spec.rb6
-rw-r--r--spec/features/profile_spec.rb15
-rw-r--r--spec/features/projects/activity/rss_spec.rb4
-rw-r--r--spec/features/projects/artifacts/file_spec.rb4
-rw-r--r--spec/features/projects/blobs/blob_show_spec.rb8
-rw-r--r--spec/features/projects/blobs/edit_spec.rb2
-rw-r--r--spec/features/projects/blobs/user_create_spec.rb2
-rw-r--r--spec/features/projects/commit/cherry_pick_spec.rb4
-rw-r--r--spec/features/projects/commit/mini_pipeline_graph_spec.rb2
-rw-r--r--spec/features/projects/commit/rss_spec.rb8
-rw-r--r--spec/features/projects/compare_spec.rb8
-rw-r--r--spec/features/projects/features_visibility_spec.rb12
-rw-r--r--spec/features/projects/files/browse_files_spec.rb4
-rw-r--r--spec/features/projects/files/dockerfile_dropdown_spec.rb4
-rw-r--r--spec/features/projects/files/find_file_keyboard_spec.rb2
-rw-r--r--spec/features/projects/files/gitignore_dropdown_spec.rb4
-rw-r--r--spec/features/projects/files/gitlab_ci_yml_dropdown_spec.rb4
-rw-r--r--spec/features/projects/files/project_owner_creates_license_file_spec.rb2
-rw-r--r--spec/features/projects/files/project_owner_sees_link_to_create_license_file_in_empty_project_spec.rb2
-rw-r--r--spec/features/projects/files/undo_template_spec.rb2
-rw-r--r--spec/features/projects/guest_navigation_menu_spec.rb62
-rw-r--r--spec/features/projects/issuable_templates_spec.rb14
-rw-r--r--spec/features/projects/issues/rss_spec.rb8
-rw-r--r--spec/features/projects/labels/update_prioritization_spec.rb10
-rw-r--r--spec/features/projects/main/rss_spec.rb4
-rw-r--r--spec/features/projects/members/group_links_spec.rb6
-rw-r--r--spec/features/projects/members/master_adds_member_with_expiration_date_spec.rb2
-rw-r--r--spec/features/projects/pipeline_schedules_spec.rb4
-rw-r--r--spec/features/projects/pipelines/pipelines_spec.rb16
-rw-r--r--spec/features/projects/ref_switcher_spec.rb6
-rw-r--r--spec/features/projects/snippets/show_spec.rb10
-rw-r--r--spec/features/projects/tree/rss_spec.rb4
-rw-r--r--spec/features/projects/view_on_env_spec.rb12
-rw-r--r--spec/features/projects/wiki/user_creates_wiki_page_spec.rb34
-rw-r--r--spec/features/search_spec.rb8
-rw-r--r--spec/features/snippets/create_snippet_spec.rb4
-rw-r--r--spec/features/snippets/notes_on_personal_snippets_spec.rb2
-rw-r--r--spec/features/snippets/public_snippets_spec.rb2
-rw-r--r--spec/features/snippets/show_spec.rb10
-rw-r--r--spec/features/task_lists_spec.rb17
-rw-r--r--spec/features/todos/todos_filtering_spec.rb8
-rw-r--r--spec/features/todos/todos_spec.rb6
-rw-r--r--spec/features/u2f_spec.rb2
-rw-r--r--spec/features/uploads/user_uploads_file_to_note_spec.rb4
-rw-r--r--spec/features/users/projects_spec.rb2
-rw-r--r--spec/features/users/rss_spec.rb4
-rw-r--r--spec/features/users/snippets_spec.rb6
-rw-r--r--spec/features/users_spec.rb8
-rw-r--r--spec/helpers/notes_helper_spec.rb8
-rw-r--r--spec/helpers/rss_helper_spec.rb8
-rw-r--r--spec/javascripts/copy_as_gfm_spec.js49
-rw-r--r--spec/javascripts/filtered_search/components/recent_searches_dropdown_content_spec.js4
-rw-r--r--spec/javascripts/filtered_search/dropdown_user_spec.js2
-rw-r--r--spec/javascripts/filtered_search/dropdown_utils_spec.js21
-rw-r--r--spec/javascripts/filtered_search/filtered_search_manager_spec.js1
-rw-r--r--spec/javascripts/filtered_search/filtered_search_tokenizer_spec.js22
-rw-r--r--spec/javascripts/filtered_search/services/recent_searches_service_spec.js52
-rw-r--r--spec/javascripts/lib/utils/cache_spec.js65
-rw-r--r--spec/javascripts/lib/utils/users_cache_spec.js136
-rw-r--r--spec/javascripts/raven/index_spec.js20
-rw-r--r--spec/javascripts/raven/raven_config_spec.js60
-rw-r--r--spec/javascripts/sidebar/sidebar_assignees_spec.js12
-rw-r--r--spec/javascripts/sidebar/sidebar_store_spec.js5
-rw-r--r--spec/javascripts/vue_mr_widget/stores/get_state_key_spec.js12
-rw-r--r--spec/lib/banzai/filter/ascii_doc_post_processing_filter_spec.rb15
-rw-r--r--spec/lib/banzai/filter/sanitization_filter_spec.rb16
-rw-r--r--spec/lib/gitlab/asciidoc_spec.rb25
-rw-r--r--spec/lib/gitlab/backup/manager_spec.rb30
-rw-r--r--spec/lib/gitlab/cycle_analytics/events_spec.rb10
-rw-r--r--spec/lib/gitlab/database/migration_helpers_spec.rb8
-rw-r--r--spec/lib/gitlab/diff/position_tracer_spec.rb315
-rw-r--r--spec/lib/gitlab/etag_caching/router_spec.rb13
-rw-r--r--spec/lib/gitlab/import_export/safe_model_attributes.yml1
-rw-r--r--spec/lib/gitlab/regex_spec.rb368
-rw-r--r--spec/migrations/update_retried_for_ci_builds_spec.rb (renamed from spec/migrations/upate_retried_for_ci_builds_spec.rb)4
-rw-r--r--spec/models/commit_status_spec.rb10
-rw-r--r--spec/models/concerns/discussion_on_diff_spec.rb26
-rw-r--r--spec/models/cycle_analytics/test_spec.rb3
-rw-r--r--spec/models/diff_discussion_spec.rb7
-rw-r--r--spec/models/merge_request_spec.rb44
-rw-r--r--spec/models/namespace_spec.rb2
-rw-r--r--spec/models/user_spec.rb16
-rw-r--r--spec/requests/api/internal_spec.rb161
-rw-r--r--spec/requests/projects/cycle_analytics_events_spec.rb3
-rw-r--r--spec/routing/project_routing_spec.rb4
-rw-r--r--spec/routing/routing_spec.rb4
-rw-r--r--spec/services/merge_requests/merge_when_pipeline_succeeds_service_spec.rb12
-rw-r--r--spec/services/merge_requests/update_service_spec.rb11
-rw-r--r--spec/services/notes/diff_position_update_service_spec.rb20
-rw-r--r--spec/services/projects/propagate_service_template_spec.rb12
-rw-r--r--spec/services/projects/transfer_service_spec.rb1
-rw-r--r--spec/services/system_note_service_spec.rb31
-rw-r--r--spec/spec_helper.rb1
-rw-r--r--spec/support/features/issuable_slash_commands_shared_examples.rb4
-rw-r--r--spec/support/features/rss_shared_examples.rb24
-rw-r--r--spec/support/protected_branches/access_control_ce_shared_examples.rb4
-rw-r--r--spec/support/protected_tags/access_control_ce_shared_examples.rb2
-rw-r--r--spec/support/snippets_shared_examples.rb2
-rw-r--r--spec/support/target_branch_helpers.rb2
-rw-r--r--spec/support/time_tracking_shared_examples.rb8
-rw-r--r--spec/support/wait_for_ajax.rb18
-rw-r--r--spec/support/wait_for_requests.rb38
-rw-r--r--spec/support/wait_for_vue_resource.rb19
-rw-r--r--spec/tasks/tokens_spec.rb6
-rw-r--r--spec/validators/dynamic_path_validator_spec.rb248
-rw-r--r--spec/workers/expire_job_cache_worker_spec.rb31
-rw-r--r--spec/workers/expire_pipeline_cache_worker_spec.rb2
347 files changed, 3466 insertions, 1874 deletions
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 45f1638f871..5ee22fa6c36 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -52,7 +52,7 @@ stages:
.use-pg: &use-pg
services:
- - postgres:latest
+ - postgres:9.2
- redis:alpine
.use-mysql: &use-mysql
@@ -63,6 +63,7 @@ stages:
.only-master-and-ee-or-mysql: &only-master-and-ee-or-mysql
only:
- /mysql/
+ - /-stable$/
- master@gitlab-org/gitlab-ce
- master@gitlab/gitlabhq
- tags@gitlab-org/gitlab-ce
@@ -149,6 +150,7 @@ stages:
# Trigger a package build on omnibus-gitlab repository
build-package:
+ image: ruby:2.3-alpine
before_script: []
services: []
variables:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 65d3a02d68f..4e6d8d398a5 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,11 @@
documentation](doc/development/changelog.md) for instructions on adding your own
entry.
+## 9.2.1 (2017-05-23)
+
+- Fix placement of note emoji on hover.
+- Fix migration for older PostgreSQL versions.
+
## 9.2.0 (2017-05-22)
- API: Filter merge requests by milestone and labels. (10924)
diff --git a/GITLAB_SHELL_VERSION b/GITLAB_SHELL_VERSION
index 50e2274e6d3..2d6c0bcf19c 100644
--- a/GITLAB_SHELL_VERSION
+++ b/GITLAB_SHELL_VERSION
@@ -1 +1 @@
-5.0.3
+5.0.4
diff --git a/app/assets/javascripts/copy_as_gfm.js b/app/assets/javascripts/copy_as_gfm.js
index 459cdd53f9b..ba9d9a3e1f7 100644
--- a/app/assets/javascripts/copy_as_gfm.js
+++ b/app/assets/javascripts/copy_as_gfm.js
@@ -18,12 +18,12 @@ const gfmRules = {
},
},
TaskListFilter: {
- 'input[type=checkbox].task-list-item-checkbox'(el, text) {
+ 'input[type=checkbox].task-list-item-checkbox'(el) {
return `[${el.checked ? 'x' : ' '}]`;
},
},
ReferenceFilter: {
- '.tooltip'(el, text) {
+ '.tooltip'(el) {
return '';
},
'a.gfm:not([data-link=true])'(el, text) {
@@ -39,15 +39,15 @@ const gfmRules = {
},
},
TableOfContentsFilter: {
- 'ul.section-nav'(el, text) {
+ 'ul.section-nav'(el) {
return '[[_TOC_]]';
},
},
EmojiFilter: {
- 'img.emoji'(el, text) {
+ 'img.emoji'(el) {
return el.getAttribute('alt');
},
- 'gl-emoji'(el, text) {
+ 'gl-emoji'(el) {
return `:${el.getAttribute('data-name')}:`;
},
},
@@ -57,13 +57,13 @@ const gfmRules = {
},
},
VideoLinkFilter: {
- '.video-container'(el, text) {
+ '.video-container'(el) {
const videoEl = el.querySelector('video');
if (!videoEl) return false;
return CopyAsGFM.nodeToGFM(videoEl);
},
- 'video'(el, text) {
+ 'video'(el) {
return `![${el.dataset.title}](${el.getAttribute('src')})`;
},
},
@@ -74,19 +74,19 @@ const gfmRules = {
'code.code.math[data-math-style=inline]'(el, text) {
return `$\`${text}\`$`;
},
- 'span.katex-display span.katex-mathml'(el, text) {
+ 'span.katex-display span.katex-mathml'(el) {
const mathAnnotation = el.querySelector('annotation[encoding="application/x-tex"]');
if (!mathAnnotation) return false;
return `\`\`\`math\n${CopyAsGFM.nodeToGFM(mathAnnotation)}\n\`\`\``;
},
- 'span.katex-mathml'(el, text) {
+ 'span.katex-mathml'(el) {
const mathAnnotation = el.querySelector('annotation[encoding="application/x-tex"]');
if (!mathAnnotation) return false;
return `$\`${CopyAsGFM.nodeToGFM(mathAnnotation)}\`$`;
},
- 'span.katex-html'(el, text) {
+ 'span.katex-html'(el) {
// We don't want to include the content of this element in the copied text.
return '';
},
@@ -95,7 +95,7 @@ const gfmRules = {
},
},
SanitizationFilter: {
- 'a[name]:not([href]):empty'(el, text) {
+ 'a[name]:not([href]):empty'(el) {
return el.outerHTML;
},
'dl'(el, text) {
@@ -143,7 +143,7 @@ const gfmRules = {
},
},
MarkdownFilter: {
- 'br'(el, text) {
+ 'br'(el) {
// Two spaces at the end of a line are turned into a BR
return ' ';
},
@@ -162,7 +162,7 @@ const gfmRules = {
'blockquote'(el, text) {
return text.trim().split('\n').map(s => `> ${s}`.trim()).join('\n');
},
- 'img'(el, text) {
+ 'img'(el) {
return `![${el.getAttribute('alt')}](${el.getAttribute('src')})`;
},
'a.anchor'(el, text) {
@@ -222,10 +222,10 @@ const gfmRules = {
'sup'(el, text) {
return `^${text}`;
},
- 'hr'(el, text) {
+ 'hr'(el) {
return '-----';
},
- 'table'(el, text) {
+ 'table'(el) {
const theadEl = el.querySelector('thead');
const tbodyEl = el.querySelector('tbody');
if (!theadEl || !tbodyEl) return false;
@@ -233,11 +233,11 @@ const gfmRules = {
const theadText = CopyAsGFM.nodeToGFM(theadEl);
const tbodyText = CopyAsGFM.nodeToGFM(tbodyEl);
- return theadText + tbodyText;
+ return [theadText, tbodyText].join('\n');
},
'thead'(el, text) {
const cells = _.map(el.querySelectorAll('th'), (cell) => {
- let chars = CopyAsGFM.nodeToGFM(cell).trim().length + 2;
+ let chars = CopyAsGFM.nodeToGFM(cell).length + 2;
let before = '';
let after = '';
@@ -262,10 +262,15 @@ const gfmRules = {
return before + middle + after;
});
- return `${text}|${cells.join('|')}|`;
+ const separatorRow = `|${cells.join('|')}|`;
+
+ return [text, separatorRow].join('\n');
},
- 'tr'(el, text) {
- const cells = _.map(el.querySelectorAll('td, th'), cell => CopyAsGFM.nodeToGFM(cell).trim());
+ 'tr'(el) {
+ const cellEls = el.querySelectorAll('td, th');
+ if (cellEls.length === 0) return false;
+
+ const cells = _.map(cellEls, cell => CopyAsGFM.nodeToGFM(cell));
return `| ${cells.join(' | ')} |`;
},
},
@@ -273,12 +278,12 @@ const gfmRules = {
class CopyAsGFM {
constructor() {
- $(document).on('copy', '.md, .wiki', (e) => { this.copyAsGFM(e, CopyAsGFM.transformGFMSelection); });
- $(document).on('copy', 'pre.code.highlight, .diff-content .line_content', (e) => { this.copyAsGFM(e, CopyAsGFM.transformCodeSelection); });
- $(document).on('paste', '.js-gfm-input', this.pasteGFM.bind(this));
+ $(document).on('copy', '.md, .wiki', (e) => { CopyAsGFM.copyAsGFM(e, CopyAsGFM.transformGFMSelection); });
+ $(document).on('copy', 'pre.code.highlight, .diff-content .line_content', (e) => { CopyAsGFM.copyAsGFM(e, CopyAsGFM.transformCodeSelection); });
+ $(document).on('paste', '.js-gfm-input', CopyAsGFM.pasteGFM);
}
- copyAsGFM(e, transformer) {
+ static copyAsGFM(e, transformer) {
const clipboardData = e.originalEvent.clipboardData;
if (!clipboardData) return;
@@ -292,26 +297,59 @@ class CopyAsGFM {
e.stopPropagation();
clipboardData.setData('text/plain', el.textContent);
- clipboardData.setData('text/x-gfm', CopyAsGFM.nodeToGFM(el));
+ clipboardData.setData('text/x-gfm', this.nodeToGFM(el));
}
- pasteGFM(e) {
+ static pasteGFM(e) {
const clipboardData = e.originalEvent.clipboardData;
if (!clipboardData) return;
+ const text = clipboardData.getData('text/plain');
const gfm = clipboardData.getData('text/x-gfm');
if (!gfm) return;
e.preventDefault();
- window.gl.utils.insertText(e.target, gfm);
+ window.gl.utils.insertText(e.target, (textBefore, textAfter) => {
+ // If the text before the cursor contains an odd number of backticks,
+ // we are either inside an inline code span that starts with 1 backtick
+ // or a code block that starts with 3 backticks.
+ // This logic still holds when there are one or more _closed_ code spans
+ // or blocks that will have 2 or 6 backticks.
+ // This will break down when the actual code block contains an uneven
+ // number of backticks, but this is a rare edge case.
+ const backtickMatch = textBefore.match(/`/g);
+ const insideCodeBlock = backtickMatch && (backtickMatch.length % 2) === 1;
+
+ if (insideCodeBlock) {
+ return text;
+ }
+
+ return gfm;
+ });
}
static transformGFMSelection(documentFragment) {
- // If the documentFragment contains more than just Markdown, don't copy as GFM.
- if (documentFragment.querySelector('.md, .wiki')) return null;
+ const gfmEls = documentFragment.querySelectorAll('.md, .wiki');
+ switch (gfmEls.length) {
+ case 0: {
+ return documentFragment;
+ }
+ case 1: {
+ return gfmEls[0];
+ }
+ default: {
+ const allGfmEl = document.createElement('div');
+
+ for (let i = 0; i < gfmEls.length; i += 1) {
+ const lineEl = gfmEls[i];
+ allGfmEl.appendChild(lineEl);
+ allGfmEl.appendChild(document.createTextNode('\n\n'));
+ }
- return documentFragment;
+ return allGfmEl;
+ }
+ }
}
static transformCodeSelection(documentFragment) {
@@ -343,7 +381,7 @@ class CopyAsGFM {
return codeEl;
}
- static nodeToGFM(node) {
+ static nodeToGFM(node, respectWhitespaceParam = false) {
if (node.nodeType === Node.COMMENT_NODE) {
return '';
}
@@ -352,7 +390,9 @@ class CopyAsGFM {
return node.textContent;
}
- const text = this.innerGFM(node);
+ const respectWhitespace = respectWhitespaceParam || (node.nodeName === 'PRE' || node.nodeName === 'CODE');
+
+ const text = this.innerGFM(node, respectWhitespace);
if (node.nodeType === Node.DOCUMENT_FRAGMENT_NODE) {
return text;
@@ -366,7 +406,17 @@ class CopyAsGFM {
if (!window.gl.utils.nodeMatchesSelector(node, selector)) continue;
- const result = func(node, text);
+ let result;
+ if (func.length === 2) {
+ // if `func` takes 2 arguments, it depends on text.
+ // if there is no text, we don't need to generate GFM for this node.
+ if (text.length === 0) continue;
+
+ result = func(node, text);
+ } else {
+ result = func(node);
+ }
+
if (result === false) continue;
return result;
@@ -376,7 +426,7 @@ class CopyAsGFM {
return text;
}
- static innerGFM(parentNode) {
+ static innerGFM(parentNode, respectWhitespace = false) {
const nodes = parentNode.childNodes;
const clonedParentNode = parentNode.cloneNode(true);
@@ -386,13 +436,19 @@ class CopyAsGFM {
const node = nodes[i];
const clonedNode = clonedNodes[i];
- const text = this.nodeToGFM(node);
+ const text = this.nodeToGFM(node, respectWhitespace);
// `clonedNode.replaceWith(text)` is not yet widely supported
clonedNode.parentNode.replaceChild(document.createTextNode(text), clonedNode);
}
- return clonedParentNode.innerText || clonedParentNode.textContent;
+ let nodeText = clonedParentNode.innerText || clonedParentNode.textContent;
+
+ if (!respectWhitespace) {
+ nodeText = nodeText.trim();
+ }
+
+ return nodeText;
}
}
diff --git a/app/assets/javascripts/dispatcher.js b/app/assets/javascripts/dispatcher.js
index a27abee5431..2090a7e12d6 100644
--- a/app/assets/javascripts/dispatcher.js
+++ b/app/assets/javascripts/dispatcher.js
@@ -40,6 +40,7 @@ import Group from './group';
import GroupName from './group_name';
import GroupsList from './groups_list';
import ProjectsList from './projects_list';
+import setupProjectEdit from './project_edit';
import MiniPipelineGraph from './mini_pipeline_graph_dropdown';
import BlobLinePermalinkUpdater from './blob/blob_line_permalink_updater';
import Landing from './landing';
@@ -264,6 +265,9 @@ import ShortcutsBlob from './shortcuts_blob';
new BlobViewer();
}
break;
+ case 'projects:edit':
+ setupProjectEdit();
+ break;
case 'projects:pipelines:builds':
case 'projects:pipelines:failures':
case 'projects:pipelines:show':
diff --git a/app/assets/javascripts/droplab/plugins/ajax_filter.js b/app/assets/javascripts/droplab/plugins/ajax_filter.js
index cfd7e2ca189..a5427417031 100644
--- a/app/assets/javascripts/droplab/plugins/ajax_filter.js
+++ b/app/assets/javascripts/droplab/plugins/ajax_filter.js
@@ -1,4 +1,5 @@
/* eslint-disable */
+import AjaxCache from '../../lib/utils/ajax_cache';
const AjaxFilter = {
init: function(hook) {
@@ -58,50 +59,24 @@ const AjaxFilter = {
this.loading = true;
var params = config.params || {};
params[config.searchKey] = searchValue;
- var self = this;
- self.cache = self.cache || {};
var url = config.endpoint + this.buildParams(params);
- var urlCachedData = self.cache[url];
- if (urlCachedData) {
- self._loadData(urlCachedData, config, self);
- } else {
- this._loadUrlData(url)
- .then(function(data) {
- self._loadData(data, config, self);
- }, config.onError).catch(config.onError);
- }
+ return AjaxCache.retrieve(url)
+ .then((data) => {
+ this._loadData(data, config);
+ })
+ .catch(config.onError);
},
- _loadUrlData: function _loadUrlData(url) {
- var self = this;
- return new Promise(function(resolve, reject) {
- var xhr = new XMLHttpRequest;
- xhr.open('GET', url, true);
- xhr.onreadystatechange = function () {
- if(xhr.readyState === XMLHttpRequest.DONE) {
- if (xhr.status === 200) {
- var data = JSON.parse(xhr.responseText);
- self.cache[url] = data;
- return resolve(data);
- } else {
- return reject([xhr.responseText, xhr.status]);
- }
- }
- };
- xhr.send();
- });
- },
-
- _loadData: function _loadData(data, config, self) {
- const list = self.hook.list;
+ _loadData(data, config) {
+ const list = this.hook.list;
if (config.loadingTemplate && list.data === undefined ||
list.data.length === 0) {
const dataLoadingTemplate = list.list.querySelector('[data-loading-template]');
if (dataLoadingTemplate) {
- dataLoadingTemplate.outerHTML = self.listTemplate;
+ dataLoadingTemplate.outerHTML = this.listTemplate;
}
}
- if (!self.destroyed) {
+ if (!this.destroyed) {
var hookListChildren = list.list.children;
var onlyDynamicList = hookListChildren.length === 1 && hookListChildren[0].hasAttribute('data-dynamic');
if (onlyDynamicList && data.length === 0) {
@@ -109,7 +84,7 @@ const AjaxFilter = {
}
list.setData.call(list, data);
}
- self.notLoading();
+ this.notLoading();
list.currentIndex = 0;
},
diff --git a/app/assets/javascripts/filtered_search/components/recent_searches_dropdown_content.js b/app/assets/javascripts/filtered_search/components/recent_searches_dropdown_content.js
index 15052dbd362..c51d4b056af 100644
--- a/app/assets/javascripts/filtered_search/components/recent_searches_dropdown_content.js
+++ b/app/assets/javascripts/filtered_search/components/recent_searches_dropdown_content.js
@@ -13,13 +13,17 @@ export default {
required: false,
default: true,
},
+ allowedKeys: {
+ type: Array,
+ required: true,
+ },
},
computed: {
processedItems() {
return this.items.map((item) => {
const { tokens, searchToken }
- = gl.FilteredSearchTokenizer.processTokens(item);
+ = gl.FilteredSearchTokenizer.processTokens(item, this.allowedKeys);
const resultantTokens = tokens.map(token => ({
prefix: `${token.key}:`,
diff --git a/app/assets/javascripts/filtered_search/dropdown_hint.js b/app/assets/javascripts/filtered_search/dropdown_hint.js
index 5d92d29c399..2af242a69df 100644
--- a/app/assets/javascripts/filtered_search/dropdown_hint.js
+++ b/app/assets/javascripts/filtered_search/dropdown_hint.js
@@ -2,14 +2,18 @@ import Filter from '~/droplab/plugins/filter';
import './filtered_search_dropdown';
class DropdownHint extends gl.FilteredSearchDropdown {
- constructor(droplab, dropdown, input, filter) {
+ constructor(droplab, dropdown, input, tokenKeys, filter) {
super(droplab, dropdown, input, filter);
this.config = {
Filter: {
template: 'hint',
- filterFunction: gl.DropdownUtils.filterHint.bind(null, input),
+ filterFunction: gl.DropdownUtils.filterHint.bind(null, {
+ input,
+ allowedKeys: tokenKeys.getKeys(),
+ }),
},
};
+ this.tokenKeys = tokenKeys;
}
itemClicked(e) {
@@ -52,20 +56,13 @@ class DropdownHint extends gl.FilteredSearchDropdown {
}
renderContent() {
- const dropdownData = [];
-
- [].forEach.call(this.input.closest('.filtered-search-box-input-container').querySelectorAll('.dropdown-menu'), (dropdownMenu) => {
- const { icon, hint, tag, type } = dropdownMenu.dataset;
- if (icon && hint && tag) {
- dropdownData.push(
- Object.assign({
- icon: `fa-${icon}`,
- hint,
- tag: `<${tag}>`,
- }, type && { type }),
- );
- }
- });
+ const dropdownData = gl.FilteredSearchTokenKeys.get()
+ .map(tokenKey => ({
+ icon: `fa-${tokenKey.icon}`,
+ hint: tokenKey.key,
+ tag: `<${tokenKey.symbol}${tokenKey.key}>`,
+ type: tokenKey.type,
+ }));
this.droplab.changeHookList(this.hookId, this.dropdown, [Filter], this.config);
this.droplab.setData(this.hookId, dropdownData);
diff --git a/app/assets/javascripts/filtered_search/dropdown_non_user.js b/app/assets/javascripts/filtered_search/dropdown_non_user.js
index f20193eecba..34a9e34070c 100644
--- a/app/assets/javascripts/filtered_search/dropdown_non_user.js
+++ b/app/assets/javascripts/filtered_search/dropdown_non_user.js
@@ -5,7 +5,7 @@ import Filter from '~/droplab/plugins/filter';
import './filtered_search_dropdown';
class DropdownNonUser extends gl.FilteredSearchDropdown {
- constructor(droplab, dropdown, input, filter, endpoint, symbol) {
+ constructor(droplab, dropdown, input, tokenKeys, filter, endpoint, symbol) {
super(droplab, dropdown, input, filter);
this.symbol = symbol;
this.config = {
diff --git a/app/assets/javascripts/filtered_search/dropdown_user.js b/app/assets/javascripts/filtered_search/dropdown_user.js
index 42538780e50..6b4338ca1d6 100644
--- a/app/assets/javascripts/filtered_search/dropdown_user.js
+++ b/app/assets/javascripts/filtered_search/dropdown_user.js
@@ -4,7 +4,7 @@ import AjaxFilter from '~/droplab/plugins/ajax_filter';
import './filtered_search_dropdown';
class DropdownUser extends gl.FilteredSearchDropdown {
- constructor(droplab, dropdown, input, filter) {
+ constructor(droplab, dropdown, input, tokenKeys, filter) {
super(droplab, dropdown, input, filter);
this.config = {
AjaxFilter: {
@@ -25,6 +25,7 @@ class DropdownUser extends gl.FilteredSearchDropdown {
},
},
};
+ this.tokenKeys = tokenKeys;
}
itemClicked(e) {
@@ -43,7 +44,7 @@ class DropdownUser extends gl.FilteredSearchDropdown {
getSearchInput() {
const query = gl.DropdownUtils.getSearchInput(this.input);
- const { lastToken } = gl.FilteredSearchTokenizer.processTokens(query);
+ const { lastToken } = gl.FilteredSearchTokenizer.processTokens(query, this.tokenKeys.get());
let value = lastToken || '';
diff --git a/app/assets/javascripts/filtered_search/dropdown_utils.js b/app/assets/javascripts/filtered_search/dropdown_utils.js
index bc7c1dffece..5c02a7a53d3 100644
--- a/app/assets/javascripts/filtered_search/dropdown_utils.js
+++ b/app/assets/javascripts/filtered_search/dropdown_utils.js
@@ -50,10 +50,12 @@ class DropdownUtils {
return updatedItem;
}
- static filterHint(input, item) {
+ static filterHint(config, item) {
+ const { input, allowedKeys } = config;
const updatedItem = item;
const searchInput = gl.DropdownUtils.getSearchQuery(input);
- const { lastToken, tokens } = gl.FilteredSearchTokenizer.processTokens(searchInput);
+ const { lastToken, tokens } =
+ gl.FilteredSearchTokenizer.processTokens(searchInput, allowedKeys);
const lastKey = lastToken.key || lastToken || '';
const allowMultiple = item.type === 'array';
const itemInExistingTokens = tokens.some(t => t.key === item.hint);
diff --git a/app/assets/javascripts/filtered_search/filtered_search_dropdown_manager.js b/app/assets/javascripts/filtered_search/filtered_search_dropdown_manager.js
index 49a6cd1ac77..6bc6bc43f51 100644
--- a/app/assets/javascripts/filtered_search/filtered_search_dropdown_manager.js
+++ b/app/assets/javascripts/filtered_search/filtered_search_dropdown_manager.js
@@ -2,10 +2,10 @@ import DropLab from '~/droplab/drop_lab';
import FilteredSearchContainer from './container';
class FilteredSearchDropdownManager {
- constructor(baseEndpoint = '', page) {
+ constructor(baseEndpoint = '', tokenizer, page) {
this.container = FilteredSearchContainer.container;
this.baseEndpoint = baseEndpoint.replace(/\/$/, '');
- this.tokenizer = gl.FilteredSearchTokenizer;
+ this.tokenizer = tokenizer;
this.filteredSearchTokenKeys = gl.FilteredSearchTokenKeys;
this.filteredSearchInput = this.container.querySelector('.filtered-search');
this.page = page;
@@ -98,7 +98,8 @@ class FilteredSearchDropdownManager {
if (!mappingKey.reference) {
const dl = this.droplab;
- const defaultArguments = [null, dl, element, this.filteredSearchInput, key];
+ const defaultArguments =
+ [null, dl, element, this.filteredSearchInput, this.filteredSearchTokenKeys, key];
const glArguments = defaultArguments.concat(mappingKey.extraArguments || []);
// Passing glArguments to `new gl[glClass](<arguments>)`
@@ -141,7 +142,8 @@ class FilteredSearchDropdownManager {
setDropdown() {
const query = gl.DropdownUtils.getSearchQuery(true);
- const { lastToken, searchToken } = this.tokenizer.processTokens(query);
+ const { lastToken, searchToken } =
+ this.tokenizer.processTokens(query, this.filteredSearchTokenKeys.getKeys());
if (this.currentDropdown) {
this.updateCurrentDropdownOffset();
diff --git a/app/assets/javascripts/filtered_search/filtered_search_manager.js b/app/assets/javascripts/filtered_search/filtered_search_manager.js
index 57d247e11a9..58f2b75bd50 100644
--- a/app/assets/javascripts/filtered_search/filtered_search_manager.js
+++ b/app/assets/javascripts/filtered_search/filtered_search_manager.js
@@ -15,6 +15,7 @@ class FilteredSearchManager {
this.recentSearchesStore = new RecentSearchesStore({
isLocalStorageAvailable: RecentSearchesService.isAvailable(),
+ allowedKeys: this.filteredSearchTokenKeys.getKeys(),
});
const searchHistoryDropdownElement = document.querySelector('.js-filtered-search-history-dropdown');
const projectPath = searchHistoryDropdownElement ?
@@ -46,7 +47,7 @@ class FilteredSearchManager {
if (this.filteredSearchInput) {
this.tokenizer = gl.FilteredSearchTokenizer;
- this.dropdownManager = new gl.FilteredSearchDropdownManager(this.filteredSearchInput.getAttribute('data-base-endpoint') || '', page);
+ this.dropdownManager = new gl.FilteredSearchDropdownManager(this.filteredSearchInput.getAttribute('data-base-endpoint') || '', this.tokenizer, page);
this.recentSearchesRoot = new RecentSearchesRoot(
this.recentSearchesStore,
@@ -318,7 +319,7 @@ class FilteredSearchManager {
handleInputVisualToken() {
const input = this.filteredSearchInput;
const { tokens, searchToken }
- = gl.FilteredSearchTokenizer.processTokens(input.value);
+ = this.tokenizer.processTokens(input.value, this.filteredSearchTokenKeys.getKeys());
const { isLastVisualTokenValid }
= gl.FilteredSearchVisualTokens.getLastVisualTokenBeforeInput();
@@ -444,7 +445,7 @@ class FilteredSearchManager {
this.saveCurrentSearchQuery();
const { tokens, searchToken }
- = this.tokenizer.processTokens(searchQuery);
+ = this.tokenizer.processTokens(searchQuery, this.filteredSearchTokenKeys.getKeys());
const currentState = gl.utils.getParameterByName('state') || 'opened';
paths.push(`state=${currentState}`);
diff --git a/app/assets/javascripts/filtered_search/filtered_search_token_keys.js b/app/assets/javascripts/filtered_search/filtered_search_token_keys.js
index 1abad9d1b73..025d4d8795b 100644
--- a/app/assets/javascripts/filtered_search/filtered_search_token_keys.js
+++ b/app/assets/javascripts/filtered_search/filtered_search_token_keys.js
@@ -3,21 +3,25 @@ const tokenKeys = [{
type: 'string',
param: 'username',
symbol: '@',
+ icon: 'pencil',
}, {
key: 'assignee',
type: 'string',
param: 'username',
symbol: '@',
+ icon: 'user',
}, {
key: 'milestone',
type: 'string',
param: 'title',
symbol: '%',
+ icon: 'clock-o',
}, {
key: 'label',
type: 'array',
param: 'name[]',
symbol: '~',
+ icon: 'tag',
}];
const alternativeTokenKeys = [{
@@ -56,6 +60,10 @@ class FilteredSearchTokenKeys {
return tokenKeys;
}
+ static getKeys() {
+ return tokenKeys.map(i => i.key);
+ }
+
static getAlternatives() {
return alternativeTokenKeys;
}
diff --git a/app/assets/javascripts/filtered_search/filtered_search_tokenizer.js b/app/assets/javascripts/filtered_search/filtered_search_tokenizer.js
index aa513b3aeae..f2e66503e5e 100644
--- a/app/assets/javascripts/filtered_search/filtered_search_tokenizer.js
+++ b/app/assets/javascripts/filtered_search/filtered_search_tokenizer.js
@@ -1,8 +1,7 @@
import './filtered_search_token_keys';
class FilteredSearchTokenizer {
- static processTokens(input) {
- const allowedKeys = gl.FilteredSearchTokenKeys.get().map(i => i.key);
+ static processTokens(input, allowedKeys) {
// Regex extracts `(token):(symbol)(value)`
// Values that start with a double quote must end in a double quote (same for single)
const tokenRegex = new RegExp(`(${allowedKeys.join('|')}):([~%@]?)(?:('[^']*'{0,1})|("[^"]*"{0,1})|(\\S+))`, 'g');
diff --git a/app/assets/javascripts/filtered_search/recent_searches_root.js b/app/assets/javascripts/filtered_search/recent_searches_root.js
index b2e6f63aacf..27e49d4fb96 100644
--- a/app/assets/javascripts/filtered_search/recent_searches_root.js
+++ b/app/assets/javascripts/filtered_search/recent_searches_root.js
@@ -37,6 +37,7 @@ class RecentSearchesRoot {
<recent-searches-dropdown-content
:items="recentSearches"
:is-local-storage-available="isLocalStorageAvailable"
+ :allowed-keys="allowedKeys"
/>
`,
components: {
diff --git a/app/assets/javascripts/filtered_search/stores/recent_searches_store.js b/app/assets/javascripts/filtered_search/stores/recent_searches_store.js
index 35fc15e4c87..aaa0c349d93 100644
--- a/app/assets/javascripts/filtered_search/stores/recent_searches_store.js
+++ b/app/assets/javascripts/filtered_search/stores/recent_searches_store.js
@@ -1,10 +1,11 @@
import _ from 'underscore';
class RecentSearchesStore {
- constructor(initialState = {}) {
+ constructor(initialState = {}, allowedKeys) {
this.state = Object.assign({
isLocalStorageAvailable: true,
recentSearches: [],
+ allowedKeys,
}, initialState);
}
diff --git a/app/assets/javascripts/group_name.js b/app/assets/javascripts/group_name.js
index 62675d7e67e..462d792b8d5 100644
--- a/app/assets/javascripts/group_name.js
+++ b/app/assets/javascripts/group_name.js
@@ -44,18 +44,18 @@ export default class GroupName {
showToggle() {
this.title.classList.add('wrap');
this.toggle.classList.remove('hidden');
- if (this.isHidden) this.groupTitle.classList.add('is-hidden');
+ if (this.isHidden) this.groupTitle.classList.add('hidden');
}
hideToggle() {
this.title.classList.remove('wrap');
this.toggle.classList.add('hidden');
- if (this.isHidden) this.groupTitle.classList.remove('is-hidden');
+ if (this.isHidden) this.groupTitle.classList.remove('hidden');
}
toggleGroups() {
this.isHidden = !this.isHidden;
- this.groupTitle.classList.toggle('is-hidden');
+ this.groupTitle.classList.toggle('hidden');
}
render() {
diff --git a/app/assets/javascripts/lib/utils/ajax_cache.js b/app/assets/javascripts/lib/utils/ajax_cache.js
index cf030d613df..f1fe95e12e8 100644
--- a/app/assets/javascripts/lib/utils/ajax_cache.js
+++ b/app/assets/javascripts/lib/utils/ajax_cache.js
@@ -1,21 +1,11 @@
-class AjaxCache {
+import Cache from './cache';
+
+class AjaxCache extends Cache {
constructor() {
- this.internalStorage = { };
+ super();
this.pendingRequests = { };
}
- get(endpoint) {
- return this.internalStorage[endpoint];
- }
-
- hasData(endpoint) {
- return Object.prototype.hasOwnProperty.call(this.internalStorage, endpoint);
- }
-
- remove(endpoint) {
- delete this.internalStorage[endpoint];
- }
-
retrieve(endpoint) {
if (this.hasData(endpoint)) {
return Promise.resolve(this.get(endpoint));
diff --git a/app/assets/javascripts/lib/utils/cache.js b/app/assets/javascripts/lib/utils/cache.js
new file mode 100644
index 00000000000..3141f1eeafc
--- /dev/null
+++ b/app/assets/javascripts/lib/utils/cache.js
@@ -0,0 +1,19 @@
+class Cache {
+ constructor() {
+ this.internalStorage = { };
+ }
+
+ get(key) {
+ return this.internalStorage[key];
+ }
+
+ hasData(key) {
+ return Object.prototype.hasOwnProperty.call(this.internalStorage, key);
+ }
+
+ remove(key) {
+ delete this.internalStorage[key];
+ }
+}
+
+export default Cache;
diff --git a/app/assets/javascripts/lib/utils/common_utils.js b/app/assets/javascripts/lib/utils/common_utils.js
index 7e62773ae6c..a537267643e 100644
--- a/app/assets/javascripts/lib/utils/common_utils.js
+++ b/app/assets/javascripts/lib/utils/common_utils.js
@@ -198,10 +198,12 @@
const textBefore = value.substring(0, selectionStart);
const textAfter = value.substring(selectionEnd, value.length);
- const newText = textBefore + text + textAfter;
+
+ const insertedText = text instanceof Function ? text(textBefore, textAfter) : text;
+ const newText = textBefore + insertedText + textAfter;
target.value = newText;
- target.selectionStart = target.selectionEnd = selectionStart + text.length;
+ target.selectionStart = target.selectionEnd = selectionStart + insertedText.length;
// Trigger autosave
$(target).trigger('input');
diff --git a/app/assets/javascripts/lib/utils/users_cache.js b/app/assets/javascripts/lib/utils/users_cache.js
new file mode 100644
index 00000000000..88f8a622c00
--- /dev/null
+++ b/app/assets/javascripts/lib/utils/users_cache.js
@@ -0,0 +1,28 @@
+import Api from '../../api';
+import Cache from './cache';
+
+class UsersCache extends Cache {
+ retrieve(username) {
+ if (this.hasData(username)) {
+ return Promise.resolve(this.get(username));
+ }
+
+ return Api.users('', { username })
+ .then((users) => {
+ if (!users.length) {
+ throw new Error(`User "${username}" could not be found!`);
+ }
+
+ if (users.length > 1) {
+ throw new Error(`Expected username "${username}" to be unique!`);
+ }
+
+ const user = users[0];
+ this.internalStorage[username] = user;
+ return user;
+ });
+ // missing catch is intentional, error handling depends on use case
+ }
+}
+
+export default new UsersCache();
diff --git a/app/assets/javascripts/project_edit.js b/app/assets/javascripts/project_edit.js
new file mode 100644
index 00000000000..d7d284b6c86
--- /dev/null
+++ b/app/assets/javascripts/project_edit.js
@@ -0,0 +1,9 @@
+export default function setupProjectEdit() {
+ const $transferForm = $('.js-project-transfer-form');
+ const $selectNamespace = $transferForm.find('.select2');
+
+ $selectNamespace.on('change', () => {
+ $transferForm.find(':submit').prop('disabled', !$selectNamespace.val());
+ });
+ $selectNamespace.trigger('change');
+}
diff --git a/app/assets/javascripts/raven/index.js b/app/assets/javascripts/raven/index.js
index 5325e495815..edc2293915f 100644
--- a/app/assets/javascripts/raven/index.js
+++ b/app/assets/javascripts/raven/index.js
@@ -6,6 +6,10 @@ const index = function index() {
currentUserId: gon.current_user_id,
whitelistUrls: [gon.gitlab_url],
isProduction: process.env.NODE_ENV,
+ release: gon.revision,
+ tags: {
+ revision: gon.revision,
+ },
});
return RavenConfig;
diff --git a/app/assets/javascripts/raven/raven_config.js b/app/assets/javascripts/raven/raven_config.js
index c7fe1cacf49..ae54fa5f1a9 100644
--- a/app/assets/javascripts/raven/raven_config.js
+++ b/app/assets/javascripts/raven/raven_config.js
@@ -1,4 +1,5 @@
import Raven from 'raven-js';
+import $ from 'jquery';
const IGNORE_ERRORS = [
// Random plugins/extensions
@@ -57,6 +58,8 @@ const RavenConfig = {
configure() {
Raven.config(this.options.sentryDsn, {
+ release: this.options.release,
+ tags: this.options.tags,
whitelistUrls: this.options.whitelistUrls,
environment: this.options.isProduction ? 'production' : 'development',
ignoreErrors: this.IGNORE_ERRORS,
@@ -72,7 +75,7 @@ const RavenConfig = {
},
bindRavenErrors() {
- window.$(document).on('ajaxError.raven', this.handleRavenErrors);
+ $(document).on('ajaxError.raven', this.handleRavenErrors);
},
handleRavenErrors(event, req, config, err) {
diff --git a/app/assets/javascripts/shortcuts_issuable.js b/app/assets/javascripts/shortcuts_issuable.js
index b07b3a4d3a5..dace03554e8 100644
--- a/app/assets/javascripts/shortcuts_issuable.js
+++ b/app/assets/javascripts/shortcuts_issuable.js
@@ -38,7 +38,7 @@ import './shortcuts_navigation';
}
ShortcutsIssuable.prototype.replyWithSelectedText = function() {
- var quote, documentFragment, selected, separator;
+ var quote, documentFragment, el, selected, separator;
var replyField = $('.js-main-target-form #note_note');
documentFragment = window.gl.utils.getSelectedFragment();
@@ -47,10 +47,8 @@ import './shortcuts_navigation';
return;
}
- // If the documentFragment contains more than just Markdown, don't copy as GFM.
- if (documentFragment.querySelector('.md, .wiki')) return;
-
- selected = window.gl.CopyAsGFM.nodeToGFM(documentFragment);
+ el = window.gl.CopyAsGFM.transformGFMSelection(documentFragment.cloneNode(true));
+ selected = window.gl.CopyAsGFM.nodeToGFM(el);
if (selected.trim() === "") {
return;
diff --git a/app/assets/javascripts/sidebar/components/assignees/sidebar_assignees.js b/app/assets/javascripts/sidebar/components/assignees/sidebar_assignees.js
index 1488a66c695..da4abf0b68f 100644
--- a/app/assets/javascripts/sidebar/components/assignees/sidebar_assignees.js
+++ b/app/assets/javascripts/sidebar/components/assignees/sidebar_assignees.js
@@ -69,10 +69,11 @@ export default {
<div>
<assignee-title
:number-of-assignees="store.assignees.length"
- :loading="loading"
+ :loading="loading || store.isFetching.assignees"
:editable="store.editable"
/>
<assignees
+ v-if="!store.isFetching.assignees"
class="value"
:root-path="store.rootPath"
:users="store.assignees"
diff --git a/app/assets/javascripts/sidebar/stores/sidebar_store.js b/app/assets/javascripts/sidebar/stores/sidebar_store.js
index 2d44c05bb8d..3356dd0191f 100644
--- a/app/assets/javascripts/sidebar/stores/sidebar_store.js
+++ b/app/assets/javascripts/sidebar/stores/sidebar_store.js
@@ -10,6 +10,9 @@ export default class SidebarStore {
this.humanTimeEstimate = '';
this.humanTimeSpent = '';
this.assignees = [];
+ this.isFetching = {
+ assignees: true,
+ };
SidebarStore.singleton = this;
}
@@ -18,6 +21,7 @@ export default class SidebarStore {
}
setAssigneeData(data) {
+ this.isFetching.assignees = false;
if (data.assignees) {
this.assignees = data.assignees;
}
diff --git a/app/assets/javascripts/vue_merge_request_widget/stores/get_state_key.js b/app/assets/javascripts/vue_merge_request_widget/stores/get_state_key.js
index fb78ea92da1..7c15abfff10 100644
--- a/app/assets/javascripts/vue_merge_request_widget/stores/get_state_key.js
+++ b/app/assets/javascripts/vue_merge_request_widget/stores/get_state_key.js
@@ -11,10 +11,6 @@ export default function deviseState(data) {
return 'conflicts';
} else if (data.work_in_progress) {
return 'workInProgress';
- } else if (this.mergeWhenPipelineSucceeds) {
- return this.mergeError ? 'autoMergeFailed' : 'mergeWhenPipelineSucceeds';
- } else if (!this.canMerge) {
- return 'notAllowedToMerge';
} else if (this.onlyAllowMergeIfPipelineSucceeds && this.isPipelineFailed) {
return 'pipelineFailed';
} else if (this.hasMergeableDiscussionsState) {
@@ -23,6 +19,10 @@ export default function deviseState(data) {
return 'pipelineBlocked';
} else if (this.hasSHAChanged) {
return 'shaMismatch';
+ } else if (this.mergeWhenPipelineSucceeds) {
+ return this.mergeError ? 'autoMergeFailed' : 'mergeWhenPipelineSucceeds';
+ } else if (!this.canMerge) {
+ return 'notAllowedToMerge';
} else if (this.canBeMerged) {
return 'readyToMerge';
}
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 06661b930e3..c07bd25e6fd 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
@@ -4,7 +4,7 @@ import { getStateKey } from '../dependencies';
export default class MergeRequestStore {
constructor(data) {
- this.startingSha = data.diff_head_sha;
+ this.sha = data.diff_head_sha;
this.setData(data);
}
@@ -16,7 +16,6 @@ export default class MergeRequestStore {
this.targetBranch = data.target_branch;
this.sourceBranch = data.source_branch;
this.mergeStatus = data.merge_status;
- this.sha = data.diff_head_sha;
this.commitMessage = data.merge_commit_message;
this.commitMessageWithDescription = data.merge_commit_message_with_description;
this.commitsCount = data.commits_count;
@@ -69,7 +68,7 @@ export default class MergeRequestStore {
this.canMerge = !!data.merge_path;
this.canCreateIssue = currentUser.can_create_issue || false;
this.canCancelAutomaticMerge = !!data.cancel_merge_when_pipeline_succeeds_path;
- this.hasSHAChanged = this.sha !== this.startingSha;
+ this.hasSHAChanged = this.sha !== data.diff_head_sha;
this.canBeMerged = data.can_be_merged || false;
// Cherry-pick and Revert actions related
diff --git a/app/assets/javascripts/vue_shared/vue_resource_interceptor.js b/app/assets/javascripts/vue_shared/vue_resource_interceptor.js
index d5f87588c28..740930dce5b 100644
--- a/app/assets/javascripts/vue_shared/vue_resource_interceptor.js
+++ b/app/assets/javascripts/vue_shared/vue_resource_interceptor.js
@@ -4,7 +4,7 @@ import VueResource from 'vue-resource';
Vue.use(VueResource);
// Maintain a global counter for active requests
-// see: spec/support/wait_for_vue_resource.rb
+// see: spec/support/wait_for_requests.rb
Vue.http.interceptors.push((request, next) => {
window.activeVueResources = window.activeVueResources || 0;
window.activeVueResources += 1;
diff --git a/app/assets/stylesheets/framework/awards.scss b/app/assets/stylesheets/framework/awards.scss
index b5f00cb20d4..d64b1237b2c 100644
--- a/app/assets/stylesheets/framework/awards.scss
+++ b/app/assets/stylesheets/framework/awards.scss
@@ -110,6 +110,7 @@
.award-control {
margin: 0 5px 6px 0;
outline: 0;
+ position: relative;
&.disabled {
cursor: default;
@@ -227,8 +228,8 @@
.award-control-icon-positive,
.award-control-icon-super-positive {
position: absolute;
- left: 11px;
- bottom: 7px;
+ left: 10px;
+ bottom: 6px;
opacity: 0;
@include transition(opacity, transform);
}
@@ -237,7 +238,3 @@
vertical-align: middle;
}
}
-
-.note-awards .award-control-icon-positive {
- left: 6px;
-}
diff --git a/app/assets/stylesheets/framework/filters.scss b/app/assets/stylesheets/framework/filters.scss
index 637731cc479..f0994e968c8 100644
--- a/app/assets/stylesheets/framework/filters.scss
+++ b/app/assets/stylesheets/framework/filters.scss
@@ -263,7 +263,9 @@
}
.filtered-search-input-dropdown-menu {
+ max-height: 215px;
max-width: 280px;
+ overflow: auto;
@media (max-width: $screen-xs-min) {
width: auto;
@@ -372,11 +374,6 @@
padding: 0;
}
-.filter-dropdown {
- max-height: 215px;
- overflow: auto;
-}
-
@media (min-width: $screen-sm-min) and (max-width: $screen-sm-max) {
.issue-bulk-update-dropdown-toggle {
width: 100px;
diff --git a/app/assets/stylesheets/framework/header.scss b/app/assets/stylesheets/framework/header.scss
index ce8b27a1951..d8645afb7da 100644
--- a/app/assets/stylesheets/framework/header.scss
+++ b/app/assets/stylesheets/framework/header.scss
@@ -40,7 +40,17 @@ header {
}
&.with-horizontal-nav {
- border-color: transparent;
+ border-bottom: 0;
+
+ .navbar-border {
+ height: 1px;
+ position: absolute;
+ right: 0;
+ left: 0;
+ bottom: -1px;
+ background-color: $border-color;
+ opacity: 0;
+ }
}
.container-fluid {
@@ -114,16 +124,6 @@ header {
}
}
- .navbar-border {
- height: 1px;
- position: absolute;
- right: 0;
- left: 0;
- bottom: 0;
- background-color: $border-color;
- opacity: 0;
- }
-
.global-dropdown {
position: absolute;
left: -10px;
diff --git a/app/assets/stylesheets/framework/sidebar.scss b/app/assets/stylesheets/framework/sidebar.scss
index 018f61ca3a8..5b62d7fa3a7 100644
--- a/app/assets/stylesheets/framework/sidebar.scss
+++ b/app/assets/stylesheets/framework/sidebar.scss
@@ -83,4 +83,8 @@
position: fixed;
top: $header-height;
}
+
+ &:not(.affix-top) {
+ min-height: 100%;
+ }
}
diff --git a/app/assets/stylesheets/framework/timeline.scss b/app/assets/stylesheets/framework/timeline.scss
index aa0c512a277..1fd734d279b 100644
--- a/app/assets/stylesheets/framework/timeline.scss
+++ b/app/assets/stylesheets/framework/timeline.scss
@@ -5,7 +5,7 @@
.note-text {
p:last-child {
- margin-bottom: 0;
+ margin-bottom: 0 !important;
}
}
@@ -23,7 +23,6 @@
}
.timeline-entry {
- padding: $gl-padding $gl-btn-padding 0;
border-color: $white-normal;
color: $gl-text-color;
border-bottom: 1px solid $border-white-light;
diff --git a/app/assets/stylesheets/pages/detail_page.scss b/app/assets/stylesheets/pages/detail_page.scss
index f3de05aa5f6..3d9eff35583 100644
--- a/app/assets/stylesheets/pages/detail_page.scss
+++ b/app/assets/stylesheets/pages/detail_page.scss
@@ -4,11 +4,7 @@
color: $gl-text-color;
line-height: 34px;
- .author {
- color: $gl-text-color;
- }
-
- .identifier {
+ a {
color: $gl-text-color;
}
diff --git a/app/assets/stylesheets/pages/note_form.scss b/app/assets/stylesheets/pages/note_form.scss
index 9db26f99a75..49e453c7dbc 100644
--- a/app/assets/stylesheets/pages/note_form.scss
+++ b/app/assets/stylesheets/pages/note_form.scss
@@ -164,10 +164,6 @@
.discussion-body,
.diff-file {
- .notes .note {
- padding: 10px 15px;
- }
-
.discussion-reply-holder {
background-color: $white-light;
padding: 10px 16px;
diff --git a/app/assets/stylesheets/pages/notes.scss b/app/assets/stylesheets/pages/notes.scss
index 4b15fc2bd82..51918917329 100644
--- a/app/assets/stylesheets/pages/notes.scss
+++ b/app/assets/stylesheets/pages/notes.scss
@@ -43,7 +43,11 @@ ul.notes {
}
.discussion-body {
- padding-top: 15px;
+ padding-top: 8px;
+
+ .panel {
+ margin-bottom: 0;
+ }
}
.discussion {
@@ -53,6 +57,7 @@ ul.notes {
}
.note {
+ padding: $gl-padding $gl-btn-padding 0;
display: block;
position: relative;
border-bottom: 1px solid $white-normal;
@@ -78,11 +83,7 @@ ul.notes {
&.note-discussion {
&.timeline-entry {
- padding: 14px 10px;
- }
-
- .system-note {
- padding: 0;
+ padding: $gl-padding 10px;
}
}
@@ -167,7 +168,7 @@ ul.notes {
margin-left: 65px;
}
- .note-header {
+ .note-header-info {
padding-bottom: 0;
}
@@ -377,7 +378,11 @@ ul.notes {
.note-header-info {
min-width: 0;
- padding-bottom: 5px;
+ padding-bottom: 8px;
+}
+
+.system-note .note-header-info {
+ padding-bottom: 0;
}
.note-headline-light {
@@ -582,6 +587,17 @@ ul.notes {
}
}
+.discussion-body,
+.diff-file {
+ .notes .note {
+ padding: 10px 15px;
+
+ &.system-note {
+ padding: 0;
+ }
+ }
+}
+
.diff-file {
.is-over {
.add-diff-note {
diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb
index 8ce9150e4a9..ab5aed24917 100644
--- a/app/controllers/application_controller.rb
+++ b/app/controllers/application_controller.rb
@@ -11,6 +11,7 @@ class ApplicationController < ActionController::Base
include EnforcesTwoFactorAuthentication
before_action :authenticate_user_from_private_token!
+ before_action :authenticate_user_from_rss_token!
before_action :authenticate_user!
before_action :validate_user_service_ticket!
before_action :check_password_expiration
@@ -72,13 +73,20 @@ class ApplicationController < ActionController::Base
user = User.find_by_authentication_token(token) || User.find_by_personal_access_token(token)
- if user && can?(user, :log_in)
- # Notice we are passing store false, so the user is not
- # actually stored in the session and a token is needed
- # for every request. If you want the token to work as a
- # sign in token, you can simply remove store: false.
- sign_in user, store: false
- end
+ sessionless_sign_in(user)
+ end
+
+ # This filter handles authentication for atom request with an rss_token
+ def authenticate_user_from_rss_token!
+ return unless request.format.atom?
+
+ token = params[:rss_token].presence
+
+ return unless token.present?
+
+ user = User.find_by_rss_token(token)
+
+ sessionless_sign_in(user)
end
def log_exception(exception)
@@ -282,4 +290,14 @@ class ApplicationController < ActionController::Base
ensure
Gitlab::I18n.reset_locale
end
+
+ def sessionless_sign_in(user)
+ if user && can?(user, :log_in)
+ # Notice we are passing store false, so the user is not
+ # actually stored in the session and a token is needed
+ # for every request. If you want the token to work as a
+ # sign in token, you can simply remove store: false.
+ sign_in user, store: false
+ end
+ end
end
diff --git a/app/controllers/profiles_controller.rb b/app/controllers/profiles_controller.rb
index 57e23cea00e..8cd1c47eb3f 100644
--- a/app/controllers/profiles_controller.rb
+++ b/app/controllers/profiles_controller.rb
@@ -40,6 +40,14 @@ class ProfilesController < Profiles::ApplicationController
redirect_to profile_account_path
end
+ def reset_rss_token
+ if current_user.reset_rss_token!
+ flash[:notice] = "RSS token was successfully reset"
+ end
+
+ redirect_to profile_account_path
+ end
+
def audit_log
@events = AuditEvent.where(entity_type: "User", entity_id: current_user.id).
order("created_at DESC").
diff --git a/app/controllers/projects/environments_controller.rb b/app/controllers/projects/environments_controller.rb
index d85bf44ae4d..4630f451445 100644
--- a/app/controllers/projects/environments_controller.rb
+++ b/app/controllers/projects/environments_controller.rb
@@ -33,6 +33,7 @@ class Projects::EnvironmentsController < Projects::ApplicationController
def folder
folder_environments = project.environments.where(environment_type: params[:id])
@environments = folder_environments.with_state(params[:scope] || :available)
+ .order(:name)
respond_to do |format|
format.html
diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb
index 544715d62ea..cc62e1fa99b 100644
--- a/app/controllers/projects_controller.rb
+++ b/app/controllers/projects_controller.rb
@@ -257,7 +257,7 @@ class ProjectsController < Projects::ApplicationController
#
# pages list order: repository readme, wiki home, issues list, customize workflow
def render_landing_page
- if @project.feature_available?(:repository, current_user)
+ if can?(current_user, :download_code, @project)
return render 'projects/no_repo' unless @project.repository_exists?
render 'projects/empty' if @project.empty_repo?
else
diff --git a/app/helpers/preferences_helper.rb b/app/helpers/preferences_helper.rb
index de959f13713..d36bb4ab074 100644
--- a/app/helpers/preferences_helper.rb
+++ b/app/helpers/preferences_helper.rb
@@ -49,7 +49,7 @@ module PreferencesHelper
user_view = current_user.project_view
- if @project.feature_available?(:repository, current_user)
+ if can?(current_user, :download_code, @project)
user_view
elsif user_view == "activity"
"activity"
diff --git a/app/helpers/rss_helper.rb b/app/helpers/rss_helper.rb
index ea5d2932ef4..9ac4df88dc3 100644
--- a/app/helpers/rss_helper.rb
+++ b/app/helpers/rss_helper.rb
@@ -1,5 +1,5 @@
module RssHelper
def rss_url_options
- { format: :atom, private_token: current_user.try(:private_token) }
+ { format: :atom, rss_token: current_user.try(:rss_token) }
end
end
diff --git a/app/helpers/system_note_helper.rb b/app/helpers/system_note_helper.rb
index d889d141101..209bd56b78a 100644
--- a/app/helpers/system_note_helper.rb
+++ b/app/helpers/system_note_helper.rb
@@ -17,7 +17,8 @@ module SystemNoteHelper
'visible' => 'icon_eye',
'milestone' => 'icon_clock_o',
'discussion' => 'icon_comment_o',
- 'moved' => 'icon_arrow_circle_o_right'
+ 'moved' => 'icon_arrow_circle_o_right',
+ 'outdated' => 'icon_edit'
}.freeze
def icon_for_system_note(note)
diff --git a/app/models/commit_status.rb b/app/models/commit_status.rb
index ffafc678968..fe63728ea23 100644
--- a/app/models/commit_status.rb
+++ b/app/models/commit_status.rb
@@ -89,6 +89,7 @@ class CommitStatus < ActiveRecord::Base
else
PipelineUpdateWorker.perform_async(pipeline.id)
end
+ ExpireJobCacheWorker.perform_async(commit_status.id)
end
end
end
diff --git a/app/models/concerns/discussion_on_diff.rb b/app/models/concerns/discussion_on_diff.rb
index a7bdf5587b2..eee1a36ac6b 100644
--- a/app/models/concerns/discussion_on_diff.rb
+++ b/app/models/concerns/discussion_on_diff.rb
@@ -47,4 +47,12 @@ module DiscussionOnDiff
prev_lines
end
+
+ def line_code_in_diffs(diff_refs)
+ if active?(diff_refs)
+ line_code
+ elsif diff_refs && created_at_diff?(diff_refs)
+ original_line_code
+ end
+ end
end
diff --git a/app/models/diff_discussion.rb b/app/models/diff_discussion.rb
index 14ddd2fcc88..800574d8be0 100644
--- a/app/models/diff_discussion.rb
+++ b/app/models/diff_discussion.rb
@@ -19,21 +19,9 @@ class DiffDiscussion < Discussion
def merge_request_version_params
return unless for_merge_request?
+ return {} if active?
- if active?
- {}
- else
- diff_refs = position.diff_refs
-
- if diff = noteable.merge_request_diff_for(diff_refs)
- { diff_id: diff.id }
- elsif diff = noteable.merge_request_diff_for(diff_refs.head_sha)
- {
- diff_id: diff.id,
- start_sha: diff_refs.start_sha
- }
- end
- end
+ noteable.version_params_for(position.diff_refs)
end
def reply_attributes
diff --git a/app/models/diff_note.rb b/app/models/diff_note.rb
index 76c59199afd..1764004078e 100644
--- a/app/models/diff_note.rb
+++ b/app/models/diff_note.rb
@@ -8,6 +8,7 @@ class DiffNote < Note
serialize :original_position, Gitlab::Diff::Position
serialize :position, Gitlab::Diff::Position
+ serialize :change_position, Gitlab::Diff::Position
validates :original_position, presence: true
validates :position, presence: true
@@ -25,7 +26,7 @@ class DiffNote < Note
DiffDiscussion
end
- %i(original_position position).each do |meth|
+ %i(original_position position change_position).each do |meth|
define_method "#{meth}=" do |new_position|
if new_position.is_a?(String)
new_position = JSON.parse(new_position) rescue nil
@@ -36,6 +37,8 @@ class DiffNote < Note
new_position = Gitlab::Diff::Position.new(new_position)
end
+ return if new_position == read_attribute(meth)
+
super(new_position)
end
end
@@ -45,7 +48,7 @@ class DiffNote < Note
end
def diff_line
- @diff_line ||= diff_file.line_for_position(self.original_position) if diff_file
+ @diff_line ||= diff_file&.line_for_position(self.original_position)
end
def for_line?(line)
diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb
index 9be00880438..2eec013fa9d 100644
--- a/app/models/merge_request.rb
+++ b/app/models/merge_request.rb
@@ -416,13 +416,24 @@ class MergeRequest < ActiveRecord::Base
@merge_request_diffs_by_diff_refs_or_sha[diff_refs_or_sha]
end
+ def version_params_for(diff_refs)
+ if diff = merge_request_diff_for(diff_refs)
+ { diff_id: diff.id }
+ elsif diff = merge_request_diff_for(diff_refs.head_sha)
+ {
+ diff_id: diff.id,
+ start_sha: diff_refs.start_sha
+ }
+ end
+ end
+
def reload_diff_if_branch_changed
if source_branch_changed? || target_branch_changed?
reload_diff
end
end
- def reload_diff
+ def reload_diff(current_user = nil)
return unless open?
old_diff_refs = self.diff_refs
@@ -432,7 +443,8 @@ class MergeRequest < ActiveRecord::Base
update_diff_notes_positions(
old_diff_refs: old_diff_refs,
- new_diff_refs: new_diff_refs
+ new_diff_refs: new_diff_refs,
+ current_user: current_user
)
end
@@ -861,7 +873,7 @@ class MergeRequest < ActiveRecord::Base
diff_sha_refs && diff_sha_refs.complete?
end
- def update_diff_notes_positions(old_diff_refs:, new_diff_refs:)
+ def update_diff_notes_positions(old_diff_refs:, new_diff_refs:, current_user: nil)
return unless has_complete_diff_refs?
return if new_diff_refs == old_diff_refs
@@ -875,7 +887,7 @@ class MergeRequest < ActiveRecord::Base
service = Notes::DiffPositionUpdateService.new(
self.project,
- nil,
+ current_user,
old_diff_refs: old_diff_refs,
new_diff_refs: new_diff_refs,
paths: paths
diff --git a/app/models/merge_request_diff.rb b/app/models/merge_request_diff.rb
index f0a3c30ea74..6e3917a10a3 100644
--- a/app/models/merge_request_diff.rb
+++ b/app/models/merge_request_diff.rb
@@ -175,12 +175,11 @@ class MergeRequestDiff < ActiveRecord::Base
self == merge_request.merge_request_diff
end
- def compare_with(sha, straight: true)
+ def compare_with(sha)
# When compare merge request versions we want diff A..B instead of A...B
# so we handle cases when user does squash and rebase of the commits between versions.
# For this reason we set straight to true by default.
- CompareService.new(project, head_commit_sha)
- .execute(project, sha, straight: straight)
+ CompareService.new(project, head_commit_sha).execute(project, sha, straight: true)
end
def commits_count
diff --git a/app/models/note.rb b/app/models/note.rb
index 46d0a4f159f..60257aac93b 100644
--- a/app/models/note.rb
+++ b/app/models/note.rb
@@ -124,13 +124,12 @@ class Note < ActiveRecord::Base
groups = {}
diff_notes.fresh.discussions.each do |discussion|
- if discussion.active?(diff_refs)
- discussions = groups[discussion.line_code] ||= []
- elsif diff_refs && discussion.created_at_diff?(diff_refs)
- discussions = groups[discussion.original_line_code] ||= []
- end
+ line_code = discussion.line_code_in_diffs(diff_refs)
- discussions << discussion if discussions
+ if line_code
+ discussions = groups[line_code] ||= []
+ discussions << discussion
+ end
end
groups
diff --git a/app/models/project.rb b/app/models/project.rb
index 65745fd6d37..cfca0dcd2f2 100644
--- a/app/models/project.rb
+++ b/app/models/project.rb
@@ -205,7 +205,7 @@ class Project < ActiveRecord::Base
presence: true,
dynamic_path: true,
length: { maximum: 255 },
- format: { with: Gitlab::Regex.project_path_regex,
+ format: { with: Gitlab::Regex.project_path_format_regex,
message: Gitlab::Regex.project_path_regex_message },
uniqueness: { scope: :namespace_id }
diff --git a/app/models/project_services/issue_tracker_service.rb b/app/models/project_services/issue_tracker_service.rb
index 50435b67eda..eddf308eae3 100644
--- a/app/models/project_services/issue_tracker_service.rb
+++ b/app/models/project_services/issue_tracker_service.rb
@@ -76,7 +76,7 @@ class IssueTrackerService < Service
message = "#{self.type} received response #{response.code} when attempting to connect to #{self.project_url}"
result = true
end
- rescue HTTParty::Error, Timeout::Error, SocketError, Errno::ECONNRESET, Errno::ECONNREFUSED => error
+ rescue HTTParty::Error, Timeout::Error, SocketError, Errno::ECONNRESET, Errno::ECONNREFUSED, OpenSSL::SSL::SSLError => error
message = "#{self.type} had an error when trying to connect to #{self.project_url}: #{error.message}"
end
Rails.logger.info(message)
diff --git a/app/models/project_services/jira_service.rb b/app/models/project_services/jira_service.rb
index f388773efee..a91a986e195 100644
--- a/app/models/project_services/jira_service.rb
+++ b/app/models/project_services/jira_service.rb
@@ -294,7 +294,7 @@ class JiraService < IssueTrackerService
def jira_request
yield
- rescue Timeout::Error, Errno::EINVAL, Errno::ECONNRESET, Errno::ECONNREFUSED, URI::InvalidURIError, JIRA::HTTPError => e
+ rescue Timeout::Error, Errno::EINVAL, Errno::ECONNRESET, Errno::ECONNREFUSED, URI::InvalidURIError, JIRA::HTTPError, OpenSSL::SSL::SSLError => e
Rails.logger.info "#{self.class.name} Send message ERROR: #{url} - #{e.message}"
nil
end
diff --git a/app/models/system_note_metadata.rb b/app/models/system_note_metadata.rb
index b44f4fe000c..414c95f7705 100644
--- a/app/models/system_note_metadata.rb
+++ b/app/models/system_note_metadata.rb
@@ -2,6 +2,7 @@ class SystemNoteMetadata < ActiveRecord::Base
ICON_TYPES = %w[
commit description merge confidential visible label assignee cross_reference
title time_tracking branch milestone discussion task moved opened closed merged
+ outdated
].freeze
validates :note, presence: true
diff --git a/app/models/user.rb b/app/models/user.rb
index 837ab78228b..cf3914568a6 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -15,6 +15,7 @@ class User < ActiveRecord::Base
add_authentication_token_field :authentication_token
add_authentication_token_field :incoming_email_token
+ add_authentication_token_field :rss_token
default_value_for :admin, false
default_value_for(:external) { current_application_settings.user_default_external }
@@ -1004,6 +1005,13 @@ class User < ActiveRecord::Base
save
end
+ # each existing user needs to have an `rss_token`.
+ # we do this on read since migrating all existing users is not a feasible
+ # solution.
+ def rss_token
+ ensure_rss_token!
+ end
+
protected
# override, from Devise::Validatable
diff --git a/app/services/merge_requests/refresh_service.rb b/app/services/merge_requests/refresh_service.rb
index 1131d6f4913..81d217929d5 100644
--- a/app/services/merge_requests/refresh_service.rb
+++ b/app/services/merge_requests/refresh_service.rb
@@ -66,12 +66,12 @@ module MergeRequests
filter_merge_requests(merge_requests).each do |merge_request|
if merge_request.source_branch == @branch_name || force_push?
- merge_request.reload_diff
+ merge_request.reload_diff(current_user)
else
mr_commit_ids = merge_request.commits_sha
push_commit_ids = @commits.map(&:id)
matches = mr_commit_ids & push_commit_ids
- merge_request.reload_diff if matches.any?
+ merge_request.reload_diff(current_user) if matches.any?
end
merge_request.mark_as_unchecked
diff --git a/app/services/merge_requests/reopen_service.rb b/app/services/merge_requests/reopen_service.rb
index fadcce5d9b6..54b19e6d651 100644
--- a/app/services/merge_requests/reopen_service.rb
+++ b/app/services/merge_requests/reopen_service.rb
@@ -8,7 +8,7 @@ module MergeRequests
create_note(merge_request)
notification_service.reopen_mr(merge_request, current_user)
execute_hooks(merge_request, 'reopen')
- merge_request.reload_diff
+ merge_request.reload_diff(current_user)
merge_request.mark_as_unchecked
end
diff --git a/app/services/notes/diff_position_update_service.rb b/app/services/notes/diff_position_update_service.rb
index 0cb731f5bc3..eff7b287269 100644
--- a/app/services/notes/diff_position_update_service.rb
+++ b/app/services/notes/diff_position_update_service.rb
@@ -1,26 +1,29 @@
module Notes
class DiffPositionUpdateService < BaseService
def execute(note)
- new_position = tracer.trace(note.position)
+ results = tracer.trace(note.position)
+ return unless results
- # Don't update the position if the type doesn't match, since that means
- # the diff line commented on was changed, and the comment is now outdated
- old_position = note.position
- if new_position &&
- new_position != old_position &&
- new_position.type == old_position.type
+ position = results[:position]
+ outdated = results[:outdated]
- note.position = new_position
- end
+ if outdated
+ note.change_position = position
- note
+ if note.persisted? && current_user
+ SystemNoteService.diff_discussion_outdated(note.to_discussion, project, current_user, position)
+ end
+ else
+ note.position = position
+ note.change_position = nil
+ end
end
private
def tracer
@tracer ||= Gitlab::Diff::PositionTracer.new(
- repository: project.repository,
+ project: project,
old_diff_refs: params[:old_diff_refs],
new_diff_refs: params[:new_diff_refs],
paths: params[:paths]
diff --git a/app/services/projects/transfer_service.rb b/app/services/projects/transfer_service.rb
index da6e6acd4a7..1c24b27a870 100644
--- a/app/services/projects/transfer_service.rb
+++ b/app/services/projects/transfer_service.rb
@@ -12,12 +12,13 @@ module Projects
TransferError = Class.new(StandardError)
def execute(new_namespace)
- if allowed_transfer?(current_user, project, new_namespace)
- transfer(project, new_namespace)
- else
- project.errors.add(:new_namespace, 'is invalid')
- false
+ if new_namespace.blank?
+ raise TransferError, 'Please select a new namespace for your project.'
end
+ unless allowed_transfer?(current_user, project, new_namespace)
+ raise TransferError, 'Transfer failed, please contact an admin.'
+ end
+ transfer(project, new_namespace)
rescue Projects::TransferService::TransferError => ex
project.reload
project.errors.add(:new_namespace, ex.message)
diff --git a/app/services/system_note_service.rb b/app/services/system_note_service.rb
index 93bf1fb1615..0837c07e6aa 100644
--- a/app/services/system_note_service.rb
+++ b/app/services/system_note_service.rb
@@ -258,7 +258,7 @@ module SystemNoteService
create_note(NoteSummary.new(noteable, project, author, body, action: 'title'))
end
- def self.resolve_all_discussions(merge_request, project, author)
+ def resolve_all_discussions(merge_request, project, author)
body = "resolved all discussions"
create_note(NoteSummary.new(merge_request, project, author, body, action: 'discussion'))
@@ -274,6 +274,28 @@ module SystemNoteService
note
end
+ def diff_discussion_outdated(discussion, project, author, change_position)
+ merge_request = discussion.noteable
+ diff_refs = change_position.diff_refs
+ version_index = merge_request.merge_request_diffs.viewable.count
+
+ body = "changed this line in"
+ if version_params = merge_request.version_params_for(diff_refs)
+ line_code = change_position.line_code(project.repository)
+ url = url_helpers.diffs_namespace_project_merge_request_url(project.namespace, project, merge_request, version_params.merge(anchor: line_code))
+
+ body << " [version #{version_index} of the diff](#{url})"
+ else
+ body << " version #{version_index} of the diff"
+ end
+
+ note_attributes = discussion.reply_attributes.merge(project: project, author: author, note: body)
+ note = Note.create(note_attributes.merge(system: true))
+ note.system_note_metadata = SystemNoteMetadata.new(action: 'outdated')
+
+ note
+ end
+
# Called when the title of a Noteable is changed
#
# noteable - Noteable object that responds to `title`
diff --git a/app/validators/dynamic_path_validator.rb b/app/validators/dynamic_path_validator.rb
index d992b0c3725..8d4d7180baf 100644
--- a/app/validators/dynamic_path_validator.rb
+++ b/app/validators/dynamic_path_validator.rb
@@ -6,199 +6,26 @@
# Values are checked for formatting and exclusion from a list of reserved path
# names.
class DynamicPathValidator < ActiveModel::EachValidator
- # All routes that appear on the top level must be listed here.
- # This will make sure that groups cannot be created with these names
- # as these routes would be masked by the paths already in place.
- #
- # Example:
- # /api/api-project
- #
- # the path `api` shouldn't be allowed because it would be masked by `api/*`
- #
- TOP_LEVEL_ROUTES = %w[
- -
- .well-known
- abuse_reports
- admin
- all
- api
- assets
- autocomplete
- ci
- dashboard
- explore
- files
- groups
- health_check
- help
- hooks
- import
- invites
- issues
- jwt
- koding
- member
- merge_requests
- new
- notes
- notification_settings
- oauth
- profile
- projects
- public
- repository
- robots.txt
- s
- search
- sent_notifications
- services
- snippets
- teams
- u
- unicorn_test
- unsubscribes
- uploads
- users
- ].freeze
-
- # This list should contain all words following `/*namespace_id/:project_id` in
- # routes that contain a second wildcard.
- #
- # Example:
- # /*namespace_id/:project_id/badges/*ref/build
- #
- # If `badges` was allowed as a project/group name, we would not be able to access the
- # `badges` route for those projects:
- #
- # Consider a namespace with path `foo/bar` and a project called `badges`.
- # The route to the build badge would then be `/foo/bar/badges/badges/master/build.svg`
- #
- # When accessing this path the route would be matched to the `badges` path
- # with the following params:
- # - namespace_id: `foo`
- # - project_id: `bar`
- # - ref: `badges/master`
- #
- # Failing to find the project, this would result in a 404.
- #
- # By rejecting `badges` the router can _count_ on the fact that `badges` will
- # be preceded by the `namespace/project`.
- WILDCARD_ROUTES = %w[
- badges
- blame
- blob
- builds
- commits
- create
- create_dir
- edit
- environments/folders
- files
- find_file
- gitlab-lfs/objects
- info/lfs/objects
- new
- preview
- raw
- refs
- tree
- update
- wikis
- ].freeze
-
- # These are all the paths that follow `/groups/*id/ or `/groups/*group_id`
- # We need to reject these because we have a `/groups/*id` page that is the same
- # as the `/*id`.
- #
- # If we would allow a subgroup to be created with the name `activity` then
- # this group would not be accessible through `/groups/parent/activity` since
- # this would map to the activity-page of it's parent.
- GROUP_ROUTES = %w[
- activity
- analytics
- audit_events
- avatar
- edit
- group_members
- hooks
- issues
- labels
- ldap
- ldap_group_links
- merge_requests
- milestones
- notification_setting
- pipeline_quota
- projects
- subgroups
- ].freeze
-
- CHILD_ROUTES = (WILDCARD_ROUTES | GROUP_ROUTES).freeze
-
- def self.without_reserved_wildcard_paths_regex
- @without_reserved_wildcard_paths_regex ||= regex_excluding_child_paths(WILDCARD_ROUTES)
- end
-
- def self.without_reserved_child_paths_regex
- @without_reserved_child_paths_regex ||= regex_excluding_child_paths(CHILD_ROUTES)
- end
-
- # This is used to validate a full path.
- # It doesn't match paths
- # - Starting with one of the top level words
- # - Containing one of the child level words in the middle of a path
- def self.regex_excluding_child_paths(child_routes)
- reserved_top_level_words = Regexp.union(TOP_LEVEL_ROUTES)
- not_starting_in_reserved_word = %r{\A/?(?!(#{reserved_top_level_words})(/|\z))}
-
- reserved_child_level_words = Regexp.union(child_routes)
- not_containing_reserved_child = %r{(?!\S+/(#{reserved_child_level_words})(/|\z))}
-
- %r{#{not_starting_in_reserved_word}
- #{not_containing_reserved_child}
- #{Gitlab::Regex.full_namespace_regex}}x
- end
-
- def self.valid?(path)
- path =~ Gitlab::Regex.full_namespace_regex && !full_path_reserved?(path)
- end
-
- def self.full_path_reserved?(path)
- path = path.to_s.downcase
- _project_part, namespace_parts = path.reverse.split('/', 2).map(&:reverse)
-
- wildcard_reserved?(path) || child_reserved?(namespace_parts)
- end
-
- def self.child_reserved?(path)
- return false unless path
-
- path !~ without_reserved_child_paths_regex
- end
-
- def self.wildcard_reserved?(path)
- return false unless path
+ class << self
+ def valid_namespace_path?(path)
+ "#{path}/" =~ Gitlab::Regex.full_namespace_path_regex
+ end
- path !~ without_reserved_wildcard_paths_regex
+ def valid_project_path?(path)
+ "#{path}/" =~ Gitlab::Regex.full_project_path_regex
+ end
end
- delegate :full_path_reserved?,
- :child_reserved?,
- to: :class
-
- def path_reserved_for_record?(record, value)
+ def path_valid_for_record?(record, value)
full_path = record.respond_to?(:full_path) ? record.full_path : value
- # For group paths the entire path cannot contain a reserved child word
- # The path doesn't contain the last `_project_part` so we need to validate
- # if the entire path.
- # Example:
- # A *group* with full path `parent/activity` is reserved.
- # A *project* with full path `parent/activity` is allowed.
- if record.is_a? Group
- child_reserved?(full_path)
+ return true unless full_path
+
+ case record
+ when Project
+ self.class.valid_project_path?(full_path)
else
- full_path_reserved?(full_path)
+ self.class.valid_namespace_path?(full_path)
end
end
@@ -208,7 +35,7 @@ class DynamicPathValidator < ActiveModel::EachValidator
return
end
- if path_reserved_for_record?(record, value)
+ unless path_valid_for_record?(record, value)
record.errors.add(attribute, "#{value} is a reserved name")
end
end
diff --git a/app/views/admin/requests_profiles/index.html.haml b/app/views/admin/requests_profiles/index.html.haml
index ae918086a57..c7b63d9de98 100644
--- a/app/views/admin/requests_profiles/index.html.haml
+++ b/app/views/admin/requests_profiles/index.html.haml
@@ -20,7 +20,7 @@
%ul.content-list
- profiles.each do |profile|
%li
- = link_to profile.time.to_s(:long), admin_requests_profile_path(profile), data: {no_turbolink: true}
+ = link_to profile.time.to_s(:long), admin_requests_profile_path(profile)
- else
%p
No profiles found
diff --git a/app/views/discussions/_discussion.html.haml b/app/views/discussions/_discussion.html.haml
index 74992e439f3..578e751ab47 100644
--- a/app/views/discussions/_discussion.html.haml
+++ b/app/views/discussions/_discussion.html.haml
@@ -32,10 +32,9 @@
- elsif discussion.diff_discussion?
on
= conditional_link_to url.present?, url do
- - if discussion.active?
- the diff
- - else
- an outdated diff
+ - unless discussion.active?
+ an old version of
+ the diff
= time_ago_with_tooltip(discussion.created_at, placement: "bottom", html_class: "note-created-ago")
= render "discussions/headline", discussion: discussion
diff --git a/app/views/profiles/accounts/_reset_token.html.haml b/app/views/profiles/accounts/_reset_token.html.haml
new file mode 100644
index 00000000000..c31a4a8ecd4
--- /dev/null
+++ b/app/views/profiles/accounts/_reset_token.html.haml
@@ -0,0 +1,11 @@
+- name = label.parameterize
+- attribute = name.underscore
+
+.reset-action
+ %p.cgray
+ = label_tag name, label, class: "label-light"
+ = text_field_tag name, current_user.send(attribute), class: 'form-control', readonly: true, onclick: 'this.select()'
+ %p.help-block
+ = help_text
+ .prepend-top-default
+ = link_to button_label, [:reset, attribute, :profile], method: :put, data: { confirm: 'Are you sure?' }, class: 'btn btn-default private-token'
diff --git a/app/views/profiles/accounts/show.html.haml b/app/views/profiles/accounts/show.html.haml
index 73f33e69d68..a319b18e507 100644
--- a/app/views/profiles/accounts/show.html.haml
+++ b/app/views/profiles/accounts/show.html.haml
@@ -8,35 +8,17 @@
.row.prepend-top-default
.col-lg-3.profile-settings-sidebar
%h4.prepend-top-0
- = incoming_email_token_enabled? ? "Private Tokens" : "Private Token"
+ Private Tokens
%p
- Keep
- = incoming_email_token_enabled? ? "these tokens" : "this token"
- secret, anyone with access to them can interact with GitLab as if they were you.
+ Keep these tokens secret, anyone with access to them can interact with
+ GitLab as if they were you.
.col-lg-9.private-tokens-reset
- .reset-action
- %p.cgray
- - if current_user.private_token
- = label_tag "private-token", "Private token", class: "label-light"
- = text_field_tag "private-token", current_user.private_token, class: "form-control", readonly: true, onclick: "this.select()"
- - else
- %span You don't have one yet. Click generate to fix it.
- %p.help-block
- Your private token is used to access the API and Atom feeds without username/password authentication.
- .prepend-top-default
- - if current_user.private_token
- = link_to 'Reset private token', reset_private_token_profile_path, method: :put, data: { confirm: "Are you sure?" }, class: "btn btn-default private-token"
- - else
- = f.submit 'Generate', class: "btn btn-default"
+ = render partial: 'reset_token', locals: { label: 'Private token', button_label: 'Reset private token', help_text: 'Your private token is used to access the API and Atom feeds without username/password authentication.' }
+
+ = render partial: 'reset_token', locals: { label: 'RSS token', button_label: 'Reset RSS token', help_text: 'Your RSS token is used to create urls for personalized RSS feeds.' }
+
- if incoming_email_token_enabled?
- .reset-action
- %p.cgray
- = label_tag "incoming-email-token", "Incoming Email Token", class: 'label-light'
- = text_field_tag "incoming-email-token", current_user.incoming_email_token, class: "form-control", readonly: true, onclick: "this.select()"
- %p.help-block
- Your incoming email token is used to create new issues by email, and is included in your project-specific email addresses.
- .prepend-top-default
- = link_to 'Reset incoming email token', reset_incoming_email_token_profile_path, method: :put, data: { confirm: "Are you sure?" }, class: "btn btn-default incoming-email-token"
+ = render partial: 'reset_token', locals: { label: 'Incoming email token', button_label: 'Reset incoming email token', help_text: 'Your incoming email token is used to create new issues by email, and is included in your project-specific email addresses.' }
%hr
.row.prepend-top-default
diff --git a/app/views/profiles/preferences/show.html.haml b/app/views/profiles/preferences/show.html.haml
index 99690e6b98a..0ff19b3eab1 100644
--- a/app/views/profiles/preferences/show.html.haml
+++ b/app/views/profiles/preferences/show.html.haml
@@ -37,9 +37,9 @@
= f.select :dashboard, dashboard_choices, {}, class: 'form-control'
.form-group
= f.label :project_view, class: 'label-light' do
- Project view
+ Project home page content
= f.select :project_view, project_view_choices, {}, class: 'form-control'
.help-block
- Choose what content you want to see on a project's home page.
+ Choose what content you want to see on a project’s home page
.form-group
= f.submit 'Save changes', class: 'btn btn-save'
diff --git a/app/views/projects/_home_panel.html.haml b/app/views/projects/_home_panel.html.haml
index 0fd19780570..9a9fca78df3 100644
--- a/app/views/projects/_home_panel.html.haml
+++ b/app/views/projects/_home_panel.html.haml
@@ -24,7 +24,7 @@
= render 'projects/buttons/fork'
%span.hidden-xs
- - if @project.feature_available?(:repository, current_user)
+ - if can?(current_user, :download_code, @project)
.project-clone-holder
= render "shared/clone_panel"
diff --git a/app/views/projects/ci/builds/_build.html.haml b/app/views/projects/ci/builds/_build.html.haml
index e796920ac82..a190a8760ef 100644
--- a/app/views/projects/ci/builds/_build.html.haml
+++ b/app/views/projects/ci/builds/_build.html.haml
@@ -36,7 +36,7 @@
= icon('warning', class: 'text-warning has-tooltip', title: 'Job is stuck. Check runners.')
- if retried
- = icon('spinner', class: 'text-warning has-tooltip', title: 'Job was retried')
+ = icon('refresh', class: 'text-warning has-tooltip', title: 'Job was retried')
.label-container
- if job.tags.any?
diff --git a/app/views/projects/commit/show.html.haml b/app/views/projects/commit/show.html.haml
index 6051ea2f1ce..3a1be3fa4b6 100644
--- a/app/views/projects/commit/show.html.haml
+++ b/app/views/projects/commit/show.html.haml
@@ -13,7 +13,7 @@
.block-connector
= render "projects/diffs/diffs", diffs: @diffs, environment: @environment
- = render "shared/notes/notes_with_form"
+ = render "shared/notes/notes_with_form", :autocomplete => true
- if can_collaborate_with_project?
- %w(revert cherry-pick).each do |type|
= render "projects/commit/change", type: type, commit: @commit, title: @commit.title
diff --git a/app/views/projects/edit.html.haml b/app/views/projects/edit.html.haml
index dd27e0866de..f5549d7f4cd 100644
--- a/app/views/projects/edit.html.haml
+++ b/app/views/projects/edit.html.haml
@@ -246,14 +246,16 @@
.row.prepend-top-default
.col-lg-3
%h4.prepend-top-0.danger-title
- Transfer project
+ 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
- = form_for([@project.namespace.becomes(Namespace), @project], url: transfer_namespace_project_path(@project.namespace, @project), method: :put, remote: true) do |f|
+ = 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
- %span Namespace
+ %span Select a new namespace
.form-group
- = select_tag :new_namespace_id, namespaces_options(@project.namespace_id), { prompt: 'Choose a project namespace', class: 'select2' }
+ = select_tag :new_namespace_id, namespaces_options(nil), include_blank: true, class: 'select2'
%ul
%li Be careful. Changing the project's namespace can have unintended side effects.
%li You can only transfer the project to namespaces you manage.
diff --git a/app/views/projects/issues/_discussion.html.haml b/app/views/projects/issues/_discussion.html.haml
index 4dfda54feb5..8b095f4ca10 100644
--- a/app/views/projects/issues/_discussion.html.haml
+++ b/app/views/projects/issues/_discussion.html.haml
@@ -1,7 +1,7 @@
- content_for :note_actions do
- if can?(current_user, :update_issue, @issue)
- = link_to 'Reopen issue', issue_path(@issue, issue: {state_event: :reopen}, format: 'json'), data: {no_turbolink: true, original_text: "Reopen issue", alternative_text: "Comment & reopen issue"}, class: "btn btn-nr btn-reopen btn-comment js-note-target-reopen #{issue_button_visibility(@issue, false)}", title: 'Reopen issue'
- = link_to 'Close issue', issue_path(@issue, issue: {state_event: :close}, format: 'json'), data: {no_turbolink: true, original_text: "Close issue", alternative_text: "Comment & close issue"}, class: "btn btn-nr btn-close btn-comment js-note-target-close #{issue_button_visibility(@issue, true)}", title: 'Close issue'
+ = link_to 'Reopen issue', issue_path(@issue, issue: {state_event: :reopen}, format: 'json'), data: {original_text: "Reopen issue", alternative_text: "Comment & reopen issue"}, class: "btn btn-nr btn-reopen btn-comment js-note-target-reopen #{issue_button_visibility(@issue, false)}", title: 'Reopen issue'
+ = link_to 'Close issue', issue_path(@issue, issue: {state_event: :close}, format: 'json'), data: {original_text: "Close issue", alternative_text: "Comment & close issue"}, class: "btn btn-nr btn-close btn-comment js-note-target-close #{issue_button_visibility(@issue, true)}", title: 'Close issue'
#notes
- = render 'shared/notes/notes_with_form'
+ = render 'shared/notes/notes_with_form', :autocomplete => true
diff --git a/app/views/projects/issues/show.html.haml b/app/views/projects/issues/show.html.haml
index 82d8e4d769b..67403c36d7f 100644
--- a/app/views/projects/issues/show.html.haml
+++ b/app/views/projects/issues/show.html.haml
@@ -2,6 +2,8 @@
- page_title "#{@issue.title} (#{@issue.to_reference})", "Issues"
- page_description @issue.description
- page_card_attributes @issue.card_attributes
+- can_update_issue = can?(current_user, :update_issue, @issue)
+- can_report_spam = @issue.submittable_as_spam_by?(current_user)
.clearfix.detail-page-header
.issuable-header
@@ -27,27 +29,29 @@
= icon('caret-down')
.dropdown-menu.dropdown-menu-align-right.hidden-lg
%ul
- %li
- = link_to 'New issue', new_namespace_project_issue_path(@project.namespace, @project), title: 'New issue', id: 'new_issue_link'
- - if can?(current_user, :update_issue, @issue)
+ - if can_update_issue
%li
- = link_to 'Reopen issue', issue_path(@issue, issue: { state_event: :reopen }, format: 'json'), data: {no_turbolink: true}, class: "btn-reopen #{issue_button_visibility(@issue, false)}", title: 'Reopen issue'
+ = link_to 'Edit', edit_namespace_project_issue_path(@project.namespace, @project, @issue)
%li
- = link_to 'Close issue', issue_path(@issue, issue: { state_event: :close }, format: 'json'), data: {no_turbolink: true}, class: "btn-close #{issue_button_visibility(@issue, true)}", title: 'Close issue'
+ = link_to 'Close issue', issue_path(@issue, issue: { state_event: :close }, format: 'json'), class: "btn-close #{issue_button_visibility(@issue, true)}", title: 'Close issue'
%li
- = link_to 'Edit', edit_namespace_project_issue_path(@project.namespace, @project, @issue)
- - if @issue.submittable_as_spam_by?(current_user)
+ = link_to 'Reopen issue', issue_path(@issue, issue: { state_event: :reopen }, format: 'json'), class: "btn-reopen #{issue_button_visibility(@issue, false)}", title: 'Reopen issue'
+ - if can_report_spam
%li
= link_to 'Submit as spam', mark_as_spam_namespace_project_issue_path(@project.namespace, @project, @issue), method: :post, class: 'btn-spam', title: 'Submit as spam'
+ - if can_update_issue || can_report_spam
+ %li.divider
+ %li
+ = link_to 'New issue', new_namespace_project_issue_path(@project.namespace, @project), title: 'New issue', id: 'new_issue_link'
+ - if can_update_issue
+ = link_to 'Edit', edit_namespace_project_issue_path(@project.namespace, @project, @issue), class: 'hidden-xs hidden-sm btn btn-grouped issuable-edit'
+ = link_to 'Close issue', issue_path(@issue, issue: { state_event: :close }, format: 'json'), class: "hidden-xs hidden-sm btn btn-grouped btn-close #{issue_button_visibility(@issue, true)}", title: 'Close issue'
+ = link_to 'Reopen issue', issue_path(@issue, issue: { state_event: :reopen }, format: 'json'), class: "hidden-xs hidden-sm btn btn-grouped btn-reopen #{issue_button_visibility(@issue, false)}", title: 'Reopen issue'
+ - if can_report_spam
+ = link_to 'Submit as spam', mark_as_spam_namespace_project_issue_path(@project.namespace, @project, @issue), method: :post, class: 'hidden-xs hidden-sm btn btn-grouped btn-spam', title: 'Submit as spam'
= link_to new_namespace_project_issue_path(@project.namespace, @project), class: 'hidden-xs hidden-sm btn btn-grouped new-issue-link btn-new btn-inverted', title: 'New issue', id: 'new_issue_link' do
New issue
- - if can?(current_user, :update_issue, @issue)
- = link_to 'Reopen issue', issue_path(@issue, issue: { state_event: :reopen }, format: 'json'), data: {no_turbolink: true}, class: "hidden-xs hidden-sm btn btn-grouped btn-reopen #{issue_button_visibility(@issue, false)}", title: 'Reopen issue'
- = link_to 'Close issue', issue_path(@issue, issue: { state_event: :close }, format: 'json'), data: {no_turbolink: true}, class: "hidden-xs hidden-sm btn btn-grouped btn-close #{issue_button_visibility(@issue, true)}", title: 'Close issue'
- - if @issue.submittable_as_spam_by?(current_user)
- = link_to 'Submit as spam', mark_as_spam_namespace_project_issue_path(@project.namespace, @project, @issue), method: :post, class: 'hidden-xs hidden-sm btn btn-grouped btn-spam', title: 'Submit as spam'
- = link_to 'Edit', edit_namespace_project_issue_path(@project.namespace, @project, @issue), class: 'hidden-xs hidden-sm btn btn-grouped issuable-edit'
.issue-details.issuable-details
.detail-page-description.content-block
diff --git a/app/views/projects/merge_requests/_discussion.html.haml b/app/views/projects/merge_requests/_discussion.html.haml
index 2e6420db212..b787edb3427 100644
--- a/app/views/projects/merge_requests/_discussion.html.haml
+++ b/app/views/projects/merge_requests/_discussion.html.haml
@@ -8,4 +8,4 @@
%button.btn.btn-nr.btn-default.append-right-10.js-comment-resolve-button{ "v-if" => "showButton", type: "submit", data: { project_path: "#{project_path(@merge_request.project)}" } }
{{ buttonText }}
-#notes= render "shared/notes/notes_with_form"
+#notes= render "shared/notes/notes_with_form", :autocomplete => true
diff --git a/app/views/projects/merge_requests/show/_mr_title.html.haml b/app/views/projects/merge_requests/show/_mr_title.html.haml
index e7c5bca6a37..d9428b8562e 100644
--- a/app/views/projects/merge_requests/show/_mr_title.html.haml
+++ b/app/views/projects/merge_requests/show/_mr_title.html.haml
@@ -13,7 +13,7 @@
= icon('angle-double-left')
.issuable-meta
- = issuable_meta(@merge_request, @project, "Merge Request")
+ = issuable_meta(@merge_request, @project, "Merge request")
- if can?(current_user, :update_merge_request, @merge_request)
.issuable-actions
diff --git a/app/views/projects/merge_requests/show/_versions.html.haml b/app/views/projects/merge_requests/show/_versions.html.haml
index 37117bc64a3..0999b95c9c9 100644
--- a/app/views/projects/merge_requests/show/_versions.html.haml
+++ b/app/views/projects/merge_requests/show/_versions.html.haml
@@ -91,7 +91,7 @@
comparing two versions
- else
viewing an old version
- of this merge request.
+ of the diff.
.pull-right
= link_to 'Show latest version', diffs_namespace_project_merge_request_path(@project.namespace, @project, @merge_request), class: 'btn btn-sm'
diff --git a/app/views/projects/pipeline_schedules/_pipeline_schedule.html.haml b/app/views/projects/pipeline_schedules/_pipeline_schedule.html.haml
index 2cd82e1b661..082a6bcbb2a 100644
--- a/app/views/projects/pipeline_schedules/_pipeline_schedule.html.haml
+++ b/app/views/projects/pipeline_schedules/_pipeline_schedule.html.haml
@@ -15,7 +15,7 @@
None
%td.next-run-cell
- if pipeline_schedule.active?
- = time_ago_with_tooltip(pipeline_schedule.next_run_at)
+ = time_ago_with_tooltip(pipeline_schedule.real_next_run)
- else
Inactive
%td
diff --git a/app/views/projects/snippets/show.html.haml b/app/views/projects/snippets/show.html.haml
index aab1c043e66..847f3c2f348 100644
--- a/app/views/projects/snippets/show.html.haml
+++ b/app/views/projects/snippets/show.html.haml
@@ -9,4 +9,4 @@
.row-content-block.top-block.content-component-block
= render 'award_emoji/awards_block', awardable: @snippet, inline: true
- #notes= render "shared/notes/notes_with_form"
+ #notes= render "shared/notes/notes_with_form", :autocomplete => true
diff --git a/app/views/shared/icons/_icon_status_skipped.svg b/app/views/shared/icons/_icon_status_skipped.svg
index 1998dfef9ea..a9ba29c922c 100755
--- a/app/views/shared/icons/_icon_status_skipped.svg
+++ b/app/views/shared/icons/_icon_status_skipped.svg
@@ -1 +1 @@
-<svg width="14" height="14" viewBox="0 0 14 14" xmlns="http://www.w3.org/2000/svg"><g fill-rule="evenodd"><path d="M0 7a7 7 0 1 1 14 0A7 7 0 0 1 0 7z"/><path d="M13 7A6 6 0 1 0 1 7a6 6 0 0 0 12 0z" fill="#FFF"/><path d="M7.69 7.7l-.905.905a.7.7 0 0 0 .99.99l1.85-1.85c.411-.412.411-1.078 0-1.49l-1.85-1.85a.7.7 0 0 0-.99.99l.905.905H4.48a.7.7 0 0 0 0 1.4h3.21z"/></g></svg>
+<svg width="14" height="14" viewBox="0 0 14 14" xmlns="http://www.w3.org/2000/svg"><path d="M7 14A7 7 0 1 1 7 0a7 7 0 0 1 0 14z"/><path d="M7 13A6 6 0 1 0 7 1a6 6 0 0 0 0 12z" fill="#FFF" fill-rule="nonzero"/><path d="M6.415 7.04L4.579 5.203a.295.295 0 0 1 .004-.416l.349-.349a.29.29 0 0 1 .416-.004l2.214 2.214a.289.289 0 0 1 .019.021l.132.133c.11.11.108.291 0 .398L5.341 9.573a.282.282 0 0 1-.398 0l-.331-.331a.285.285 0 0 1 0-.399L6.415 7.04zm2.54 0L7.119 5.203a.295.295 0 0 1 .004-.416l.349-.349a.29.29 0 0 1 .416-.004l2.214 2.214a.289.289 0 0 1 .019.021l.132.133c.11.11.108.291 0 .398L7.881 9.573a.282.282 0 0 1-.398 0l-.331-.331a.285.285 0 0 1 0-.399L8.955 7.04z"/></svg>
diff --git a/app/views/shared/icons/_icon_status_skipped_borderless.svg b/app/views/shared/icons/_icon_status_skipped_borderless.svg
index fb3e930b3cb..3c8a26d7f4d 100644
--- a/app/views/shared/icons/_icon_status_skipped_borderless.svg
+++ b/app/views/shared/icons/_icon_status_skipped_borderless.svg
@@ -1 +1 @@
-<svg width="22px" height="22px" viewBox="0 0 22 22" version="1.1" xmlns="http://www.w3.org/2000/svg"><path d="M12.0846,12.1 L10.6623,13.5223 C10.2454306,13.9539168 10.2513924,14.6399933 10.6756996,15.0643004 C11.1000067,15.4886076 11.7860832,15.4945694 12.2177,15.0777 L15.1261,12.1693 C15.7708612,11.5230891 15.7708612,10.4769109 15.1261,9.8307 L12.2177,6.9223 C11.7860832,6.50543057 11.1000067,6.51139239 10.6756996,6.93569957 C10.2513924,7.36000675 10.2454306,8.04608322 10.6623,8.4777 L12.0846,9.9 L7.04,9.9 C6.43248678,9.9 5.94,10.3924868 5.94,11 C5.94,11.6075132 6.43248678,12.1 7.04,12.1 L12.0846,12.1 L12.0846,12.1 Z" id="Shape"></path></svg>
+<svg width="22" height="22" viewBox="0 0 22 22" xmlns="http://www.w3.org/2000/svg"><path d="M14.072 11.063l-2.82 2.82a.46.46 0 0 0-.001.652l.495.495a.457.457 0 0 0 .653-.001l3.7-3.7a.46.46 0 0 0 .001-.653l-.196-.196a.453.453 0 0 0-.03-.033l-3.479-3.479a.464.464 0 0 0-.654.007l-.548.548a.463.463 0 0 0-.007.654l2.886 2.886z"/><path d="M10.08 11.063l-2.819 2.82a.46.46 0 0 0-.002.652l.496.495a.457.457 0 0 0 .652-.001l3.7-3.7a.46.46 0 0 0 .002-.653l-.196-.196a.453.453 0 0 0-.03-.033l-3.48-3.479a.464.464 0 0 0-.653.007l-.548.548a.463.463 0 0 0-.007.654l2.886 2.886z"/></svg>
diff --git a/app/views/shared/issuable/_search_bar.html.haml b/app/views/shared/issuable/_search_bar.html.haml
index 80974bdb066..d36707dd042 100644
--- a/app/views/shared/issuable/_search_bar.html.haml
+++ b/app/views/shared/issuable/_search_bar.html.haml
@@ -45,7 +45,7 @@
{{hint}}
%span.js-filter-tag.dropdown-light-content
{{tag}}
- #js-dropdown-author.filtered-search-input-dropdown-menu.dropdown-menu{ data: { icon: 'pencil', hint: 'author', tag: '@author' } }
+ #js-dropdown-author.filtered-search-input-dropdown-menu.dropdown-menu
%ul.filter-dropdown{ data: { dynamic: true, dropdown: true } }
%li.filter-dropdown-item
%button.btn.btn-link.dropdown-user
@@ -55,7 +55,7 @@
{{name}}
%span.dropdown-light-content
@{{username}}
- #js-dropdown-assignee.filtered-search-input-dropdown-menu.dropdown-menu{ data: { icon: 'user', hint: 'assignee', tag: '@assignee' } }
+ #js-dropdown-assignee.filtered-search-input-dropdown-menu.dropdown-menu
%ul{ data: { dropdown: true } }
%li.filter-dropdown-item{ data: { value: 'none' } }
%button.btn.btn-link
@@ -70,7 +70,7 @@
{{name}}
%span.dropdown-light-content
@{{username}}
- #js-dropdown-milestone.filtered-search-input-dropdown-menu.dropdown-menu{ data: { icon: 'clock-o', hint: 'milestone', tag: '%milestone' } }
+ #js-dropdown-milestone.filtered-search-input-dropdown-menu.dropdown-menu
%ul{ data: { dropdown: true } }
%li.filter-dropdown-item{ data: { value: 'none' } }
%button.btn.btn-link
@@ -86,7 +86,7 @@
%li.filter-dropdown-item
%button.btn.btn-link.js-data-value
{{title}}
- #js-dropdown-label.filtered-search-input-dropdown-menu.dropdown-menu{ data: { icon: 'tag', hint: 'label', tag: '~label', type: 'array' } }
+ #js-dropdown-label.filtered-search-input-dropdown-menu.dropdown-menu
%ul{ data: { dropdown: true } }
%li.filter-dropdown-item{ data: { value: 'none' } }
%button.btn.btn-link
diff --git a/app/views/shared/issuable/_sidebar_assignees.html.haml b/app/views/shared/issuable/_sidebar_assignees.html.haml
index e9ce7b7ce9c..26567c08eb6 100644
--- a/app/views/shared/issuable/_sidebar_assignees.html.haml
+++ b/app/views/shared/issuable/_sidebar_assignees.html.haml
@@ -1,5 +1,8 @@
- if issuable.is_a?(Issue)
#js-vue-sidebar-assignees{ data: { field: "#{issuable.to_ability_name}[assignee_ids]" } }
+ .title.hide-collapsed
+ Assignee
+ = icon('spinner spin')
- else
.sidebar-collapsed-icon.sidebar-collapsed-user{ data: { toggle: "tooltip", placement: "left", container: "body" }, title: (issuable.assignee.name if issuable.assignee) }
- if issuable.assignee
diff --git a/app/views/shared/notes/_notes_with_form.html.haml b/app/views/shared/notes/_notes_with_form.html.haml
index 05bb1970e21..785b1b22a49 100644
--- a/app/views/shared/notes/_notes_with_form.html.haml
+++ b/app/views/shared/notes/_notes_with_form.html.haml
@@ -23,4 +23,4 @@
to post a comment
:javascript
- var notes = new Notes("#{notes_url}", #{@notes.map(&:id).to_json}, #{Time.now.to_i}, "#{diff_view}", false)
+ var notes = new Notes("#{notes_url}", #{@notes.map(&:id).to_json}, #{Time.now.to_i}, "#{diff_view}", #{autocomplete})
diff --git a/app/views/snippets/show.html.haml b/app/views/snippets/show.html.haml
index 51dbbc32cc9..216184eb839 100644
--- a/app/views/snippets/show.html.haml
+++ b/app/views/snippets/show.html.haml
@@ -9,4 +9,4 @@
.row-content-block.top-block.content-component-block
= render 'award_emoji/awards_block', awardable: @snippet, inline: true
- #notes= render "shared/notes/notes_with_form"
+ #notes= render "shared/notes/notes_with_form", :autocomplete => false
diff --git a/app/views/users/show.html.haml b/app/views/users/show.html.haml
index 2b70d70e360..c587155bc4f 100644
--- a/app/views/users/show.html.haml
+++ b/app/views/users/show.html.haml
@@ -71,7 +71,7 @@
= @user.location
- unless @user.organization.blank?
.profile-link-holder.middle-dot-divider
- = icon('building')
+ = icon('briefcase')
= @user.organization
- if @user.bio.present?
diff --git a/app/workers/expire_job_cache_worker.rb b/app/workers/expire_job_cache_worker.rb
new file mode 100644
index 00000000000..08e281e7350
--- /dev/null
+++ b/app/workers/expire_job_cache_worker.rb
@@ -0,0 +1,35 @@
+class ExpireJobCacheWorker
+ include Sidekiq::Worker
+ include BuildQueue
+
+ def perform(job_id)
+ job = CommitStatus.joins(:pipeline, :project).find_by(id: job_id)
+ return unless job
+
+ pipeline = job.pipeline
+ project = job.project
+
+ Gitlab::EtagCaching::Store.new.tap do |store|
+ store.touch(project_pipeline_path(project, pipeline))
+ store.touch(project_job_path(project, job))
+ end
+ end
+
+ private
+
+ def project_pipeline_path(project, pipeline)
+ Gitlab::Routing.url_helpers.namespace_project_pipeline_path(
+ project.namespace,
+ project,
+ pipeline,
+ format: :json)
+ end
+
+ def project_job_path(project, job)
+ Gitlab::Routing.url_helpers.namespace_project_build_path(
+ project.namespace,
+ project,
+ job.id,
+ format: :json)
+ end
+end
diff --git a/app/workers/expire_pipeline_cache_worker.rb b/app/workers/expire_pipeline_cache_worker.rb
index 603e2f1aaea..d760f5b140f 100644
--- a/app/workers/expire_pipeline_cache_worker.rb
+++ b/app/workers/expire_pipeline_cache_worker.rb
@@ -10,6 +10,7 @@ class ExpirePipelineCacheWorker
store = Gitlab::EtagCaching::Store.new
store.touch(project_pipelines_path(project))
+ store.touch(project_pipeline_path(project, pipeline))
store.touch(commit_pipelines_path(project, pipeline.commit)) if pipeline.commit
store.touch(new_merge_request_pipelines_path(project))
each_pipelines_merge_request_path(project, pipeline) do |path|
@@ -28,6 +29,14 @@ class ExpirePipelineCacheWorker
format: :json)
end
+ def project_pipeline_path(project, pipeline)
+ Gitlab::Routing.url_helpers.namespace_project_pipeline_path(
+ project.namespace,
+ project,
+ pipeline,
+ format: :json)
+ end
+
def commit_pipelines_path(project, commit)
Gitlab::Routing.url_helpers.pipelines_namespace_project_commit_path(
project.namespace,
diff --git a/changelogs/unreleased/17489-hide-code-from-guests.yml b/changelogs/unreleased/17489-hide-code-from-guests.yml
new file mode 100644
index 00000000000..eb6daffedfe
--- /dev/null
+++ b/changelogs/unreleased/17489-hide-code-from-guests.yml
@@ -0,0 +1,4 @@
+---
+title: Hide clone panel and file list when user is only a guest
+merge_request:
+author: James Clark
diff --git a/changelogs/unreleased/18927-reorder-issue-action-buttons.yml b/changelogs/unreleased/18927-reorder-issue-action-buttons.yml
new file mode 100644
index 00000000000..793d6582940
--- /dev/null
+++ b/changelogs/unreleased/18927-reorder-issue-action-buttons.yml
@@ -0,0 +1,4 @@
+---
+title: Reorder Issue action buttons in order of usability
+merge_request: 11642
+author:
diff --git a/changelogs/unreleased/29852-latex-formatting.yml b/changelogs/unreleased/29852-latex-formatting.yml
new file mode 100644
index 00000000000..e96cda1d6b3
--- /dev/null
+++ b/changelogs/unreleased/29852-latex-formatting.yml
@@ -0,0 +1,4 @@
+---
+title: Fix LaTeX formatting for AsciiDoc wiki
+merge_request: 11212
+author:
diff --git a/changelogs/unreleased/32418-make-link-to-self-less-obvious.yml b/changelogs/unreleased/32418-make-link-to-self-less-obvious.yml
new file mode 100644
index 00000000000..aabe87dac0f
--- /dev/null
+++ b/changelogs/unreleased/32418-make-link-to-self-less-obvious.yml
@@ -0,0 +1,4 @@
+---
+title: Change links in issuable meta to black
+merge_request:
+author:
diff --git a/changelogs/unreleased/32682-skipped-ci-icon.yml b/changelogs/unreleased/32682-skipped-ci-icon.yml
new file mode 100644
index 00000000000..ad498b51900
--- /dev/null
+++ b/changelogs/unreleased/32682-skipped-ci-icon.yml
@@ -0,0 +1,4 @@
+---
+title: Adds new icon for CI skipped status
+merge_request:
+author:
diff --git a/changelogs/unreleased/32715-fix-note-padding.yml b/changelogs/unreleased/32715-fix-note-padding.yml
new file mode 100644
index 00000000000..867ed7eb171
--- /dev/null
+++ b/changelogs/unreleased/32715-fix-note-padding.yml
@@ -0,0 +1,4 @@
+---
+title: Make all notes use equal padding
+merge_request:
+author:
diff --git a/changelogs/unreleased/32799-remove-no_turbolink-attribute-from-haml.yml b/changelogs/unreleased/32799-remove-no_turbolink-attribute-from-haml.yml
new file mode 100644
index 00000000000..9c1c1fe77f2
--- /dev/null
+++ b/changelogs/unreleased/32799-remove-no_turbolink-attribute-from-haml.yml
@@ -0,0 +1,4 @@
+---
+title: Remove redundant data-turbolink attributes from links
+merge_request: 11672
+author: blackst0ne
diff --git a/changelogs/unreleased/32807-company-icon.yml b/changelogs/unreleased/32807-company-icon.yml
new file mode 100644
index 00000000000..718108d3733
--- /dev/null
+++ b/changelogs/unreleased/32807-company-icon.yml
@@ -0,0 +1,4 @@
+---
+title: Use briefcase icon for company in profile page
+merge_request:
+author:
diff --git a/changelogs/unreleased/add-unicode-trace-feature-test.yml b/changelogs/unreleased/add-unicode-trace-feature-test.yml
new file mode 100644
index 00000000000..90c6a9afefc
--- /dev/null
+++ b/changelogs/unreleased/add-unicode-trace-feature-test.yml
@@ -0,0 +1,4 @@
+---
+title: Add a feature test for Unicode trace
+merge_request: 10736
+author: dosuken123
diff --git a/changelogs/unreleased/dm-copy-as-gfm-without-empty-elements.yml b/changelogs/unreleased/dm-copy-as-gfm-without-empty-elements.yml
new file mode 100644
index 00000000000..45a61320ff2
--- /dev/null
+++ b/changelogs/unreleased/dm-copy-as-gfm-without-empty-elements.yml
@@ -0,0 +1,4 @@
+---
+title: Don't copy empty elements that were not selected on purpose as GFM
+merge_request:
+author:
diff --git a/changelogs/unreleased/dm-copy-gfm-when-parts-of-other-elements-are-selected.yml b/changelogs/unreleased/dm-copy-gfm-when-parts-of-other-elements-are-selected.yml
new file mode 100644
index 00000000000..ae916c30ff8
--- /dev/null
+++ b/changelogs/unreleased/dm-copy-gfm-when-parts-of-other-elements-are-selected.yml
@@ -0,0 +1,4 @@
+---
+title: Copy as GFM even when parts of other elements are selected
+merge_request:
+author:
diff --git a/changelogs/unreleased/dm-outdated-system-note.yml b/changelogs/unreleased/dm-outdated-system-note.yml
new file mode 100644
index 00000000000..a1038a1051b
--- /dev/null
+++ b/changelogs/unreleased/dm-outdated-system-note.yml
@@ -0,0 +1,4 @@
+---
+title: Add system note with link to diff comparison when MR discussion becomes outdated
+merge_request:
+author:
diff --git a/changelogs/unreleased/dm-paste-code-inside-gfm-code.yml b/changelogs/unreleased/dm-paste-code-inside-gfm-code.yml
new file mode 100644
index 00000000000..d078ca449a5
--- /dev/null
+++ b/changelogs/unreleased/dm-paste-code-inside-gfm-code.yml
@@ -0,0 +1,4 @@
+---
+title: Don't wrap pasted code when it's already inside code tags
+merge_request:
+author:
diff --git a/changelogs/unreleased/feature-rss-scoped-token.yml b/changelogs/unreleased/feature-rss-scoped-token.yml
new file mode 100644
index 00000000000..740d8778be2
--- /dev/null
+++ b/changelogs/unreleased/feature-rss-scoped-token.yml
@@ -0,0 +1,4 @@
+---
+title: Expose atom links with an RSS token instead of using the private token
+merge_request: 11647
+author: Alexis Reigel
diff --git a/changelogs/unreleased/mrchrisw-catch-openssl.yml b/changelogs/unreleased/mrchrisw-catch-openssl.yml
new file mode 100644
index 00000000000..a8b433fb0cd
--- /dev/null
+++ b/changelogs/unreleased/mrchrisw-catch-openssl.yml
@@ -0,0 +1,4 @@
+---
+title: Rescue OpenSSL::SSL::SSLError in JiraService & IssueTrackerService
+merge_request:
+author:
diff --git a/changelogs/unreleased/prevent-project-transfer.yml b/changelogs/unreleased/prevent-project-transfer.yml
new file mode 100644
index 00000000000..a5c74676aab
--- /dev/null
+++ b/changelogs/unreleased/prevent-project-transfer.yml
@@ -0,0 +1,4 @@
+---
+title: Prevent project transfers if a new group is not selected
+merge_request:
+author:
diff --git a/changelogs/unreleased/wait-for-ajax-handling-all-js-requests.yml b/changelogs/unreleased/wait-for-ajax-handling-all-js-requests.yml
new file mode 100644
index 00000000000..14aebe792c2
--- /dev/null
+++ b/changelogs/unreleased/wait-for-ajax-handling-all-js-requests.yml
@@ -0,0 +1,4 @@
+---
+title: Use wait_for_requests for both ajax and Vue requests
+merge_request:
+author:
diff --git a/changelogs/unreleased/zj-fix-pipeline-etag.yml b/changelogs/unreleased/zj-fix-pipeline-etag.yml
new file mode 100644
index 00000000000..03ebef8c575
--- /dev/null
+++ b/changelogs/unreleased/zj-fix-pipeline-etag.yml
@@ -0,0 +1,4 @@
+---
+title: Fix issue where real time pipelines were not cached
+merge_request: 11615
+author:
diff --git a/changelogs/unreleased/zj-sort-env-folders.yml b/changelogs/unreleased/zj-sort-env-folders.yml
new file mode 100644
index 00000000000..b3ca97aef94
--- /dev/null
+++ b/changelogs/unreleased/zj-sort-env-folders.yml
@@ -0,0 +1,4 @@
+---
+title: Sort folder for environments
+merge_request:
+author:
diff --git a/config/application.rb b/config/application.rb
index 95ba6774916..b0533759252 100644
--- a/config/application.rb
+++ b/config/application.rb
@@ -65,6 +65,7 @@ module Gitlab
hook
import_url
incoming_email_token
+ rss_token
key
otp_attempt
password
diff --git a/config/gitlab.yml.example b/config/gitlab.yml.example
index 14d99c243fc..a727f7e2fa3 100644
--- a/config/gitlab.yml.example
+++ b/config/gitlab.yml.example
@@ -182,7 +182,7 @@ production: &base
cron: "0 * * * *"
# Execute scheduled triggers
pipeline_schedule_worker:
- cron: "0 */12 * * *"
+ cron: "19 * * * *"
# Remove expired build artifacts
expire_build_artifacts_worker:
cron: "50 * * * *"
diff --git a/config/routes/admin.rb b/config/routes/admin.rb
index 48993420ed9..b1b6ef33a47 100644
--- a/config/routes/admin.rb
+++ b/config/routes/admin.rb
@@ -68,7 +68,9 @@ namespace :admin do
resources :projects, only: [:index]
- scope(path: 'projects/*namespace_id', as: :namespace) do
+ scope(path: 'projects/*namespace_id',
+ as: :namespace,
+ constraints: { namespace_id: Gitlab::Regex.namespace_route_regex }) do
resources(:projects,
path: '/',
constraints: { id: Gitlab::Regex.project_route_regex },
diff --git a/config/routes/git_http.rb b/config/routes/git_http.rb
index 42d874eeebc..cdf658c3e4a 100644
--- a/config/routes/git_http.rb
+++ b/config/routes/git_http.rb
@@ -1,4 +1,6 @@
-scope(path: '*namespace_id/:project_id', constraints: { format: nil }) do
+scope(path: '*namespace_id/:project_id',
+ format: nil,
+ constraints: { namespace_id: Gitlab::Regex.namespace_route_regex }) do
scope(constraints: { project_id: Gitlab::Regex.project_git_route_regex }, module: :projects) do
# Git HTTP clients ('git clone' etc.)
scope(controller: :git_http) do
diff --git a/config/routes/profile.rb b/config/routes/profile.rb
index 07c341999ea..3dc890e5785 100644
--- a/config/routes/profile.rb
+++ b/config/routes/profile.rb
@@ -5,6 +5,7 @@ resource :profile, only: [:show, :update] do
put :reset_private_token
put :reset_incoming_email_token
+ put :reset_rss_token
put :update_username
end
diff --git a/config/routes/project.rb b/config/routes/project.rb
index 01b94f9f2b8..9fe8372edf9 100644
--- a/config/routes/project.rb
+++ b/config/routes/project.rb
@@ -5,7 +5,22 @@ resources :projects, only: [:index, :new, :create]
draw :git_http
constraints(ProjectUrlConstrainer.new) do
- scope(path: '*namespace_id', as: :namespace) do
+ # If the route has a wildcard segment, the segment has a regex constraint,
+ # the segment is potentially followed by _another_ wildcard segment, and
+ # the `format` option is not set to false, we need to specify that
+ # regex constraint _outside_ of `constraints: {}`.
+ #
+ # Otherwise, Rails will overwrite the constraint with `/.+?/`,
+ # which breaks some of our wildcard routes like `/blob/*id`
+ # and `/tree/*id` that depend on the negative lookahead inside
+ # `Gitlab::Regex.namespace_route_regex`, which helps the router
+ # determine whether a certain path segment is part of `*namespace_id`,
+ # `:project_id`, or `*id`.
+ #
+ # See https://github.com/rails/rails/blob/v4.2.8/actionpack/lib/action_dispatch/routing/mapper.rb#L155
+ scope(path: '*namespace_id',
+ as: :namespace,
+ namespace_id: Gitlab::Regex.namespace_route_regex) do
scope(path: ':project_id',
constraints: { project_id: Gitlab::Regex.project_route_regex },
module: :projects,
diff --git a/config/routes/user.rb b/config/routes/user.rb
index b064a15e802..0f3bec9cf58 100644
--- a/config/routes/user.rb
+++ b/config/routes/user.rb
@@ -13,17 +13,17 @@ end
constraints(UserUrlConstrainer.new) do
# Get all keys of user
- get ':username.keys' => 'profiles/keys#get_keys', constraints: { username: Gitlab::Regex.namespace_route_regex }
+ get ':username.keys' => 'profiles/keys#get_keys', constraints: { username: Gitlab::Regex.root_namespace_route_regex }
scope(path: ':username',
as: :user,
- constraints: { username: Gitlab::Regex.namespace_route_regex },
+ constraints: { username: Gitlab::Regex.root_namespace_route_regex },
controller: :users) do
get '/', action: :show
end
end
-scope(constraints: { username: Gitlab::Regex.namespace_route_regex }) do
+scope(constraints: { username: Gitlab::Regex.root_namespace_route_regex }) do
scope(path: 'users/:username',
as: :user,
controller: :users) do
diff --git a/config/webpack.config.js b/config/webpack.config.js
index 7bc225968de..42024739fe9 100644
--- a/config/webpack.config.js
+++ b/config/webpack.config.js
@@ -126,10 +126,8 @@ var config = {
jQuery: 'jquery',
}),
- // use deterministic module ids in all environments
- IS_PRODUCTION ?
- new webpack.HashedModuleIdsPlugin() :
- new webpack.NamedModulesPlugin(),
+ // use deterministic module ids
+ new webpack.NamedModulesPlugin(),
// create cacheable common library bundle for all vue chunks
new webpack.optimize.CommonsChunkPlugin({
diff --git a/db/migrate/20170521184006_add_change_position_to_notes.rb b/db/migrate/20170521184006_add_change_position_to_notes.rb
new file mode 100644
index 00000000000..219ed1ade4c
--- /dev/null
+++ b/db/migrate/20170521184006_add_change_position_to_notes.rb
@@ -0,0 +1,13 @@
+# See http://doc.gitlab.com/ce/development/migration_style_guide.html
+# for more information on how to write migrations for GitLab.
+
+class AddChangePositionToNotes < ActiveRecord::Migration
+ include Gitlab::Database::MigrationHelpers
+
+ # Set this constant to true if this migration requires downtime.
+ DOWNTIME = false
+
+ def change
+ add_column :notes, :change_position, :text
+ end
+end
diff --git a/db/migrate/20170523091700_add_rss_token_to_users.rb b/db/migrate/20170523091700_add_rss_token_to_users.rb
new file mode 100644
index 00000000000..06a85f6ac3d
--- /dev/null
+++ b/db/migrate/20170523091700_add_rss_token_to_users.rb
@@ -0,0 +1,19 @@
+class AddRssTokenToUsers < ActiveRecord::Migration
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+
+ disable_ddl_transaction!
+
+ def up
+ add_column :users, :rss_token, :string
+
+ add_concurrent_index :users, :rss_token
+ end
+
+ def down
+ remove_concurrent_index :users, :rss_token if index_exists? :users, :rss_token
+
+ remove_column :users, :rss_token
+ end
+end
diff --git a/db/post_migrate/20170503004427_upate_retried_for_ci_build.rb b/db/post_migrate/20170503004427_update_retried_for_ci_build.rb
index 738e46b9207..705e11ed47d 100644
--- a/db/post_migrate/20170503004427_upate_retried_for_ci_build.rb
+++ b/db/post_migrate/20170503004427_update_retried_for_ci_build.rb
@@ -1,4 +1,4 @@
-class UpateRetriedForCiBuild < ActiveRecord::Migration
+class UpdateRetriedForCiBuild < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
@@ -54,13 +54,15 @@ class UpateRetriedForCiBuild < ActiveRecord::Migration
def with_temporary_partial_index
if Gitlab::Database.postgresql?
- execute 'CREATE INDEX CONCURRENTLY IF NOT EXISTS index_for_ci_builds_retried_migration ON ci_builds (id) WHERE retried IS NULL;'
+ unless index_exists?(:ci_builds, name: :index_for_ci_builds_retried_migration)
+ execute 'CREATE INDEX CONCURRENTLY index_for_ci_builds_retried_migration ON ci_builds (id) WHERE retried IS NULL;'
+ end
end
yield
- if Gitlab::Database.postgresql?
- execute 'DROP INDEX CONCURRENTLY IF EXISTS index_for_ci_builds_retried_migration'
+ if Gitlab::Database.postgresql? && index_exists?(:ci_builds, name: :index_for_ci_builds_retried_migration)
+ execute 'DROP INDEX CONCURRENTLY index_for_ci_builds_retried_migration'
end
end
end
diff --git a/db/schema.rb b/db/schema.rb
index d14126401c9..ca33c8cc2a2 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: 20170518231126) do
+ActiveRecord::Schema.define(version: 20170523091700) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
@@ -794,6 +794,7 @@ ActiveRecord::Schema.define(version: 20170518231126) do
t.string "discussion_id"
t.text "note_html"
t.integer "cached_markdown_version"
+ t.text "change_position"
end
add_index "notes", ["author_id"], name: "index_notes_on_author_id", using: :btree
@@ -1361,6 +1362,7 @@ ActiveRecord::Schema.define(version: 20170518231126) do
t.date "last_activity_on"
t.boolean "notified_of_own_activity"
t.string "preferred_language"
+ t.string "rss_token"
end
add_index "users", ["admin"], name: "index_users_on_admin", using: :btree
@@ -1374,6 +1376,7 @@ ActiveRecord::Schema.define(version: 20170518231126) do
add_index "users", ["name"], name: "index_users_on_name", using: :btree
add_index "users", ["name"], name: "index_users_on_name_trigram", using: :gin, opclasses: {"name"=>"gin_trgm_ops"}
add_index "users", ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true, using: :btree
+ add_index "users", ["rss_token"], name: "index_users_on_rss_token", using: :btree
add_index "users", ["state"], name: "index_users_on_state", using: :btree
add_index "users", ["username"], name: "index_users_on_username", using: :btree
add_index "users", ["username"], name: "index_users_on_username_trigram", using: :gin, opclasses: {"username"=>"gin_trgm_ops"}
diff --git a/doc/README.md b/doc/README.md
index 7bab42bc135..9f12eed1471 100644
--- a/doc/README.md
+++ b/doc/README.md
@@ -42,6 +42,9 @@ Shortcuts to GitLab's most visited docs:
- [Create a group](gitlab-basics/create-group.md)
- [GitLab Subgroups](user/group/subgroups/index.md)
- [Search through GitLab](user/search/index.md): Search for issues, merge requests, projects, groups, todos, and issues in Issue Boards.
+- [Snippets](user/snippets.md): Snippets allow you to create little bits of code.
+- [Wikis](user/project/wiki/index.md): Enhance your repository documentation with built-in wikis.
+- [GitLab Pages](user/project/pages/index.md): Build, test, and deploy your static website with GitLab Pages.
### Repository
@@ -83,14 +86,6 @@ Manage files and branches from the UI (user interface):
- [Importing to GitLab](workflow/importing/README.md): Import your projects from GitHub, Bitbucket, GitLab.com, FogBugz and SVN into GitLab.
- [Migrating from SVN](workflow/importing/migrating_from_svn.md): Convert a SVN repository to Git and GitLab.
-## GitLab's superpowers
-
-Take a step ahead and dive into GitLab's advanced features.
-
-- [GitLab Pages](user/project/pages/index.md): Build, test, and deploy your static website with GitLab Pages.
-- [Snippets](user/snippets.md): Snippets allow you to create little bits of code.
-- [Wikis](user/project/wiki/index.md): Enhance your repository documentation with built-in wikis.
-
### Continuous Integration, Delivery, and Deployment
- [GitLab CI](ci/README.md): Explore the features and capabilities of Continuous Integration, Continuous Delivery, and Continuous Deployment with GitLab.
diff --git a/doc/install/kubernetes/gitlab_chart.md b/doc/install/kubernetes/gitlab_chart.md
index 2d7edbe16e4..b4ffd57afbb 100644
--- a/doc/install/kubernetes/gitlab_chart.md
+++ b/doc/install/kubernetes/gitlab_chart.md
@@ -1,4 +1,7 @@
# GitLab Helm Chart
+> Officially supported cloud providers are Google Container Service and Azure Container Service.
+
+> Officially supported schedulers are Kubernetes and Terraform.
The `gitlab` Helm chart deploys GitLab into your Kubernetes cluster.
@@ -14,7 +17,7 @@ This chart includes the following:
## Prerequisites
-- _At least_ 3 GB of RAM available on your cluster, in chunks of 1 GB
+- _At least_ 3 GB of RAM available on your cluster, in chunks of 1 GB. 41GB of storage and 2 CPU are also required.
- Kubernetes 1.4+ with Beta APIs enabled
- [Persistent Volume](https://kubernetes.io/docs/concepts/storage/persistent-volumes/) provisioner support in the underlying infrastructure
- The ability to point a DNS entry or URL at your GitLab install
@@ -203,9 +206,43 @@ its class in an annotation.
>**Note:**
The Ingress alone doesn't expose GitLab externally. You need to have a Ingress controller setup to do that.
-Setting up an Ingress controller can be as simple as installing the `nginx-ingress` helm chart. But be sure
+Setting up an Ingress controller can be done by installing the `nginx-ingress` helm chart. But be sure
to read the [documentation](https://github.com/kubernetes/charts/blob/master/stable/nginx-ingress/README.md)
+#### Preserving Source IPs
+
+If you are using the `LoadBalancer` serviceType you may run into issues where user IP addresses in the GitLab
+logs, and used in abuse throttling are not accurate. This is due to how Kubernetes uses source NATing on cluster nodes without endpoints.
+
+See the [Kubernetes documentation](https://kubernetes.io/docs/tutorials/services/source-ip/#source-ip-for-services-with-typeloadbalancer) for more information.
+
+To fix this you can add the following service annotation to your `values.yaml`
+
+```yaml
+## For minikube, set this to NodePort, elsewhere use LoadBalancer
+## ref: http://kubernetes.io/docs/user-guide/services/#publishing-services---service-types
+##
+serviceType: LoadBalancer
+
+## Optional annotations for gitlab service.
+serviceAnnotations:
+ service.beta.kubernetes.io/external-traffic: "OnlyLocal"
+```
+
+>**Note:**
+If you are using the ingress routing, you will likely also need to specify the annotation on the service for the ingress
+controller. For `nginx-ingress` you can check the
+[configuration documentation](https://github.com/kubernetes/charts/blob/master/stable/nginx-ingress/README.md#configuration)
+on how to add the annotation to the `controller.service.annotations` array.
+
+>**Note:**
+When using the `nginx-ingress` controller on Google Container Engine (GKE), and using the `external-traffic` annotation,
+you will need to additionally set the `controller.kind` to be DaemonSet. Otherwise only pods running on the same node
+as the nginx controller will be able to reach GitLab. This may result in pods within your cluster not being able to reach GitLab.
+See the [Kubernetes documentation](https://kubernetes.io/docs/tutorials/services/source-ip/#source-ip-for-services-with-typeloadbalancer) and
+[nginx-ingress configuration documentation](https://github.com/kubernetes/charts/blob/master/stable/nginx-ingress/README.md#configuration)
+for more information.
+
### External database
You can configure the GitLab Helm chart to connect to an external PostgreSQL
@@ -387,6 +424,7 @@ ingress:
```
## Installing GitLab using the Helm Chart
+> You may see a temporary error message `SchedulerPredicates failed due to PersistentVolumeClaim is not bound` while storage provisions. Once the storage provisions, the pods will automatically restart. This may take a couple minutes depending on your cloud provider. If the error persists, please review the [prerequisites](#prerequisites) to ensure you have enough RAM, CPU, and storage.
Once you [have configured](#configuration) GitLab in your `values.yml` file,
run the following:
diff --git a/doc/install/kubernetes/gitlab_runner_chart.md b/doc/install/kubernetes/gitlab_runner_chart.md
index dbd9ae3f70c..305b4593c73 100644
--- a/doc/install/kubernetes/gitlab_runner_chart.md
+++ b/doc/install/kubernetes/gitlab_runner_chart.md
@@ -1,4 +1,7 @@
# GitLab Runner Helm Chart
+> Officially supported cloud providers are Google Container Service and Azure Container Service.
+
+> Officially supported schedulers are Kubernetes and Terraform.
The `gitlab-runner` Helm chart deploys a GitLab Runner instance into your
Kubernetes cluster.
diff --git a/doc/install/kubernetes/index.md b/doc/install/kubernetes/index.md
index cae5837a12b..88c56a1d17c 100644
--- a/doc/install/kubernetes/index.md
+++ b/doc/install/kubernetes/index.md
@@ -1,5 +1,6 @@
-# Installing GitLab in Kubernetes
+# Installing GitLab on Kubernetes
> Officially supported cloud providers are Google Container Service and Azure Container Service.
+
> Officially supported schedulers are Kubernetes and Terraform.
The easiest method to deploy GitLab in [Kubernetes](https://kubernetes.io/) is
diff --git a/doc/integration/github.md b/doc/integration/github.md
index de9aedbc596..b0d67db8b59 100644
--- a/doc/integration/github.md
+++ b/doc/integration/github.md
@@ -114,7 +114,8 @@ If everything goes well the user will be returned to GitLab and will be signed i
If you are attempting to import projects from GitHub Enterprise with a self-signed
certificate and the imports are failing, you will need to disable SSL verification.
-It should be disabled by adding `verify_ssl` to `false` to the provider configuration.
+It should be disabled by adding `verify_ssl` to `false` in the provider configuration
+and changing the global Git `sslVerify` option to `false` in the GitLab server.
For omnibus package:
@@ -131,6 +132,12 @@ For omnibus package:
]
```
+You will also need to disable Git SSL verification on the server hosting GitLab.
+
+```ruby
+omnibus_gitconfig['system'] = { "http" => ["sslVerify = false"] }
+```
+
For installation from source:
```
@@ -141,16 +148,14 @@ For installation from source:
args: { scope: 'user:email' } }
```
-
-For the changes to take effect, [reconfigure Gitlab] if you installed
-via Omnibus, or [restart GitLab] if you installed from source.
-
-You will also need to disable Git SSL verification on the server hosting GitLab with the following command:
+You will also need to disable Git SSL verification on the server hosting GitLab.
```
$ git config --global http.sslVerify false
```
-[reconfigure GitLab]: ../administration/restart_gitlab.md#omnibus-gitlab-reconfigure
-[restart GitLab]: ../administration/restart_gitlab.md#installations-from-source
+For the changes to take effect, [reconfigure Gitlab] if you installed
+via Omnibus, or [restart GitLab] if you installed from source.
+[reconfigure GitLab]: ../administration/restart_gitlab.md#omnibus-gitlab-reconfigure
+[restart GitLab]: ../administration/restart_gitlab.md#installations-from-source
diff --git a/doc/update/9.0-to-9.1.md b/doc/update/9.0-to-9.1.md
index 2b582d4eefd..2d597894517 100644
--- a/doc/update/9.0-to-9.1.md
+++ b/doc/update/9.0-to-9.1.md
@@ -104,7 +104,6 @@ cd /home/git/gitlab-shell
sudo -u git -H git fetch --all --tags
sudo -u git -H git checkout v$(</home/git/gitlab/GITLAB_SHELL_VERSION)
-sudo -u git -H bin/compile
```
### 7. Update gitlab-workhorse
diff --git a/doc/user/group/subgroups/index.md b/doc/user/group/subgroups/index.md
index a4726673fc4..151c17f3bf1 100644
--- a/doc/user/group/subgroups/index.md
+++ b/doc/user/group/subgroups/index.md
@@ -71,9 +71,9 @@ structure.
- You need to be an Owner of a group in order to be able to create
a subgroup. For more information check the [permissions table][permissions].
- For a list of words that are not allowed to be used as group names see the
- [`dynamic_path_validator.rb` file][reserved] under the `TOP_LEVEL_ROUTES`, `WILDCARD_ROUTES` and `GROUP_ROUTES` lists:
+ [`regex.rb` file][reserved] under the `TOP_LEVEL_ROUTES`, `PROJECT_WILDCARD_ROUTES` and `GROUP_ROUTES` lists:
- `TOP_LEVEL_ROUTES`: are names that are reserved as usernames or top level groups
- - `WILDCARD_ROUTES`: are names that are reserved for child groups or projects.
+ - `PROJECT_WILDCARD_ROUTES`: are names that are reserved for child groups or projects.
- `GROUP_ROUTES`: are names that are reserved for all groups or projects.
To create a subgroup:
@@ -163,4 +163,4 @@ Here's a list of what you can't do with subgroups:
[ce-2772]: https://gitlab.com/gitlab-org/gitlab-ce/issues/2772
[permissions]: ../../permissions.md#group
-[reserved]: https://gitlab.com/gitlab-org/gitlab-ce/blob/master/app/validators/dynamic_path_validator.rb
+[reserved]: https://gitlab.com/gitlab-org/gitlab-ce/blob/master/lib/gitlab/regex.rb
diff --git a/doc/user/profile/preferences.md b/doc/user/profile/preferences.md
index e5038835027..f2ad42f21fd 100644
--- a/doc/user/profile/preferences.md
+++ b/doc/user/profile/preferences.md
@@ -50,15 +50,15 @@ You have 6 options here that you can use for your default dashboard view:
- Your groups
- Your [Todos]
-### Default project view
+### Project home page content
-The default project view settings allows you to choose what content you want to
-see on a project's landing page.
+The project home page content setting allows you to choose what content you want to
+see on a project’s home page.
You can choose between 2 options:
- Show the files and the readme (default)
-- Show the project's activity
+- Show the project’s activity
[rouge]: http://rouge.jneen.net/ "Rouge website"
[todos]: ../../workflow/todos.md
diff --git a/doc/user/project/milestones/img/progress.png b/doc/user/project/milestones/img/progress.png
new file mode 100644
index 00000000000..c85aecca729
--- /dev/null
+++ b/doc/user/project/milestones/img/progress.png
Binary files differ
diff --git a/doc/user/project/milestones/index.md b/doc/user/project/milestones/index.md
index a43a42a8fe8..99233ed5ae2 100644
--- a/doc/user/project/milestones/index.md
+++ b/doc/user/project/milestones/index.md
@@ -44,3 +44,11 @@ special options available when filtering by milestone:
* **Started** - show issues or merge requests from any milestone with a start
date less than today. Note that this can return results from several
milestones in the same project.
+
+## Milestone progress statistics
+
+Milestone statistics can be viewed in the milestone sidebar. The milestone percentage statistic
+is calculated as; closed and merged merge requests plus all closed issues divided by
+total merge requests and issues.
+
+![Milestone statistics](img/progress.png)
diff --git a/doc/workflow/lfs/lfs_administration.md b/doc/workflow/lfs/lfs_administration.md
index 3a6773909d6..d768b73286d 100644
--- a/doc/workflow/lfs/lfs_administration.md
+++ b/doc/workflow/lfs/lfs_administration.md
@@ -50,7 +50,7 @@ and [projects APIs](../../api/projects.md).
* Currently, storing GitLab Git LFS objects on a non-local storage (like S3 buckets)
is not supported
-* Currently, removing LFS objects from GitLab Git LFS storage is not supported
+* Support for removing unreferenced LFS objects was added in 8.14 onwards.
* LFS authentications via SSH was added with GitLab 8.12
* Only compatible with the GitLFS client versions 1.1.0 and up, or 1.0.2.
* The storage statistics currently count each LFS object multiple times for
diff --git a/features/steps/dashboard/event_filters.rb b/features/steps/dashboard/event_filters.rb
index ca3cd0ecc4e..a745254cc31 100644
--- a/features/steps/dashboard/event_filters.rb
+++ b/features/steps/dashboard/event_filters.rb
@@ -1,5 +1,5 @@
class Spinach::Features::EventFilters < Spinach::FeatureSteps
- include WaitForAjax
+ include WaitForRequests
include SharedAuthentication
include SharedPaths
include SharedProject
@@ -73,20 +73,20 @@ class Spinach::Features::EventFilters < Spinach::FeatureSteps
end
When 'I click "push" event filter' do
- wait_for_ajax
+ wait_for_requests
click_link("Push events")
- wait_for_ajax
+ wait_for_requests
end
When 'I click "team" event filter' do
- wait_for_ajax
+ wait_for_requests
click_link("Team")
- wait_for_ajax
+ wait_for_requests
end
When 'I click "merge" event filter' do
- wait_for_ajax
+ wait_for_requests
click_link("Merge events")
- wait_for_ajax
+ wait_for_requests
end
end
diff --git a/features/steps/dashboard/todos.rb b/features/steps/dashboard/todos.rb
index 14c13c4818a..4a33babe3bd 100644
--- a/features/steps/dashboard/todos.rb
+++ b/features/steps/dashboard/todos.rb
@@ -3,7 +3,7 @@ class Spinach::Features::DashboardTodos < Spinach::FeatureSteps
include SharedPaths
include SharedProject
include SharedUser
- include WaitForAjax
+ include WaitForRequests
step '"John Doe" is a developer of project "Shop"' do
project.team << [john_doe, :developer]
@@ -138,9 +138,9 @@ class Spinach::Features::DashboardTodos < Spinach::FeatureSteps
end
step 'I should be directed to the corresponding page' do
- page.should have_css('.identifier', text: 'Merge Request !1')
+ page.should have_css('.identifier', text: 'Merge request !1')
# Merge request page loads and issues a number of Ajax requests
- wait_for_ajax
+ wait_for_requests
end
def should_see_todo(position, title, body, state: :pending)
diff --git a/features/steps/group/members.rb b/features/steps/group/members.rb
index b04a7015d4e..0ab1012660c 100644
--- a/features/steps/group/members.rb
+++ b/features/steps/group/members.rb
@@ -1,5 +1,5 @@
class Spinach::Features::GroupMembers < Spinach::FeatureSteps
- include WaitForAjax
+ include WaitForRequests
include SharedAuthentication
include SharedPaths
include SharedGroup
@@ -58,7 +58,7 @@ class Spinach::Features::GroupMembers < Spinach::FeatureSteps
click_link 'Developer'
end
- wait_for_ajax
+ wait_for_requests
end
end
diff --git a/features/steps/group/milestones.rb b/features/steps/group/milestones.rb
index 0b0983f0d06..0542b06c0ab 100644
--- a/features/steps/group/milestones.rb
+++ b/features/steps/group/milestones.rb
@@ -1,5 +1,5 @@
class Spinach::Features::GroupMilestones < Spinach::FeatureSteps
- include WaitForAjax
+ include WaitForRequests
include SharedAuthentication
include SharedPaths
include SharedGroup
@@ -91,7 +91,7 @@ class Spinach::Features::GroupMilestones < Spinach::FeatureSteps
end
step 'I should see the list of labels' do
- wait_for_ajax
+ wait_for_requests
page.within('#tab-labels') do
expect(page).to have_content 'bug'
diff --git a/features/steps/project/builds/artifacts.rb b/features/steps/project/builds/artifacts.rb
index 89132ff068f..4b72355b125 100644
--- a/features/steps/project/builds/artifacts.rb
+++ b/features/steps/project/builds/artifacts.rb
@@ -3,7 +3,7 @@ class Spinach::Features::ProjectBuildsArtifacts < Spinach::FeatureSteps
include SharedProject
include SharedBuilds
include RepoHelpers
- include WaitForAjax
+ include WaitForRequests
step 'I click artifacts download button' do
click_link 'Download'
@@ -79,7 +79,7 @@ class Spinach::Features::ProjectBuildsArtifacts < Spinach::FeatureSteps
step 'I click a link to file within build artifacts' do
page.within('.tree-table') { find_link('ci_artifacts.txt').click }
- wait_for_ajax
+ wait_for_requests
end
step 'I see a download link' do
diff --git a/features/steps/project/forked_merge_requests.rb b/features/steps/project/forked_merge_requests.rb
index 29055373a57..25514eb9ef2 100644
--- a/features/steps/project/forked_merge_requests.rb
+++ b/features/steps/project/forked_merge_requests.rb
@@ -4,8 +4,7 @@ class Spinach::Features::ProjectForkedMergeRequests < Spinach::FeatureSteps
include SharedNote
include SharedPaths
include Select2Helper
- include WaitForVueResource
- include WaitForAjax
+ include WaitForRequests
step 'I am a member of project "Shop"' do
@project = ::Project.find_by(name: "Shop")
@@ -34,7 +33,7 @@ class Spinach::Features::ProjectForkedMergeRequests < Spinach::FeatureSteps
expect(page).to have_content @merge_request.source_branch
expect(page).to have_content @merge_request.target_branch
- wait_for_vue_resource
+ wait_for_requests
end
step 'I fill out a "Merge Request On Forked Project" merge request' do
@@ -48,7 +47,7 @@ class Spinach::Features::ProjectForkedMergeRequests < Spinach::FeatureSteps
first('.dropdown-target-project a', text: @project.path_with_namespace)
first('.js-source-branch').click
- wait_for_ajax
+ wait_for_requests
first('.dropdown-source-branch .dropdown-content a', text: 'fix').click
click_button "Compare branches and continue"
diff --git a/features/steps/project/merge_requests.rb b/features/steps/project/merge_requests.rb
index 8133760e619..54b6352c952 100644
--- a/features/steps/project/merge_requests.rb
+++ b/features/steps/project/merge_requests.rb
@@ -7,11 +7,10 @@ class Spinach::Features::ProjectMergeRequests < Spinach::FeatureSteps
include SharedMarkdown
include SharedDiffNote
include SharedUser
- include WaitForAjax
- include WaitForVueResource
+ include WaitForRequests
after do
- wait_for_ajax if javascript_test?
+ wait_for_requests if javascript_test?
end
step 'I click link "New Merge Request"' do
@@ -46,23 +45,23 @@ class Spinach::Features::ProjectMergeRequests < Spinach::FeatureSteps
page.within '.merge-request' do
expect(page).to have_content "Wiki Feature"
end
- wait_for_vue_resource
+ wait_for_requests
end
step 'I should see closed merge request "Bug NS-04"' do
expect(page).to have_content "Bug NS-04"
expect(page).to have_content "Closed by"
- wait_for_vue_resource
+ wait_for_requests
end
step 'I should see merge request "Bug NS-04"' do
expect(page).to have_content "Bug NS-04"
- wait_for_vue_resource
+ wait_for_requests
end
step 'I should see merge request "Feature NS-05"' do
expect(page).to have_content "Feature NS-05"
- wait_for_vue_resource
+ wait_for_requests
end
step 'I should not see "master" branch' do
@@ -99,7 +98,7 @@ class Spinach::Features::ProjectMergeRequests < Spinach::FeatureSteps
step 'I click button "Unsubscribe"' do
click_on "Unsubscribe"
- wait_for_ajax
+ wait_for_requests
end
step 'I click link "Close"' do
@@ -353,7 +352,7 @@ class Spinach::Features::ProjectMergeRequests < Spinach::FeatureSteps
step 'I should see a discussion by user "John Doe" has started on diff' do
# Trigger a refresh of notes
execute_script("$(document).trigger('visibilitychange');")
- wait_for_ajax
+ wait_for_requests
page.within(".notes .discussion") do
page.should have_content "#{user_exists("John Doe").name} #{user_exists("John Doe").to_reference} started a discussion"
page.should have_content sample_commit.line_code_path
@@ -363,12 +362,12 @@ class Spinach::Features::ProjectMergeRequests < Spinach::FeatureSteps
step 'I should see a badge of "1" next to the discussion link' do
expect_discussion_badge_to_have_counter("1")
- wait_for_vue_resource
+ wait_for_requests
end
step 'I should see a badge of "0" next to the discussion link' do
expect_discussion_badge_to_have_counter("0")
- wait_for_vue_resource
+ wait_for_requests
end
step 'I should see a discussion has started on commit diff' do
@@ -376,7 +375,7 @@ class Spinach::Features::ProjectMergeRequests < Spinach::FeatureSteps
page.should have_content "#{current_user.name} #{current_user.to_reference} started a discussion on commit"
page.should have_content sample_commit.line_code_path
page.should have_content "Line is wrong"
- wait_for_vue_resource
+ wait_for_requests
end
end
@@ -384,7 +383,7 @@ class Spinach::Features::ProjectMergeRequests < Spinach::FeatureSteps
page.within(".notes .discussion") do
page.should have_content "#{current_user.name} #{current_user.to_reference} started a discussion on commit"
page.should have_content "One comment to rule them all"
- wait_for_vue_resource
+ wait_for_requests
end
end
@@ -410,7 +409,7 @@ class Spinach::Features::ProjectMergeRequests < Spinach::FeatureSteps
step 'I should see merged request' do
page.within '.status-box' do
expect(page).to have_content "Merged"
- wait_for_vue_resource
+ wait_for_requests
end
end
@@ -422,7 +421,7 @@ class Spinach::Features::ProjectMergeRequests < Spinach::FeatureSteps
page.within '.status-box' do
expect(page).to have_content "Open"
end
- wait_for_vue_resource
+ wait_for_requests
end
step 'I click link "Hide inline discussion" of the third file' do
@@ -446,7 +445,7 @@ class Spinach::Features::ProjectMergeRequests < Spinach::FeatureSteps
step 'I should see a comment like "Line is wrong" in the third file' do
page.within '.files>div:nth-child(3) .note-body > .note-text' do
expect(page).to have_visible_content "Line is wrong"
- wait_for_vue_resource
+ wait_for_requests
end
end
@@ -470,7 +469,7 @@ class Spinach::Features::ProjectMergeRequests < Spinach::FeatureSteps
click_button "Comment"
end
- wait_for_ajax
+ wait_for_requests
page.within ".files>div:nth-child(2) .note-body > .note-text" do
expect(page).to have_content "Line is correct"
@@ -485,7 +484,7 @@ class Spinach::Features::ProjectMergeRequests < Spinach::FeatureSteps
click_button "Comment"
end
- wait_for_ajax
+ wait_for_requests
end
step 'I should still see a comment like "Line is correct" in the second file' do
@@ -514,7 +513,7 @@ class Spinach::Features::ProjectMergeRequests < Spinach::FeatureSteps
step 'I should see comments on the side-by-side diff page' do
page.within '.files>div:nth-child(2) .parallel .note-body > .note-text' do
expect(page).to have_visible_content "Line is correct"
- wait_for_vue_resource
+ wait_for_requests
end
end
@@ -538,7 +537,7 @@ class Spinach::Features::ProjectMergeRequests < Spinach::FeatureSteps
step 'I should see new target branch changes' do
expect(page).to have_content 'Request to merge fix into feature'
expect(page).to have_content 'changed target branch from merge-test to feature'
- wait_for_ajax
+ wait_for_requests
end
step 'I click on "Email Patches"' do
@@ -556,8 +555,14 @@ class Spinach::Features::ProjectMergeRequests < Spinach::FeatureSteps
step '"Bug NS-05" has CI status' do
project = merge_request.source_project
project.enable_ci
- pipeline = create :ci_pipeline, project: project, sha: merge_request.diff_head_sha, ref: merge_request.source_branch
- merge_request.update(head_pipeline: pipeline)
+
+ pipeline =
+ create(:ci_pipeline,
+ project: project,
+ sha: merge_request.diff_head_sha,
+ ref: merge_request.source_branch,
+ head_pipeline_of: merge_request)
+
create :ci_build, pipeline: pipeline
end
@@ -572,7 +577,7 @@ class Spinach::Features::ProjectMergeRequests < Spinach::FeatureSteps
expect(page).to have_content /([0-9]+ commits behind)/
end
- wait_for_vue_resource
+ wait_for_requests
end
step 'I should not see the diverged commits count' do
@@ -580,7 +585,7 @@ class Spinach::Features::ProjectMergeRequests < Spinach::FeatureSteps
expect(page).not_to have_content /([0-9]+ commit[s]? behind)/
end
- wait_for_vue_resource
+ wait_for_requests
end
def merge_request
@@ -597,7 +602,7 @@ class Spinach::Features::ProjectMergeRequests < Spinach::FeatureSteps
click_button "Comment"
end
- wait_for_ajax
+ wait_for_requests
page.within(".notes_holder", visible: true) do
expect(page).to have_content message
diff --git a/features/steps/project/merge_requests/acceptance.rb b/features/steps/project/merge_requests/acceptance.rb
index 3c976f675a2..023f9bef8e5 100644
--- a/features/steps/project/merge_requests/acceptance.rb
+++ b/features/steps/project/merge_requests/acceptance.rb
@@ -1,7 +1,7 @@
class Spinach::Features::ProjectMergeRequestsAcceptance < Spinach::FeatureSteps
include LoginHelpers
include GitlabRoutingHelper
- include WaitForVueResource
+ include WaitForRequests
step 'I am on the Merge Request detail page' do
visit merge_request_path(@merge_request)
@@ -24,7 +24,7 @@ class Spinach::Features::ProjectMergeRequestsAcceptance < Spinach::FeatureSteps
# Wait for View Resource requests to complete so they don't blow up if they are
# only handled after `DatabaseCleaner` has already run
- wait_for_vue_resource
+ wait_for_requests
end
step 'I should not see the Remove Source Branch button' do
@@ -32,7 +32,7 @@ class Spinach::Features::ProjectMergeRequestsAcceptance < Spinach::FeatureSteps
# Wait for View Resource requests to complete so they don't blow up if they are
# only handled after `DatabaseCleaner` has already run
- wait_for_vue_resource
+ wait_for_requests
end
step 'There is an open Merge Request' do
diff --git a/features/steps/project/merge_requests/revert.rb b/features/steps/project/merge_requests/revert.rb
index aa76d6f8c48..98d990f112f 100644
--- a/features/steps/project/merge_requests/revert.rb
+++ b/features/steps/project/merge_requests/revert.rb
@@ -1,7 +1,7 @@
class Spinach::Features::RevertMergeRequests < Spinach::FeatureSteps
include LoginHelpers
include GitlabRoutingHelper
- include WaitForVueResource
+ include WaitForRequests
step 'I click on the revert button' do
find("a[href='#modal-revert-commit']").click
@@ -16,7 +16,7 @@ class Spinach::Features::RevertMergeRequests < Spinach::FeatureSteps
step 'I should see the revert merge request notice' do
page.should have_content('The merge request has been successfully reverted.')
- wait_for_vue_resource
+ wait_for_requests
end
step 'I should not see the revert button' do
diff --git a/features/steps/project/project.rb b/features/steps/project/project.rb
index 9c2196a8ef7..de32c9afcca 100644
--- a/features/steps/project/project.rb
+++ b/features/steps/project/project.rb
@@ -2,7 +2,7 @@ class Spinach::Features::Project < Spinach::FeatureSteps
include SharedAuthentication
include SharedProject
include SharedPaths
- include WaitForAjax
+ include WaitForRequests
step 'change project settings' do
fill_in 'project_name_edit', with: 'NewName'
@@ -87,7 +87,7 @@ class Spinach::Features::Project < Spinach::FeatureSteps
end
step 'I should see project "Shop" README' do
- wait_for_ajax
+ wait_for_requests
page.within('.readme-holder') do
expect(page).to have_content 'testme'
end
diff --git a/features/steps/project/project_milestone.rb b/features/steps/project/project_milestone.rb
index dc1190b7eea..a7d3352b8c4 100644
--- a/features/steps/project/project_milestone.rb
+++ b/features/steps/project/project_milestone.rb
@@ -2,7 +2,7 @@ class Spinach::Features::ProjectMilestone < Spinach::FeatureSteps
include SharedAuthentication
include SharedProject
include SharedPaths
- include WaitForAjax
+ include WaitForRequests
step 'milestone has issue "Bugfix1" with labels: "bug", "feature"' do
project = Project.find_by(name: "Shop")
@@ -35,7 +35,7 @@ class Spinach::Features::ProjectMilestone < Spinach::FeatureSteps
end
step 'I should see the labels "bug", "enhancement" and "feature"' do
- wait_for_ajax
+ wait_for_requests
page.within('#tab-issues') do
expect(page).to have_content 'bug'
diff --git a/features/steps/project/snippets.rb b/features/steps/project/snippets.rb
index 60febd20104..e3f5e9e3ef3 100644
--- a/features/steps/project/snippets.rb
+++ b/features/steps/project/snippets.rb
@@ -3,7 +3,7 @@ class Spinach::Features::ProjectSnippets < Spinach::FeatureSteps
include SharedProject
include SharedNote
include SharedPaths
- include WaitForAjax
+ include WaitForRequests
step 'project "Shop" have "Snippet one" snippet' do
create(:project_snippet,
@@ -59,7 +59,7 @@ class Spinach::Features::ProjectSnippets < Spinach::FeatureSteps
find('.ace_editor').native.send_keys 'Content of snippet three'
end
click_button "Create snippet"
- wait_for_ajax
+ wait_for_requests
end
step 'I should see snippet "Snippet three"' do
@@ -81,7 +81,7 @@ class Spinach::Features::ProjectSnippets < Spinach::FeatureSteps
fill_in "note_note", with: "Good snippet!"
click_button "Comment"
end
- wait_for_ajax
+ wait_for_requests
end
step 'I should see comment "Good snippet!"' do
diff --git a/features/steps/project/source/browse_files.rb b/features/steps/project/source/browse_files.rb
index ef09bddddd8..6efd4374b32 100644
--- a/features/steps/project/source/browse_files.rb
+++ b/features/steps/project/source/browse_files.rb
@@ -4,7 +4,7 @@ class Spinach::Features::ProjectSourceBrowseFiles < Spinach::FeatureSteps
include SharedProject
include SharedPaths
include RepoHelpers
- include WaitForAjax
+ include WaitForRequests
step "I don't have write access" do
@project = create(:project, :repository, name: "Other Project", path: "other-project")
@@ -37,12 +37,12 @@ class Spinach::Features::ProjectSourceBrowseFiles < Spinach::FeatureSteps
end
step 'I should see its content' do
- wait_for_ajax
+ wait_for_requests
expect(page).to have_content old_gitignore_content
end
step 'I should see its new content' do
- wait_for_ajax
+ wait_for_requests
expect(page).to have_content new_gitignore_content
end
diff --git a/features/steps/project/source/markdown_render.rb b/features/steps/project/source/markdown_render.rb
index ada0ff20585..0fee158d590 100644
--- a/features/steps/project/source/markdown_render.rb
+++ b/features/steps/project/source/markdown_render.rb
@@ -5,7 +5,7 @@ class Spinach::Features::ProjectSourceMarkdownRender < Spinach::FeatureSteps
include SharedAuthentication
include SharedPaths
include SharedMarkdown
- include WaitForAjax
+ include WaitForRequests
step 'I own project "Delta"' do
@project = ::Project.find_by(name: "Delta")
@@ -35,7 +35,7 @@ class Spinach::Features::ProjectSourceMarkdownRender < Spinach::FeatureSteps
step 'I should see correct document rendered' do
expect(current_path).to eq namespace_project_blob_path(@project.namespace, @project, "markdown/doc/api/README.md")
- wait_for_ajax
+ wait_for_requests
expect(page).to have_content "All API requests require authentication"
end
@@ -65,7 +65,7 @@ class Spinach::Features::ProjectSourceMarkdownRender < Spinach::FeatureSteps
step 'I should see correct maintenance file rendered' do
expect(current_path).to eq namespace_project_blob_path(@project.namespace, @project, "markdown/doc/raketasks/maintenance.md")
- wait_for_ajax
+ wait_for_requests
expect(page).to have_content "bundle exec rake gitlab:env:info RAILS_ENV=production"
end
@@ -97,7 +97,7 @@ class Spinach::Features::ProjectSourceMarkdownRender < Spinach::FeatureSteps
step 'I see correct file rendered' do
expect(current_path).to eq namespace_project_blob_path(@project.namespace, @project, "markdown/doc/api/README.md")
- wait_for_ajax
+ wait_for_requests
expect(page).to have_content "Contents"
expect(page).to have_link "Users"
expect(page).to have_link "Rake tasks"
@@ -120,7 +120,7 @@ class Spinach::Features::ProjectSourceMarkdownRender < Spinach::FeatureSteps
When 'I visit markdown branch' do
visit namespace_project_tree_path(@project.namespace, @project, "markdown")
- wait_for_ajax
+ wait_for_requests
end
When 'I visit markdown branch "README.md" blob' do
@@ -143,7 +143,7 @@ class Spinach::Features::ProjectSourceMarkdownRender < Spinach::FeatureSteps
step 'I see correct file rendered in markdown branch' do
expect(current_path).to eq namespace_project_blob_path(@project.namespace, @project, "markdown/doc/api/README.md")
- wait_for_ajax
+ wait_for_requests
expect(page).to have_content "Contents"
expect(page).to have_link "Users"
expect(page).to have_link "Rake tasks"
@@ -151,7 +151,7 @@ class Spinach::Features::ProjectSourceMarkdownRender < Spinach::FeatureSteps
step 'I should see correct document rendered for markdown branch' do
expect(current_path).to eq namespace_project_blob_path(@project.namespace, @project, "markdown/doc/api/README.md")
- wait_for_ajax
+ wait_for_requests
expect(page).to have_content "All API requests require authentication"
end
@@ -169,7 +169,7 @@ class Spinach::Features::ProjectSourceMarkdownRender < Spinach::FeatureSteps
# Expected link contents
step 'The link with text "empty" should have url "tree/markdown"' do
- wait_for_ajax
+ wait_for_requests
find('a', text: /^empty$/)['href'] == current_host + namespace_project_tree_path(@project.namespace, @project, "markdown")
end
@@ -205,7 +205,7 @@ class Spinach::Features::ProjectSourceMarkdownRender < Spinach::FeatureSteps
end
step 'The link with text "ID" should have url "blob/markdown/README.mdID"' do
- wait_for_ajax
+ wait_for_requests
find('a', text: /^#id$/)['href'] == current_host + namespace_project_blob_path(@project.namespace, @project, "markdown/README.md") + '#id'
end
@@ -300,12 +300,12 @@ class Spinach::Features::ProjectSourceMarkdownRender < Spinach::FeatureSteps
step 'I should see the correct markdown' do
expect(current_path).to eq namespace_project_blob_path(@project.namespace, @project, "markdown/doc/api/users.md")
- wait_for_ajax
+ wait_for_requests
expect(page).to have_content "List users"
end
step 'Header "Application details" should have correct id and link' do
- wait_for_ajax
+ wait_for_requests
header_should_have_correct_id_and_link(2, 'Application details', 'application-details')
end
diff --git a/features/steps/shared/active_tab.rb b/features/steps/shared/active_tab.rb
index 8bae80a8707..af5db05e9e8 100644
--- a/features/steps/shared/active_tab.rb
+++ b/features/steps/shared/active_tab.rb
@@ -1,9 +1,9 @@
module SharedActiveTab
include Spinach::DSL
- include WaitForAjax
+ include WaitForRequests
after do
- wait_for_ajax if javascript_test?
+ wait_for_requests if javascript_test?
end
def ensure_active_main_tab(content)
diff --git a/features/steps/shared/diff_note.rb b/features/steps/shared/diff_note.rb
index 071aa2e3eff..36fc315599e 100644
--- a/features/steps/shared/diff_note.rb
+++ b/features/steps/shared/diff_note.rb
@@ -1,10 +1,10 @@
module SharedDiffNote
include Spinach::DSL
include RepoHelpers
- include WaitForAjax
+ include WaitForRequests
after do
- wait_for_ajax if javascript_test?
+ wait_for_requests if javascript_test?
end
step 'I cancel the diff comment' do
diff --git a/features/steps/shared/note.rb b/features/steps/shared/note.rb
index 7d260025052..44eb8f321dd 100644
--- a/features/steps/shared/note.rb
+++ b/features/steps/shared/note.rb
@@ -1,9 +1,9 @@
module SharedNote
include Spinach::DSL
- include WaitForAjax
+ include WaitForRequests
after do
- wait_for_ajax if javascript_test?
+ wait_for_requests if javascript_test?
end
step 'I delete a comment' do
@@ -25,7 +25,7 @@ module SharedNote
click_button "Comment"
end
- wait_for_ajax
+ wait_for_requests
end
step 'I preview a comment text like "Bug fixed :smile:"' do
@@ -40,7 +40,7 @@ module SharedNote
click_button "Comment"
end
- wait_for_ajax
+ wait_for_requests
end
step 'I write a comment like ":+1: Nice"' do
@@ -127,7 +127,7 @@ module SharedNote
click_button "Comment"
end
- wait_for_ajax
+ wait_for_requests
end
step 'The comment with the header should not have an ID' do
diff --git a/features/steps/shared/paths.rb b/features/steps/shared/paths.rb
index bef3eac4d26..f0e751b820a 100644
--- a/features/steps/shared/paths.rb
+++ b/features/steps/shared/paths.rb
@@ -2,7 +2,7 @@ module SharedPaths
include Spinach::DSL
include RepoHelpers
include DashboardHelper
- include WaitForVueResource
+ include WaitForRequests
step 'I visit new project page' do
visit new_project_path
@@ -378,28 +378,28 @@ module SharedPaths
step 'I visit merge request page "Bug NS-04"' do
visit merge_request_path("Bug NS-04")
- wait_for_vue_resource
+ wait_for_requests
end
step 'I visit merge request page "Bug NS-05"' do
visit merge_request_path("Bug NS-05")
- wait_for_vue_resource
+ wait_for_requests
end
step 'I visit merge request page "Bug NS-07"' do
visit merge_request_path("Bug NS-07")
- wait_for_vue_resource
+ wait_for_requests
end
step 'I visit merge request page "Bug NS-08"' do
visit merge_request_path("Bug NS-08")
- wait_for_vue_resource
+ wait_for_requests
end
step 'I visit merge request page "Bug CO-01"' do
mr = MergeRequest.find_by(title: "Bug CO-01")
visit namespace_project_merge_request_path(mr.target_project.namespace, mr.target_project, mr)
- wait_for_vue_resource
+ wait_for_requests
end
step 'I visit project "Shop" merge requests page' do
diff --git a/features/steps/snippets/snippets.rb b/features/steps/snippets/snippets.rb
index 0b3e942a4fd..a4fc77746ee 100644
--- a/features/steps/snippets/snippets.rb
+++ b/features/steps/snippets/snippets.rb
@@ -3,7 +3,7 @@ class Spinach::Features::Snippets < Spinach::FeatureSteps
include SharedPaths
include SharedProject
include SharedSnippet
- include WaitForAjax
+ include WaitForRequests
step 'I click link "Personal snippet one"' do
click_link "Personal snippet one"
@@ -30,7 +30,7 @@ class Spinach::Features::Snippets < Spinach::FeatureSteps
find('.ace_editor').native.send_keys 'Content of snippet three'
end
click_button "Create snippet"
- wait_for_ajax
+ wait_for_requests
end
step 'I submit new internal snippet' do
diff --git a/features/support/env.rb b/features/support/env.rb
index 23a1f702068..1690465d9b2 100644
--- a/features/support/env.rb
+++ b/features/support/env.rb
@@ -10,7 +10,7 @@ if ENV['CI']
Knapsack::Adapters::SpinachAdapter.bind
end
-%w(select2_helper test_env repo_helpers wait_for_ajax wait_for_requests sidekiq wait_for_vue_resource).each do |f|
+%w(select2_helper test_env repo_helpers wait_for_requests sidekiq).each do |f|
require Rails.root.join('spec', 'support', f)
end
@@ -33,7 +33,7 @@ end
Spinach.hooks.after_scenario do |scenario_data, step_definitions|
if scenario_data.tags.include?('javascript')
include WaitForRequests
- wait_for_requests_complete
+ block_and_wait_for_requests_complete
end
end
diff --git a/lib/api/internal.rb b/lib/api/internal.rb
index 96aaaf868ea..9ebd4841296 100644
--- a/lib/api/internal.rb
+++ b/lib/api/internal.rb
@@ -136,14 +136,15 @@ module API
post "/notify_post_receive" do
status 200
- return unless Gitlab::GitalyClient.enabled?
-
- begin
- repository = wiki? ? project.wiki.repository : project.repository
- Gitlab::GitalyClient::Notifications.new(repository.raw_repository).post_receive
- rescue GRPC::Unavailable => e
- render_api_error!(e, 500)
- end
+ # TODO: Re-enable when Gitaly is processing the post-receive notification
+ # return unless Gitlab::GitalyClient.enabled?
+ #
+ # begin
+ # repository = wiki? ? project.wiki.repository : project.repository
+ # Gitlab::GitalyClient::Notifications.new(repository.raw_repository).post_receive
+ # rescue GRPC::Unavailable => e
+ # render_api_error!(e, 500)
+ # end
end
end
end
diff --git a/lib/backup/manager.rb b/lib/backup/manager.rb
index 330cd963626..f755c99ea4a 100644
--- a/lib/backup/manager.rb
+++ b/lib/backup/manager.rb
@@ -84,7 +84,11 @@ module Backup
Dir.chdir(backup_path) do
backup_file_list.each do |file|
- next unless file =~ /(\d+)(?:_\d{4}_\d{2}_\d{2})?_gitlab_backup\.tar/
+ # For backward compatibility, there are 3 names the backups can have:
+ # - 1495527122_gitlab_backup.tar
+ # - 1495527068_2017_05_23_gitlab_backup.tar
+ # - 1495527097_2017_05_23_9.3.0-pre_gitlab_backup.tar
+ next unless file =~ /(\d+)(?:_\d{4}_\d{2}_\d{2}(_\d+\.\d+\.\d+.*)?)?_gitlab_backup\.tar$/
timestamp = $1.to_i
diff --git a/lib/banzai/filter/ascii_doc_post_processing_filter.rb b/lib/banzai/filter/ascii_doc_post_processing_filter.rb
new file mode 100644
index 00000000000..c9fcf057c5f
--- /dev/null
+++ b/lib/banzai/filter/ascii_doc_post_processing_filter.rb
@@ -0,0 +1,13 @@
+module Banzai
+ module Filter
+ class AsciiDocPostProcessingFilter < HTML::Pipeline::Filter
+ def call
+ doc.search('[data-math-style]').each do |node|
+ node.set_attribute('class', 'code math js-render-math')
+ end
+
+ doc
+ end
+ end
+ end
+end
diff --git a/lib/banzai/filter/sanitization_filter.rb b/lib/banzai/filter/sanitization_filter.rb
index 522217deae4..2d6e8ffc90f 100644
--- a/lib/banzai/filter/sanitization_filter.rb
+++ b/lib/banzai/filter/sanitization_filter.rb
@@ -31,6 +31,10 @@ module Banzai
# Allow span elements
whitelist[:elements].push('span')
+ # Allow data-math-style attribute in order to support LaTeX formatting
+ whitelist[:attributes]['code'] = %w(data-math-style)
+ whitelist[:attributes]['pre'] = %w(data-math-style)
+
# Allow html5 details/summary elements
whitelist[:elements].push('details')
whitelist[:elements].push('summary')
diff --git a/lib/banzai/pipeline/ascii_doc_pipeline.rb b/lib/banzai/pipeline/ascii_doc_pipeline.rb
new file mode 100644
index 00000000000..1048b927cd3
--- /dev/null
+++ b/lib/banzai/pipeline/ascii_doc_pipeline.rb
@@ -0,0 +1,14 @@
+module Banzai
+ module Pipeline
+ class AsciiDocPipeline < BasePipeline
+ def self.filters
+ FilterArray[
+ Filter::SanitizationFilter,
+ Filter::ExternalLinkFilter,
+ Filter::PlantumlFilter,
+ Filter::AsciiDocPostProcessingFilter
+ ]
+ end
+ end
+ end
+end
diff --git a/lib/constraints/group_url_constrainer.rb b/lib/constraints/group_url_constrainer.rb
index 5f379756c11..0ea2f97352d 100644
--- a/lib/constraints/group_url_constrainer.rb
+++ b/lib/constraints/group_url_constrainer.rb
@@ -1,8 +1,8 @@
class GroupUrlConstrainer
def matches?(request)
- id = request.params[:id]
+ id = request.params[:group_id] || request.params[:id]
- return false unless DynamicPathValidator.valid?(id)
+ return false unless DynamicPathValidator.valid_namespace_path?(id)
Group.find_by_full_path(id, follow_redirects: request.get?).present?
end
diff --git a/lib/constraints/project_url_constrainer.rb b/lib/constraints/project_url_constrainer.rb
index 6f542f63f98..4444a1abee3 100644
--- a/lib/constraints/project_url_constrainer.rb
+++ b/lib/constraints/project_url_constrainer.rb
@@ -4,7 +4,7 @@ class ProjectUrlConstrainer
project_path = request.params[:project_id] || request.params[:id]
full_path = namespace_path + '/' + project_path
- return false unless DynamicPathValidator.valid?(full_path)
+ return false unless DynamicPathValidator.valid_project_path?(full_path)
Project.find_by_full_path(full_path, follow_redirects: request.get?).present?
end
diff --git a/lib/gitlab/asciidoc.rb b/lib/gitlab/asciidoc.rb
index 96d38f6daa0..3d41ac76406 100644
--- a/lib/gitlab/asciidoc.rb
+++ b/lib/gitlab/asciidoc.rb
@@ -20,21 +20,20 @@ module Gitlab
backend: :gitlab_html5,
attributes: DEFAULT_ADOC_ATTRS }
- context[:pipeline] = :markup
+ context[:pipeline] = :ascii_doc
plantuml_setup
html = ::Asciidoctor.convert(input, asciidoc_opts)
html = Banzai.render(html, context)
-
html.html_safe
end
def self.plantuml_setup
Asciidoctor::PlantUml.configure do |conf|
- conf.url = ApplicationSetting.current.plantuml_url
- conf.svg_enable = ApplicationSetting.current.plantuml_enabled
- conf.png_enable = ApplicationSetting.current.plantuml_enabled
+ conf.url = current_application_settings.plantuml_url
+ conf.svg_enable = current_application_settings.plantuml_enabled
+ conf.png_enable = current_application_settings.plantuml_enabled
conf.txt_enable = false
end
end
@@ -47,13 +46,13 @@ module Gitlab
def stem(node)
return super unless node.style.to_sym == :latexmath
- %(<pre#{id_attribute(node)} class="code math js-render-math #{node.role}" data-math-style="display"><code>#{node.content}</code></pre>)
+ %(<pre#{id_attribute(node)} data-math-style="display"><code>#{node.content}</code></pre>)
end
def inline_quoted(node)
return super unless node.type.to_sym == :latexmath
- %(<code#{id_attribute(node)} class="code math js-render-math #{node.role}" data-math-style="inline">#{node.text}</code>)
+ %(<code#{id_attribute(node)} data-math-style="inline">#{node.text}</code>)
end
private
diff --git a/lib/gitlab/diff/file_collection/base.rb b/lib/gitlab/diff/file_collection/base.rb
index 2b9fc65b985..7c32adc6ce7 100644
--- a/lib/gitlab/diff/file_collection/base.rb
+++ b/lib/gitlab/diff/file_collection/base.rb
@@ -24,6 +24,14 @@ module Gitlab
@diff_files ||= @diffs.decorate! { |diff| decorate_diff!(diff) }
end
+ def diff_file_with_old_path(old_path)
+ diff_files.find { |diff_file| diff_file.old_path == old_path }
+ end
+
+ def diff_file_with_new_path(new_path)
+ diff_files.find { |diff_file| diff_file.new_path == new_path }
+ end
+
private
def decorate_diff!(diff)
diff --git a/lib/gitlab/diff/position.rb b/lib/gitlab/diff/position.rb
index fc728123c97..4d96778a2b2 100644
--- a/lib/gitlab/diff/position.rb
+++ b/lib/gitlab/diff/position.rb
@@ -12,20 +12,26 @@ module Gitlab
attr_reader :head_sha
def initialize(attrs = {})
+ if diff_file = attrs[:diff_file]
+ attrs[:diff_refs] = diff_file.diff_refs
+ attrs[:old_path] = diff_file.old_path
+ attrs[:new_path] = diff_file.new_path
+ end
+
+ if diff_refs = attrs[:diff_refs]
+ attrs[:base_sha] = diff_refs.base_sha
+ attrs[:start_sha] = diff_refs.start_sha
+ attrs[:head_sha] = diff_refs.head_sha
+ end
+
@old_path = attrs[:old_path]
@new_path = attrs[:new_path]
+ @base_sha = attrs[:base_sha]
+ @start_sha = attrs[:start_sha]
+ @head_sha = attrs[:head_sha]
+
@old_line = attrs[:old_line]
@new_line = attrs[:new_line]
-
- if attrs[:diff_refs]
- @base_sha = attrs[:diff_refs].base_sha
- @start_sha = attrs[:diff_refs].start_sha
- @head_sha = attrs[:diff_refs].head_sha
- else
- @base_sha = attrs[:base_sha]
- @start_sha = attrs[:start_sha]
- @head_sha = attrs[:head_sha]
- end
end
# `Gitlab::Diff::Position` objects are stored as serialized attributes in
@@ -129,11 +135,11 @@ module Gitlab
end
def diff_line(repository)
- @diff_line ||= diff_file(repository).line_for_position(self)
+ @diff_line ||= diff_file(repository)&.line_for_position(self)
end
def line_code(repository)
- @line_code ||= diff_file(repository).line_code_for_position(self)
+ @line_code ||= diff_file(repository)&.line_code_for_position(self)
end
private
diff --git a/lib/gitlab/diff/position_tracer.rb b/lib/gitlab/diff/position_tracer.rb
index e89ff238ec7..dcabb5f7fe5 100644
--- a/lib/gitlab/diff/position_tracer.rb
+++ b/lib/gitlab/diff/position_tracer.rb
@@ -3,21 +3,21 @@
module Gitlab
module Diff
class PositionTracer
- attr_accessor :repository
+ attr_accessor :project
attr_accessor :old_diff_refs
attr_accessor :new_diff_refs
attr_accessor :paths
- def initialize(repository:, old_diff_refs:, new_diff_refs:, paths: nil)
- @repository = repository
+ def initialize(project:, old_diff_refs:, new_diff_refs:, paths: nil)
+ @project = project
@old_diff_refs = old_diff_refs
@new_diff_refs = new_diff_refs
@paths = paths
end
- def trace(old_position)
+ def trace(ab_position)
return unless old_diff_refs&.complete? && new_diff_refs&.complete?
- return unless old_position.diff_refs == old_diff_refs
+ return unless ab_position.diff_refs == old_diff_refs
# Suppose we have an MR with source branch `feature` and target branch `master`.
# When the MR was created, the head of `master` was commit A, and the
@@ -44,14 +44,16 @@ module Gitlab
#
# For diff notes for diff A->B, the position looks like this:
# Position
- # base_sha - ID of commit A
+ # start_sha - ID of commit A
# head_sha - ID of commit B
+ # base_sha - ID of base commit of A and B
# old_path - path as of A (nil if file was newly created)
# new_path - path as of B (nil if file was deleted)
# old_line - line number as of A (nil if file was newly created)
# new_line - line number as of B (nil if file was deleted)
#
- # We can easily update `base_sha` and `head_sha` to hold the IDs of commits C and D,
+ # We can easily update `start_sha` and `head_sha` to hold the IDs of
+ # commits C and D, and can trivially determine `base_sha` based on those,
# but need to find the paths and line numbers as of C and D.
#
# If the file was unchanged or newly created in A->B, the path as of D can be found
@@ -68,107 +70,161 @@ module Gitlab
# by generating diff A->C ("base to base"), finding the diff file with
# `diff_file.old_path == position.old_path`, and taking `diff_file.new_path`.
# The path as of D can be found by taking diff C->D, finding the diff file
- # with that same `old_path` and taking `diff_file.new_path`.
+ # with `old_path` set to that `diff_file.new_path` and taking `diff_file.new_path`.
# The line number as of C can be found by using the LineMapper on diff A->C
# and providing the line number as of A.
# The line number as of D can be found by using the LineMapper on diff C->D
# and providing the line number as of C.
- results = nil
- results ||= trace_added_line(old_position) if old_position.added? || old_position.unchanged?
- results ||= trace_removed_line(old_position) if old_position.removed? || old_position.unchanged?
-
- return unless results
-
- file_diff, old_line, new_line = results
-
- new_position = Position.new(
- old_path: file_diff.old_path,
- new_path: file_diff.new_path,
- head_sha: new_diff_refs.head_sha,
- start_sha: new_diff_refs.start_sha,
- base_sha: new_diff_refs.base_sha,
- old_line: old_line,
- new_line: new_line
- )
-
- # If a position is found, but is not actually contained in the diff, for example
- # because it was an unchanged line in the context of a change that was undone,
- # we cannot return this as a successful trace.
- return unless new_position.diff_line(repository)
-
- new_position
+ if ab_position.added?
+ trace_added_line(ab_position)
+ elsif ab_position.removed?
+ trace_removed_line(ab_position)
+ else # unchanged
+ trace_unchanged_line(ab_position)
+ end
end
private
- def trace_added_line(old_position)
- file_path = old_position.new_path
-
- return unless diff_head_to_head
-
- file_head_to_head = diff_head_to_head.find { |diff_file| diff_file.old_path == file_path }
-
- file_path = file_head_to_head.new_path if file_head_to_head
-
- new_line = LineMapper.new(file_head_to_head).old_to_new(old_position.new_line)
-
- return unless new_line
-
- file_diff = new_diffs.find { |diff_file| diff_file.new_path == file_path }
- return unless file_diff
-
- old_line = LineMapper.new(file_diff).new_to_old(new_line)
-
- [file_diff, old_line, new_line]
+ def trace_added_line(ab_position)
+ b_path = ab_position.new_path
+ b_line = ab_position.new_line
+
+ bd_diff = bd_diffs.diff_file_with_old_path(b_path)
+
+ d_path = bd_diff&.new_path || b_path
+ d_line = LineMapper.new(bd_diff).old_to_new(b_line)
+
+ if d_line
+ cd_diff = cd_diffs.diff_file_with_new_path(d_path)
+
+ c_path = cd_diff&.old_path || d_path
+ c_line = LineMapper.new(cd_diff).new_to_old(d_line)
+
+ if c_line
+ # If the line is still in D but also in C, it has turned from an
+ # added line into an unchanged one.
+ new_position = position(cd_diff, c_line, d_line)
+ if valid_position?(new_position)
+ # If the line is still in the MR, we don't treat this as outdated.
+ { position: new_position, outdated: false }
+ else
+ # If the line is no longer in the MR, we unfortunately cannot show
+ # the current state on the CD diff, so we treat it as outdated.
+ ac_diff = ac_diffs.diff_file_with_new_path(c_path)
+
+ { position: position(ac_diff, nil, c_line), outdated: true }
+ end
+ else
+ # If the line is still in D and not in C, it is still added.
+ { position: position(cd_diff, nil, d_line), outdated: false }
+ end
+ else
+ # If the line is no longer in D, it has been removed from the MR.
+ { position: position(bd_diff, b_line, nil), outdated: true }
+ end
end
- def trace_removed_line(old_position)
- file_path = old_position.old_path
+ def trace_removed_line(ab_position)
+ a_path = ab_position.old_path
+ a_line = ab_position.old_line
- return unless diff_base_to_base
+ ac_diff = ac_diffs.diff_file_with_old_path(a_path)
- file_base_to_base = diff_base_to_base.find { |diff_file| diff_file.old_path == file_path }
+ c_path = ac_diff&.new_path || a_path
+ c_line = LineMapper.new(ac_diff).old_to_new(a_line)
- file_path = file_base_to_base.old_path if file_base_to_base
+ if c_line
+ cd_diff = cd_diffs.diff_file_with_old_path(c_path)
- old_line = LineMapper.new(file_base_to_base).old_to_new(old_position.old_line)
+ d_path = cd_diff&.new_path || c_path
+ d_line = LineMapper.new(cd_diff).old_to_new(c_line)
- return unless old_line
+ if d_line
+ # If the line is still in C but also in D, it has turned from a
+ # removed line into an unchanged one.
+ bd_diff = bd_diffs.diff_file_with_new_path(d_path)
- file_diff = new_diffs.find { |diff_file| diff_file.old_path == file_path }
- return unless file_diff
-
- new_line = LineMapper.new(file_diff).old_to_new(old_line)
+ { position: position(bd_diff, nil, d_line), outdated: true }
+ else
+ # If the line is still in C and not in D, it is still removed.
+ { position: position(cd_diff, c_line, nil), outdated: false }
+ end
+ else
+ # If the line is no longer in C, it has been removed outside of the MR.
+ { position: position(ac_diff, a_line, nil), outdated: true }
+ end
+ end
- [file_diff, old_line, new_line]
+ def trace_unchanged_line(ab_position)
+ a_path = ab_position.old_path
+ a_line = ab_position.old_line
+ b_path = ab_position.new_path
+ b_line = ab_position.new_line
+
+ ac_diff = ac_diffs.diff_file_with_old_path(a_path)
+
+ c_path = ac_diff&.new_path || a_path
+ c_line = LineMapper.new(ac_diff).old_to_new(a_line)
+
+ bd_diff = bd_diffs.diff_file_with_old_path(b_path)
+
+ d_line = LineMapper.new(bd_diff).old_to_new(b_line)
+
+ cd_diff = cd_diffs.diff_file_with_old_path(c_path)
+
+ if c_line && d_line
+ # If the line is still in C and D, it is still unchanged.
+ new_position = position(cd_diff, c_line, d_line)
+ if valid_position?(new_position)
+ # If the line is still in the MR, we don't treat this as outdated.
+ { position: new_position, outdated: false }
+ else
+ # If the line is no longer in the MR, we unfortunately cannot show
+ # the current state on the CD diff or any change on the BD diff,
+ # so we treat it as outdated.
+ { position: nil, outdated: true }
+ end
+ elsif d_line # && !c_line
+ # If the line is still in D but no longer in C, it has turned from
+ # an unchanged line into an added one.
+ # We don't treat this as outdated since the line is still in the MR.
+ { position: position(cd_diff, nil, d_line), outdated: false }
+ else # !d_line && (c_line || !c_line)
+ # If the line is no longer in D, it has turned from an unchanged line
+ # into a removed one.
+ { position: position(bd_diff, b_line, nil), outdated: true }
+ end
end
- def diff_base_to_base
- @diff_base_to_base ||= diff_files(old_diff_refs.base_sha || old_diff_refs.start_sha, new_diff_refs.base_sha || new_diff_refs.start_sha)
+ def ac_diffs
+ @ac_diffs ||= compare(
+ old_diff_refs.base_sha || old_diff_refs.start_sha,
+ new_diff_refs.base_sha || new_diff_refs.start_sha,
+ straight: true
+ )
end
- def diff_head_to_head
- @diff_head_to_head ||= diff_files(old_diff_refs.head_sha, new_diff_refs.head_sha)
+ def bd_diffs
+ @bd_diffs ||= compare(old_diff_refs.head_sha, new_diff_refs.head_sha, straight: true)
end
- def new_diffs
- @new_diffs ||= diff_files(new_diff_refs.start_sha, new_diff_refs.head_sha, use_base: true)
+ def cd_diffs
+ @cd_diffs ||= compare(new_diff_refs.start_sha, new_diff_refs.head_sha)
end
- def diff_files(start_sha, head_sha, use_base: false)
- base_sha = self.repository.merge_base(start_sha, head_sha) || start_sha
+ def compare(start_sha, head_sha, straight: false)
+ compare = CompareService.new(project, head_sha).execute(project, start_sha, straight: straight)
+ compare.diffs(paths: paths)
+ end
- diffs = self.repository.raw_repository.diff(
- use_base ? base_sha : start_sha,
- head_sha,
- {},
- *paths
- )
+ def position(diff_file, old_line, new_line)
+ Position.new(diff_file: diff_file, old_line: old_line, new_line: new_line)
+ end
- diffs.decorate! do |diff|
- Gitlab::Diff::File.new(diff, repository: self.repository)
- end
+ def valid_position?(position)
+ !!position.diff_line(project.repository)
end
end
end
diff --git a/lib/gitlab/etag_caching/router.rb b/lib/gitlab/etag_caching/router.rb
index 9deca760469..cf279628beb 100644
--- a/lib/gitlab/etag_caching/router.rb
+++ b/lib/gitlab/etag_caching/router.rb
@@ -11,7 +11,7 @@ module Gitlab
USED_IN_ROUTES = %w[noteable issue notes issues realtime_changes
commit pipelines merge_requests new
environments].freeze
- RESERVED_WORDS = DynamicPathValidator::WILDCARD_ROUTES - USED_IN_ROUTES
+ RESERVED_WORDS = Gitlab::Regex::ILLEGAL_PROJECT_PATH_WORDS - USED_IN_ROUTES
RESERVED_WORDS_REGEX = Regexp.union(*RESERVED_WORDS)
ROUTES = [
Gitlab::EtagCaching::Router::Route.new(
@@ -39,7 +39,7 @@ module Gitlab
'project_pipelines'
),
Gitlab::EtagCaching::Router::Route.new(
- %r(^(?!.*(#{RESERVED_WORDS})).*/pipelines/\d+\.json\z),
+ %r(^(?!.*(#{RESERVED_WORDS_REGEX})).*/pipelines/\d+\.json\z),
'project_pipeline'
),
Gitlab::EtagCaching::Router::Route.new(
diff --git a/lib/gitlab/gon_helper.rb b/lib/gitlab/gon_helper.rb
index 6200bd460ea..21f2e6b6970 100644
--- a/lib/gitlab/gon_helper.rb
+++ b/lib/gitlab/gon_helper.rb
@@ -12,6 +12,7 @@ module Gitlab
gon.katex_js_url = ActionController::Base.helpers.asset_path('katex.js')
gon.sentry_dsn = current_application_settings.clientside_sentry_dsn if current_application_settings.clientside_sentry_enabled
gon.gitlab_url = Gitlab.config.gitlab.url
+ gon.revision = Gitlab::REVISION
if current_user
gon.current_user_id = current_user.id
diff --git a/lib/gitlab/regex.rb b/lib/gitlab/regex.rb
index b7fef5dd068..f609850f8fa 100644
--- a/lib/gitlab/regex.rb
+++ b/lib/gitlab/regex.rb
@@ -2,6 +2,136 @@ module Gitlab
module Regex
extend self
+ # All routes that appear on the top level must be listed here.
+ # This will make sure that groups cannot be created with these names
+ # as these routes would be masked by the paths already in place.
+ #
+ # Example:
+ # /api/api-project
+ #
+ # the path `api` shouldn't be allowed because it would be masked by `api/*`
+ #
+ TOP_LEVEL_ROUTES = %w[
+ -
+ .well-known
+ abuse_reports
+ admin
+ all
+ api
+ assets
+ autocomplete
+ ci
+ dashboard
+ explore
+ files
+ groups
+ health_check
+ help
+ hooks
+ import
+ invites
+ issues
+ jwt
+ koding
+ member
+ merge_requests
+ new
+ notes
+ notification_settings
+ oauth
+ profile
+ projects
+ public
+ repository
+ robots.txt
+ s
+ search
+ sent_notifications
+ services
+ snippets
+ teams
+ u
+ unicorn_test
+ unsubscribes
+ uploads
+ users
+ ].freeze
+
+ # This list should contain all words following `/*namespace_id/:project_id` in
+ # routes that contain a second wildcard.
+ #
+ # Example:
+ # /*namespace_id/:project_id/badges/*ref/build
+ #
+ # If `badges` was allowed as a project/group name, we would not be able to access the
+ # `badges` route for those projects:
+ #
+ # Consider a namespace with path `foo/bar` and a project called `badges`.
+ # The route to the build badge would then be `/foo/bar/badges/badges/master/build.svg`
+ #
+ # When accessing this path the route would be matched to the `badges` path
+ # with the following params:
+ # - namespace_id: `foo`
+ # - project_id: `bar`
+ # - ref: `badges/master`
+ #
+ # Failing to find the project, this would result in a 404.
+ #
+ # By rejecting `badges` the router can _count_ on the fact that `badges` will
+ # be preceded by the `namespace/project`.
+ PROJECT_WILDCARD_ROUTES = %w[
+ badges
+ blame
+ blob
+ builds
+ commits
+ create
+ create_dir
+ edit
+ environments/folders
+ files
+ find_file
+ gitlab-lfs/objects
+ info/lfs/objects
+ new
+ preview
+ raw
+ refs
+ tree
+ update
+ wikis
+ ].freeze
+
+ # These are all the paths that follow `/groups/*id/ or `/groups/*group_id`
+ # We need to reject these because we have a `/groups/*id` page that is the same
+ # as the `/*id`.
+ #
+ # If we would allow a subgroup to be created with the name `activity` then
+ # this group would not be accessible through `/groups/parent/activity` since
+ # this would map to the activity-page of its parent.
+ GROUP_ROUTES = %w[
+ activity
+ analytics
+ audit_events
+ avatar
+ edit
+ group_members
+ hooks
+ issues
+ labels
+ ldap
+ ldap_group_links
+ merge_requests
+ milestones
+ notification_setting
+ pipeline_quota
+ projects
+ subgroups
+ ].freeze
+
+ ILLEGAL_PROJECT_PATH_WORDS = PROJECT_WILDCARD_ROUTES
+ ILLEGAL_GROUP_PATH_WORDS = (PROJECT_WILDCARD_ROUTES | GROUP_ROUTES).freeze
+
# The namespace regex is used in Javascript to validate usernames in the "Register" form. However, Javascript
# does not support the negative lookbehind assertion (?<!) that disallows usernames ending in `.git` and `.atom`.
# Since this is a non-trivial problem to solve in Javascript (heavily complicate the regex, modify view code to
@@ -18,6 +148,29 @@ module Gitlab
# So `group/subgroup` will match this regex but not NAMESPACE_REGEX_STR
FULL_NAMESPACE_REGEX_STR = "(?:#{NAMESPACE_REGEX_STR}/)*#{NAMESPACE_REGEX_STR}".freeze
+ def root_namespace_route_regex
+ @root_namespace_route_regex ||= begin
+ illegal_words = Regexp.new(Regexp.union(TOP_LEVEL_ROUTES).source, Regexp::IGNORECASE)
+
+ single_line_regexp %r{
+ (?!(#{illegal_words})/)
+ #{NAMESPACE_REGEX_STR}
+ }x
+ end
+ end
+
+ def root_namespace_path_regex
+ @root_namespace_path_regex ||= %r{\A#{root_namespace_route_regex}/\z}
+ end
+
+ def full_namespace_path_regex
+ @full_namespace_path_regex ||= %r{\A#{namespace_route_regex}/\z}
+ end
+
+ def full_project_path_regex
+ @full_project_path_regex ||= %r{\A#{namespace_route_regex}/#{project_route_regex}/\z}
+ end
+
def namespace_regex
@namespace_regex ||= /\A#{NAMESPACE_REGEX_STR}\z/.freeze
end
@@ -27,7 +180,18 @@ module Gitlab
end
def namespace_route_regex
- @namespace_route_regex ||= /#{NAMESPACE_REGEX_STR}/.freeze
+ @namespace_route_regex ||= begin
+ illegal_words = Regexp.new(Regexp.union(ILLEGAL_GROUP_PATH_WORDS).source, Regexp::IGNORECASE)
+
+ single_line_regexp %r{
+ #{root_namespace_route_regex}
+ (?:
+ /
+ (?!#{illegal_words}/)
+ #{NAMESPACE_REGEX_STR}
+ )*
+ }x
+ end
end
def namespace_regex_message
@@ -53,15 +217,26 @@ module Gitlab
end
def project_path_regex
- @project_path_regex ||= /\A#{PROJECT_REGEX_STR}\z/.freeze
+ @project_path_regex ||= %r{\A#{project_route_regex}/\z}
end
def project_route_regex
- @project_route_regex ||= /#{PROJECT_REGEX_STR}/.freeze
+ @project_route_regex ||= begin
+ illegal_words = Regexp.new(Regexp.union(ILLEGAL_PROJECT_PATH_WORDS).source, Regexp::IGNORECASE)
+
+ single_line_regexp %r{
+ (?!(#{illegal_words})/)
+ #{PROJECT_REGEX_STR}
+ }x
+ end
end
def project_git_route_regex
- @project_route_git_regex ||= /#{PATH_REGEX_STR}\.git/.freeze
+ @project_git_route_regex ||= /#{project_route_regex}\.git/.freeze
+ end
+
+ def project_path_format_regex
+ @project_path_format_regex ||= /\A#{PROJECT_REGEX_STR}\z/.freeze
end
def project_path_regex_message
@@ -86,7 +261,7 @@ module Gitlab
# Valid git ref regex, see:
# https://www.kernel.org/pub/software/scm/git/docs/git-check-ref-format.html
- @git_reference_regex ||= %r{
+ @git_reference_regex ||= single_line_regexp %r{
(?!
(?# doesn't begins with)
\/| (?# rule #6)
@@ -102,7 +277,7 @@ module Gitlab
(?# doesn't end with)
(?<!\.lock) (?# rule #1)
(?<![\/.]) (?# rule #6-7)
- }x.freeze
+ }x
end
def container_registry_reference_regex
@@ -140,5 +315,13 @@ module Gitlab
"can contain only lowercase letters, digits, and '-'. " \
"Must start with a letter, and cannot end with '-'"
end
+
+ private
+
+ def single_line_regexp(regex)
+ # Turns a multiline extended regexp into a single line one,
+ # beacuse `rake routes` breaks on multiline regexes.
+ Regexp.new(regex.source.gsub(/\(\?#.+?\)/, '').gsub(/\s*/, ''), regex.options ^ Regexp::EXTENDED).freeze
+ end
end
end
diff --git a/lib/tasks/tokens.rake b/lib/tasks/tokens.rake
index 95735f43802..ad1818ff1fa 100644
--- a/lib/tasks/tokens.rake
+++ b/lib/tasks/tokens.rake
@@ -11,6 +11,11 @@ namespace :tokens do
reset_all_users_token(:reset_incoming_email_token!)
end
+ desc "Reset all GitLab RSS tokens"
+ task reset_all_rss: :environment do
+ reset_all_users_token(:reset_rss_token!)
+ end
+
def reset_all_users_token(reset_token_method)
TmpUser.find_in_batches do |batch|
puts "Processing batch starting with user ID: #{batch.first.id}"
@@ -35,4 +40,9 @@ class TmpUser < ActiveRecord::Base
write_new_token(:incoming_email_token)
save!(validate: false)
end
+
+ def reset_rss_token!
+ write_new_token(:rss_token)
+ save!(validate: false)
+ end
end
diff --git a/spec/controllers/application_controller_spec.rb b/spec/controllers/application_controller_spec.rb
index d40aae04fc3..3f99e2ff596 100644
--- a/spec/controllers/application_controller_spec.rb
+++ b/spec/controllers/application_controller_spec.rb
@@ -99,6 +99,42 @@ describe ApplicationController do
end
end
+ describe '#authenticate_user_from_rss_token' do
+ describe "authenticating a user from an RSS token" do
+ controller(described_class) do
+ def index
+ render text: 'authenticated'
+ end
+ end
+
+ context "when the 'rss_token' param is populated with the RSS token" do
+ context 'when the request format is atom' do
+ it "logs the user in" do
+ get :index, rss_token: user.rss_token, format: :atom
+ expect(response).to have_http_status 200
+ expect(response.body).to eq 'authenticated'
+ end
+ end
+
+ context 'when the request format is not atom' do
+ it "doesn't log the user in" do
+ get :index, rss_token: user.rss_token
+ expect(response.status).not_to have_http_status 200
+ expect(response.body).not_to eq 'authenticated'
+ end
+ end
+ end
+
+ context "when the 'rss_token' param is populated with an invalid RSS token" do
+ it "doesn't log the user" do
+ get :index, rss_token: "token"
+ expect(response.status).not_to eq 200
+ expect(response.body).not_to eq 'authenticated'
+ end
+ end
+ end
+ end
+
describe '#route_not_found' do
it 'renders 404 if authenticated' do
allow(controller).to receive(:current_user).and_return(user)
diff --git a/spec/controllers/import/gitlab_controller_spec.rb b/spec/controllers/import/gitlab_controller_spec.rb
index 2dbb89219d0..3270ea059fa 100644
--- a/spec/controllers/import/gitlab_controller_spec.rb
+++ b/spec/controllers/import/gitlab_controller_spec.rb
@@ -174,7 +174,7 @@ describe Import::GitlabController do
end
end
end
-
+
context 'user has chosen an existing nested namespace for the project' do
let(:parent_namespace) { create(:namespace, name: 'foo', owner: user) }
let(:nested_namespace) { create(:namespace, name: 'bar', parent: parent_namespace, owner: user) }
diff --git a/spec/controllers/projects/builds_controller_spec.rb b/spec/controllers/projects/builds_controller_spec.rb
index 3ce23c17cdc..f41503fd34e 100644
--- a/spec/controllers/projects/builds_controller_spec.rb
+++ b/spec/controllers/projects/builds_controller_spec.rb
@@ -144,6 +144,8 @@ describe Projects::BuildsController do
it 'returns a trace' do
expect(response).to have_http_status(:ok)
+ expect(json_response['id']).to eq build.id
+ expect(json_response['status']).to eq build.status
expect(json_response['html']).to eq('BUILD TRACE')
end
end
@@ -153,10 +155,23 @@ describe Projects::BuildsController do
it 'returns no traces' do
expect(response).to have_http_status(:ok)
+ expect(json_response['id']).to eq build.id
+ expect(json_response['status']).to eq build.status
expect(json_response['html']).to be_nil
end
end
+ context 'when build has a trace with ANSI sequence and Unicode' do
+ let(:build) { create(:ci_build, :unicode_trace, pipeline: pipeline) }
+
+ it 'returns a trace with Unicode' do
+ expect(response).to have_http_status(:ok)
+ expect(json_response['id']).to eq build.id
+ expect(json_response['status']).to eq build.status
+ expect(json_response['html']).to include("ヾ(´༎ຶД༎ຶ`)ノ")
+ end
+ end
+
def get_trace
get :trace, namespace_id: project.namespace,
project_id: project,
@@ -185,48 +200,6 @@ describe Projects::BuildsController do
end
end
- describe 'GET trace.json' do
- let(:pipeline) { create(:ci_pipeline, project: project) }
- let(:build) { create(:ci_build, pipeline: pipeline) }
- let(:user) { create(:user) }
-
- context 'when user is logged in as developer' do
- before do
- project.add_developer(user)
- sign_in(user)
-
- get_trace
- end
-
- it 'traces build log' do
- expect(response).to have_http_status(:ok)
- expect(json_response['id']).to eq build.id
- expect(json_response['status']).to eq build.status
- end
- end
-
- context 'when user is logged in as non member' do
- before do
- sign_in(user)
-
- get_trace
- end
-
- it 'traces build log' do
- expect(response).to have_http_status(:ok)
- expect(json_response['id']).to eq build.id
- expect(json_response['status']).to eq build.status
- end
- end
-
- def get_trace
- get :trace, namespace_id: project.namespace,
- project_id: project,
- id: build.id,
- format: :json
- end
- end
-
describe 'POST retry' do
before do
project.add_developer(user)
diff --git a/spec/controllers/projects/environments_controller_spec.rb b/spec/controllers/projects/environments_controller_spec.rb
index 62938a56bb4..7bdb4879afd 100644
--- a/spec/controllers/projects/environments_controller_spec.rb
+++ b/spec/controllers/projects/environments_controller_spec.rb
@@ -15,11 +15,11 @@ describe Projects::EnvironmentsController do
end
describe 'GET index' do
- context 'when standardrequest has been made' do
+ context 'when a request for the HTML is made' do
it 'responds with status code 200' do
get :index, environment_params
- expect(response).to be_ok
+ expect(response).to have_http_status(:ok)
end
end
@@ -89,6 +89,9 @@ describe Projects::EnvironmentsController do
create(:environment, project: project,
name: 'staging-1.0/review',
state: :available)
+ create(:environment, project: project,
+ name: 'staging-1.0/zzz',
+ state: :available)
end
context 'when using default format' do
@@ -103,7 +106,7 @@ describe Projects::EnvironmentsController do
end
context 'when using JSON format' do
- it 'responds with JSON' do
+ it 'sorts the subfolders lexicographically' do
get :folder, namespace_id: project.namespace,
project_id: project,
id: 'staging-1.0',
@@ -113,6 +116,8 @@ describe Projects::EnvironmentsController do
expect(response).not_to render_template 'folder'
expect(json_response['environments'][0])
.to include('name' => 'staging-1.0/review')
+ expect(json_response['environments'][1])
+ .to include('name' => 'staging-1.0/zzz')
end
end
end
diff --git a/spec/controllers/projects/merge_requests_controller_spec.rb b/spec/controllers/projects/merge_requests_controller_spec.rb
index f0dc6df15ee..587a5820c6f 100644
--- a/spec/controllers/projects/merge_requests_controller_spec.rb
+++ b/spec/controllers/projects/merge_requests_controller_spec.rb
@@ -127,7 +127,7 @@ describe Projects::MergeRequestsController do
recorded = ActiveRecord::QueryRecorder.new { go(format: :json) }
- expect(recorded.count).to be_within(1).of(51)
+ expect(recorded.count).to be_within(5).of(50)
expect(recorded.cached_count).to eq(0)
end
end
@@ -357,8 +357,7 @@ describe Projects::MergeRequestsController do
end
before do
- pipeline = create(:ci_empty_pipeline, project: project, sha: merge_request.diff_head_sha, ref: merge_request.source_branch)
- merge_request.update(head_pipeline: pipeline)
+ create(:ci_empty_pipeline, project: project, sha: merge_request.diff_head_sha, ref: merge_request.source_branch, head_pipeline_of: merge_request)
end
it 'returns :merge_when_pipeline_succeeds' do
@@ -1173,13 +1172,13 @@ describe Projects::MergeRequestsController do
let!(:pipeline) do
create(:ci_pipeline, project: merge_request.source_project,
ref: merge_request.source_branch,
- sha: merge_request.diff_head_sha)
+ sha: merge_request.diff_head_sha,
+ head_pipeline_of: merge_request)
end
let(:status) { pipeline.detailed_status(double('user')) }
before do
- merge_request.update(head_pipeline: pipeline)
get_pipeline_status
end
diff --git a/spec/controllers/projects_controller_spec.rb b/spec/controllers/projects_controller_spec.rb
index a8be6768a47..4f6fc6691be 100644
--- a/spec/controllers/projects_controller_spec.rb
+++ b/spec/controllers/projects_controller_spec.rb
@@ -226,6 +226,50 @@ describe ProjectsController do
end
end
+ describe '#transfer' do
+ render_views
+
+ let(:project) { create(:project) }
+ let(:admin) { create(:admin) }
+ let(:new_namespace) { create(:namespace) }
+
+ it 'updates namespace' do
+ sign_in(admin)
+
+ put :transfer,
+ namespace_id: project.namespace.path,
+ new_namespace_id: new_namespace.id,
+ id: project.path,
+ format: :js
+
+ project.reload
+
+ expect(project.namespace).to eq(new_namespace)
+ expect(response).to have_http_status(200)
+ end
+
+ context 'when new namespace is empty' do
+ it 'project namespace is not changed' do
+ controller.instance_variable_set(:@project, project)
+ sign_in(admin)
+
+ old_namespace = project.namespace
+
+ put :transfer,
+ namespace_id: old_namespace.path,
+ new_namespace_id: nil,
+ id: project.path,
+ format: :js
+
+ project.reload
+
+ expect(project.namespace).to eq(old_namespace)
+ expect(response).to have_http_status(200)
+ expect(flash[:alert]).to eq 'Please select a new namespace for your project.'
+ end
+ end
+ end
+
describe "#destroy" do
let(:admin) { create(:admin) }
diff --git a/spec/factories/ci/builds.rb b/spec/factories/ci/builds.rb
index 78ddd8d5584..f5e99fdf00b 100644
--- a/spec/factories/ci/builds.rb
+++ b/spec/factories/ci/builds.rb
@@ -128,6 +128,16 @@ FactoryGirl.define do
end
end
+ trait :unicode_trace do
+ after(:create) do |build, evaluator|
+ trace = File.binread(
+ File.expand_path(
+ Rails.root.join('spec/fixtures/trace/ansi-sequence-and-unicode')))
+
+ build.trace.set(trace)
+ end
+ end
+
trait :erased do
erased_at Time.now
erased_by factory: :user
diff --git a/spec/factories/ci/pipelines.rb b/spec/factories/ci/pipelines.rb
index 561fbc8e247..361c5b9a49e 100644
--- a/spec/factories/ci/pipelines.rb
+++ b/spec/factories/ci/pipelines.rb
@@ -20,6 +20,15 @@ FactoryGirl.define do
end
end
+ # Persist merge request head_pipeline_id
+ # on pipeline factories to avoid circular references
+ transient { head_pipeline_of nil }
+
+ after(:create) do |pipeline, evaluator|
+ merge_request = evaluator.head_pipeline_of
+ merge_request&.update(head_pipeline: pipeline)
+ end
+
factory :ci_pipeline do
transient { config nil }
diff --git a/spec/factories/users.rb b/spec/factories/users.rb
index 33fa80772ff..e60fe713bc3 100644
--- a/spec/factories/users.rb
+++ b/spec/factories/users.rb
@@ -8,6 +8,10 @@ FactoryGirl.define do
confirmation_token { nil }
can_create_group true
+ before(:create) do |user|
+ user.ensure_rss_token
+ end
+
trait :admin do
admin true
end
diff --git a/spec/features/admin/admin_labels_spec.rb b/spec/features/admin/admin_labels_spec.rb
index fa3d9ee25c0..a9251db13e5 100644
--- a/spec/features/admin/admin_labels_spec.rb
+++ b/spec/features/admin/admin_labels_spec.rb
@@ -34,11 +34,11 @@ RSpec.describe 'admin issues labels' do
page.within '.labels' do
page.all('.btn-remove').each do |remove|
remove.click
- wait_for_ajax
+ wait_for_requests
end
end
- wait_for_ajax
+ wait_for_requests
expect(page).to have_content("There are no labels yet")
expect(page).not_to have_content('bug')
diff --git a/spec/features/admin/admin_users_spec.rb b/spec/features/admin/admin_users_spec.rb
index c5b1ef1295c..12cf59f42b0 100644
--- a/spec/features/admin/admin_users_spec.rb
+++ b/spec/features/admin/admin_users_spec.rb
@@ -277,7 +277,7 @@ describe "Admin::Users", feature: true do
page.within(first('.group_member')) do
find('.btn-remove').click
end
- wait_for_ajax
+ wait_for_requests
expect(page).not_to have_selector('.group_member')
end
diff --git a/spec/features/atom/dashboard_issues_spec.rb b/spec/features/atom/dashboard_issues_spec.rb
index 9ea325ab41b..711c8a710f3 100644
--- a/spec/features/atom/dashboard_issues_spec.rb
+++ b/spec/features/atom/dashboard_issues_spec.rb
@@ -20,13 +20,20 @@ describe "Dashboard Issues Feed", feature: true do
expect(body).to have_selector('title', text: "#{user.name} issues")
end
+ it "renders atom feed via RSS token" do
+ visit issues_dashboard_path(:atom, rss_token: user.rss_token)
+
+ expect(response_headers['Content-Type']).to have_content('application/atom+xml')
+ expect(body).to have_selector('title', text: "#{user.name} issues")
+ end
+
it "renders atom feed with url parameters" do
- visit issues_dashboard_path(:atom, private_token: user.private_token, state: 'opened', assignee_id: user.id)
+ visit issues_dashboard_path(:atom, rss_token: user.rss_token, state: 'opened', assignee_id: user.id)
link = find('link[type="application/atom+xml"]')
params = CGI.parse(URI.parse(link[:href]).query)
- expect(params).to include('private_token' => [user.private_token])
+ expect(params).to include('rss_token' => [user.rss_token])
expect(params).to include('state' => ['opened'])
expect(params).to include('assignee_id' => [user.id.to_s])
end
@@ -35,7 +42,7 @@ describe "Dashboard Issues Feed", feature: true do
let!(:issue2) { create(:issue, author: user, assignees: [assignee], project: project2, description: 'test desc') }
it "renders issue fields" do
- visit issues_dashboard_path(:atom, private_token: user.private_token)
+ visit issues_dashboard_path(:atom, rss_token: user.rss_token)
entry = find(:xpath, "//feed/entry[contains(summary/text(),'#{issue2.title}')]")
@@ -58,7 +65,7 @@ describe "Dashboard Issues Feed", feature: true do
end
it "renders issue label and milestone info" do
- visit issues_dashboard_path(:atom, private_token: user.private_token)
+ visit issues_dashboard_path(:atom, rss_token: user.rss_token)
entry = find(:xpath, "//feed/entry[contains(summary/text(),'#{issue1.title}')]")
diff --git a/spec/features/atom/dashboard_spec.rb b/spec/features/atom/dashboard_spec.rb
index 746df36bb25..1df058b023c 100644
--- a/spec/features/atom/dashboard_spec.rb
+++ b/spec/features/atom/dashboard_spec.rb
@@ -11,6 +11,13 @@ describe "Dashboard Feed", feature: true do
end
end
+ context "projects atom feed via RSS token" do
+ it "renders projects atom feed" do
+ visit dashboard_projects_path(:atom, rss_token: user.rss_token)
+ expect(body).to have_selector('feed title')
+ end
+ end
+
context 'feed content' do
let(:project) { create(:project) }
let(:issue) { create(:issue, project: project, author: user, description: '') }
@@ -20,7 +27,7 @@ describe "Dashboard Feed", feature: true do
project.team << [user, :master]
issue_event(issue, user)
note_event(note, user)
- visit dashboard_projects_path(:atom, private_token: user.private_token)
+ visit dashboard_projects_path(:atom, rss_token: user.rss_token)
end
it "has issue opened event" do
diff --git a/spec/features/atom/issues_spec.rb b/spec/features/atom/issues_spec.rb
index 4f6754ad541..a61231ea254 100644
--- a/spec/features/atom/issues_spec.rb
+++ b/spec/features/atom/issues_spec.rb
@@ -43,25 +43,40 @@ describe 'Issues Feed', feature: true do
end
end
+ context 'when authenticated via RSS token' do
+ it 'renders atom feed' 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(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)
+ expect(body).to have_selector('assignee email', text: issue.assignees.first.public_email)
+ expect(body).to have_selector('entry summary', text: issue.title)
+ end
+ end
+
it "renders atom feed with url parameters for project issues" do
visit namespace_project_issues_path(project.namespace, project,
- :atom, private_token: user.private_token, state: 'opened', assignee_id: user.id)
+ :atom, rss_token: user.rss_token, state: 'opened', assignee_id: user.id)
link = find('link[type="application/atom+xml"]')
params = CGI.parse(URI.parse(link[:href]).query)
- expect(params).to include('private_token' => [user.private_token])
+ expect(params).to include('rss_token' => [user.rss_token])
expect(params).to include('state' => ['opened'])
expect(params).to include('assignee_id' => [user.id.to_s])
end
it "renders atom feed with url parameters for group issues" do
- visit issues_group_path(group, :atom, private_token: user.private_token, state: 'opened', assignee_id: user.id)
+ visit issues_group_path(group, :atom, rss_token: user.rss_token, state: 'opened', assignee_id: user.id)
link = find('link[type="application/atom+xml"]')
params = CGI.parse(URI.parse(link[:href]).query)
- expect(params).to include('private_token' => [user.private_token])
+ expect(params).to include('rss_token' => [user.rss_token])
expect(params).to include('state' => ['opened'])
expect(params).to include('assignee_id' => [user.id.to_s])
end
diff --git a/spec/features/atom/users_spec.rb b/spec/features/atom/users_spec.rb
index 7a2987e815d..fae5aaa52bd 100644
--- a/spec/features/atom/users_spec.rb
+++ b/spec/features/atom/users_spec.rb
@@ -11,6 +11,13 @@ describe "User Feed", feature: true do
end
end
+ context 'user atom feed via RSS token' do
+ it "renders user atom feed" do
+ visit user_path(user, :atom, rss_token: user.rss_token)
+ expect(body).to have_selector('feed title')
+ end
+ end
+
context 'feed content' do
let(:project) { create(:project) }
let(:issue) do
@@ -40,7 +47,7 @@ describe "User Feed", feature: true do
issue_event(issue, user)
note_event(note, user)
merge_request_event(merge_request, user)
- visit user_path(user, :atom, private_token: user.private_token)
+ visit user_path(user, :atom, rss_token: user.rss_token)
end
it 'has issue opened event' do
diff --git a/spec/features/auto_deploy_spec.rb b/spec/features/auto_deploy_spec.rb
index 6c7423e4922..1cf7396bbac 100644
--- a/spec/features/auto_deploy_spec.rb
+++ b/spec/features/auto_deploy_spec.rb
@@ -46,7 +46,7 @@ describe 'Auto deploy' do
within '.gitlab-ci-yml-selector' do
click_on 'OpenShift'
end
- wait_for_ajax
+ wait_for_requests
click_button 'Commit changes'
expect(page).to have_content('New Merge Request From auto-deploy into master')
diff --git a/spec/features/boards/add_issues_modal_spec.rb b/spec/features/boards/add_issues_modal_spec.rb
index 505e0b5c355..32ac265814f 100644
--- a/spec/features/boards/add_issues_modal_spec.rb
+++ b/spec/features/boards/add_issues_modal_spec.rb
@@ -1,8 +1,6 @@
require 'rails_helper'
describe 'Issue Boards add issue modal', :feature, :js do
- include WaitForVueResource
-
let(:project) { create(:empty_project, :public) }
let(:board) { create(:board, project: project) }
let(:user) { create(:user) }
@@ -19,13 +17,13 @@ describe 'Issue Boards add issue modal', :feature, :js do
login_as(user)
visit namespace_project_board_path(project.namespace, project, board)
- wait_for_vue_resource
+ wait_for_requests
end
it 'resets filtered search state' do
visit namespace_project_board_path(project.namespace, project, board, search: 'testing')
- wait_for_vue_resource
+ wait_for_requests
click_button('Add issues')
@@ -74,7 +72,7 @@ describe 'Issue Boards add issue modal', :feature, :js do
before do
click_button('Add issues')
- wait_for_vue_resource
+ wait_for_requests
end
it 'loads issues' do
@@ -107,7 +105,7 @@ describe 'Issue Boards add issue modal', :feature, :js do
click_button('Add issues')
- wait_for_vue_resource
+ wait_for_requests
page.within('.add-issues-modal') do
expect(find('.add-issues-footer')).not_to have_button(planning.title)
@@ -122,7 +120,7 @@ describe 'Issue Boards add issue modal', :feature, :js do
find('.form-control').native.send_keys(issue.title)
find('.form-control').native.send_keys(:enter)
- wait_for_vue_resource
+ wait_for_requests
expect(page).to have_selector('.card', count: 1)
end
@@ -133,7 +131,7 @@ describe 'Issue Boards add issue modal', :feature, :js do
find('.form-control').native.send_keys('testing search')
find('.form-control').native.send_keys(:enter)
- wait_for_vue_resource
+ wait_for_requests
expect(page).not_to have_selector('.card')
expect(page).not_to have_content("You haven't added any issues to your project yet")
diff --git a/spec/features/boards/boards_spec.rb b/spec/features/boards/boards_spec.rb
index 18585488e26..ba27db23ced 100644
--- a/spec/features/boards/boards_spec.rb
+++ b/spec/features/boards/boards_spec.rb
@@ -1,7 +1,6 @@
require 'rails_helper'
describe 'Issue Boards', feature: true, js: true do
- include WaitForVueResource
include DragTo
let(:project) { create(:empty_project, :public) }
@@ -19,7 +18,7 @@ describe 'Issue Boards', feature: true, js: true do
context 'no lists' do
before do
visit namespace_project_board_path(project.namespace, project, board)
- wait_for_vue_resource
+ wait_for_requests
expect(page).to have_selector('.board', count: 2)
end
@@ -46,7 +45,7 @@ describe 'Issue Boards', feature: true, js: true do
page.within(find('.board-blank-state')) do
click_button('Add default lists')
end
- wait_for_vue_resource
+ wait_for_requests
expect(page).to have_selector('.board', count: 3)
@@ -84,7 +83,7 @@ describe 'Issue Boards', feature: true, js: true do
before do
visit namespace_project_board_path(project.namespace, project, board)
- wait_for_vue_resource
+ wait_for_requests
expect(page).to have_selector('.board', count: 3)
expect(find('.board:nth-child(1)')).to have_selector('.card')
@@ -117,7 +116,7 @@ describe 'Issue Boards', feature: true, js: true do
find('.filtered-search').set(issue8.title)
find('.filtered-search').native.send_keys(:enter)
- wait_for_vue_resource
+ wait_for_requests
expect(find('.board:nth-child(1)')).to have_selector('.card', count: 0)
expect(find('.board:nth-child(2)')).to have_selector('.card', count: 0)
@@ -128,7 +127,7 @@ describe 'Issue Boards', feature: true, js: true do
find('.filtered-search').set(issue5.title)
find('.filtered-search').native.send_keys(:enter)
- wait_for_vue_resource
+ wait_for_requests
expect(find('.board:nth-child(1)')).to have_selector('.card', count: 1)
expect(find('.board:nth-child(2)')).to have_selector('.card', count: 0)
@@ -140,20 +139,20 @@ describe 'Issue Boards', feature: true, js: true do
find('.board-delete').click
end
- wait_for_vue_resource
+ wait_for_requests
expect(page).to have_selector('.board', count: 2)
end
it 'removes checkmark in new list dropdown after deleting' do
click_button 'Add list'
- wait_for_ajax
+ wait_for_requests
page.within(find('.board:nth-child(1)')) do
find('.board-delete').click
end
- wait_for_vue_resource
+ wait_for_requests
expect(page).to have_selector('.board', count: 2)
end
@@ -164,7 +163,7 @@ describe 'Issue Boards', feature: true, js: true do
end
visit namespace_project_board_path(project.namespace, project, board)
- wait_for_vue_resource
+ wait_for_requests
page.within(find('.board', match: :first)) do
expect(page.find('.board-header')).to have_content('58')
@@ -172,13 +171,13 @@ describe 'Issue Boards', feature: true, js: true do
expect(page).to have_content('Showing 20 of 58 issues')
evaluate_script("document.querySelectorAll('.board .board-list')[0].scrollTop = document.querySelectorAll('.board .board-list')[0].scrollHeight")
- wait_for_vue_resource
+ wait_for_requests
expect(page).to have_selector('.card', count: 40)
expect(page).to have_content('Showing 40 of 58 issues')
evaluate_script("document.querySelectorAll('.board .board-list')[0].scrollTop = document.querySelectorAll('.board .board-list')[0].scrollHeight")
- wait_for_vue_resource
+ wait_for_requests
expect(page).to have_selector('.card', count: 58)
expect(page).to have_content('Showing all issues')
@@ -188,7 +187,7 @@ describe 'Issue Boards', feature: true, js: true do
context 'closed' do
it 'shows list of closed issues' do
wait_for_board_cards(3, 1)
- wait_for_ajax
+ wait_for_requests
end
it 'moves issue to closed' do
@@ -272,7 +271,7 @@ describe 'Issue Boards', feature: true, js: true do
context 'new list' do
it 'shows all labels in new list dropdown' do
click_button 'Add list'
- wait_for_ajax
+ wait_for_requests
page.within('.dropdown-menu-issues-board-new') do
expect(page).to have_content(planning.title)
@@ -283,52 +282,52 @@ describe 'Issue Boards', feature: true, js: true do
it 'creates new list for label' do
click_button 'Add list'
- wait_for_ajax
+ wait_for_requests
page.within('.dropdown-menu-issues-board-new') do
click_link testing.title
end
- wait_for_vue_resource
+ wait_for_requests
expect(page).to have_selector('.board', count: 4)
end
it 'creates new list for Backlog label' do
click_button 'Add list'
- wait_for_ajax
+ wait_for_requests
page.within('.dropdown-menu-issues-board-new') do
click_link backlog.title
end
- wait_for_vue_resource
+ wait_for_requests
expect(page).to have_selector('.board', count: 4)
end
it 'creates new list for Closed label' do
click_button 'Add list'
- wait_for_ajax
+ wait_for_requests
page.within('.dropdown-menu-issues-board-new') do
click_link closed.title
end
- wait_for_vue_resource
+ wait_for_requests
expect(page).to have_selector('.board', count: 4)
end
it 'keeps dropdown open after adding new list' do
click_button 'Add list'
- wait_for_ajax
+ wait_for_requests
page.within('.dropdown-menu-issues-board-new') do
click_link closed.title
end
- wait_for_vue_resource
+ wait_for_requests
expect(page).to have_css('#js-add-list.open')
end
@@ -336,7 +335,7 @@ describe 'Issue Boards', feature: true, js: true do
it 'creates new list from a new label' do
click_button 'Add list'
- wait_for_ajax
+ wait_for_requests
click_link 'Create new label'
@@ -346,8 +345,8 @@ describe 'Issue Boards', feature: true, js: true do
click_button 'Create'
- wait_for_ajax
- wait_for_vue_resource
+ wait_for_requests
+ wait_for_requests
expect(page).to have_selector('.board', count: 4)
end
@@ -360,7 +359,7 @@ describe 'Issue Boards', feature: true, js: true do
click_filter_link(user2.username)
submit_filter
- wait_for_vue_resource
+ wait_for_requests
wait_for_board_cards(1, 1)
wait_for_empty_boards((2..3))
end
@@ -370,7 +369,7 @@ describe 'Issue Boards', feature: true, js: true do
click_filter_link(user.username)
submit_filter
- wait_for_vue_resource
+ wait_for_requests
wait_for_board_cards(1, 1)
wait_for_empty_boards((2..3))
@@ -381,7 +380,7 @@ describe 'Issue Boards', feature: true, js: true do
click_filter_link(milestone.title)
submit_filter
- wait_for_vue_resource
+ wait_for_requests
wait_for_board_cards(1, 1)
wait_for_board_cards(2, 0)
wait_for_board_cards(3, 0)
@@ -392,7 +391,7 @@ describe 'Issue Boards', feature: true, js: true do
click_filter_link(testing.title)
submit_filter
- wait_for_vue_resource
+ wait_for_requests
wait_for_board_cards(1, 1)
wait_for_empty_boards((2..3))
end
@@ -407,7 +406,7 @@ describe 'Issue Boards', feature: true, js: true do
wait_for_board_cards(1, 1)
wait_for_empty_boards((2..3))
- wait_for_vue_resource
+ wait_for_requests
page.within(find('.board', match: :first)) do
expect(page.find('.board-header')).to have_content('1')
@@ -442,7 +441,7 @@ describe 'Issue Boards', feature: true, js: true do
click_filter_link(testing.title)
submit_filter
- wait_for_vue_resource
+ wait_for_requests
page.within(find('.board', match: :first)) do
expect(page.find('.board-header')).to have_content('51')
@@ -470,7 +469,7 @@ describe 'Issue Boards', feature: true, js: true do
submit_filter
- wait_for_vue_resource
+ wait_for_requests
wait_for_board_cards(1, 1)
wait_for_empty_boards((2..3))
@@ -481,14 +480,14 @@ describe 'Issue Boards', feature: true, js: true do
expect(page).to have_selector('.card', count: 8)
expect(find('.card', match: :first)).to have_content(bug.title)
click_button(bug.title)
- wait_for_vue_resource
+ wait_for_requests
end
page.within('.tokens-container') do
expect(page).to have_content(bug.title)
end
- wait_for_vue_resource
+ wait_for_requests
wait_for_board_cards(1, 1)
wait_for_empty_boards((2..3))
@@ -500,12 +499,12 @@ describe 'Issue Boards', feature: true, js: true do
click_button(bug.title)
end
- wait_for_vue_resource
+ wait_for_requests
expect(page).to have_selector('.card', count: 1)
end
- wait_for_vue_resource
+ wait_for_requests
end
end
end
@@ -513,7 +512,7 @@ describe 'Issue Boards', feature: true, js: true do
context 'keyboard shortcuts' do
before do
visit namespace_project_board_path(project.namespace, project, board)
- wait_for_vue_resource
+ wait_for_requests
end
it 'allows user to use keyboard shortcuts' do
@@ -526,7 +525,7 @@ describe 'Issue Boards', feature: true, js: true do
before do
logout
visit namespace_project_board_path(project.namespace, project, board)
- wait_for_vue_resource
+ wait_for_requests
end
it 'displays lists' do
@@ -550,7 +549,7 @@ describe 'Issue Boards', feature: true, js: true do
logout
login_as(user_guest)
visit namespace_project_board_path(project.namespace, project, board)
- wait_for_vue_resource
+ wait_for_requests
end
it 'does not show create new list' do
diff --git a/spec/features/boards/issue_ordering_spec.rb b/spec/features/boards/issue_ordering_spec.rb
index bfa2a72a256..6c40cb2c9eb 100644
--- a/spec/features/boards/issue_ordering_spec.rb
+++ b/spec/features/boards/issue_ordering_spec.rb
@@ -1,7 +1,6 @@
require 'rails_helper'
describe 'Issue Boards', :feature, :js do
- include WaitForVueResource
include DragTo
let(:project) { create(:empty_project, :public) }
@@ -24,7 +23,7 @@ describe 'Issue Boards', :feature, :js do
before do
visit namespace_project_board_path(project.namespace, project, board)
- wait_for_vue_resource
+ wait_for_requests
expect(page).to have_selector('.board', count: 2)
end
@@ -38,7 +37,7 @@ describe 'Issue Boards', :feature, :js do
it 'moves un-ordered issue to top of list' do
drag(from_index: 3, to_index: 0)
- wait_for_vue_resource
+ wait_for_requests
page.within(first('.board')) do
expect(first('.card')).to have_content(issue4.title)
@@ -49,7 +48,7 @@ describe 'Issue Boards', :feature, :js do
context 'ordering in list' do
before do
visit namespace_project_board_path(project.namespace, project, board)
- wait_for_vue_resource
+ wait_for_requests
expect(page).to have_selector('.board', count: 2)
end
@@ -57,7 +56,7 @@ describe 'Issue Boards', :feature, :js do
it 'moves from middle to top' do
drag(from_index: 1, to_index: 0)
- wait_for_vue_resource
+ wait_for_requests
expect(first('.card')).to have_content(issue2.title)
end
@@ -65,7 +64,7 @@ describe 'Issue Boards', :feature, :js do
it 'moves from middle to bottom' do
drag(from_index: 1, to_index: 2)
- wait_for_vue_resource
+ wait_for_requests
expect(all('.card').last).to have_content(issue2.title)
end
@@ -73,7 +72,7 @@ describe 'Issue Boards', :feature, :js do
it 'moves from top to bottom' do
drag(from_index: 0, to_index: 2)
- wait_for_vue_resource
+ wait_for_requests
expect(all('.card').last).to have_content(issue3.title)
end
@@ -81,7 +80,7 @@ describe 'Issue Boards', :feature, :js do
it 'moves from bottom to top' do
drag(from_index: 2, to_index: 0)
- wait_for_vue_resource
+ wait_for_requests
expect(first('.card')).to have_content(issue1.title)
end
@@ -89,7 +88,7 @@ describe 'Issue Boards', :feature, :js do
it 'moves from top to middle' do
drag(from_index: 0, to_index: 1)
- wait_for_vue_resource
+ wait_for_requests
expect(first('.card')).to have_content(issue2.title)
end
@@ -97,7 +96,7 @@ describe 'Issue Boards', :feature, :js do
it 'moves from bottom to middle' do
drag(from_index: 2, to_index: 1)
- wait_for_vue_resource
+ wait_for_requests
expect(all('.card').last).to have_content(issue2.title)
end
@@ -112,7 +111,7 @@ describe 'Issue Boards', :feature, :js do
before do
visit namespace_project_board_path(project.namespace, project, board)
- wait_for_vue_resource
+ wait_for_requests
expect(page).to have_selector('.board', count: 3)
end
@@ -120,7 +119,7 @@ describe 'Issue Boards', :feature, :js do
it 'moves to top of another list' do
drag(list_from_index: 0, list_to_index: 1)
- wait_for_vue_resource
+ wait_for_requests
expect(first('.board')).to have_selector('.card', count: 2)
expect(all('.board')[1]).to have_selector('.card', count: 4)
@@ -133,7 +132,7 @@ describe 'Issue Boards', :feature, :js do
it 'moves to bottom of another list' do
drag(list_from_index: 0, list_to_index: 1, to_index: 2)
- wait_for_vue_resource
+ wait_for_requests
expect(first('.board')).to have_selector('.card', count: 2)
expect(all('.board')[1]).to have_selector('.card', count: 4)
@@ -146,7 +145,7 @@ describe 'Issue Boards', :feature, :js do
it 'moves to index of another list' do
drag(list_from_index: 0, list_to_index: 1, to_index: 1)
- wait_for_vue_resource
+ wait_for_requests
expect(first('.board')).to have_selector('.card', count: 2)
expect(all('.board')[1]).to have_selector('.card', count: 4)
diff --git a/spec/features/boards/keyboard_shortcut_spec.rb b/spec/features/boards/keyboard_shortcut_spec.rb
index a9cc6c49f8e..c2167ba12cd 100644
--- a/spec/features/boards/keyboard_shortcut_spec.rb
+++ b/spec/features/boards/keyboard_shortcut_spec.rb
@@ -1,8 +1,6 @@
require 'rails_helper'
describe 'Issue Boards shortcut', feature: true, js: true do
- include WaitForVueResource
-
let(:project) { create(:empty_project) }
before do
@@ -17,6 +15,6 @@ describe 'Issue Boards shortcut', feature: true, js: true do
find('body').native.send_keys('gb')
expect(page).to have_selector('.boards-list')
- wait_for_vue_resource
+ wait_for_requests
end
end
diff --git a/spec/features/boards/modal_filter_spec.rb b/spec/features/boards/modal_filter_spec.rb
index e1367c675e5..ce132bfd979 100644
--- a/spec/features/boards/modal_filter_spec.rb
+++ b/spec/features/boards/modal_filter_spec.rb
@@ -1,8 +1,6 @@
require 'rails_helper'
describe 'Issue Boards add issue modal filtering', :feature, :js do
- include WaitForVueResource
-
let(:project) { create(:empty_project, :public) }
let(:board) { create(:board, project: project) }
let(:planning) { create(:label, project: project, name: 'Planning') }
@@ -24,7 +22,7 @@ describe 'Issue Boards add issue modal filtering', :feature, :js do
find('.form-control').native.send_keys('testing empty state')
find('.form-control').native.send_keys(:enter)
- wait_for_vue_resource
+ wait_for_requests
expect(page).to have_content('There are no issues to show.')
end
@@ -38,7 +36,7 @@ describe 'Issue Boards add issue modal filtering', :feature, :js do
submit_filter
page.within('.add-issues-modal') do
- wait_for_vue_resource
+ wait_for_requests
expect(page).to have_selector('.card', count: 0)
@@ -48,7 +46,7 @@ describe 'Issue Boards add issue modal filtering', :feature, :js do
click_button('Add issues')
page.within('.add-issues-modal') do
- wait_for_vue_resource
+ wait_for_requests
expect(page).to have_selector('.card', count: 1)
end
@@ -62,13 +60,13 @@ describe 'Issue Boards add issue modal filtering', :feature, :js do
submit_filter
page.within('.add-issues-modal') do
- wait_for_vue_resource
+ wait_for_requests
expect(page).to have_selector('.card', count: 0)
find('.clear-search').click
- wait_for_vue_resource
+ wait_for_requests
expect(page).to have_selector('.card', count: 1)
end
@@ -89,7 +87,7 @@ describe 'Issue Boards add issue modal filtering', :feature, :js do
submit_filter
page.within('.add-issues-modal') do
- wait_for_vue_resource
+ wait_for_requests
expect(page).to have_selector('.js-visual-token', text: user2.username)
expect(page).to have_selector('.card', count: 1)
@@ -112,7 +110,7 @@ describe 'Issue Boards add issue modal filtering', :feature, :js do
submit_filter
page.within('.add-issues-modal') do
- wait_for_vue_resource
+ wait_for_requests
expect(page).to have_selector('.js-visual-token', text: 'none')
expect(page).to have_selector('.card', count: 1)
@@ -125,7 +123,7 @@ describe 'Issue Boards add issue modal filtering', :feature, :js do
submit_filter
page.within('.add-issues-modal') do
- wait_for_vue_resource
+ wait_for_requests
expect(page).to have_selector('.js-visual-token', text: user2.username)
expect(page).to have_selector('.card', count: 1)
@@ -147,7 +145,7 @@ describe 'Issue Boards add issue modal filtering', :feature, :js do
submit_filter
page.within('.add-issues-modal') do
- wait_for_vue_resource
+ wait_for_requests
expect(page).to have_selector('.js-visual-token', text: 'upcoming')
expect(page).to have_selector('.card', count: 0)
@@ -160,7 +158,7 @@ describe 'Issue Boards add issue modal filtering', :feature, :js do
submit_filter
page.within('.add-issues-modal') do
- wait_for_vue_resource
+ wait_for_requests
expect(page).to have_selector('.js-visual-token', text: milestone.name)
expect(page).to have_selector('.card', count: 1)
@@ -182,7 +180,7 @@ describe 'Issue Boards add issue modal filtering', :feature, :js do
submit_filter
page.within('.add-issues-modal') do
- wait_for_vue_resource
+ wait_for_requests
expect(page).to have_selector('.js-visual-token', text: 'none')
expect(page).to have_selector('.card', count: 1)
@@ -195,7 +193,7 @@ describe 'Issue Boards add issue modal filtering', :feature, :js do
submit_filter
page.within('.add-issues-modal') do
- wait_for_vue_resource
+ wait_for_requests
expect(page).to have_selector('.js-visual-token', text: label.title)
expect(page).to have_selector('.card', count: 1)
@@ -205,7 +203,7 @@ describe 'Issue Boards add issue modal filtering', :feature, :js do
def visit_board
visit namespace_project_board_path(project.namespace, project, board)
- wait_for_vue_resource
+ wait_for_requests
click_button('Add issues')
end
diff --git a/spec/features/boards/new_issue_spec.rb b/spec/features/boards/new_issue_spec.rb
index f04a1a89e96..0e98f994018 100644
--- a/spec/features/boards/new_issue_spec.rb
+++ b/spec/features/boards/new_issue_spec.rb
@@ -1,8 +1,6 @@
require 'rails_helper'
describe 'Issue Boards new issue', feature: true, js: true do
- include WaitForVueResource
-
let(:project) { create(:empty_project, :public) }
let(:board) { create(:board, project: project) }
let!(:list) { create(:list, board: board, position: 0) }
@@ -15,7 +13,7 @@ describe 'Issue Boards new issue', feature: true, js: true do
login_as(user)
visit namespace_project_board_path(project.namespace, project, board)
- wait_for_vue_resource
+ wait_for_requests
expect(page).to have_selector('.board', count: 2)
end
@@ -60,7 +58,7 @@ describe 'Issue Boards new issue', feature: true, js: true do
click_button 'Submit issue'
end
- wait_for_vue_resource
+ wait_for_requests
page.within(first('.board .board-issue-count')) do
expect(page).to have_content('1')
@@ -77,7 +75,7 @@ describe 'Issue Boards new issue', feature: true, js: true do
click_button 'Submit issue'
end
- wait_for_vue_resource
+ wait_for_requests
expect(page).to have_selector('.issue-boards-sidebar')
end
@@ -86,7 +84,7 @@ describe 'Issue Boards new issue', feature: true, js: true do
context 'unauthorized user' do
before do
visit namespace_project_board_path(project.namespace, project, board)
- wait_for_vue_resource
+ wait_for_requests
end
it 'does not display new issue button' do
diff --git a/spec/features/boards/sidebar_spec.rb b/spec/features/boards/sidebar_spec.rb
index 4667be49fe6..34f4d765117 100644
--- a/spec/features/boards/sidebar_spec.rb
+++ b/spec/features/boards/sidebar_spec.rb
@@ -1,8 +1,6 @@
require 'rails_helper'
describe 'Issue Boards', feature: true, js: true do
- include WaitForVueResource
-
let(:user) { create(:user) }
let(:user2) { create(:user) }
let(:project) { create(:empty_project, :public) }
@@ -25,7 +23,7 @@ describe 'Issue Boards', feature: true, js: true do
login_as(user)
visit namespace_project_board_path(project.namespace, project, board)
- wait_for_vue_resource
+ wait_for_requests
end
after do
@@ -74,7 +72,7 @@ describe 'Issue Boards', feature: true, js: true do
click_button 'Remove from board'
end
- wait_for_vue_resource
+ wait_for_requests
page.within(first('.board')) do
expect(page).to have_selector('.card', count: 1)
@@ -88,12 +86,12 @@ describe 'Issue Boards', feature: true, js: true do
page.within('.assignee') do
click_link 'Edit'
- wait_for_ajax
+ wait_for_requests
page.within('.dropdown-menu-user') do
click_link user.name
- wait_for_vue_resource
+ wait_for_requests
end
expect(page).to have_content(user.name)
@@ -109,13 +107,13 @@ describe 'Issue Boards', feature: true, js: true do
page.within('.assignee') do
click_link 'Edit'
- wait_for_ajax
+ wait_for_requests
page.within('.dropdown-menu-user') do
click_link 'Unassigned'
end
- wait_for_vue_resource
+ wait_for_requests
expect(page).to have_content('No assignee')
end
@@ -131,7 +129,7 @@ describe 'Issue Boards', feature: true, js: true do
click_button 'assign yourself'
- wait_for_vue_resource
+ wait_for_requests
expect(page).to have_content(user.name)
end
@@ -145,12 +143,12 @@ describe 'Issue Boards', feature: true, js: true do
page.within('.assignee') do
click_link 'Edit'
- wait_for_ajax
+ wait_for_requests
page.within('.dropdown-menu-user') do
click_link user.name
- wait_for_vue_resource
+ wait_for_requests
end
expect(page).to have_content(user.name)
@@ -162,7 +160,7 @@ describe 'Issue Boards', feature: true, js: true do
page.within('.assignee') do
click_link 'Edit'
-
+
expect(find('.dropdown-menu')).to have_selector('.is-active')
end
end
@@ -175,11 +173,11 @@ describe 'Issue Boards', feature: true, js: true do
page.within('.milestone') do
click_link 'Edit'
- wait_for_ajax
+ wait_for_requests
click_link milestone.title
- wait_for_vue_resource
+ wait_for_requests
page.within('.value') do
expect(page).to have_content(milestone.title)
@@ -193,11 +191,11 @@ describe 'Issue Boards', feature: true, js: true do
page.within('.milestone') do
click_link 'Edit'
- wait_for_ajax
+ wait_for_requests
click_link "No Milestone"
- wait_for_vue_resource
+ wait_for_requests
page.within('.value') do
expect(page).not_to have_content(milestone.title)
@@ -215,7 +213,7 @@ describe 'Issue Boards', feature: true, js: true do
click_button Date.today.day
- wait_for_vue_resource
+ wait_for_requests
expect(page).to have_content(Date.today.to_s(:medium))
end
@@ -229,11 +227,11 @@ describe 'Issue Boards', feature: true, js: true do
page.within('.labels') do
click_link 'Edit'
- wait_for_ajax
+ wait_for_requests
click_link bug.title
- wait_for_vue_resource
+ wait_for_requests
find('.dropdown-menu-close-icon').click
@@ -253,12 +251,12 @@ describe 'Issue Boards', feature: true, js: true do
page.within('.labels') do
click_link 'Edit'
- wait_for_ajax
+ wait_for_requests
click_link bug.title
click_link regression.title
- wait_for_vue_resource
+ wait_for_requests
find('.dropdown-menu-close-icon').click
@@ -280,11 +278,11 @@ describe 'Issue Boards', feature: true, js: true do
page.within('.labels') do
click_link 'Edit'
- wait_for_ajax
+ wait_for_requests
click_link stretch.title
- wait_for_vue_resource
+ wait_for_requests
find('.dropdown-menu-close-icon').click
@@ -305,7 +303,7 @@ describe 'Issue Boards', feature: true, js: true do
page.within('.subscription') do
click_button 'Subscribe'
- wait_for_ajax
+ wait_for_requests
expect(page).to have_content("Unsubscribe")
end
end
diff --git a/spec/features/boards/sub_group_project_spec.rb b/spec/features/boards/sub_group_project_spec.rb
index 6cd7fddd288..4cd05010a93 100644
--- a/spec/features/boards/sub_group_project_spec.rb
+++ b/spec/features/boards/sub_group_project_spec.rb
@@ -1,8 +1,6 @@
require 'rails_helper'
describe 'Sub-group project issue boards', :feature, :js do
- include WaitForVueResource
-
let(:group) { create(:group) }
let(:nested_group_1) { create(:group, parent: group) }
let(:project) { create(:empty_project, group: nested_group_1) }
@@ -18,7 +16,7 @@ describe 'Sub-group project issue boards', :feature, :js do
login_as(user)
visit namespace_project_board_path(project.namespace, project, board)
- wait_for_vue_resource
+ wait_for_requests
end
it 'creates new label from sidebar' do
@@ -35,7 +33,7 @@ describe 'Sub-group project issue boards', :feature, :js do
click_button 'Create'
- wait_for_ajax
+ wait_for_requests
end
page.within '.labels' do
diff --git a/spec/features/calendar_spec.rb b/spec/features/calendar_spec.rb
index 496faf87a16..1b6d8439f92 100644
--- a/spec/features/calendar_spec.rb
+++ b/spec/features/calendar_spec.rb
@@ -74,7 +74,7 @@ feature 'Contributions Calendar', :feature, :js do
describe 'calendar day selection' do
before do
visit user.username
- wait_for_ajax
+ wait_for_requests
end
it 'displays calendar' do
@@ -86,7 +86,7 @@ feature 'Contributions Calendar', :feature, :js do
before do
cells[0].click
- wait_for_ajax
+ wait_for_requests
@first_day_activities = selected_day_activities
end
@@ -97,7 +97,7 @@ feature 'Contributions Calendar', :feature, :js do
describe 'select another calendar day' do
before do
cells[1].click
- wait_for_ajax
+ wait_for_requests
end
it 'displays different calendar day activities' do
@@ -108,7 +108,7 @@ feature 'Contributions Calendar', :feature, :js do
describe 'deselect calendar day' do
before do
cells[0].click
- wait_for_ajax
+ wait_for_requests
end
it 'hides calendar day activities' do
@@ -122,7 +122,7 @@ feature 'Contributions Calendar', :feature, :js do
shared_context 'visit user page' do
before do
visit user.username
- wait_for_ajax
+ wait_for_requests
end
end
diff --git a/spec/features/copy_as_gfm_spec.rb b/spec/features/copy_as_gfm_spec.rb
index be615519a09..740f60c05cc 100644
--- a/spec/features/copy_as_gfm_spec.rb
+++ b/spec/features/copy_as_gfm_spec.rb
@@ -51,7 +51,6 @@ describe 'Copy as GFM', feature: true, js: true do
To see how GitLab looks please see the [features page on our website](https://about.gitlab.com/features/).
-
- Manage Git repositories with fine grained access controls that keep your code secure
- Perform code reviews and enhance collaboration with merge requests
@@ -66,6 +65,38 @@ describe 'Copy as GFM', feature: true, js: true do
GFM
)
+ aggregate_failures('an accidentally selected empty element') do
+ gfm = '# Heading1'
+
+ html = <<-HTML.strip_heredoc
+ <h1>Heading1</h1>
+
+ <h2></h2>
+ HTML
+
+ output_gfm = html_to_gfm(html)
+ expect(output_gfm.strip).to eq(gfm.strip)
+ end
+
+ aggregate_failures('an accidentally selected other element') do
+ gfm = 'Test comment with **Markdown!**'
+
+ html = <<-HTML.strip_heredoc
+ <li class="note">
+ <div class="md">
+ <p>
+ Test comment with <strong>Markdown!</strong>
+ </p>
+ </div>
+ </li>
+
+ <li class="note"></li>
+ HTML
+
+ output_gfm = html_to_gfm(html)
+ expect(output_gfm.strip).to eq(gfm.strip)
+ end
+
verify(
'InlineDiffFilter',
@@ -352,7 +383,6 @@ describe 'Copy as GFM', feature: true, js: true do
<<-GFM.strip_heredoc,
- Nested
-
- Lists
GFM
@@ -375,7 +405,6 @@ describe 'Copy as GFM', feature: true, js: true do
<<-GFM.strip_heredoc,
1. Nested
-
1. Numbered lists
GFM
@@ -479,7 +508,7 @@ describe 'Copy as GFM', feature: true, js: true do
context 'from a blob' do
before do
visit namespace_project_blob_path(project.namespace, project, File.join('master', 'files/ruby/popen.rb'))
- wait_for_ajax
+ wait_for_requests
end
context 'selecting one word of text' do
@@ -521,7 +550,7 @@ describe 'Copy as GFM', feature: true, js: true do
context 'from a GFM code block' do
before do
visit namespace_project_blob_path(project.namespace, project, File.join('markdown', 'doc/api/users.md'))
- wait_for_ajax
+ wait_for_requests
end
context 'selecting one word of text' do
diff --git a/spec/features/cycle_analytics_spec.rb b/spec/features/cycle_analytics_spec.rb
index cbeb73d9cae..b416bbd3c79 100644
--- a/spec/features/cycle_analytics_spec.rb
+++ b/spec/features/cycle_analytics_spec.rb
@@ -7,7 +7,7 @@ feature 'Cycle Analytics', feature: true, js: true do
let(:issue) { create(:issue, project: project, created_at: 2.days.ago) }
let(:milestone) { create(:milestone, project: project) }
let(:mr) { create_merge_request_closing_issue(issue, commit_message: "References #{issue.to_reference}") }
- let(:pipeline) { create(:ci_empty_pipeline, status: 'created', project: project, ref: mr.source_branch, sha: mr.source_branch_sha) }
+ let(:pipeline) { create(:ci_empty_pipeline, status: 'created', project: project, ref: mr.source_branch, sha: mr.source_branch_sha, head_pipeline_of: mr) }
context 'as an allowed user' do
context 'when project is new' do
@@ -17,7 +17,7 @@ feature 'Cycle Analytics', feature: true, js: true do
login_as(user)
visit namespace_project_cycle_analytics_path(project.namespace, project)
- wait_for_ajax
+ wait_for_requests
end
it 'shows introductory message' do
@@ -33,7 +33,6 @@ feature 'Cycle Analytics', feature: true, js: true do
context "when there's cycle analytics data" do
before do
allow_any_instance_of(Gitlab::ReferenceExtractor).to receive(:issues).and_return([issue])
- mr.update(head_pipeline: pipeline)
project.add_master(user)
create_cycle
@@ -73,7 +72,7 @@ feature 'Cycle Analytics', feature: true, js: true do
project.team << [user, :master]
login_as(user)
visit namespace_project_cycle_analytics_path(project.namespace, project)
- wait_for_ajax
+ wait_for_requests
end
it 'shows the content in Spanish' do
@@ -96,7 +95,7 @@ feature 'Cycle Analytics', feature: true, js: true do
login_as(guest)
visit namespace_project_cycle_analytics_path(project.namespace, project)
- wait_for_ajax
+ wait_for_requests
end
it 'needs permissions to see restricted stages' do
@@ -140,6 +139,6 @@ feature 'Cycle Analytics', feature: true, js: true do
def click_stage(stage_name)
find('.stage-nav li', text: stage_name).click
- wait_for_ajax
+ wait_for_requests
end
end
diff --git a/spec/features/dashboard/activity_spec.rb b/spec/features/dashboard/activity_spec.rb
index c977f266296..0764044260e 100644
--- a/spec/features/dashboard/activity_spec.rb
+++ b/spec/features/dashboard/activity_spec.rb
@@ -5,7 +5,7 @@ RSpec.describe 'Dashboard Activity', feature: true do
login_as(create :user)
visit activity_dashboard_path
end
-
- it_behaves_like "it has an RSS button with current_user's private token"
- it_behaves_like "an autodiscoverable RSS feed with current_user's private token"
+
+ 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/dashboard/datetime_on_tooltips_spec.rb b/spec/features/dashboard/datetime_on_tooltips_spec.rb
index 0e9e3f78be2..1793e323588 100644
--- a/spec/features/dashboard/datetime_on_tooltips_spec.rb
+++ b/spec/features/dashboard/datetime_on_tooltips_spec.rb
@@ -15,7 +15,7 @@ feature 'Tooltips on .timeago dates', feature: true, js: true do
login_as user
visit user_path(user)
- wait_for_ajax()
+ wait_for_requests()
page.find('.js-timeago').hover
end
@@ -32,7 +32,7 @@ feature 'Tooltips on .timeago dates', feature: true, js: true do
login_as user
visit user_snippets_path(user)
- wait_for_ajax()
+ wait_for_requests()
page.find('.js-timeago.snippet-created-ago').hover
end
diff --git a/spec/features/dashboard/groups_list_spec.rb b/spec/features/dashboard/groups_list_spec.rb
index 52b4d82e856..b0e2953dda2 100644
--- a/spec/features/dashboard/groups_list_spec.rb
+++ b/spec/features/dashboard/groups_list_spec.rb
@@ -23,7 +23,7 @@ describe 'Dashboard Groups page', js: true, feature: true do
it 'filters groups' do
fill_in 'filter_groups', with: group.name
- wait_for_ajax
+ wait_for_requests
expect(page).to have_content(group.full_name)
expect(page).not_to have_content(nested_group.full_name)
@@ -32,10 +32,10 @@ describe 'Dashboard Groups page', js: true, feature: true do
it 'resets search when user cleans the input' do
fill_in 'filter_groups', with: group.name
- wait_for_ajax
+ wait_for_requests
fill_in 'filter_groups', with: ""
- wait_for_ajax
+ wait_for_requests
expect(page).to have_content(group.full_name)
expect(page).to have_content(nested_group.full_name)
diff --git a/spec/features/dashboard/issues_spec.rb b/spec/features/dashboard/issues_spec.rb
index 7a132dba1e9..2346a9ec2ed 100644
--- a/spec/features/dashboard/issues_spec.rb
+++ b/spec/features/dashboard/issues_spec.rb
@@ -62,6 +62,6 @@ RSpec.describe 'Dashboard Issues', feature: true do
expect(page).to have_content(other_issue.title)
end
- it_behaves_like "it has an RSS button with current_user's private token"
- it_behaves_like "an autodiscoverable RSS feed with current_user's private token"
+ 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/dashboard/milestone_filter_spec.rb b/spec/features/dashboard/milestone_filter_spec.rb
index d60a002a8d7..b5b92c36895 100644
--- a/spec/features/dashboard/milestone_filter_spec.rb
+++ b/spec/features/dashboard/milestone_filter_spec.rb
@@ -1,8 +1,6 @@
require 'spec_helper'
describe 'Dashboard > milestone filter', :feature, :js do
- include WaitForAjax
-
let(:user) { create(:user) }
let(:project) { create(:project, name: 'test', namespace: user.namespace) }
let(:milestone) { create(:milestone, title: "v1.0", project: project) }
@@ -28,14 +26,14 @@ describe 'Dashboard > milestone filter', :feature, :js do
before do
find(milestone_select).click
- wait_for_ajax
+ wait_for_requests
page.within('.dropdown-content') do
click_link 'v1.0'
end
find(milestone_select).click
- wait_for_ajax
+ wait_for_requests
end
it 'shows issues with Milestone v1.0' do
@@ -48,9 +46,9 @@ describe 'Dashboard > milestone filter', :feature, :js do
# open & close dropdown
find('.dropdown-menu-close').click
-
+
expect(find('.milestone-filter')).not_to have_selector('.dropdown.open')
-
+
find(milestone_select).click
expect(find('.dropdown-content')).to have_selector('a.is-active', count: 1)
diff --git a/spec/features/dashboard/project_member_activity_index_spec.rb b/spec/features/dashboard/project_member_activity_index_spec.rb
index 16c214ae060..cdf919af9b5 100644
--- a/spec/features/dashboard/project_member_activity_index_spec.rb
+++ b/spec/features/dashboard/project_member_activity_index_spec.rb
@@ -11,7 +11,7 @@ feature 'Project member activity', feature: true, js: true do
def visit_activities_and_wait_with_event(event_type)
Event.create(project: project, author_id: user.id, action: event_type)
visit activity_namespace_project_path(project.namespace, project)
- wait_for_ajax
+ wait_for_requests
end
subject { page.find(".event-title").text }
diff --git a/spec/features/dashboard/projects_spec.rb b/spec/features/dashboard/projects_spec.rb
index f1789fc9d43..01351548a99 100644
--- a/spec/features/dashboard/projects_spec.rb
+++ b/spec/features/dashboard/projects_spec.rb
@@ -31,5 +31,5 @@ RSpec.describe 'Dashboard Projects', feature: true do
end
end
- it_behaves_like "an autodiscoverable RSS feed with current_user's private token"
+ it_behaves_like "an autodiscoverable RSS feed with current_user's RSS token"
end
diff --git a/spec/features/dashboard_issues_spec.rb b/spec/features/dashboard_issues_spec.rb
index ad60fb2c74f..1c53f6dff06 100644
--- a/spec/features/dashboard_issues_spec.rb
+++ b/spec/features/dashboard_issues_spec.rb
@@ -53,10 +53,10 @@ describe "Dashboard Issues filtering", feature: true, js: true do
auto_discovery_link = find('link[type="application/atom+xml"]', visible: false)
auto_discovery_params = CGI.parse(URI.parse(auto_discovery_link[:href]).query)
- expect(params).to include('private_token' => [user.private_token])
+ expect(params).to include('rss_token' => [user.rss_token])
expect(params).to include('milestone_title' => [''])
expect(params).to include('assignee_id' => [user.id.to_s])
- expect(auto_discovery_params).to include('private_token' => [user.private_token])
+ expect(auto_discovery_params).to include('rss_token' => [user.rss_token])
expect(auto_discovery_params).to include('milestone_title' => [''])
expect(auto_discovery_params).to include('assignee_id' => [user.id.to_s])
end
diff --git a/spec/features/expand_collapse_diffs_spec.rb b/spec/features/expand_collapse_diffs_spec.rb
index 76c77e0bc5f..0cb75538311 100644
--- a/spec/features/expand_collapse_diffs_spec.rb
+++ b/spec/features/expand_collapse_diffs_spec.rb
@@ -36,7 +36,7 @@ feature 'Expand and collapse diffs', js: true, feature: true do
visit namespace_project_commit_path(project.namespace, project, project.commit(branch), anchor: "#{large_diff[:id]}_0_1")
execute_script('window.location.reload()')
- wait_for_ajax
+ wait_for_requests
expect(large_diff).to have_selector('.code')
expect(large_diff).not_to have_selector('.nothing-here-block')
@@ -50,7 +50,7 @@ feature 'Expand and collapse diffs', js: true, feature: true do
visit namespace_project_commit_path(project.namespace, project, project.commit(branch), anchor: large_diff[:id])
execute_script('window.location.reload()')
- wait_for_ajax
+ wait_for_requests
expect(large_diff).to have_selector('.code')
expect(large_diff).not_to have_selector('.nothing-here-block')
@@ -94,7 +94,7 @@ feature 'Expand and collapse diffs', js: true, feature: true do
context 'expanding a diff for a renamed file' do
before do
large_diff_renamed.find('.click-to-expand').click
- wait_for_ajax
+ wait_for_requests
end
it 'shows the old content' do
@@ -116,7 +116,7 @@ feature 'Expand and collapse diffs', js: true, feature: true do
find('.js-file-title', match: :first)
# Click `large_diff.md` title
all('.diff-toggle-caret')[1].click
- wait_for_ajax
+ wait_for_requests
end
it 'makes a request to get the content' do
@@ -139,7 +139,7 @@ feature 'Expand and collapse diffs', js: true, feature: true do
large_diff.find('.add-diff-note').click
large_diff.find('.note-textarea').send_keys comment_text
large_diff.find_button('Comment').click
- wait_for_ajax
+ wait_for_requests
end
it 'adds the comment' do
@@ -160,7 +160,7 @@ feature 'Expand and collapse diffs', js: true, feature: true do
find('.js-file-title', match: :first)
# Click `large_diff.md` title
all('.diff-toggle-caret')[1].click
- wait_for_ajax
+ wait_for_requests
end
it 'shows the diff content' do
@@ -216,7 +216,7 @@ feature 'Expand and collapse diffs', js: true, feature: true do
expect(page).to have_no_content('No longer a symlink')
find('.click-to-expand').click
- wait_for_ajax
+ wait_for_requests
expect(page).to have_content('No longer a symlink')
end
@@ -273,7 +273,7 @@ feature 'Expand and collapse diffs', js: true, feature: true do
expect(page).to have_content('too_large_image.jpg')
find('.note-textarea')
- wait_for_ajax
+ wait_for_requests
execute_script('window.ajaxUris = []; $(document).ajaxSend(function(event, xhr, settings) { ajaxUris.push(settings.url) });')
end
diff --git a/spec/features/explore/groups_list_spec.rb b/spec/features/explore/groups_list_spec.rb
index 9828cb179a7..d4284ed099b 100644
--- a/spec/features/explore/groups_list_spec.rb
+++ b/spec/features/explore/groups_list_spec.rb
@@ -23,7 +23,7 @@ describe 'Explore Groups page', :js, :feature do
it 'filters groups' do
fill_in 'filter_groups', with: group.name
- wait_for_ajax
+ wait_for_requests
expect(page).to have_content(group.full_name)
expect(page).not_to have_content(public_group.full_name)
@@ -32,10 +32,10 @@ describe 'Explore Groups page', :js, :feature do
it 'resets search when user cleans the input' do
fill_in 'filter_groups', with: group.name
- wait_for_ajax
+ wait_for_requests
fill_in 'filter_groups', with: ""
- wait_for_ajax
+ wait_for_requests
expect(page).to have_content(group.full_name)
expect(page).to have_content(public_group.full_name)
diff --git a/spec/features/gitlab_flavored_markdown_spec.rb b/spec/features/gitlab_flavored_markdown_spec.rb
index 005a029a393..55092412340 100644
--- a/spec/features/gitlab_flavored_markdown_spec.rb
+++ b/spec/features/gitlab_flavored_markdown_spec.rb
@@ -49,8 +49,6 @@ describe "GitLab Flavored Markdown", feature: true do
end
describe "for issues", feature: true, js: true do
- include WaitForVueResource
-
before do
@other_issue = create(:issue,
author: @user,
diff --git a/spec/features/groups/activity_spec.rb b/spec/features/groups/activity_spec.rb
index 3b481cba424..81f9c103e95 100644
--- a/spec/features/groups/activity_spec.rb
+++ b/spec/features/groups/activity_spec.rb
@@ -11,8 +11,8 @@ feature 'Group activity page', feature: true do
visit path
end
- it_behaves_like "it has an RSS button with current_user's private token"
- it_behaves_like "an autodiscoverable RSS feed with current_user's private token"
+ 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
context 'when signed out' do
@@ -20,7 +20,7 @@ feature 'Group activity page', feature: true do
visit path
end
- it_behaves_like "it has an RSS button without a private token"
- it_behaves_like "an autodiscoverable RSS feed without a private token"
+ it_behaves_like "it has an RSS button without an RSS token"
+ it_behaves_like "an autodiscoverable RSS feed without an RSS token"
end
end
diff --git a/spec/features/groups/issues_spec.rb b/spec/features/groups/issues_spec.rb
index 45f57845c74..d6b88542ef7 100644
--- a/spec/features/groups/issues_spec.rb
+++ b/spec/features/groups/issues_spec.rb
@@ -12,15 +12,15 @@ feature 'Group issues page', feature: true do
context 'when signed in' do
let(:user) { user_in_group }
- it_behaves_like "it has an RSS button with current_user's private token"
- it_behaves_like "an autodiscoverable RSS feed with current_user's private token"
+ 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
context 'when signed out' do
let(:user) { nil }
- it_behaves_like "it has an RSS button without a private token"
- it_behaves_like "an autodiscoverable RSS feed without a private token"
+ it_behaves_like "it has an RSS button without an RSS token"
+ it_behaves_like "an autodiscoverable RSS feed without an RSS token"
end
end
@@ -33,7 +33,7 @@ feature 'Group issues page', feature: true do
it 'filters by only group users' do
click_button('Assignee')
- wait_for_ajax
+ wait_for_requests
expect(find('.dropdown-menu-assignee')).to have_link(user.name)
expect(find('.dropdown-menu-assignee')).not_to have_link(user2.name)
diff --git a/spec/features/groups/show_spec.rb b/spec/features/groups/show_spec.rb
index fb39693e8ca..d3c49c37374 100644
--- a/spec/features/groups/show_spec.rb
+++ b/spec/features/groups/show_spec.rb
@@ -11,7 +11,7 @@ feature 'Group show page', feature: true do
visit path
end
- it_behaves_like "an autodiscoverable RSS feed with current_user's private token"
+ it_behaves_like "an autodiscoverable RSS feed with current_user's RSS token"
end
context 'when signed out' do
@@ -19,6 +19,6 @@ feature 'Group show page', feature: true do
visit path
end
- it_behaves_like "an autodiscoverable RSS feed without a private token"
+ it_behaves_like "an autodiscoverable RSS feed without an RSS token"
end
end
diff --git a/spec/features/issues/award_emoji_spec.rb b/spec/features/issues/award_emoji_spec.rb
index 853632614c4..81ae54c7a10 100644
--- a/spec/features/issues/award_emoji_spec.rb
+++ b/spec/features/issues/award_emoji_spec.rb
@@ -1,8 +1,6 @@
require 'rails_helper'
describe 'Awards Emoji', feature: true do
- include WaitForVueResource
-
let!(:project) { create(:project, :public) }
let!(:user) { create(:user) }
let(:issue) do
@@ -22,7 +20,7 @@ describe 'Awards Emoji', feature: true do
# The `heart_tip` emoji is not valid anymore so we need to skip validation
issue.award_emoji.build(user: user, name: 'heart_tip').save!(validate: false)
visit namespace_project_issue_path(project.namespace, project, issue)
- wait_for_vue_resource
+ wait_for_requests
end
# Regression test: https://gitlab.com/gitlab-org/gitlab-ce/issues/29529
@@ -36,19 +34,19 @@ describe 'Awards Emoji', feature: true do
before do
visit namespace_project_issue_path(project.namespace, project, issue)
- wait_for_vue_resource
+ wait_for_requests
end
it 'increments the thumbsdown emoji', js: true do
find('[data-name="thumbsdown"]').click
- wait_for_ajax
+ wait_for_requests
expect(thumbsdown_emoji).to have_text("1")
end
context 'click the thumbsup emoji' do
it 'increments the thumbsup emoji', js: true do
find('[data-name="thumbsup"]').click
- wait_for_ajax
+ wait_for_requests
expect(thumbsup_emoji).to have_text("1")
end
@@ -60,7 +58,7 @@ describe 'Awards Emoji', feature: true do
context 'click the thumbsdown emoji' do
it 'increments the thumbsdown emoji', js: true do
find('[data-name="thumbsdown"]').click
- wait_for_ajax
+ wait_for_requests
expect(thumbsdown_emoji).to have_text("1")
end
@@ -113,7 +111,7 @@ describe 'Awards Emoji', feature: true do
click_button 'Comment'
end
- wait_for_ajax
+ wait_for_requests
end
def thumbsup_emoji
@@ -143,6 +141,6 @@ describe 'Awards Emoji', feature: true do
find('[data-name="smiley"]').click
end
- wait_for_ajax
+ wait_for_requests
end
end
diff --git a/spec/features/issues/award_spec.rb b/spec/features/issues/award_spec.rb
index 08e3f99e29f..fcf22dd5033 100644
--- a/spec/features/issues/award_spec.rb
+++ b/spec/features/issues/award_spec.rb
@@ -6,12 +6,10 @@ feature 'Issue awards', js: true, feature: true do
let(:issue) { create(:issue, project: project) }
describe 'logged in' do
- include WaitForVueResource
-
before do
login_as(user)
visit namespace_project_issue_path(project.namespace, project, issue)
- wait_for_vue_resource
+ wait_for_requests
end
it 'adds award to issue' do
@@ -41,11 +39,9 @@ feature 'Issue awards', js: true, feature: true do
end
describe 'logged out' do
- include WaitForVueResource
-
before do
visit namespace_project_issue_path(project.namespace, project, issue)
- wait_for_vue_resource
+ wait_for_requests
end
it 'does not see award menu button' do
diff --git a/spec/features/issues/bulk_assignment_labels_spec.rb b/spec/features/issues/bulk_assignment_labels_spec.rb
index 1de50d6d77e..0a6f645b27e 100644
--- a/spec/features/issues/bulk_assignment_labels_spec.rb
+++ b/spec/features/issues/bulk_assignment_labels_spec.rb
@@ -306,7 +306,7 @@ feature 'Issues > Labels bulk assignment', feature: true do
page.within('.issues_bulk_update') do
click_button 'Labels'
- wait_for_ajax
+ wait_for_requests
expect(find('.dropdown-menu-labels li', text: 'bug')).to have_css('.is-active')
expect(find('.dropdown-menu-labels li', text: 'feature')).to have_css('.is-indeterminate')
@@ -349,7 +349,7 @@ feature 'Issues > Labels bulk assignment', feature: true do
def open_milestone_dropdown(items = [])
page.within('.issues_bulk_update') do
click_button 'Milestone'
- wait_for_ajax
+ wait_for_requests
items.map do |item|
click_link item
end
@@ -359,7 +359,7 @@ feature 'Issues > Labels bulk assignment', feature: true do
def open_labels_dropdown(items = [], unmark = false)
page.within('.issues_bulk_update') do
click_button 'Labels'
- wait_for_ajax
+ wait_for_requests
items.map do |item|
click_link item
end
@@ -392,6 +392,6 @@ feature 'Issues > Labels bulk assignment', feature: true do
def update_issues
click_button 'Update issues'
- wait_for_ajax
+ wait_for_requests
end
end
diff --git a/spec/features/issues/create_branch_merge_request_spec.rb b/spec/features/issues/create_branch_merge_request_spec.rb
index 44c19275ae5..1d7d8d291b2 100644
--- a/spec/features/issues/create_branch_merge_request_spec.rb
+++ b/spec/features/issues/create_branch_merge_request_spec.rb
@@ -16,7 +16,7 @@ feature 'Create Branch/Merge Request Dropdown on issue page', feature: true, js:
select_dropdown_option('create-mr')
- wait_for_ajax
+ wait_for_requests
expect(page).to have_content("created branch 1-cherry-coloured-funk")
expect(page).to have_content("mentioned in merge request !1")
@@ -32,7 +32,7 @@ feature 'Create Branch/Merge Request Dropdown on issue page', feature: true, js:
select_dropdown_option('create-branch')
- wait_for_ajax
+ wait_for_requests
expect(page).to have_selector('.dropdown-toggle-text ', text: '1-cherry-coloured-funk')
expect(current_path).to eq namespace_project_tree_path(project.namespace, project, '1-cherry-coloured-funk')
diff --git a/spec/features/issues/filtered_search/dropdown_author_spec.rb b/spec/features/issues/filtered_search/dropdown_author_spec.rb
index 0579d6c80ab..b29177bed06 100644
--- a/spec/features/issues/filtered_search/dropdown_author_spec.rb
+++ b/spec/features/issues/filtered_search/dropdown_author_spec.rb
@@ -16,7 +16,7 @@ describe 'Dropdown author', js: true, feature: true do
end
sleep 0.5
- wait_for_ajax
+ wait_for_requests
end
def dropdown_author_size
diff --git a/spec/features/issues/filtered_search/filter_issues_spec.rb b/spec/features/issues/filtered_search/filter_issues_spec.rb
index a8f4e2d7e10..7958ad7e24f 100644
--- a/spec/features/issues/filtered_search/filter_issues_spec.rb
+++ b/spec/features/issues/filtered_search/filter_issues_spec.rb
@@ -761,7 +761,7 @@ describe 'Filter issues', js: true, feature: true do
sort_toggle.click
find('.filtered-search-wrapper .dropdown-menu li a', text: 'Oldest updated').click
- wait_for_ajax
+ wait_for_requests
expect(find('.issues-list .issue:first-of-type .issue-title-text a')).to have_content(old_issue.title)
end
@@ -778,17 +778,17 @@ describe 'Filter issues', js: true, feature: true do
it 'open state' do
find('.issues-state-filters a', text: 'Closed').click
- wait_for_ajax
+ wait_for_requests
find('.issues-state-filters a', text: 'Open').click
- wait_for_ajax
+ wait_for_requests
expect(page).to have_selector('.issues-list .issue', count: 4)
end
it 'closed state' do
find('.issues-state-filters a', text: 'Closed').click
- wait_for_ajax
+ wait_for_requests
expect(page).to have_selector('.issues-list .issue', count: 1)
expect(find('.issues-list .issue:first-of-type .issue-title-text a')).to have_content(closed_issue.title)
@@ -796,7 +796,7 @@ describe 'Filter issues', js: true, feature: true do
it 'all state' do
find('.issues-state-filters a', text: 'All').click
- wait_for_ajax
+ wait_for_requests
expect(page).to have_selector('.issues-list .issue', count: 5)
end
@@ -810,10 +810,10 @@ describe 'Filter issues', js: true, feature: true do
auto_discovery_link = find('link[type="application/atom+xml"]', visible: false)
auto_discovery_params = CGI.parse(URI.parse(auto_discovery_link[:href]).query)
- expect(params).to include('private_token' => [user.private_token])
+ expect(params).to include('rss_token' => [user.rss_token])
expect(params).to include('milestone_title' => [milestone.title])
expect(params).to include('assignee_id' => [user.id.to_s])
- expect(auto_discovery_params).to include('private_token' => [user.private_token])
+ expect(auto_discovery_params).to include('rss_token' => [user.rss_token])
expect(auto_discovery_params).to include('milestone_title' => [milestone.title])
expect(auto_discovery_params).to include('assignee_id' => [user.id.to_s])
end
@@ -825,10 +825,10 @@ describe 'Filter issues', js: true, feature: true do
auto_discovery_link = find('link[type="application/atom+xml"]', visible: false)
auto_discovery_params = CGI.parse(URI.parse(auto_discovery_link[:href]).query)
- expect(params).to include('private_token' => [user.private_token])
+ expect(params).to include('rss_token' => [user.rss_token])
expect(params).to include('milestone_title' => [milestone.title])
expect(params).to include('assignee_id' => [user.id.to_s])
- expect(auto_discovery_params).to include('private_token' => [user.private_token])
+ expect(auto_discovery_params).to include('rss_token' => [user.rss_token])
expect(auto_discovery_params).to include('milestone_title' => [milestone.title])
expect(auto_discovery_params).to include('assignee_id' => [user.id.to_s])
end
diff --git a/spec/features/issues/form_spec.rb b/spec/features/issues/form_spec.rb
index 5c0907e26df..65d854d0896 100644
--- a/spec/features/issues/form_spec.rb
+++ b/spec/features/issues/form_spec.rb
@@ -3,7 +3,6 @@ require 'rails_helper'
describe 'New/edit issue', :feature, :js do
include GitlabRoutingHelper
include ActionView::Helpers::JavaScriptHelper
- include WaitForAjax
let!(:project) { create(:project) }
let!(:user) { create(:user)}
@@ -28,7 +27,7 @@ describe 'New/edit issue', :feature, :js do
before do
click_button 'Unassigned'
- wait_for_ajax
+ wait_for_requests
end
it 'unselects other assignees when unassigned is selected' do
@@ -69,7 +68,7 @@ describe 'New/edit issue', :feature, :js do
expect(find('a', text: 'Assign to me')).to be_visible
click_button 'Unassigned'
- wait_for_ajax
+ wait_for_requests
page.within '.dropdown-menu-user' do
click_link user2.name
@@ -155,7 +154,7 @@ describe 'New/edit issue', :feature, :js do
it 'correctly updates the selected user when changing assignee' do
click_button 'Unassigned'
- wait_for_ajax
+ wait_for_requests
page.within '.dropdown-menu-user' do
click_link user.name
diff --git a/spec/features/issues/gfm_autocomplete_spec.rb b/spec/features/issues/gfm_autocomplete_spec.rb
index ad29911248f..350473437a8 100644
--- a/spec/features/issues/gfm_autocomplete_spec.rb
+++ b/spec/features/issues/gfm_autocomplete_spec.rb
@@ -11,7 +11,7 @@ feature 'GFM autocomplete', feature: true, js: true do
login_as(user)
visit namespace_project_issue_path(project.namespace, project, issue)
- wait_for_ajax
+ wait_for_requests
end
it 'opens autocomplete menu when field starts with text' do
@@ -40,7 +40,7 @@ feature 'GFM autocomplete', feature: true, js: true do
expect(page).to have_selector('.atwho-container')
- wait_for_ajax
+ wait_for_requests
expect(find('#at-view-58')).not_to have_selector('.cur:first-of-type')
end
@@ -80,7 +80,7 @@ feature 'GFM autocomplete', feature: true, js: true do
expect(page).to have_selector('.atwho-container')
- wait_for_ajax
+ wait_for_requests
expect(find('#at-view-64')).to have_selector('.cur:first-of-type')
end
@@ -93,7 +93,7 @@ feature 'GFM autocomplete', feature: true, js: true do
expect(page).to have_selector('.atwho-container')
- wait_for_ajax
+ wait_for_requests
expect(find('#at-view-64')).to have_content(user.name)
end
@@ -106,7 +106,7 @@ feature 'GFM autocomplete', feature: true, js: true do
expect(page).to have_selector('.atwho-container')
- wait_for_ajax
+ wait_for_requests
expect(find('#at-view-58')).to have_selector('.cur:first-of-type')
end
diff --git a/spec/features/issues/issue_sidebar_spec.rb b/spec/features/issues/issue_sidebar_spec.rb
index 0de0f93089a..99ad8013023 100644
--- a/spec/features/issues/issue_sidebar_spec.rb
+++ b/spec/features/issues/issue_sidebar_spec.rb
@@ -23,7 +23,7 @@ feature 'Issue Sidebar', feature: true do
find('.block.assignee .edit-link').click
- wait_for_ajax
+ wait_for_requests
end
it 'shows author in assignee dropdown' do
@@ -37,7 +37,7 @@ feature 'Issue Sidebar', feature: true do
find('.dropdown-input-field').native.send_keys user2.name
sleep 1 # Required to wait for end of input delay
- wait_for_ajax
+ wait_for_requests
expect(page).to have_content(user2.name)
end
@@ -48,7 +48,7 @@ feature 'Issue Sidebar', feature: true do
click_button 'assign yourself'
- wait_for_ajax
+ wait_for_requests
find('.block.assignee .edit-link').click
diff --git a/spec/features/issues/notes_on_issues_spec.rb b/spec/features/issues/notes_on_issues_spec.rb
index a4035324d2b..15c817cabac 100644
--- a/spec/features/issues/notes_on_issues_spec.rb
+++ b/spec/features/issues/notes_on_issues_spec.rb
@@ -15,7 +15,7 @@ describe 'Create notes on issues', :js, :feature do
fill_in 'note[note]', with: note_text
click_button 'Comment'
- wait_for_ajax
+ wait_for_requests
end
it 'creates a note with reference and cross references the issue' do
diff --git a/spec/features/issues/update_issues_spec.rb b/spec/features/issues/update_issues_spec.rb
index b250fa2ed3c..0911f1db9ba 100644
--- a/spec/features/issues/update_issues_spec.rb
+++ b/spec/features/issues/update_issues_spec.rb
@@ -108,11 +108,11 @@ feature 'Multiple issue updating from issues#index', feature: true do
def click_update_assignee_button
find('.js-update-assignee').click
- wait_for_ajax
+ wait_for_requests
end
def click_update_issues_button
find('.update_selected_issues').click
- wait_for_ajax
+ wait_for_requests
end
end
diff --git a/spec/features/issues/user_uses_slash_commands_spec.rb b/spec/features/issues/user_uses_slash_commands_spec.rb
index 4cd6c1171ac..d14c319707c 100644
--- a/spec/features/issues/user_uses_slash_commands_spec.rb
+++ b/spec/features/issues/user_uses_slash_commands_spec.rb
@@ -18,7 +18,7 @@ feature 'Issues > User uses slash commands', feature: true, js: true do
end
after do
- wait_for_ajax
+ wait_for_requests
end
describe 'adding a due date from note' do
diff --git a/spec/features/issues_spec.rb b/spec/features/issues_spec.rb
index 06ed2dbac64..eecc565d2bd 100644
--- a/spec/features/issues_spec.rb
+++ b/spec/features/issues_spec.rb
@@ -377,7 +377,7 @@ describe 'Issues', feature: true do
previous_token = find('input#issue_email').value
find('.incoming-email-token-reset').trigger('click')
- wait_for_ajax
+ wait_for_requests
expect(page).to have_no_field('issue_email', with: previous_token)
new_token = project1.new_issue_address(@user.reload)
@@ -423,7 +423,7 @@ describe 'Issues', feature: true do
expect(page).to have_content 'No assignee'
end
- # wait_for_ajax does not work with vue-resource at the moment
+ # wait_for_requests does not work with vue-resource at the moment
sleep 1
expect(issue.reload.assignees).to be_empty
@@ -661,7 +661,7 @@ describe 'Issues', feature: true do
click_button date.day
end
- wait_for_ajax
+ wait_for_requests
expect(find('.value').text).to have_content date.strftime('%b %-d, %Y')
end
@@ -677,7 +677,7 @@ describe 'Issues', feature: true do
click_button date.day
end
- wait_for_ajax
+ wait_for_requests
expect(page).to have_no_content 'No due date'
@@ -689,8 +689,6 @@ describe 'Issues', feature: true do
end
describe 'title issue#show', js: true do
- include WaitForVueResource
-
it 'updates the title', js: true do
issue = create(:issue, author: @user, assignees: [@user], project: project, title: 'new title')
@@ -700,7 +698,7 @@ describe 'Issues', feature: true do
issue.update(title: "updated title")
- wait_for_vue_resource
+ wait_for_requests
expect(page).to have_text("updated title")
end
end
diff --git a/spec/features/merge_requests/closes_issues_spec.rb b/spec/features/merge_requests/closes_issues_spec.rb
index ee0880a1e2f..e627618042a 100644
--- a/spec/features/merge_requests/closes_issues_spec.rb
+++ b/spec/features/merge_requests/closes_issues_spec.rb
@@ -1,8 +1,6 @@
require 'spec_helper'
feature 'Merge Request closing issues message', feature: true, js: true do
- include WaitForAjax
-
let(:user) { create(:user) }
let(:project) { create(:project, :public) }
let(:issue_1) { create(:issue, project: project)}
@@ -25,7 +23,7 @@ feature 'Merge Request closing issues message', feature: true, js: true do
login_as user
visit namespace_project_merge_request_path(project.namespace, project, merge_request)
- wait_for_ajax
+ wait_for_requests
end
context 'not closing or mentioning any issue' do
diff --git a/spec/features/merge_requests/conflicts_spec.rb b/spec/features/merge_requests/conflicts_spec.rb
index 04b7593ce68..7f669565085 100644
--- a/spec/features/merge_requests/conflicts_spec.rb
+++ b/spec/features/merge_requests/conflicts_spec.rb
@@ -23,13 +23,13 @@ feature 'Merge request conflict resolution', js: true, feature: true do
end
click_button 'Commit conflict resolution'
- wait_for_ajax
+ wait_for_requests
expect(page).to have_content('All merge conflicts were resolved')
merge_request.reload_diff
click_on 'Changes'
- wait_for_ajax
+ wait_for_requests
within find('.diff-file', text: 'files/ruby/popen.rb') do
expect(page).to have_selector('.line_content.new', text: "vars = { 'PWD' => path }")
@@ -53,23 +53,23 @@ feature 'Merge request conflict resolution', js: true, feature: true do
within find('.files-wrapper .diff-file', text: 'files/ruby/popen.rb') do
click_button 'Edit inline'
- wait_for_ajax
+ wait_for_requests
execute_script('ace.edit($(".files-wrapper .diff-file pre")[0]).setValue("One morning");')
end
within find('.files-wrapper .diff-file', text: 'files/ruby/regex.rb') do
click_button 'Edit inline'
- wait_for_ajax
+ wait_for_requests
execute_script('ace.edit($(".files-wrapper .diff-file pre")[1]).setValue("Gregor Samsa woke from troubled dreams");')
end
click_button 'Commit conflict resolution'
- wait_for_ajax
+ wait_for_requests
expect(page).to have_content('All merge conflicts were resolved')
merge_request.reload_diff
click_on 'Changes'
- wait_for_ajax
+ wait_for_requests
expect(page).to have_content('One morning')
expect(page).to have_content('Gregor Samsa woke from troubled dreams')
@@ -126,21 +126,21 @@ feature 'Merge request conflict resolution', js: true, feature: true do
it 'conflicts are resolved in Edit inline mode' do
within find('.files-wrapper .diff-file', text: 'files/markdown/ruby-style-guide.md') do
- wait_for_ajax
+ wait_for_requests
execute_script('ace.edit($(".files-wrapper .diff-file pre")[0]).setValue("Gregor Samsa woke from troubled dreams");')
end
click_button 'Commit conflict resolution'
- wait_for_ajax
+ wait_for_requests
expect(page).to have_content('All merge conflicts were resolved')
merge_request.reload_diff
click_on 'Changes'
- wait_for_ajax
+ wait_for_requests
click_link 'Expand all'
- wait_for_ajax
+ wait_for_requests
expect(page).to have_content('Gregor Samsa woke from troubled dreams')
end
@@ -171,7 +171,7 @@ feature 'Merge request conflict resolution', js: true, feature: true do
it 'shows an error if the conflicts page is visited directly' do
visit current_url + '/conflicts'
- wait_for_ajax
+ wait_for_requests
expect(find('#conflicts')).to have_content('Please try to resolve them locally.')
end
diff --git a/spec/features/merge_requests/create_new_mr_spec.rb b/spec/features/merge_requests/create_new_mr_spec.rb
index f1b3e7f158c..82987c768d1 100644
--- a/spec/features/merge_requests/create_new_mr_spec.rb
+++ b/spec/features/merge_requests/create_new_mr_spec.rb
@@ -1,8 +1,6 @@
require 'spec_helper'
feature 'Create New Merge Request', feature: true, js: true do
- include WaitForVueResource
-
let(:user) { create(:user) }
let(:project) { create(:project, :public) }
@@ -146,7 +144,7 @@ feature 'Create New Merge Request', feature: true, js: true do
page.within('.merge-request') do
click_link 'Pipelines'
- wait_for_vue_resource
+ wait_for_requests
expect(page).to have_content "##{pipeline.id}"
end
diff --git a/spec/features/merge_requests/deleted_source_branch_spec.rb b/spec/features/merge_requests/deleted_source_branch_spec.rb
index 01e5e4f3a05..1723fb7d365 100644
--- a/spec/features/merge_requests/deleted_source_branch_spec.rb
+++ b/spec/features/merge_requests/deleted_source_branch_spec.rb
@@ -32,7 +32,7 @@ describe 'Deleted source branch', feature: true, js: true do
end
click_on 'Changes'
- wait_for_ajax
+ wait_for_requests
expect(page).to have_selector('.diffs.tab-pane .nothing-here-block')
expect(page).to have_content('Source branch does not exist.')
diff --git a/spec/features/merge_requests/diff_notes_avatars_spec.rb b/spec/features/merge_requests/diff_notes_avatars_spec.rb
index ccf047d3efa..854e2d1758f 100644
--- a/spec/features/merge_requests/diff_notes_avatars_spec.rb
+++ b/spec/features/merge_requests/diff_notes_avatars_spec.rb
@@ -60,7 +60,7 @@ feature 'Diff note avatars', feature: true, js: true do
click_button 'Comment'
- wait_for_ajax
+ wait_for_requests
end
visit namespace_project_merge_request_path(project.namespace, project, merge_request)
@@ -76,7 +76,7 @@ feature 'Diff note avatars', feature: true, js: true do
before do
visit diffs_namespace_project_merge_request_path(project.namespace, project, merge_request, view: view)
- wait_for_ajax
+ wait_for_requests
end
it 'shows note avatar' do
@@ -114,7 +114,7 @@ feature 'Diff note avatars', feature: true, js: true do
find('.js-note-delete').click
end
- wait_for_ajax
+ wait_for_requests
page.within find("[id='#{position.line_code(project.repository)}']") do
expect(page).not_to have_selector('img.js-diff-comment-avatar')
@@ -129,7 +129,7 @@ feature 'Diff note avatars', feature: true, js: true do
click_button 'Comment'
- wait_for_ajax
+ wait_for_requests
end
page.within find("[id='#{position.line_code(project.repository)}']") do
@@ -148,7 +148,7 @@ feature 'Diff note avatars', feature: true, js: true do
find('.js-comment-button').trigger 'click'
- wait_for_ajax
+ wait_for_requests
end
end
@@ -166,7 +166,7 @@ feature 'Diff note avatars', feature: true, js: true do
visit diffs_namespace_project_merge_request_path(project.namespace, project, merge_request, view: view)
- wait_for_ajax
+ wait_for_requests
end
it 'shows extra comment count' do
diff --git a/spec/features/merge_requests/discussion_spec.rb b/spec/features/merge_requests/discussion_spec.rb
index f59d0faa274..1a09cc54c2e 100644
--- a/spec/features/merge_requests/discussion_spec.rb
+++ b/spec/features/merge_requests/discussion_spec.rb
@@ -43,7 +43,7 @@ feature 'Merge Request Discussions', feature: true do
it 'shows a link to the outdated diff' do
within(".discussion[data-discussion-id='#{outdated_discussion.id}']") do
path = diffs_namespace_project_merge_request_path(project.namespace, project, merge_request, diff_id: old_merge_request_diff.id, anchor: outdated_discussion.line_code)
- expect(page).to have_link('an outdated diff', href: path)
+ expect(page).to have_link('an old version of the diff', href: path)
end
end
end
diff --git a/spec/features/merge_requests/filter_merge_requests_spec.rb b/spec/features/merge_requests/filter_merge_requests_spec.rb
index 2da60e9f4ad..1e26b3d601e 100644
--- a/spec/features/merge_requests/filter_merge_requests_spec.rb
+++ b/spec/features/merge_requests/filter_merge_requests_spec.rb
@@ -289,7 +289,7 @@ describe 'Filter merge requests', feature: true do
page.within '.dropdown-menu-sort' do
click_link 'Oldest created'
end
- wait_for_ajax
+ wait_for_requests
page.within '.mr-list' do
expect(page).to have_content('Frontend')
diff --git a/spec/features/merge_requests/form_spec.rb b/spec/features/merge_requests/form_spec.rb
index f8518f450dc..00ef1ffdddc 100644
--- a/spec/features/merge_requests/form_spec.rb
+++ b/spec/features/merge_requests/form_spec.rb
@@ -90,7 +90,7 @@ describe 'New/edit merge request', feature: true, js: true do
page.within '.issuable-meta' do
merge_request = MergeRequest.find_by(source_branch: 'fix')
- expect(page).to have_text("Merge Request #{merge_request.to_reference}")
+ expect(page).to have_text("Merge request #{merge_request.to_reference}")
# compare paths because the host differ in test
expect(find_link(merge_request.to_reference)[:href])
.to end_with(merge_request_path(merge_request))
diff --git a/spec/features/merge_requests/merge_immediately_with_pipeline_spec.rb b/spec/features/merge_requests/merge_immediately_with_pipeline_spec.rb
index c102722d6db..c1d4d508e57 100644
--- a/spec/features/merge_requests/merge_immediately_with_pipeline_spec.rb
+++ b/spec/features/merge_requests/merge_immediately_with_pipeline_spec.rb
@@ -39,7 +39,7 @@ feature 'Merge immediately', :feature, :js do
expect(find('.accept-merge-request.btn-info')).to have_content('Merge in progress')
- wait_for_vue_resource
+ wait_for_requests
end
end
end
diff --git a/spec/features/merge_requests/merge_when_pipeline_succeeds_spec.rb b/spec/features/merge_requests/merge_when_pipeline_succeeds_spec.rb
index 11b6f0c0a64..e08721b4724 100644
--- a/spec/features/merge_requests/merge_when_pipeline_succeeds_spec.rb
+++ b/spec/features/merge_requests/merge_when_pipeline_succeeds_spec.rb
@@ -13,12 +13,12 @@ feature 'Merge When Pipeline Succeeds', :feature, :js do
let(:pipeline) do
create(:ci_pipeline, project: project,
sha: merge_request.diff_head_sha,
- ref: merge_request.source_branch)
+ ref: merge_request.source_branch,
+ head_pipeline_of: merge_request)
end
before do
project.add_master(user)
- merge_request.update(head_pipeline_id: pipeline.id)
end
context 'when there is active pipeline for merge request' do
diff --git a/spec/features/merge_requests/mini_pipeline_graph_spec.rb b/spec/features/merge_requests/mini_pipeline_graph_spec.rb
index 5b2798af32f..51e7467c14c 100644
--- a/spec/features/merge_requests/mini_pipeline_graph_spec.rb
+++ b/spec/features/merge_requests/mini_pipeline_graph_spec.rb
@@ -56,7 +56,7 @@ feature 'Mini Pipeline Graph', :js, :feature do
before do
toggle.click
- wait_for_ajax
+ wait_for_requests
end
it 'should open when toggle is clicked' do
diff --git a/spec/features/merge_requests/only_allow_merge_if_build_succeeds_spec.rb b/spec/features/merge_requests/only_allow_merge_if_build_succeeds_spec.rb
index cdda0542c51..b1dc81a606a 100644
--- a/spec/features/merge_requests/only_allow_merge_if_build_succeeds_spec.rb
+++ b/spec/features/merge_requests/only_allow_merge_if_build_succeeds_spec.rb
@@ -1,8 +1,6 @@
require 'spec_helper'
feature 'Only allow merge requests to be merged if the pipeline succeeds', feature: true, js: true do
- include WaitForVueResource
-
let(:merge_request) { create(:merge_request_with_diffs) }
let(:project) { merge_request.target_project }
@@ -16,7 +14,7 @@ feature 'Only allow merge requests to be merged if the pipeline succeeds', featu
it 'allows MR to be merged' do
visit_merge_request(merge_request)
- wait_for_vue_resource
+ wait_for_requests
expect(page).to have_button 'Merge'
end
@@ -28,11 +26,9 @@ feature 'Only allow merge requests to be merged if the pipeline succeeds', featu
project: project,
sha: merge_request.diff_head_sha,
ref: merge_request.source_branch,
- status: status)
+ status: status, head_pipeline_of: merge_request)
end
- before { merge_request.update(head_pipeline: pipeline) }
-
context 'when merge requests can only be merged if the pipeline succeeds' do
before do
project.update_attribute(:only_allow_merge_if_pipeline_succeeds, true)
@@ -44,7 +40,7 @@ feature 'Only allow merge requests to be merged if the pipeline succeeds', featu
it 'does not allow to merge immediately' do
visit_merge_request(merge_request)
- wait_for_vue_resource
+ wait_for_requests
expect(page).to have_button 'Merge when pipeline succeeds'
expect(page).not_to have_button 'Select merge moment'
@@ -57,7 +53,7 @@ feature 'Only allow merge requests to be merged if the pipeline succeeds', featu
it 'does not allow MR to be merged' do
visit_merge_request(merge_request)
- wait_for_vue_resource
+ wait_for_requests
expect(page).to have_css('button[disabled="disabled"]', text: 'Merge')
expect(page).to have_content('Please retry the job or push a new commit to fix the failure.')
@@ -70,7 +66,7 @@ feature 'Only allow merge requests to be merged if the pipeline succeeds', featu
it 'does not allow MR to be merged' do
visit_merge_request(merge_request)
- wait_for_vue_resource
+ wait_for_requests
expect(page).not_to have_button 'Merge'
expect(page).to have_content('Please retry the job or push a new commit to fix the failure.')
@@ -83,7 +79,7 @@ feature 'Only allow merge requests to be merged if the pipeline succeeds', featu
it 'allows MR to be merged' do
visit_merge_request(merge_request)
- wait_for_vue_resource
+ wait_for_requests
expect(page).to have_button 'Merge'
end
@@ -95,7 +91,7 @@ feature 'Only allow merge requests to be merged if the pipeline succeeds', featu
it 'allows MR to be merged' do
visit_merge_request(merge_request)
- wait_for_vue_resource
+ wait_for_requests
expect(page).to have_button 'Merge'
end
@@ -113,7 +109,7 @@ feature 'Only allow merge requests to be merged if the pipeline succeeds', featu
it 'allows MR to be merged immediately' do
visit_merge_request(merge_request)
- wait_for_vue_resource
+ wait_for_requests
expect(page).to have_button 'Merge when pipeline succeeds'
@@ -128,7 +124,7 @@ feature 'Only allow merge requests to be merged if the pipeline succeeds', featu
it 'allows MR to be merged' do
visit_merge_request(merge_request)
- wait_for_vue_resource
+ wait_for_requests
expect(page).to have_button 'Merge'
end
@@ -140,7 +136,7 @@ feature 'Only allow merge requests to be merged if the pipeline succeeds', featu
it 'allows MR to be merged' do
visit_merge_request(merge_request)
- wait_for_vue_resource
+ wait_for_requests
expect(page).to have_button 'Merge'
end
diff --git a/spec/features/merge_requests/pipelines_spec.rb b/spec/features/merge_requests/pipelines_spec.rb
index 99e283ac181..4c76004cb93 100644
--- a/spec/features/merge_requests/pipelines_spec.rb
+++ b/spec/features/merge_requests/pipelines_spec.rb
@@ -26,7 +26,7 @@ feature 'Pipelines for Merge Requests', feature: true, js: true do
page.within('.merge-request-tabs') do
click_link('Pipelines')
end
- wait_for_ajax
+ wait_for_requests
expect(page).to have_selector('.pipeline-actions')
end
diff --git a/spec/features/merge_requests/update_merge_requests_spec.rb b/spec/features/merge_requests/update_merge_requests_spec.rb
index 9ecc998785b..4ef59a8aeb8 100644
--- a/spec/features/merge_requests/update_merge_requests_spec.rb
+++ b/spec/features/merge_requests/update_merge_requests_spec.rb
@@ -107,7 +107,7 @@ feature 'Multiple merge requests updating from merge_requests#index', feature: t
def change_assignee(text)
find('#check_all_issues').click
find('.js-update-assignee').click
- wait_for_ajax
+ wait_for_requests
page.within '.dropdown-menu-user' do
click_link text
@@ -125,6 +125,6 @@ feature 'Multiple merge requests updating from merge_requests#index', feature: t
def click_update_merge_requests_button
find('.update_selected_issues').click
- wait_for_ajax
+ wait_for_requests
end
end
diff --git a/spec/features/merge_requests/user_posts_diff_notes_spec.rb b/spec/features/merge_requests/user_posts_diff_notes_spec.rb
index 7756202e3f5..14bc549c9f9 100644
--- a/spec/features/merge_requests/user_posts_diff_notes_spec.rb
+++ b/spec/features/merge_requests/user_posts_diff_notes_spec.rb
@@ -73,7 +73,7 @@ feature 'Merge requests > User posts diff notes', :js do
context 'with an unfolded line' do
before(:each) do
find('.js-unfold', match: :first).click
- wait_for_ajax
+ wait_for_requests
end
# The first `.js-unfold` unfolds upwards, therefore the first
@@ -122,7 +122,7 @@ feature 'Merge requests > User posts diff notes', :js do
context 'with an unfolded line' do
before(:each) do
find('.js-unfold', match: :first).click
- wait_for_ajax
+ wait_for_requests
end
# The first `.js-unfold` unfolds upwards, therefore the first
@@ -213,7 +213,7 @@ feature 'Merge requests > User posts diff notes', :js do
write_comment_on_line(line_holder, diff_side)
click_button 'Comment'
- wait_for_ajax
+ wait_for_requests
assert_comment_persistence(line_holder, asset_form_reset: asset_form_reset)
end
diff --git a/spec/features/merge_requests/user_posts_notes_spec.rb b/spec/features/merge_requests/user_posts_notes_spec.rb
index 7fc0e2ce6ec..06de072257a 100644
--- a/spec/features/merge_requests/user_posts_notes_spec.rb
+++ b/spec/features/merge_requests/user_posts_notes_spec.rb
@@ -98,7 +98,7 @@ describe 'Merge requests > User posts notes', :js do
find('.btn-save').click
end
- wait_for_ajax
+ wait_for_requests
find('.note').hover
find('.js-note-edit').click
@@ -139,7 +139,7 @@ describe 'Merge requests > User posts notes', :js do
find('.js-note-attachment-delete').click
is_expected.not_to have_css('.note-attachment')
is_expected.not_to have_css('.current-note-edit-form')
- wait_for_ajax
+ wait_for_requests
end
end
end
diff --git a/spec/features/merge_requests/user_uses_slash_commands_spec.rb b/spec/features/merge_requests/user_uses_slash_commands_spec.rb
index f0ad57eb92f..0e64a3e1a4b 100644
--- a/spec/features/merge_requests/user_uses_slash_commands_spec.rb
+++ b/spec/features/merge_requests/user_uses_slash_commands_spec.rb
@@ -21,7 +21,7 @@ feature 'Merge Requests > User uses slash commands', feature: true, js: true do
end
after do
- wait_for_ajax
+ wait_for_requests
end
describe 'toggling the WIP prefix in the title from note' do
@@ -160,7 +160,7 @@ feature 'Merge Requests > User uses slash commands', feature: true, js: true do
it 'changes target branch from a note' do
write_note("message start \n/target_branch merge-test\n message end.")
- wait_for_ajax
+ wait_for_requests
expect(page).not_to have_content('/target_branch')
expect(page).to have_content('message start')
expect(page).to have_content('message end.')
diff --git a/spec/features/merge_requests/versions_spec.rb b/spec/features/merge_requests/versions_spec.rb
index 2b5b803946c..aad522ee26e 100644
--- a/spec/features/merge_requests/versions_spec.rb
+++ b/spec/features/merge_requests/versions_spec.rb
@@ -75,7 +75,7 @@ feature 'Merge Request versions', js: true, feature: true do
find(".js-comment-button").click
end
- wait_for_ajax
+ wait_for_requests
expect(page).to have_content("Typo, please fix")
end
@@ -124,9 +124,11 @@ feature 'Merge Request versions', js: true, feature: true do
diff_refs: merge_request_diff3.compare_with(merge_request_diff1.head_commit_sha).diff_refs
)
outdated_diff_note = create(:diff_note_on_merge_request, project: project, noteable: merge_request, position: position)
+ outdated_diff_note.position = outdated_diff_note.original_position
+ outdated_diff_note.save!
visit current_url
- wait_for_ajax
+ wait_for_requests
expect(page).to have_css(".diffs .notes[data-discussion-id='#{outdated_diff_note.discussion_id}']")
end
@@ -144,7 +146,7 @@ feature 'Merge Request versions', js: true, feature: true do
find(".js-comment-button").click
end
- wait_for_ajax
+ wait_for_requests
expect(page).to have_content("Typo, please fix")
end
diff --git a/spec/features/merge_requests/widget_deployments_spec.rb b/spec/features/merge_requests/widget_deployments_spec.rb
index 8370499f6ed..118ecd9cba5 100644
--- a/spec/features/merge_requests/widget_deployments_spec.rb
+++ b/spec/features/merge_requests/widget_deployments_spec.rb
@@ -18,7 +18,7 @@ feature 'Widget Deployments Header', feature: true, js: true do
end
scenario 'displays that the environment is deployed' do
- wait_for_ajax
+ wait_for_requests
expect(page).to have_content("Deployed to #{environment.name}")
expect(find('.js-deploy-time')['data-title']).to eq(deployment.created_at.to_time.in_time_zone.to_s(:medium))
@@ -34,7 +34,7 @@ feature 'Widget Deployments Header', feature: true, js: true do
end
background do
- wait_for_ajax
+ wait_for_requests
end
scenario 'does show stop button' do
diff --git a/spec/features/merge_requests/widget_spec.rb b/spec/features/merge_requests/widget_spec.rb
index ae799584c0f..be8b1423c20 100644
--- a/spec/features/merge_requests/widget_spec.rb
+++ b/spec/features/merge_requests/widget_spec.rb
@@ -27,7 +27,7 @@ describe 'Merge request', :feature, :js do
it 'shows widget status after creating new merge request' do
click_button 'Submit merge request'
- wait_for_ajax
+ wait_for_requests
expect(page).to have_selector('.accept-merge-request')
expect(find('.accept-merge-request')['disabled']).not_to be(true)
@@ -48,7 +48,7 @@ describe 'Merge request', :feature, :js do
end
it 'shows environments link' do
- wait_for_ajax
+ wait_for_requests
page.within('.mr-widget-heading') do
expect(page).to have_content("Deployed to #{environment.name}")
@@ -58,7 +58,7 @@ describe 'Merge request', :feature, :js do
it 'shows green accept merge request button' do
# Wait for the `ci_status` and `merge_check` requests
- wait_for_ajax
+ wait_for_requests
expect(page).to have_selector('.accept-merge-request')
expect(find('.accept-merge-request')['disabled']).not_to be(true)
end
@@ -76,7 +76,7 @@ describe 'Merge request', :feature, :js do
it 'has danger button while waiting for external CI status' do
# Wait for the `ci_status` and `merge_check` requests
- wait_for_ajax
+ wait_for_requests
expect(page).to have_selector('.accept-merge-request.btn-danger')
end
end
@@ -88,32 +88,29 @@ describe 'Merge request', :feature, :js do
sha: merge_request.diff_head_sha,
ref: merge_request.source_branch,
status: 'failed',
- statuses: [commit_status])
+ statuses: [commit_status],
+ head_pipeline_of: merge_request)
create(:ci_build, :pending, pipeline: pipeline)
- merge_request.update(head_pipeline: pipeline)
-
visit namespace_project_merge_request_path(project.namespace, project, merge_request)
end
it 'has danger button when not succeeded' do
# Wait for the `ci_status` and `merge_check` requests
- wait_for_ajax
+ wait_for_requests
expect(page).to have_selector('.accept-merge-request.btn-danger')
end
end
context 'when merge request is in the blocked pipeline state' do
before do
- pipeline = create(
+ create(
:ci_pipeline,
project: project,
sha: merge_request.diff_head_sha,
ref: merge_request.source_branch,
- status: :manual
- )
-
- merge_request.update(head_pipeline: pipeline)
+ status: :manual,
+ head_pipeline_of: merge_request)
visit namespace_project_merge_request_path(project.namespace,
project,
@@ -135,17 +132,16 @@ describe 'Merge request', :feature, :js do
sha: merge_request.diff_head_sha,
ref: merge_request.source_branch,
status: 'pending',
- statuses: [commit_status])
+ statuses: [commit_status],
+ head_pipeline_of: merge_request)
create(:ci_build, :pending, pipeline: pipeline)
- merge_request.update(head_pipeline: pipeline)
-
visit namespace_project_merge_request_path(project.namespace, project, merge_request)
end
it 'has info button when MWBS button' do
# Wait for the `ci_status` and `merge_check` requests
- wait_for_ajax
+ wait_for_requests
expect(page).to have_selector('.accept-merge-request.btn-info')
end
end
@@ -163,7 +159,7 @@ describe 'Merge request', :feature, :js do
it 'shows information about the merge error' do
# Wait for the `ci_status` and `merge_check` requests
- wait_for_ajax
+ wait_for_requests
page.within('.mr-widget-body') do
expect(page).to have_content('Something went wrong')
@@ -184,7 +180,7 @@ describe 'Merge request', :feature, :js do
it 'shows information about the merge error' do
# Wait for the `ci_status` and `merge_check` requests
- wait_for_ajax
+ wait_for_requests
page.within('.mr-widget-body') do
expect(page).to have_content('Something went wrong')
diff --git a/spec/features/milestones/milestones_spec.rb b/spec/features/milestones/milestones_spec.rb
index 9eec3d7f270..b3dfd6d0e81 100644
--- a/spec/features/milestones/milestones_spec.rb
+++ b/spec/features/milestones/milestones_spec.rb
@@ -78,7 +78,7 @@ describe 'Milestone draggable', feature: true, js: true do
scroll_into_view('.milestone-content')
drag_to(selector: '.issues-sortable-list', list_to_index: 1)
- wait_for_ajax
+ wait_for_requests
end
def create_and_drag_merge_request(params = {})
@@ -87,12 +87,12 @@ describe 'Milestone draggable', feature: true, js: true do
visit namespace_project_milestone_path(project.namespace, project, milestone)
page.find("a[href='#tab-merge-requests']").click
- wait_for_ajax
+ wait_for_requests
scroll_into_view('.milestone-content')
drag_to(selector: '.merge_requests-sortable-list', list_to_index: 1)
- wait_for_ajax
+ wait_for_requests
end
def scroll_into_view(selector)
diff --git a/spec/features/profile_spec.rb b/spec/features/profile_spec.rb
index e63feb14b7e..7df628fd7a0 100644
--- a/spec/features/profile_spec.rb
+++ b/spec/features/profile_spec.rb
@@ -47,6 +47,21 @@ describe 'Profile account page', feature: true do
end
end
+ describe 'when I reset RSS token' do
+ before do
+ visit profile_account_path
+ end
+
+ it 'resets RSS token' do
+ previous_token = find("#rss-token").value
+
+ click_link('Reset RSS token')
+
+ expect(page).to have_content 'RSS token was successfully reset'
+ expect(find('#rss-token').value).not_to eq(previous_token)
+ end
+ end
+
describe 'when I reset incoming email token' do
before do
allow(Gitlab.config.incoming_email).to receive(:enabled).and_return(true)
diff --git a/spec/features/projects/activity/rss_spec.rb b/spec/features/projects/activity/rss_spec.rb
index b47c6d431eb..3c1de5c09b2 100644
--- a/spec/features/projects/activity/rss_spec.rb
+++ b/spec/features/projects/activity/rss_spec.rb
@@ -16,7 +16,7 @@ feature 'Project Activity RSS' do
visit path
end
- it_behaves_like "it has an RSS button with current_user's private token"
+ it_behaves_like "it has an RSS button with current_user's RSS token"
end
context 'when signed out' do
@@ -24,6 +24,6 @@ feature 'Project Activity RSS' do
visit path
end
- it_behaves_like "it has an RSS button without a private token"
+ it_behaves_like "it has an RSS button without an RSS token"
end
end
diff --git a/spec/features/projects/artifacts/file_spec.rb b/spec/features/projects/artifacts/file_spec.rb
index 74308a7e8dd..25db908d917 100644
--- a/spec/features/projects/artifacts/file_spec.rb
+++ b/spec/features/projects/artifacts/file_spec.rb
@@ -13,7 +13,7 @@ feature 'Artifact file', :js, feature: true do
before do
visit_file('other_artifacts_0.1.2/doc_sample.txt')
- wait_for_ajax
+ wait_for_requests
end
it 'displays an error' do
@@ -37,7 +37,7 @@ feature 'Artifact file', :js, feature: true do
before do
visit_file('rails_sample.jpg')
- wait_for_ajax
+ wait_for_requests
end
it 'displays the blob' do
diff --git a/spec/features/projects/blobs/blob_show_spec.rb b/spec/features/projects/blobs/blob_show_spec.rb
index fc242082278..82cfbfda157 100644
--- a/spec/features/projects/blobs/blob_show_spec.rb
+++ b/spec/features/projects/blobs/blob_show_spec.rb
@@ -6,7 +6,7 @@ feature 'File blob', :js, feature: true do
def visit_blob(path, fragment = nil)
visit namespace_project_blob_path(project.namespace, project, File.join('master', path), anchor: fragment)
- wait_for_ajax
+ wait_for_requests
end
context 'Ruby file' do
@@ -61,7 +61,7 @@ feature 'File blob', :js, feature: true do
before do
find('.js-blob-viewer-switch-btn[data-viewer=simple]').click
- wait_for_ajax
+ wait_for_requests
end
it 'displays the blob using the simple viewer' do
@@ -82,7 +82,7 @@ feature 'File blob', :js, feature: true do
before do
find('.js-blob-viewer-switch-btn[data-viewer=rich]').click
- wait_for_ajax
+ wait_for_requests
end
it 'displays the blob using the rich viewer' do
@@ -170,7 +170,7 @@ feature 'File blob', :js, feature: true do
before do
find('.js-blob-viewer-switcher .js-blob-viewer-switch-btn[data-viewer=simple]').click
- wait_for_ajax
+ wait_for_requests
end
it 'displays an error' do
diff --git a/spec/features/projects/blobs/edit_spec.rb b/spec/features/projects/blobs/edit_spec.rb
index cc5b1a7e734..1a38997450d 100644
--- a/spec/features/projects/blobs/edit_spec.rb
+++ b/spec/features/projects/blobs/edit_spec.rb
@@ -18,7 +18,7 @@ feature 'Editing file blob', feature: true, js: true do
end
def edit_and_commit
- wait_for_ajax
+ wait_for_requests
find('.js-edit-blob').click
execute_script('ace.edit("editor").setValue("class NextFeature\nend\n")')
click_button 'Commit changes'
diff --git a/spec/features/projects/blobs/user_create_spec.rb b/spec/features/projects/blobs/user_create_spec.rb
index d805450e095..4b6c55f5f44 100644
--- a/spec/features/projects/blobs/user_create_spec.rb
+++ b/spec/features/projects/blobs/user_create_spec.rb
@@ -15,7 +15,7 @@ feature 'New blob creation', feature: true, js: true do
end
def edit_file
- wait_for_ajax
+ wait_for_requests
fill_in 'file_name', with: 'feature.rb'
execute_script("ace.edit('editor').setValue('#{content}')")
end
diff --git a/spec/features/projects/commit/cherry_pick_spec.rb b/spec/features/projects/commit/cherry_pick_spec.rb
index fa67d390c47..bc7ca0ddd38 100644
--- a/spec/features/projects/commit/cherry_pick_spec.rb
+++ b/spec/features/projects/commit/cherry_pick_spec.rb
@@ -72,11 +72,11 @@ describe 'Cherry-pick Commits' do
click_button 'master'
end
- wait_for_ajax
+ wait_for_requests
page.within('#modal-cherry-pick-commit .dropdown-menu') do
find('.dropdown-input input').set('feature')
- wait_for_ajax
+ wait_for_requests
click_link "feature"
end
diff --git a/spec/features/projects/commit/mini_pipeline_graph_spec.rb b/spec/features/projects/commit/mini_pipeline_graph_spec.rb
index 98c0f2c63b0..f2de195eb7f 100644
--- a/spec/features/projects/commit/mini_pipeline_graph_spec.rb
+++ b/spec/features/projects/commit/mini_pipeline_graph_spec.rb
@@ -32,7 +32,7 @@ feature 'Mini Pipeline Graph in Commit View', :js, :feature do
it 'should show the builds list when stage is clicked' do
first('.mini-pipeline-graph-dropdown-toggle').click
- wait_for_ajax
+ wait_for_requests
page.within '.js-builds-dropdown-list' do
expect(page).to have_selector('.ci-status-icon-running')
diff --git a/spec/features/projects/commit/rss_spec.rb b/spec/features/projects/commit/rss_spec.rb
index 6e0e1916f87..03b6d560c96 100644
--- a/spec/features/projects/commit/rss_spec.rb
+++ b/spec/features/projects/commit/rss_spec.rb
@@ -12,8 +12,8 @@ feature 'Project Commits RSS' do
visit path
end
- it_behaves_like "it has an RSS button with current_user's private token"
- it_behaves_like "an autodiscoverable RSS feed with current_user's private token"
+ 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
context 'when signed out' do
@@ -21,7 +21,7 @@ feature 'Project Commits RSS' do
visit path
end
- it_behaves_like "it has an RSS button without a private token"
- it_behaves_like "an autodiscoverable RSS feed without a private token"
+ it_behaves_like "it has an RSS button without an RSS token"
+ it_behaves_like "an autodiscoverable RSS feed without an RSS token"
end
end
diff --git a/spec/features/projects/compare_spec.rb b/spec/features/projects/compare_spec.rb
index b2a3b111c9e..4162f2579d1 100644
--- a/spec/features/projects/compare_spec.rb
+++ b/spec/features/projects/compare_spec.rb
@@ -52,8 +52,12 @@ describe "Compare", js: true do
def select_using_dropdown(dropdown_type, selection)
dropdown = find(".js-compare-#{dropdown_type}-dropdown")
dropdown.find(".compare-dropdown-toggle").click
+ # find input before using to wait for the inputs visiblity
+ dropdown.find('.dropdown-menu')
dropdown.fill_in("Filter by Git revision", with: selection)
- wait_for_ajax
- dropdown.find_all("a[data-ref=\"#{selection}\"]", visible: true).last.click
+ wait_for_requests
+ # find before all to wait for the items visiblity
+ dropdown.find("a[data-ref=\"#{selection}\"]", match: :first)
+ dropdown.all("a[data-ref=\"#{selection}\"]").last.click
end
end
diff --git a/spec/features/projects/features_visibility_spec.rb b/spec/features/projects/features_visibility_spec.rb
index 4533a6fb144..c49648f54bd 100644
--- a/spec/features/projects/features_visibility_spec.rb
+++ b/spec/features/projects/features_visibility_spec.rb
@@ -21,17 +21,17 @@ describe 'Edit Project Settings', feature: true do
select 'Disabled', from: "project_project_feature_attributes_#{tool_name}_access_level"
click_button 'Save changes'
- wait_for_ajax
+ wait_for_requests
expect(page).not_to have_selector(".shortcuts-#{shortcut_name}")
select 'Everyone with access', from: "project_project_feature_attributes_#{tool_name}_access_level"
click_button 'Save changes'
- wait_for_ajax
+ wait_for_requests
expect(page).to have_selector(".shortcuts-#{shortcut_name}")
select 'Only team members', from: "project_project_feature_attributes_#{tool_name}_access_level"
click_button 'Save changes'
- wait_for_ajax
+ wait_for_requests
expect(page).to have_selector(".shortcuts-#{shortcut_name}")
sleep 0.1
@@ -169,7 +169,7 @@ describe 'Edit Project Settings', feature: true do
select "Disabled", from: "project_project_feature_attributes_wiki_access_level"
click_button "Save changes"
- wait_for_ajax
+ wait_for_requests
visit namespace_project_path(project.namespace, project)
@@ -182,7 +182,7 @@ describe 'Edit Project Settings', feature: true do
select "Disabled", from: "project_project_feature_attributes_wiki_access_level"
click_button "Save changes"
- wait_for_ajax
+ wait_for_requests
visit activity_namespace_project_path(project.namespace, project)
@@ -223,7 +223,7 @@ describe 'Edit Project Settings', feature: true do
def save_changes_and_check_activity_tab
click_button "Save changes"
- wait_for_ajax
+ wait_for_requests
visit activity_namespace_project_path(project.namespace, project)
diff --git a/spec/features/projects/files/browse_files_spec.rb b/spec/features/projects/files/browse_files_spec.rb
index 4166aec1956..c0a9327249c 100644
--- a/spec/features/projects/files/browse_files_spec.rb
+++ b/spec/features/projects/files/browse_files_spec.rb
@@ -24,7 +24,7 @@ feature 'user browses project', feature: true, js: true do
click_link 'files'
click_link 'lfs'
click_link 'lfs_object.iso'
- wait_for_ajax
+ wait_for_requests
expect(page).not_to have_content 'Download (1.5 MB)'
expect(page).to have_content 'version https://git-lfs.github.com/spec/v1'
@@ -36,7 +36,7 @@ feature 'user browses project', feature: true, js: true do
last_commit = project.repository.last_commit_for_path(project.default_branch, 'files')
click_link 'files'
- wait_for_ajax
+ wait_for_requests
page.within('.blob-commit-info') do
expect(page).to have_content last_commit.short_id
diff --git a/spec/features/projects/files/dockerfile_dropdown_spec.rb b/spec/features/projects/files/dockerfile_dropdown_spec.rb
index 548131c7cd4..93909e91d05 100644
--- a/spec/features/projects/files/dockerfile_dropdown_spec.rb
+++ b/spec/features/projects/files/dockerfile_dropdown_spec.rb
@@ -19,14 +19,14 @@ feature 'User wants to add a Dockerfile file', feature: true do
scenario 'user can pick a Dockerfile file from the dropdown', js: true do
find('.js-dockerfile-selector').click
- wait_for_ajax
+ wait_for_requests
within '.dockerfile-selector' do
find('.dropdown-input-field').set('HTTPd')
find('.dropdown-content li', text: 'HTTPd').click
end
- wait_for_ajax
+ wait_for_requests
expect(page).to have_css('.dockerfile-selector .dropdown-toggle-text', text: 'HTTPd')
expect(page).to have_content('COPY ./ /usr/local/apache2/htdocs/')
diff --git a/spec/features/projects/files/find_file_keyboard_spec.rb b/spec/features/projects/files/find_file_keyboard_spec.rb
index e7a6749d8ac..ee42bcaec4b 100644
--- a/spec/features/projects/files/find_file_keyboard_spec.rb
+++ b/spec/features/projects/files/find_file_keyboard_spec.rb
@@ -10,7 +10,7 @@ feature 'Find file keyboard shortcuts', feature: true, js: true do
visit namespace_project_find_file_path(project.namespace, project, project.repository.root_ref)
- wait_for_ajax
+ wait_for_requests
end
it 'opens file when pressing enter key' do
diff --git a/spec/features/projects/files/gitignore_dropdown_spec.rb b/spec/features/projects/files/gitignore_dropdown_spec.rb
index e59428f8b24..e9f49453121 100644
--- a/spec/features/projects/files/gitignore_dropdown_spec.rb
+++ b/spec/features/projects/files/gitignore_dropdown_spec.rb
@@ -15,12 +15,12 @@ feature 'User wants to add a .gitignore file', feature: true do
scenario 'user can pick a .gitignore file from the dropdown', js: true do
find('.js-gitignore-selector').click
- wait_for_ajax
+ wait_for_requests
within '.gitignore-selector' do
find('.dropdown-input-field').set('rails')
find('.dropdown-content li', text: 'Rails').click
end
- wait_for_ajax
+ wait_for_requests
expect(page).to have_css('.gitignore-selector .dropdown-toggle-text', text: 'Rails')
expect(page).to have_content('/.bundle')
diff --git a/spec/features/projects/files/gitlab_ci_yml_dropdown_spec.rb b/spec/features/projects/files/gitlab_ci_yml_dropdown_spec.rb
index 85b66b93fba..031b89d0499 100644
--- a/spec/features/projects/files/gitlab_ci_yml_dropdown_spec.rb
+++ b/spec/features/projects/files/gitlab_ci_yml_dropdown_spec.rb
@@ -15,12 +15,12 @@ feature 'User wants to add a .gitlab-ci.yml file', feature: true do
scenario 'user can pick a template from the dropdown', js: true do
find('.js-gitlab-ci-yml-selector').click
- wait_for_ajax
+ wait_for_requests
within '.gitlab-ci-yml-selector' do
find('.dropdown-input-field').set('Jekyll')
find('.dropdown-content li', text: 'Jekyll').click
end
- wait_for_ajax
+ wait_for_requests
expect(page).to have_css('.gitlab-ci-yml-selector .dropdown-toggle-text', text: 'Jekyll')
expect(page).to have_content('This file is a template, and might need editing before it works on your project')
diff --git a/spec/features/projects/files/project_owner_creates_license_file_spec.rb b/spec/features/projects/files/project_owner_creates_license_file_spec.rb
index 249830921ac..8d410cc3f2e 100644
--- a/spec/features/projects/files/project_owner_creates_license_file_spec.rb
+++ b/spec/features/projects/files/project_owner_creates_license_file_spec.rb
@@ -63,7 +63,7 @@ feature 'project owner creates a license file', feature: true, js: true do
page.within('.js-license-selector-wrap') do
click_button 'Apply a license template'
click_link template
- wait_for_ajax
+ wait_for_requests
end
end
end
diff --git a/spec/features/projects/files/project_owner_sees_link_to_create_license_file_in_empty_project_spec.rb b/spec/features/projects/files/project_owner_sees_link_to_create_license_file_in_empty_project_spec.rb
index 70a41886985..8e197bccabf 100644
--- a/spec/features/projects/files/project_owner_sees_link_to_create_license_file_in_empty_project_spec.rb
+++ b/spec/features/projects/files/project_owner_sees_link_to_create_license_file_in_empty_project_spec.rb
@@ -40,7 +40,7 @@ feature 'project owner sees a link to create a license file in empty project', f
page.within('.js-license-selector-wrap') do
click_button 'Apply a license template'
click_link template
- wait_for_ajax
+ wait_for_requests
end
end
end
diff --git a/spec/features/projects/files/undo_template_spec.rb b/spec/features/projects/files/undo_template_spec.rb
index cd3af0b7d29..de10eec0557 100644
--- a/spec/features/projects/files/undo_template_spec.rb
+++ b/spec/features/projects/files/undo_template_spec.rb
@@ -57,7 +57,7 @@ end
def select_file_template(template_selector_selector, template_name)
find(template_selector_selector).click
find('.dropdown-content li', text: template_name).click
- wait_for_ajax
+ wait_for_requests
end
def select_file_template_type(template_type)
diff --git a/spec/features/projects/guest_navigation_menu_spec.rb b/spec/features/projects/guest_navigation_menu_spec.rb
index 726469daba4..b91c3eff478 100644
--- a/spec/features/projects/guest_navigation_menu_spec.rb
+++ b/spec/features/projects/guest_navigation_menu_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe "Guest navigation menu" do
+describe 'Guest navigation menu' do
let(:project) { create(:empty_project, :private, public_builds: false) }
let(:guest) { create(:user) }
@@ -10,10 +10,10 @@ describe "Guest navigation menu" do
login_as(guest)
end
- it "shows allowed tabs only" do
+ it 'shows allowed tabs only' do
visit namespace_project_path(project.namespace, project)
- within(".nav-links") do
+ within('.layout-nav') do
expect(page).to have_content 'Project'
expect(page).to have_content 'Issues'
expect(page).to have_content 'Wiki'
@@ -23,4 +23,60 @@ describe "Guest navigation menu" do
expect(page).not_to have_content 'Merge Requests'
end
end
+
+ it 'does not show fork button' do
+ visit namespace_project_path(project.namespace, project)
+
+ within('.count-buttons') do
+ expect(page).not_to have_link 'Fork'
+ end
+ end
+
+ it 'does not show clone path' do
+ visit namespace_project_path(project.namespace, project)
+
+ within('.project-repo-buttons') do
+ expect(page).not_to have_selector '.project-clone-holder'
+ end
+ end
+
+ describe 'project landing page' do
+ before do
+ project.project_feature.update!(
+ issues_access_level: ProjectFeature::DISABLED,
+ wiki_access_level: ProjectFeature::DISABLED
+ )
+ end
+
+ it 'does not show the project file list landing page' do
+ visit namespace_project_path(project.namespace, project)
+
+ expect(page).not_to have_selector '.project-stats'
+ expect(page).not_to have_selector '.project-last-commit'
+ expect(page).not_to have_selector '.project-show-files'
+ expect(page).to have_selector '.project-show-customize_workflow'
+ end
+
+ it 'shows the customize workflow when issues and wiki are disabled' do
+ visit namespace_project_path(project.namespace, project)
+
+ expect(page).to have_selector '.project-show-customize_workflow'
+ end
+
+ it 'shows the wiki when enabled' do
+ project.project_feature.update!(wiki_access_level: ProjectFeature::PRIVATE)
+
+ visit namespace_project_path(project.namespace, project)
+
+ expect(page).to have_selector '.project-show-wiki'
+ end
+
+ it 'shows the issues when enabled' do
+ project.project_feature.update!(issues_access_level: ProjectFeature::PRIVATE)
+
+ visit namespace_project_path(project.namespace, project)
+
+ expect(page).to have_selector '.issues-list'
+ end
+ end
end
diff --git a/spec/features/projects/issuable_templates_spec.rb b/spec/features/projects/issuable_templates_spec.rb
index fa5e30075e3..3076c863dcb 100644
--- a/spec/features/projects/issuable_templates_spec.rb
+++ b/spec/features/projects/issuable_templates_spec.rb
@@ -34,14 +34,14 @@ feature 'issuable templates', feature: true, js: true do
scenario 'user selects "bug" template' do
select_template 'bug'
- wait_for_ajax
+ wait_for_requests
assert_template
save_changes
end
scenario 'user selects "bug" template and then "no template"' do
select_template 'bug'
- wait_for_ajax
+ wait_for_requests
select_option 'No template'
assert_template('')
save_changes('')
@@ -49,7 +49,7 @@ feature 'issuable templates', feature: true, js: true do
scenario 'user selects "bug" template, edits description and then selects "reset template"' do
select_template 'bug'
- wait_for_ajax
+ wait_for_requests
find_field('issue_description').send_keys(description_addition)
assert_template(template_content + description_addition)
select_option 'Reset template'
@@ -61,7 +61,7 @@ feature 'issuable templates', feature: true, js: true do
start_height = page.evaluate_script('$(".markdown-area").outerHeight()')
select_template 'test'
- wait_for_ajax
+ wait_for_requests
end_height = page.evaluate_script('$(".markdown-area").outerHeight()')
@@ -88,7 +88,7 @@ feature 'issuable templates', feature: true, js: true do
scenario 'user selects "bug" template' do
select_template 'bug'
- wait_for_ajax
+ wait_for_requests
assert_template("#{template_content}")
save_changes
end
@@ -111,7 +111,7 @@ feature 'issuable templates', feature: true, js: true do
scenario 'user selects "feature-proposal" template' do
select_template 'feature-proposal'
- wait_for_ajax
+ wait_for_requests
assert_template
save_changes
end
@@ -143,7 +143,7 @@ feature 'issuable templates', feature: true, js: true do
context 'template exists in target project' do
scenario 'user selects template' do
select_template 'feature-proposal'
- wait_for_ajax
+ wait_for_requests
assert_template
save_changes
end
diff --git a/spec/features/projects/issues/rss_spec.rb b/spec/features/projects/issues/rss_spec.rb
index 71429f00095..f6852192aef 100644
--- a/spec/features/projects/issues/rss_spec.rb
+++ b/spec/features/projects/issues/rss_spec.rb
@@ -16,8 +16,8 @@ feature 'Project Issues RSS' do
visit path
end
- it_behaves_like "it has an RSS button with current_user's private token"
- it_behaves_like "an autodiscoverable RSS feed with current_user's private token"
+ 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
context 'when signed out' do
@@ -25,7 +25,7 @@ feature 'Project Issues RSS' do
visit path
end
- it_behaves_like "it has an RSS button without a private token"
- it_behaves_like "an autodiscoverable RSS feed without a private token"
+ it_behaves_like "it has an RSS button without an RSS token"
+ it_behaves_like "an autodiscoverable RSS feed without an RSS token"
end
end
diff --git a/spec/features/projects/labels/update_prioritization_spec.rb b/spec/features/projects/labels/update_prioritization_spec.rb
index 836f81fb16d..34fafe072a3 100644
--- a/spec/features/projects/labels/update_prioritization_spec.rb
+++ b/spec/features/projects/labels/update_prioritization_spec.rb
@@ -24,7 +24,7 @@ feature 'Prioritize labels', feature: true do
page.within('.other-labels') do
all('.js-toggle-priority')[1].click
- wait_for_ajax
+ wait_for_requests
expect(page).not_to have_content('feature')
end
@@ -43,7 +43,7 @@ feature 'Prioritize labels', feature: true do
expect(page).to have_content('feature')
first('.js-toggle-priority').click
- wait_for_ajax
+ wait_for_requests
expect(page).not_to have_content('bug')
end
@@ -59,7 +59,7 @@ feature 'Prioritize labels', feature: true do
page.within('.other-labels') do
first('.js-toggle-priority').click
- wait_for_ajax
+ wait_for_requests
expect(page).not_to have_content('bug')
end
@@ -78,7 +78,7 @@ feature 'Prioritize labels', feature: true do
expect(page).to have_content('bug')
first('.js-toggle-priority').click
- wait_for_ajax
+ wait_for_requests
expect(page).not_to have_content('bug')
end
@@ -107,7 +107,7 @@ feature 'Prioritize labels', feature: true do
end
refresh
- wait_for_ajax
+ wait_for_requests
page.within('.prioritized-labels') do
expect(first('li')).to have_content('feature')
diff --git a/spec/features/projects/main/rss_spec.rb b/spec/features/projects/main/rss_spec.rb
index b1a3af612a1..53966229a2a 100644
--- a/spec/features/projects/main/rss_spec.rb
+++ b/spec/features/projects/main/rss_spec.rb
@@ -12,7 +12,7 @@ feature 'Project RSS' do
visit path
end
- it_behaves_like "an autodiscoverable RSS feed with current_user's private token"
+ it_behaves_like "an autodiscoverable RSS feed with current_user's RSS token"
end
context 'when signed out' do
@@ -20,6 +20,6 @@ feature 'Project RSS' do
visit path
end
- it_behaves_like "an autodiscoverable RSS feed without a private token"
+ it_behaves_like "an autodiscoverable RSS feed without an RSS token"
end
end
diff --git a/spec/features/projects/members/group_links_spec.rb b/spec/features/projects/members/group_links_spec.rb
index ab2b089db2e..3d253f01484 100644
--- a/spec/features/projects/members/group_links_spec.rb
+++ b/spec/features/projects/members/group_links_spec.rb
@@ -20,7 +20,7 @@ feature 'Projects > Members > Anonymous user sees members', feature: true, js: t
click_link 'Guest'
end
- wait_for_ajax
+ wait_for_requests
visit namespace_project_settings_members_path(project.namespace, project)
@@ -31,7 +31,7 @@ feature 'Projects > Members > Anonymous user sees members', feature: true, js: t
tomorrow = Date.today + 3
fill_in "member_expires_at_#{group.id}", with: tomorrow.strftime("%F")
- wait_for_ajax
+ wait_for_requests
page.within(find('li.group_member')) do
expect(page).to have_content('Expires in')
@@ -42,7 +42,7 @@ feature 'Projects > Members > Anonymous user sees members', feature: true, js: t
page.within(first('.group_member')) do
find('.btn-remove').click
end
- wait_for_ajax
+ wait_for_requests
expect(page).not_to have_selector('.group_member')
end
diff --git a/spec/features/projects/members/master_adds_member_with_expiration_date_spec.rb b/spec/features/projects/members/master_adds_member_with_expiration_date_spec.rb
index 19d14ad9af4..1e6f15d8258 100644
--- a/spec/features/projects/members/master_adds_member_with_expiration_date_spec.rb
+++ b/spec/features/projects/members/master_adds_member_with_expiration_date_spec.rb
@@ -38,7 +38,7 @@ feature 'Projects > Members > Master adds member with expiration date', feature:
page.within "#project_member_#{new_member.project_members.first.id}" do
find('.js-access-expiration-date').set date.to_s(:medium)
- wait_for_ajax
+ wait_for_requests
expect(page).to have_content('Expires in 3 days')
end
end
diff --git a/spec/features/projects/pipeline_schedules_spec.rb b/spec/features/projects/pipeline_schedules_spec.rb
index 1211b17b3d8..f40e1bc4930 100644
--- a/spec/features/projects/pipeline_schedules_spec.rb
+++ b/spec/features/projects/pipeline_schedules_spec.rb
@@ -2,10 +2,9 @@ require 'spec_helper'
feature 'Pipeline Schedules', :feature do
include PipelineSchedulesHelper
- include WaitForAjax
let!(:project) { create(:project) }
- let!(:pipeline_schedule) { create(:ci_pipeline_schedule, project: project) }
+ let!(:pipeline_schedule) { create(:ci_pipeline_schedule, :nightly, project: project ) }
let!(:pipeline) { create(:ci_pipeline, pipeline_schedule: pipeline_schedule) }
let(:scope) { nil }
let!(:user) { create(:user) }
@@ -32,6 +31,7 @@ feature 'Pipeline Schedules', :feature do
it 'displays the required information description' do
page.within('.pipeline-schedule-table-row') do
expect(page).to have_content('pipeline schedule')
+ expect(page).to have_content(pipeline_schedule.real_next_run.strftime('%b %d, %Y'))
expect(page).to have_link('master')
expect(page).to have_link("##{pipeline.id}")
end
diff --git a/spec/features/projects/pipelines/pipelines_spec.rb b/spec/features/projects/pipelines/pipelines_spec.rb
index 5f82cf2f5e5..a97a92aa64f 100644
--- a/spec/features/projects/pipelines/pipelines_spec.rb
+++ b/spec/features/projects/pipelines/pipelines_spec.rb
@@ -1,8 +1,6 @@
require 'spec_helper'
describe 'Pipelines', :feature, :js do
- include WaitForVueResource
-
let(:project) { create(:empty_project) }
context 'when user is logged in' do
@@ -54,7 +52,7 @@ describe 'Pipelines', :feature, :js do
context 'header tabs' do
before do
visit namespace_project_pipelines_path(project.namespace, project)
- wait_for_vue_resource
+ wait_for_requests
end
it 'shows a tab for All pipelines and count' do
@@ -106,7 +104,7 @@ describe 'Pipelines', :feature, :js do
context 'when canceling' do
before do
find('.js-pipelines-cancel-button').click
- wait_for_vue_resource
+ wait_for_requests
end
it 'indicated that pipelines was canceled' do
@@ -136,7 +134,7 @@ describe 'Pipelines', :feature, :js do
context 'when retrying' do
before do
find('.js-pipelines-retry-button').click
- wait_for_vue_resource
+ wait_for_requests
end
it 'shows running pipeline that is not retryable' do
@@ -356,14 +354,14 @@ describe 'Pipelines', :feature, :js do
it 'should render pagination' do
visit namespace_project_pipelines_path(project.namespace, project)
- wait_for_vue_resource
+ wait_for_requests
expect(page).to have_selector('.gl-pagination')
end
it 'should render second page of pipelines' do
visit namespace_project_pipelines_path(project.namespace, project, page: '2')
- wait_for_vue_resource
+ wait_for_requests
expect(page).to have_selector('.gl-pagination .page', count: 2)
end
@@ -392,7 +390,7 @@ describe 'Pipelines', :feature, :js do
create(:generic_commit_status, pipeline: pipeline, stage: 'external', name: 'jenkins', stage_idx: 3)
visit namespace_project_pipeline_path(project.namespace, project, pipeline)
- wait_for_vue_resource
+ wait_for_requests
end
it 'shows a graph with grouped stages' do
@@ -507,6 +505,6 @@ describe 'Pipelines', :feature, :js do
def visit_project_pipelines(**query)
visit namespace_project_pipelines_path(project.namespace, project, query)
- wait_for_vue_resource
+ wait_for_requests
end
end
diff --git a/spec/features/projects/ref_switcher_spec.rb b/spec/features/projects/ref_switcher_spec.rb
index 881ad7910dd..04414490571 100644
--- a/spec/features/projects/ref_switcher_spec.rb
+++ b/spec/features/projects/ref_switcher_spec.rb
@@ -12,12 +12,12 @@ feature 'Ref switcher', feature: true, js: true do
it 'allow user to change ref by enter key' do
click_button 'master'
- wait_for_ajax
+ wait_for_requests
page.within '.project-refs-form' do
input = find('input[type="search"]')
input.set 'binary'
- wait_for_ajax
+ wait_for_requests
expect(find('.dropdown-content ul')).to have_selector('li', count: 6)
@@ -31,7 +31,7 @@ feature 'Ref switcher', feature: true, js: true do
it "user selects ref with special characters" do
click_button 'master'
- wait_for_ajax
+ wait_for_requests
page.within '.project-refs-form' do
page.fill_in 'Search branches and tags', with: "'test'"
diff --git a/spec/features/projects/snippets/show_spec.rb b/spec/features/projects/snippets/show_spec.rb
index cedf3778c7e..b844e60e5d5 100644
--- a/spec/features/projects/snippets/show_spec.rb
+++ b/spec/features/projects/snippets/show_spec.rb
@@ -17,7 +17,7 @@ feature 'Project snippet', :js, feature: true do
before do
visit namespace_project_snippet_path(project.namespace, project, snippet)
- wait_for_ajax
+ wait_for_requests
end
it 'displays the blob' do
@@ -48,7 +48,7 @@ feature 'Project snippet', :js, feature: true do
before do
visit namespace_project_snippet_path(project.namespace, project, snippet)
- wait_for_ajax
+ wait_for_requests
end
it 'displays the blob using the rich viewer' do
@@ -78,7 +78,7 @@ feature 'Project snippet', :js, feature: true do
before do
find('.js-blob-viewer-switch-btn[data-viewer=simple]').click
- wait_for_ajax
+ wait_for_requests
end
it 'displays the blob using the simple viewer' do
@@ -99,7 +99,7 @@ feature 'Project snippet', :js, feature: true do
before do
find('.js-blob-viewer-switch-btn[data-viewer=rich]').click
- wait_for_ajax
+ wait_for_requests
end
it 'displays the blob using the rich viewer' do
@@ -120,7 +120,7 @@ feature 'Project snippet', :js, feature: true do
before do
visit namespace_project_snippet_path(project.namespace, project, snippet, anchor: 'L1')
- wait_for_ajax
+ wait_for_requests
end
it 'displays the blob using the simple viewer' do
diff --git a/spec/features/projects/tree/rss_spec.rb b/spec/features/projects/tree/rss_spec.rb
index 9ac51997d65..9bf59c4139c 100644
--- a/spec/features/projects/tree/rss_spec.rb
+++ b/spec/features/projects/tree/rss_spec.rb
@@ -12,7 +12,7 @@ feature 'Project Tree RSS' do
visit path
end
- it_behaves_like "an autodiscoverable RSS feed with current_user's private token"
+ it_behaves_like "an autodiscoverable RSS feed with current_user's RSS token"
end
context 'when signed out' do
@@ -20,6 +20,6 @@ feature 'Project Tree RSS' do
visit path
end
- it_behaves_like "an autodiscoverable RSS feed without a private token"
+ it_behaves_like "an autodiscoverable RSS feed without an RSS token"
end
end
diff --git a/spec/features/projects/view_on_env_spec.rb b/spec/features/projects/view_on_env_spec.rb
index b7a41ca54e6..640f1376548 100644
--- a/spec/features/projects/view_on_env_spec.rb
+++ b/spec/features/projects/view_on_env_spec.rb
@@ -54,7 +54,7 @@ describe 'View on environment', js: true do
visit diffs_namespace_project_merge_request_path(project.namespace, project, merge_request)
- wait_for_ajax
+ wait_for_requests
end
it 'has a "View on env" button' do
@@ -70,7 +70,7 @@ describe 'View on environment', js: true do
visit namespace_project_compare_path(project.namespace, project, from: 'master', to: branch_name)
- wait_for_ajax
+ wait_for_requests
end
it 'has a "View on env" button' do
@@ -84,7 +84,7 @@ describe 'View on environment', js: true do
visit namespace_project_compare_path(project.namespace, project, from: 'master', to: sha)
- wait_for_ajax
+ wait_for_requests
end
it 'has a "View on env" button' do
@@ -98,7 +98,7 @@ describe 'View on environment', js: true do
visit namespace_project_blob_path(project.namespace, project, File.join(branch_name, file_path))
- wait_for_ajax
+ wait_for_requests
end
it 'has a "View on env" button' do
@@ -112,7 +112,7 @@ describe 'View on environment', js: true do
visit namespace_project_blob_path(project.namespace, project, File.join(sha, file_path))
- wait_for_ajax
+ wait_for_requests
end
it 'has a "View on env" button' do
@@ -126,7 +126,7 @@ describe 'View on environment', js: true do
visit namespace_project_commit_path(project.namespace, project, sha)
- wait_for_ajax
+ wait_for_requests
end
it 'has a "View on env" button' do
diff --git a/spec/features/projects/wiki/user_creates_wiki_page_spec.rb b/spec/features/projects/wiki/user_creates_wiki_page_spec.rb
index 5c502ce4fb5..8912d575878 100644
--- a/spec/features/projects/wiki/user_creates_wiki_page_spec.rb
+++ b/spec/features/projects/wiki/user_creates_wiki_page_spec.rb
@@ -28,6 +28,40 @@ feature 'Projects > Wiki > User creates wiki page', js: true, feature: true do
expect(page).to have_content("Last edited by #{user.name}")
expect(page).to have_content('My awesome wiki!')
end
+
+ scenario 'creates ASCII wiki with LaTeX blocks' do
+ stub_application_setting(plantuml_url: 'http://localhost', plantuml_enabled: true)
+
+ ascii_content = <<~MD
+ :stem: latexmath
+
+ [stem]
+ ++++
+ \sqrt{4} = 2
+ ++++
+
+ another part
+
+ [latexmath]
+ ++++
+ \beta_x \gamma
+ ++++
+
+ stem:[2+2] is 4
+ MD
+
+ find('#wiki_format option[value=asciidoc]').select_option
+ fill_in :wiki_content, with: ascii_content
+
+ page.within '.wiki-form' do
+ click_button 'Create page'
+ end
+
+ page.within '.wiki' do
+ expect(page).to have_selector('.katex', count: 3)
+ expect(page).to have_content('2+2 is 4')
+ end
+ end
end
context 'when wiki is not empty' do
diff --git a/spec/features/search_spec.rb b/spec/features/search_spec.rb
index 2fda7758407..7834807b1f1 100644
--- a/spec/features/search_spec.rb
+++ b/spec/features/search_spec.rb
@@ -28,7 +28,7 @@ describe "Search", feature: true do
it 'shows group name after filtering' do
find('.js-search-group-dropdown').trigger('click')
- wait_for_ajax
+ wait_for_requests
page.within '.search-holder' do
click_link group.name
@@ -39,7 +39,7 @@ describe "Search", feature: true do
it 'filters by group projects after filtering by group' do
find('.js-search-group-dropdown').trigger('click')
- wait_for_ajax
+ wait_for_requests
page.within '.search-holder' do
click_link group.name
@@ -49,7 +49,7 @@ describe "Search", feature: true do
page.within('.project-filter') do
find('.js-search-project-dropdown').trigger('click')
- wait_for_ajax
+ wait_for_requests
expect(page).to have_link(group_project.name_with_namespace)
end
@@ -58,7 +58,7 @@ describe "Search", feature: true do
it 'shows project name after filtering' do
page.within('.project-filter') do
find('.js-search-project-dropdown').trigger('click')
- wait_for_ajax
+ wait_for_requests
click_link project.name_with_namespace
end
diff --git a/spec/features/snippets/create_snippet_spec.rb b/spec/features/snippets/create_snippet_spec.rb
index 9409c323288..31a2d4ae984 100644
--- a/spec/features/snippets/create_snippet_spec.rb
+++ b/spec/features/snippets/create_snippet_spec.rb
@@ -13,7 +13,7 @@ feature 'Create Snippet', :js, feature: true do
end
click_button 'Create snippet'
- wait_for_ajax
+ wait_for_requests
expect(page).to have_content('My Snippet Title')
expect(page).to have_content('Hello World!')
@@ -27,7 +27,7 @@ feature 'Create Snippet', :js, feature: true do
end
click_button 'Create snippet'
- wait_for_ajax
+ wait_for_requests
expect(page).to have_content('My Snippet Title')
expect(page).to have_content('snippet+file+name')
diff --git a/spec/features/snippets/notes_on_personal_snippets_spec.rb b/spec/features/snippets/notes_on_personal_snippets_spec.rb
index 698eb46573f..f7afc174019 100644
--- a/spec/features/snippets/notes_on_personal_snippets_spec.rb
+++ b/spec/features/snippets/notes_on_personal_snippets_spec.rb
@@ -93,7 +93,7 @@ describe 'Comments on personal snippets', :js, feature: true do
click_on 'Remove comment'
end
- wait_for_ajax
+ wait_for_requests
expect(page).not_to have_selector("#notes-list li#note_#{snippet_notes[0].id}")
end
diff --git a/spec/features/snippets/public_snippets_spec.rb b/spec/features/snippets/public_snippets_spec.rb
index 2df483818c3..afd945a8555 100644
--- a/spec/features/snippets/public_snippets_spec.rb
+++ b/spec/features/snippets/public_snippets_spec.rb
@@ -5,7 +5,7 @@ feature 'Public Snippets', :js, feature: true do
public_snippet = create(:personal_snippet, :public)
visit snippet_path(public_snippet)
- wait_for_ajax
+ wait_for_requests
expect(page).to have_content(public_snippet.content)
end
diff --git a/spec/features/snippets/show_spec.rb b/spec/features/snippets/show_spec.rb
index e36cf547f80..95fc1d2bb62 100644
--- a/spec/features/snippets/show_spec.rb
+++ b/spec/features/snippets/show_spec.rb
@@ -11,7 +11,7 @@ feature 'Snippet', :js, feature: true do
before do
visit snippet_path(snippet)
- wait_for_ajax
+ wait_for_requests
end
it 'displays the blob' do
@@ -42,7 +42,7 @@ feature 'Snippet', :js, feature: true do
before do
visit snippet_path(snippet)
- wait_for_ajax
+ wait_for_requests
end
it 'displays the blob using the rich viewer' do
@@ -72,7 +72,7 @@ feature 'Snippet', :js, feature: true do
before do
find('.js-blob-viewer-switch-btn[data-viewer=simple]').click
- wait_for_ajax
+ wait_for_requests
end
it 'displays the blob using the simple viewer' do
@@ -93,7 +93,7 @@ feature 'Snippet', :js, feature: true do
before do
find('.js-blob-viewer-switch-btn[data-viewer=rich]').click
- wait_for_ajax
+ wait_for_requests
end
it 'displays the blob using the rich viewer' do
@@ -114,7 +114,7 @@ feature 'Snippet', :js, feature: true do
before do
visit snippet_path(snippet, anchor: 'L1')
- wait_for_ajax
+ wait_for_requests
end
it 'displays the blob using the simple viewer' do
diff --git a/spec/features/task_lists_spec.rb b/spec/features/task_lists_spec.rb
index 8bd13caf2b0..563e65d3cc5 100644
--- a/spec/features/task_lists_spec.rb
+++ b/spec/features/task_lists_spec.rb
@@ -64,13 +64,11 @@ feature 'Task Lists', feature: true do
describe 'for Issues', feature: true do
describe 'multiple tasks', js: true do
- include WaitForVueResource
-
let!(:issue) { create(:issue, description: markdown, author: user, project: project) }
it 'renders' do
visit_issue(project, issue)
- wait_for_vue_resource
+ wait_for_requests
expect(page).to have_selector('ul.task-list', count: 1)
expect(page).to have_selector('li.task-list-item', count: 6)
@@ -79,7 +77,7 @@ feature 'Task Lists', feature: true do
it 'contains the required selectors' do
visit_issue(project, issue)
- wait_for_vue_resource
+ wait_for_requests
expect(page).to have_selector(".wiki .task-list .task-list-item .task-list-item-checkbox")
expect(page).to have_selector('a.btn-close')
@@ -87,14 +85,14 @@ feature 'Task Lists', feature: true do
it 'is only editable by author' do
visit_issue(project, issue)
- wait_for_vue_resource
+ wait_for_requests
expect(page).to have_selector(".wiki .task-list .task-list-item .task-list-item-checkbox")
logout(:user)
login_as(user2)
visit current_path
- wait_for_vue_resource
+ wait_for_requests
expect(page).to have_selector(".wiki .task-list .task-list-item .task-list-item-checkbox")
end
@@ -106,13 +104,11 @@ feature 'Task Lists', feature: true do
end
describe 'single incomplete task', js: true do
- include WaitForVueResource
-
let!(:issue) { create(:issue, description: singleIncompleteMarkdown, author: user, project: project) }
it 'renders' do
visit_issue(project, issue)
- wait_for_vue_resource
+ wait_for_requests
expect(page).to have_selector('ul.task-list', count: 1)
expect(page).to have_selector('li.task-list-item', count: 1)
@@ -127,12 +123,11 @@ feature 'Task Lists', feature: true do
end
describe 'single complete task', js: true do
- include WaitForVueResource
let!(:issue) { create(:issue, description: singleCompleteMarkdown, author: user, project: project) }
it 'renders' do
visit_issue(project, issue)
- wait_for_vue_resource
+ wait_for_requests
expect(page).to have_selector('ul.task-list', count: 1)
expect(page).to have_selector('li.task-list-item', count: 1)
diff --git a/spec/features/todos/todos_filtering_spec.rb b/spec/features/todos/todos_filtering_spec.rb
index f32e70c2c3f..bbfa4e08379 100644
--- a/spec/features/todos/todos_filtering_spec.rb
+++ b/spec/features/todos/todos_filtering_spec.rb
@@ -28,7 +28,7 @@ describe 'Dashboard > User filters todos', feature: true, js: true do
click_link project_1.name_with_namespace
end
- wait_for_ajax
+ wait_for_requests
expect(page).to have_content project_1.name_with_namespace
expect(page).not_to have_content project_2.name_with_namespace
@@ -43,7 +43,7 @@ describe 'Dashboard > User filters todos', feature: true, js: true do
click_link user_1.name
end
- wait_for_ajax
+ wait_for_requests
expect(find('.todos-list')).to have_content 'merge request'
expect(find('.todos-list')).not_to have_content 'issue'
@@ -90,7 +90,7 @@ describe 'Dashboard > User filters todos', feature: true, js: true do
click_link 'Issue'
end
- wait_for_ajax
+ wait_for_requests
expect(find('.todos-list')).to have_content issue.to_reference
expect(find('.todos-list')).not_to have_content merge_request.to_reference
@@ -132,7 +132,7 @@ describe 'Dashboard > User filters todos', feature: true, js: true do
click_link name
end
- wait_for_ajax
+ wait_for_requests
end
def expect_to_see_action(action_name)
diff --git a/spec/features/todos/todos_spec.rb b/spec/features/todos/todos_spec.rb
index 55b3e3d9424..bb4b2aed0e3 100644
--- a/spec/features/todos/todos_spec.rb
+++ b/spec/features/todos/todos_spec.rb
@@ -64,7 +64,7 @@ describe 'Dashboard Todos', feature: true do
before do
within first('.todo') do
click_link 'Done'
- wait_for_ajax
+ wait_for_requests
click_link 'Undo'
end
end
@@ -309,9 +309,9 @@ describe 'Dashboard Todos', feature: true do
def mark_all_and_undo
find('.js-todos-mark-all').trigger('click')
- wait_for_ajax
+ wait_for_requests
find('.js-todos-undo-all').trigger('click')
- wait_for_ajax
+ wait_for_requests
end
end
end
diff --git a/spec/features/u2f_spec.rb b/spec/features/u2f_spec.rb
index 544d2dcb87f..2fed8067042 100644
--- a/spec/features/u2f_spec.rb
+++ b/spec/features/u2f_spec.rb
@@ -6,7 +6,7 @@ feature 'Using U2F (Universal 2nd Factor) Devices for Authentication', :js do
def manage_two_factor_authentication
click_on 'Manage two-factor authentication'
expect(page).to have_content("Setup new U2F device")
- wait_for_ajax
+ wait_for_requests
end
def register_u2f_device(u2f_device = nil, name: 'My device')
diff --git a/spec/features/uploads/user_uploads_file_to_note_spec.rb b/spec/features/uploads/user_uploads_file_to_note_spec.rb
index 8f03024ea06..9332d3b88d2 100644
--- a/spec/features/uploads/user_uploads_file_to_note_spec.rb
+++ b/spec/features/uploads/user_uploads_file_to_note_spec.rb
@@ -64,7 +64,7 @@ feature 'User uploads file to note', feature: true do
context 'uploading is complete' do
it 'shows "Attach a file" button on uploading complete', js: true do
dropzone_file([Rails.root.join('spec', 'fixtures', 'dk.png')])
- wait_for_ajax
+ wait_for_requests
expect(page).to have_button('Attach a file')
expect(page).not_to have_selector('.uploading-progress-container', visible: true)
@@ -73,7 +73,7 @@ feature 'User uploads file to note', feature: true do
scenario 'they see the attached file', js: true do
dropzone_file([Rails.root.join('spec', 'fixtures', 'dk.png')])
click_button 'Comment'
- wait_for_ajax
+ wait_for_requests
expect(find('a.no-attachment-icon img[alt="dk"]')['src'])
.to match(%r{/#{project.full_path}/uploads/\h{32}/dk\.png$})
diff --git a/spec/features/users/projects_spec.rb b/spec/features/users/projects_spec.rb
index 373b64808f8..67ce4b44464 100644
--- a/spec/features/users/projects_spec.rb
+++ b/spec/features/users/projects_spec.rb
@@ -16,7 +16,7 @@ describe 'Projects tab on a user profile', :feature, :js do
click_link('Personal projects')
end
- wait_for_ajax
+ wait_for_requests
end
it 'paginates results' do
diff --git a/spec/features/users/rss_spec.rb b/spec/features/users/rss_spec.rb
index 14564abb16d..dbd5f66b55e 100644
--- a/spec/features/users/rss_spec.rb
+++ b/spec/features/users/rss_spec.rb
@@ -9,7 +9,7 @@ feature 'User RSS' do
visit path
end
- it_behaves_like "it has an RSS button with current_user's private token"
+ it_behaves_like "it has an RSS button with current_user's RSS token"
end
context 'when signed out' do
@@ -17,6 +17,6 @@ feature 'User RSS' do
visit path
end
- it_behaves_like "it has an RSS button without a private token"
+ it_behaves_like "it has an RSS button without an RSS token"
end
end
diff --git a/spec/features/users/snippets_spec.rb b/spec/features/users/snippets_spec.rb
index 4efbd672322..2e388115633 100644
--- a/spec/features/users/snippets_spec.rb
+++ b/spec/features/users/snippets_spec.rb
@@ -11,7 +11,7 @@ describe 'Snippets tab on a user profile', feature: true, js: true do
allow(Snippet).to receive(:default_per_page).and_return(1)
visit user_path(user)
page.within('.user-profile-nav') { click_link 'Snippets' }
- wait_for_ajax
+ wait_for_requests
end
it_behaves_like 'paginated snippets', remote: true
@@ -27,7 +27,7 @@ describe 'Snippets tab on a user profile', feature: true, js: true do
login_as(:user)
visit user_path(user)
page.within('.user-profile-nav') { click_link 'Snippets' }
- wait_for_ajax
+ wait_for_requests
expect(page).to have_selector('.snippet-row', count: 2)
@@ -38,7 +38,7 @@ describe 'Snippets tab on a user profile', feature: true, js: true do
it 'contains only public snippets of a user when a user is not logged in' do
visit user_path(user)
page.within('.user-profile-nav') { click_link 'Snippets' }
- wait_for_ajax
+ wait_for_requests
expect(page).to have_selector('.snippet-row', count: 1)
expect(page).to have_content(public_snippet.title)
diff --git a/spec/features/users_spec.rb b/spec/features/users_spec.rb
index c43feadc808..fbe078bd136 100644
--- a/spec/features/users_spec.rb
+++ b/spec/features/users_spec.rb
@@ -78,25 +78,25 @@ feature 'Users', feature: true, js: true do
scenario 'doesn\'t show an error border if the username is available' do
fill_in username_input, with: 'new-user'
- wait_for_ajax
+ wait_for_requests
expect(find('.username')).not_to have_css '.gl-field-error-outline'
end
scenario 'does not show an error border if the username contains dots (.)' do
fill_in username_input, with: 'new.user.username'
- wait_for_ajax
+ wait_for_requests
expect(find('.username')).not_to have_css '.gl-field-error-outline'
end
scenario 'shows an error border if the username already exists' do
fill_in username_input, with: user.username
- wait_for_ajax
+ wait_for_requests
expect(find('.username')).to have_css '.gl-field-error-outline'
end
scenario 'shows an error border if the username contains special characters' do
fill_in username_input, with: 'new$user!username'
- wait_for_ajax
+ wait_for_requests
expect(find('.username')).to have_css '.gl-field-error-outline'
end
end
diff --git a/spec/helpers/notes_helper_spec.rb b/spec/helpers/notes_helper_spec.rb
index 099146678ae..355a4845afb 100644
--- a/spec/helpers/notes_helper_spec.rb
+++ b/spec/helpers/notes_helper_spec.rb
@@ -92,7 +92,13 @@ describe NotesHelper do
)
end
- let(:discussion) { create(:diff_note_on_merge_request, noteable: merge_request, project: project, position: position).to_discussion }
+ let(:diff_note) { create(:diff_note_on_merge_request, noteable: merge_request, project: project, position: position) }
+ let(:discussion) { diff_note.to_discussion }
+
+ before do
+ diff_note.position = diff_note.original_position
+ diff_note.save!
+ end
it 'returns the diff version comparison path with the line code' do
expect(helper.discussion_path(discussion)).to eq(diffs_namespace_project_merge_request_path(project.namespace, project, merge_request, diff_id: merge_request_diff3, start_sha: merge_request_diff1.head_commit_sha, anchor: discussion.line_code))
diff --git a/spec/helpers/rss_helper_spec.rb b/spec/helpers/rss_helper_spec.rb
index f3f174f3d14..269e1057e8d 100644
--- a/spec/helpers/rss_helper_spec.rb
+++ b/spec/helpers/rss_helper_spec.rb
@@ -3,17 +3,17 @@ require 'spec_helper'
describe RssHelper do
describe '#rss_url_options' do
context 'when signed in' do
- it "includes the current_user's private_token" do
+ it "includes the current_user's rss_token" do
current_user = create(:user)
allow(helper).to receive(:current_user).and_return(current_user)
- expect(helper.rss_url_options).to include private_token: current_user.private_token
+ expect(helper.rss_url_options).to include rss_token: current_user.rss_token
end
end
context 'when signed out' do
- it "does not have a private_token" do
+ it "does not have an rss_token" do
allow(helper).to receive(:current_user).and_return(nil)
- expect(helper.rss_url_options[:private_token]).to be_nil
+ expect(helper.rss_url_options[:rss_token]).to be_nil
end
end
end
diff --git a/spec/javascripts/copy_as_gfm_spec.js b/spec/javascripts/copy_as_gfm_spec.js
new file mode 100644
index 00000000000..ded450749d3
--- /dev/null
+++ b/spec/javascripts/copy_as_gfm_spec.js
@@ -0,0 +1,49 @@
+import '~/copy_as_gfm';
+
+(() => {
+ describe('gl.CopyAsGFM', () => {
+ describe('gl.CopyAsGFM.pasteGFM', () => {
+ function callPasteGFM() {
+ const e = {
+ originalEvent: {
+ clipboardData: {
+ getData(mimeType) {
+ // When GFM code is copied, we put the regular plain text
+ // on the clipboard as `text/plain`, and the GFM as `text/x-gfm`.
+ // This emulates the behavior of `getData` with that data.
+ if (mimeType === 'text/plain') {
+ return 'code';
+ }
+ if (mimeType === 'text/x-gfm') {
+ return '`code`';
+ }
+ return null;
+ },
+ },
+ },
+ preventDefault() {},
+ };
+
+ window.gl.CopyAsGFM.pasteGFM(e);
+ }
+
+ it('wraps pasted code when not already in code tags', () => {
+ spyOn(window.gl.utils, 'insertText').and.callFake((el, textFunc) => {
+ const insertedText = textFunc('This is code: ', '');
+ expect(insertedText).toEqual('`code`');
+ });
+
+ callPasteGFM();
+ });
+
+ it('does not wrap pasted code when already in code tags', () => {
+ spyOn(window.gl.utils, 'insertText').and.callFake((el, textFunc) => {
+ const insertedText = textFunc('This is code: `', '`');
+ expect(insertedText).toEqual('code');
+ });
+
+ callPasteGFM();
+ });
+ });
+ });
+})();
diff --git a/spec/javascripts/filtered_search/components/recent_searches_dropdown_content_spec.js b/spec/javascripts/filtered_search/components/recent_searches_dropdown_content_spec.js
index d0f09a561d5..79447787fc9 100644
--- a/spec/javascripts/filtered_search/components/recent_searches_dropdown_content_spec.js
+++ b/spec/javascripts/filtered_search/components/recent_searches_dropdown_content_spec.js
@@ -2,6 +2,8 @@ import Vue from 'vue';
import eventHub from '~/filtered_search/event_hub';
import RecentSearchesDropdownContent from '~/filtered_search/components/recent_searches_dropdown_content';
+import '~/filtered_search/filtered_search_token_keys';
+
const createComponent = (propsData) => {
const Component = Vue.extend(RecentSearchesDropdownContent);
@@ -17,12 +19,14 @@ const trimMarkupWhitespace = text => text.replace(/(\n|\s)+/gm, ' ').trim();
describe('RecentSearchesDropdownContent', () => {
const propsDataWithoutItems = {
items: [],
+ allowedKeys: gl.FilteredSearchTokenKeys.getKeys(),
};
const propsDataWithItems = {
items: [
'foo',
'author:@root label:~foo bar',
],
+ allowedKeys: gl.FilteredSearchTokenKeys.getKeys(),
};
let vm;
diff --git a/spec/javascripts/filtered_search/dropdown_user_spec.js b/spec/javascripts/filtered_search/dropdown_user_spec.js
index 0d8bdf4c8e7..f7708301b6e 100644
--- a/spec/javascripts/filtered_search/dropdown_user_spec.js
+++ b/spec/javascripts/filtered_search/dropdown_user_spec.js
@@ -12,7 +12,7 @@ describe('Dropdown User', () => {
spyOn(gl.DropdownUser.prototype, 'getProjectId').and.callFake(() => {});
spyOn(gl.DropdownUtils, 'getSearchInput').and.callFake(() => {});
- dropdownUser = new gl.DropdownUser();
+ dropdownUser = new gl.DropdownUser(null, null, null, gl.FilteredSearchTokenKeys);
});
it('should not return the double quote found in value', () => {
diff --git a/spec/javascripts/filtered_search/dropdown_utils_spec.js b/spec/javascripts/filtered_search/dropdown_utils_spec.js
index a68e315e3e4..bb02abdeea2 100644
--- a/spec/javascripts/filtered_search/dropdown_utils_spec.js
+++ b/spec/javascripts/filtered_search/dropdown_utils_spec.js
@@ -122,6 +122,7 @@ describe('Dropdown Utils', () => {
describe('filterHint', () => {
let input;
+ let allowedKeys;
beforeEach(() => {
setFixtures(`
@@ -133,30 +134,38 @@ describe('Dropdown Utils', () => {
`);
input = document.getElementById('test');
+ allowedKeys = gl.FilteredSearchTokenKeys.getKeys();
});
+ function config() {
+ return {
+ input,
+ allowedKeys,
+ };
+ }
+
it('should filter', () => {
input.value = 'l';
- let updatedItem = gl.DropdownUtils.filterHint(input, {
+ let updatedItem = gl.DropdownUtils.filterHint(config(), {
hint: 'label',
});
expect(updatedItem.droplab_hidden).toBe(false);
input.value = 'o';
- updatedItem = gl.DropdownUtils.filterHint(input, {
+ updatedItem = gl.DropdownUtils.filterHint(config(), {
hint: 'label',
});
expect(updatedItem.droplab_hidden).toBe(true);
});
it('should return droplab_hidden false when item has no hint', () => {
- const updatedItem = gl.DropdownUtils.filterHint(input, {}, '');
+ const updatedItem = gl.DropdownUtils.filterHint(config(), {}, '');
expect(updatedItem.droplab_hidden).toBe(false);
});
it('should allow multiple if item.type is array', () => {
input.value = 'label:~first la';
- const updatedItem = gl.DropdownUtils.filterHint(input, {
+ const updatedItem = gl.DropdownUtils.filterHint(config(), {
hint: 'label',
type: 'array',
});
@@ -165,12 +174,12 @@ describe('Dropdown Utils', () => {
it('should prevent multiple if item.type is not array', () => {
input.value = 'milestone:~first mile';
- let updatedItem = gl.DropdownUtils.filterHint(input, {
+ let updatedItem = gl.DropdownUtils.filterHint(config(), {
hint: 'milestone',
});
expect(updatedItem.droplab_hidden).toBe(true);
- updatedItem = gl.DropdownUtils.filterHint(input, {
+ updatedItem = gl.DropdownUtils.filterHint(config(), {
hint: 'milestone',
type: 'string',
});
diff --git a/spec/javascripts/filtered_search/filtered_search_manager_spec.js b/spec/javascripts/filtered_search/filtered_search_manager_spec.js
index 7c7def3470d..8688332782d 100644
--- a/spec/javascripts/filtered_search/filtered_search_manager_spec.js
+++ b/spec/javascripts/filtered_search/filtered_search_manager_spec.js
@@ -80,6 +80,7 @@ describe('Filtered Search Manager', () => {
expect(RecentSearchesService.isAvailable).toHaveBeenCalled();
expect(recentSearchesStoreSrc.default).toHaveBeenCalledWith({
isLocalStorageAvailable,
+ allowedKeys: gl.FilteredSearchTokenKeys.getKeys(),
});
});
diff --git a/spec/javascripts/filtered_search/filtered_search_tokenizer_spec.js b/spec/javascripts/filtered_search/filtered_search_tokenizer_spec.js
index 9561580c839..e4a15c83c23 100644
--- a/spec/javascripts/filtered_search/filtered_search_tokenizer_spec.js
+++ b/spec/javascripts/filtered_search/filtered_search_tokenizer_spec.js
@@ -3,9 +3,11 @@ import '~/filtered_search/filtered_search_token_keys';
import '~/filtered_search/filtered_search_tokenizer';
describe('Filtered Search Tokenizer', () => {
+ const allowedKeys = gl.FilteredSearchTokenKeys.getKeys();
+
describe('processTokens', () => {
it('returns for input containing only search value', () => {
- const results = gl.FilteredSearchTokenizer.processTokens('searchTerm');
+ const results = gl.FilteredSearchTokenizer.processTokens('searchTerm', allowedKeys);
expect(results.searchToken).toBe('searchTerm');
expect(results.tokens.length).toBe(0);
expect(results.lastToken).toBe(results.searchToken);
@@ -13,7 +15,7 @@ describe('Filtered Search Tokenizer', () => {
it('returns for input containing only tokens', () => {
const results = gl.FilteredSearchTokenizer
- .processTokens('author:@root label:~"Very Important" milestone:%v1.0 assignee:none');
+ .processTokens('author:@root label:~"Very Important" milestone:%v1.0 assignee:none', allowedKeys);
expect(results.searchToken).toBe('');
expect(results.tokens.length).toBe(4);
expect(results.tokens[3]).toBe(results.lastToken);
@@ -37,7 +39,7 @@ describe('Filtered Search Tokenizer', () => {
it('returns for input starting with search value and ending with tokens', () => {
const results = gl.FilteredSearchTokenizer
- .processTokens('searchTerm anotherSearchTerm milestone:none');
+ .processTokens('searchTerm anotherSearchTerm milestone:none', allowedKeys);
expect(results.searchToken).toBe('searchTerm anotherSearchTerm');
expect(results.tokens.length).toBe(1);
expect(results.tokens[0]).toBe(results.lastToken);
@@ -48,7 +50,7 @@ describe('Filtered Search Tokenizer', () => {
it('returns for input starting with tokens and ending with search value', () => {
const results = gl.FilteredSearchTokenizer
- .processTokens('assignee:@user searchTerm');
+ .processTokens('assignee:@user searchTerm', allowedKeys);
expect(results.searchToken).toBe('searchTerm');
expect(results.tokens.length).toBe(1);
@@ -60,7 +62,7 @@ describe('Filtered Search Tokenizer', () => {
it('returns for input containing search value wrapped between tokens', () => {
const results = gl.FilteredSearchTokenizer
- .processTokens('author:@root label:~"Won\'t fix" searchTerm anotherSearchTerm milestone:none');
+ .processTokens('author:@root label:~"Won\'t fix" searchTerm anotherSearchTerm milestone:none', allowedKeys);
expect(results.searchToken).toBe('searchTerm anotherSearchTerm');
expect(results.tokens.length).toBe(3);
@@ -81,7 +83,7 @@ describe('Filtered Search Tokenizer', () => {
it('returns for input containing search value in between tokens', () => {
const results = gl.FilteredSearchTokenizer
- .processTokens('author:@root searchTerm assignee:none anotherSearchTerm label:~Doing');
+ .processTokens('author:@root searchTerm assignee:none anotherSearchTerm label:~Doing', allowedKeys);
expect(results.searchToken).toBe('searchTerm anotherSearchTerm');
expect(results.tokens.length).toBe(3);
expect(results.tokens[2]).toBe(results.lastToken);
@@ -100,14 +102,14 @@ describe('Filtered Search Tokenizer', () => {
});
it('returns search value for invalid tokens', () => {
- const results = gl.FilteredSearchTokenizer.processTokens('fake:token');
+ const results = gl.FilteredSearchTokenizer.processTokens('fake:token', allowedKeys);
expect(results.lastToken).toBe('fake:token');
expect(results.searchToken).toBe('fake:token');
expect(results.tokens.length).toEqual(0);
});
it('returns search value and token for mix of valid and invalid tokens', () => {
- const results = gl.FilteredSearchTokenizer.processTokens('label:real fake:token');
+ const results = gl.FilteredSearchTokenizer.processTokens('label:real fake:token', allowedKeys);
expect(results.tokens.length).toEqual(1);
expect(results.tokens[0].key).toBe('label');
expect(results.tokens[0].value).toBe('real');
@@ -117,13 +119,13 @@ describe('Filtered Search Tokenizer', () => {
});
it('returns search value for invalid symbols', () => {
- const results = gl.FilteredSearchTokenizer.processTokens('std::includes');
+ const results = gl.FilteredSearchTokenizer.processTokens('std::includes', allowedKeys);
expect(results.lastToken).toBe('std::includes');
expect(results.searchToken).toBe('std::includes');
});
it('removes duplicated values', () => {
- const results = gl.FilteredSearchTokenizer.processTokens('label:~foo label:~foo');
+ const results = gl.FilteredSearchTokenizer.processTokens('label:~foo label:~foo', allowedKeys);
expect(results.tokens.length).toBe(1);
expect(results.tokens[0].key).toBe('label');
expect(results.tokens[0].value).toBe('foo');
diff --git a/spec/javascripts/filtered_search/services/recent_searches_service_spec.js b/spec/javascripts/filtered_search/services/recent_searches_service_spec.js
index 31fa478804a..c293c0afa97 100644
--- a/spec/javascripts/filtered_search/services/recent_searches_service_spec.js
+++ b/spec/javascripts/filtered_search/services/recent_searches_service_spec.js
@@ -1,6 +1,5 @@
-/* eslint-disable promise/catch-or-return */
-
import RecentSearchesService from '~/filtered_search/services/recent_searches_service';
+import RecentSearchesServiceError from '~/filtered_search/services/recent_searches_service_error';
import AccessorUtilities from '~/lib/utils/accessor';
describe('RecentSearchesService', () => {
@@ -22,11 +21,9 @@ describe('RecentSearchesService', () => {
fetchItemsPromise
.then((items) => {
expect(items).toEqual([]);
- done();
})
- .catch((err) => {
- done.fail('Shouldn\'t reject with empty localStorage key', err);
- });
+ .then(done)
+ .catch(done.fail);
});
it('should reject when unable to parse', (done) => {
@@ -34,19 +31,24 @@ describe('RecentSearchesService', () => {
const fetchItemsPromise = service.fetch();
fetchItemsPromise
+ .then(done.fail)
.catch((error) => {
expect(error).toEqual(jasmine.any(SyntaxError));
- done();
- });
+ })
+ .then(done)
+ .catch(done.fail);
});
it('should reject when service is unavailable', (done) => {
RecentSearchesService.isAvailable.and.returnValue(false);
- service.fetch().catch((error) => {
- expect(error).toEqual(jasmine.any(Error));
- done();
- });
+ service.fetch()
+ .then(done.fail)
+ .catch((error) => {
+ expect(error).toEqual(jasmine.any(Error));
+ })
+ .then(done)
+ .catch(done.fail);
});
it('should return items from localStorage', (done) => {
@@ -56,8 +58,9 @@ describe('RecentSearchesService', () => {
fetchItemsPromise
.then((items) => {
expect(items).toEqual(['foo', 'bar']);
- done();
- });
+ })
+ .then(done)
+ .catch(done.fail);
});
describe('if .isAvailable returns `false`', () => {
@@ -65,12 +68,17 @@ describe('RecentSearchesService', () => {
RecentSearchesService.isAvailable.and.returnValue(false);
spyOn(window.localStorage, 'getItem');
-
- RecentSearchesService.prototype.fetch();
});
- it('should not call .getItem', () => {
- expect(window.localStorage.getItem).not.toHaveBeenCalled();
+ it('should not call .getItem', (done) => {
+ RecentSearchesService.prototype.fetch()
+ .then(done.fail)
+ .catch((err) => {
+ expect(err).toEqual(new RecentSearchesServiceError());
+ expect(window.localStorage.getItem).not.toHaveBeenCalled();
+ })
+ .then(done)
+ .catch(done.fail);
});
});
});
@@ -105,11 +113,11 @@ describe('RecentSearchesService', () => {
RecentSearchesService.isAvailable.and.returnValue(true);
spyOn(JSON, 'stringify').and.returnValue(searchesString);
-
- RecentSearchesService.prototype.save.call(recentSearchesService);
});
it('should call .setItem', () => {
+ RecentSearchesService.prototype.save.call(recentSearchesService);
+
expect(window.localStorage.setItem).toHaveBeenCalledWith(localStorageKey, searchesString);
});
});
@@ -117,11 +125,11 @@ describe('RecentSearchesService', () => {
describe('if .isAvailable returns `false`', () => {
beforeEach(() => {
RecentSearchesService.isAvailable.and.returnValue(false);
-
- RecentSearchesService.prototype.save();
});
it('should not call .setItem', () => {
+ RecentSearchesService.prototype.save();
+
expect(window.localStorage.setItem).not.toHaveBeenCalled();
});
});
diff --git a/spec/javascripts/lib/utils/cache_spec.js b/spec/javascripts/lib/utils/cache_spec.js
new file mode 100644
index 00000000000..2fe02a7592c
--- /dev/null
+++ b/spec/javascripts/lib/utils/cache_spec.js
@@ -0,0 +1,65 @@
+import Cache from '~/lib/utils/cache';
+
+describe('Cache', () => {
+ const dummyKey = 'just some key';
+ const dummyValue = 'more than a value';
+ let cache;
+
+ beforeEach(() => {
+ cache = new Cache();
+ });
+
+ describe('get', () => {
+ it('return cached data', () => {
+ cache.internalStorage[dummyKey] = dummyValue;
+
+ expect(cache.get(dummyKey)).toBe(dummyValue);
+ });
+
+ it('returns undefined for missing data', () => {
+ expect(cache.internalStorage[dummyKey]).toBe(undefined);
+ expect(cache.get(dummyKey)).toBe(undefined);
+ });
+ });
+
+ describe('hasData', () => {
+ it('return true for cached data', () => {
+ cache.internalStorage[dummyKey] = dummyValue;
+
+ expect(cache.hasData(dummyKey)).toBe(true);
+ });
+
+ it('returns false for missing data', () => {
+ expect(cache.internalStorage[dummyKey]).toBe(undefined);
+ expect(cache.hasData(dummyKey)).toBe(false);
+ });
+ });
+
+ describe('remove', () => {
+ it('removes data from cache', () => {
+ cache.internalStorage[dummyKey] = dummyValue;
+
+ cache.remove(dummyKey);
+
+ expect(cache.internalStorage[dummyKey]).toBe(undefined);
+ });
+
+ it('does nothing for missing data', () => {
+ expect(cache.internalStorage[dummyKey]).toBe(undefined);
+
+ cache.remove(dummyKey);
+
+ expect(cache.internalStorage[dummyKey]).toBe(undefined);
+ });
+
+ it('does not remove wrong data', () => {
+ cache.internalStorage[dummyKey] = dummyValue;
+ cache.internalStorage[dummyKey + dummyKey] = dummyValue + dummyValue;
+
+ cache.remove(dummyKey);
+
+ expect(cache.internalStorage[dummyKey]).toBe(undefined);
+ expect(cache.internalStorage[dummyKey + dummyKey]).toBe(dummyValue + dummyValue);
+ });
+ });
+});
diff --git a/spec/javascripts/lib/utils/users_cache_spec.js b/spec/javascripts/lib/utils/users_cache_spec.js
new file mode 100644
index 00000000000..ec6ea35952b
--- /dev/null
+++ b/spec/javascripts/lib/utils/users_cache_spec.js
@@ -0,0 +1,136 @@
+import Api from '~/api';
+import UsersCache from '~/lib/utils/users_cache';
+
+describe('UsersCache', () => {
+ const dummyUsername = 'win';
+ const dummyUser = 'has a farm';
+
+ beforeEach(() => {
+ UsersCache.internalStorage = { };
+ });
+
+ describe('get', () => {
+ it('returns undefined for empty cache', () => {
+ expect(UsersCache.internalStorage).toEqual({ });
+
+ const user = UsersCache.get(dummyUsername);
+
+ expect(user).toBe(undefined);
+ });
+
+ it('returns undefined for missing user', () => {
+ UsersCache.internalStorage['no body'] = 'no data';
+
+ const user = UsersCache.get(dummyUsername);
+
+ expect(user).toBe(undefined);
+ });
+
+ it('returns matching user', () => {
+ UsersCache.internalStorage[dummyUsername] = dummyUser;
+
+ const user = UsersCache.get(dummyUsername);
+
+ expect(user).toBe(dummyUser);
+ });
+ });
+
+ describe('hasData', () => {
+ it('returns false for empty cache', () => {
+ expect(UsersCache.internalStorage).toEqual({ });
+
+ expect(UsersCache.hasData(dummyUsername)).toBe(false);
+ });
+
+ it('returns false for missing user', () => {
+ UsersCache.internalStorage['no body'] = 'no data';
+
+ expect(UsersCache.hasData(dummyUsername)).toBe(false);
+ });
+
+ it('returns true for matching user', () => {
+ UsersCache.internalStorage[dummyUsername] = dummyUser;
+
+ expect(UsersCache.hasData(dummyUsername)).toBe(true);
+ });
+ });
+
+ describe('remove', () => {
+ it('does nothing if cache is empty', () => {
+ expect(UsersCache.internalStorage).toEqual({ });
+
+ UsersCache.remove(dummyUsername);
+
+ expect(UsersCache.internalStorage).toEqual({ });
+ });
+
+ it('does nothing if cache contains no matching data', () => {
+ UsersCache.internalStorage['no body'] = 'no data';
+
+ UsersCache.remove(dummyUsername);
+
+ expect(UsersCache.internalStorage['no body']).toBe('no data');
+ });
+
+ it('removes matching data', () => {
+ UsersCache.internalStorage[dummyUsername] = dummyUser;
+
+ UsersCache.remove(dummyUsername);
+
+ expect(UsersCache.internalStorage).toEqual({ });
+ });
+ });
+
+ describe('retrieve', () => {
+ let apiSpy;
+
+ beforeEach(() => {
+ spyOn(Api, 'users').and.callFake((query, options) => apiSpy(query, options));
+ });
+
+ it('stores and returns data from API call if cache is empty', (done) => {
+ apiSpy = (query, options) => {
+ expect(query).toBe('');
+ expect(options).toEqual({ username: dummyUsername });
+ return Promise.resolve([dummyUser]);
+ };
+
+ UsersCache.retrieve(dummyUsername)
+ .then((user) => {
+ expect(user).toBe(dummyUser);
+ expect(UsersCache.internalStorage[dummyUsername]).toBe(dummyUser);
+ })
+ .then(done)
+ .catch(done.fail);
+ });
+
+ it('returns undefined if Ajax call fails and cache is empty', (done) => {
+ const dummyError = new Error('server exploded');
+ apiSpy = (query, options) => {
+ expect(query).toBe('');
+ expect(options).toEqual({ username: dummyUsername });
+ return Promise.reject(dummyError);
+ };
+
+ UsersCache.retrieve(dummyUsername)
+ .then(user => fail(`Received unexpected user: ${JSON.stringify(user)}`))
+ .catch((error) => {
+ expect(error).toBe(dummyError);
+ })
+ .then(done)
+ .catch(done.fail);
+ });
+
+ it('makes no Ajax call if matching data exists', (done) => {
+ UsersCache.internalStorage[dummyUsername] = dummyUser;
+ apiSpy = () => fail(new Error('expected no Ajax call!'));
+
+ UsersCache.retrieve(dummyUsername)
+ .then((user) => {
+ expect(user).toBe(dummyUser);
+ })
+ .then(done)
+ .catch(done.fail);
+ });
+ });
+});
diff --git a/spec/javascripts/raven/index_spec.js b/spec/javascripts/raven/index_spec.js
index b5662cd0331..a503a54029f 100644
--- a/spec/javascripts/raven/index_spec.js
+++ b/spec/javascripts/raven/index_spec.js
@@ -2,25 +2,23 @@ import RavenConfig from '~/raven/raven_config';
import index from '~/raven/index';
describe('RavenConfig options', () => {
- let sentryDsn;
- let currentUserId;
- let gitlabUrl;
- let isProduction;
+ const sentryDsn = 'sentryDsn';
+ const currentUserId = 'currentUserId';
+ const gitlabUrl = 'gitlabUrl';
+ const isProduction = 'isProduction';
+ const revision = 'revision';
let indexReturnValue;
beforeEach(() => {
- sentryDsn = 'sentryDsn';
- currentUserId = 'currentUserId';
- gitlabUrl = 'gitlabUrl';
- isProduction = 'isProduction';
-
window.gon = {
sentry_dsn: sentryDsn,
current_user_id: currentUserId,
gitlab_url: gitlabUrl,
+ revision,
};
process.env.NODE_ENV = isProduction;
+ process.env.HEAD_COMMIT_SHA = revision;
spyOn(RavenConfig, 'init');
@@ -33,6 +31,10 @@ describe('RavenConfig options', () => {
currentUserId,
whitelistUrls: [gitlabUrl],
isProduction,
+ release: revision,
+ tags: {
+ revision,
+ },
});
});
diff --git a/spec/javascripts/raven/raven_config_spec.js b/spec/javascripts/raven/raven_config_spec.js
index a2d720760fc..c82658b9262 100644
--- a/spec/javascripts/raven/raven_config_spec.js
+++ b/spec/javascripts/raven/raven_config_spec.js
@@ -25,17 +25,11 @@ describe('RavenConfig', () => {
});
describe('init', () => {
- let options;
+ const options = {
+ currentUserId: 1,
+ };
beforeEach(() => {
- options = {
- sentryDsn: '//sentryDsn',
- ravenAssetUrl: '//ravenAssetUrl',
- currentUserId: 1,
- whitelistUrls: ['//gitlabUrl'],
- isProduction: true,
- };
-
spyOn(RavenConfig, 'configure');
spyOn(RavenConfig, 'bindRavenErrors');
spyOn(RavenConfig, 'setUser');
@@ -62,30 +56,28 @@ describe('RavenConfig', () => {
it('should not call setUser if there is no current user ID', () => {
RavenConfig.setUser.calls.reset();
- RavenConfig.init({
- sentryDsn: '//sentryDsn',
- ravenAssetUrl: '//ravenAssetUrl',
- currentUserId: undefined,
- whitelistUrls: ['//gitlabUrl'],
- isProduction: true,
- });
+ options.currentUserId = undefined;
+
+ RavenConfig.init(options);
expect(RavenConfig.setUser).not.toHaveBeenCalled();
});
});
describe('configure', () => {
- let options;
let raven;
let ravenConfig;
+ const options = {
+ sentryDsn: '//sentryDsn',
+ whitelistUrls: ['//gitlabUrl'],
+ isProduction: true,
+ release: 'revision',
+ tags: {
+ revision: 'revision',
+ },
+ };
beforeEach(() => {
- options = {
- sentryDsn: '//sentryDsn',
- whitelistUrls: ['//gitlabUrl'],
- isProduction: true,
- };
-
ravenConfig = jasmine.createSpyObj('ravenConfig', ['shouldSendSample']);
raven = jasmine.createSpyObj('raven', ['install']);
@@ -100,6 +92,8 @@ describe('RavenConfig', () => {
it('should call Raven.config', () => {
expect(Raven.config).toHaveBeenCalledWith(options.sentryDsn, {
+ release: options.release,
+ tags: options.tags,
whitelistUrls: options.whitelistUrls,
environment: 'production',
ignoreErrors: ravenConfig.IGNORE_ERRORS,
@@ -118,6 +112,8 @@ describe('RavenConfig', () => {
RavenConfig.configure.call(ravenConfig);
expect(Raven.config).toHaveBeenCalledWith(options.sentryDsn, {
+ release: options.release,
+ tags: options.tags,
whitelistUrls: options.whitelistUrls,
environment: 'development',
ignoreErrors: ravenConfig.IGNORE_ERRORS,
@@ -144,24 +140,6 @@ describe('RavenConfig', () => {
});
});
- describe('bindRavenErrors', () => {
- let $document;
- let $;
-
- beforeEach(() => {
- $document = jasmine.createSpyObj('$document', ['on']);
- $ = jasmine.createSpy('$').and.returnValue($document);
-
- window.$ = $;
-
- RavenConfig.bindRavenErrors();
- });
-
- it('should call .on', function () {
- expect($document.on).toHaveBeenCalledWith('ajaxError.raven', RavenConfig.handleRavenErrors);
- });
- });
-
describe('handleRavenErrors', () => {
let event;
let req;
diff --git a/spec/javascripts/sidebar/sidebar_assignees_spec.js b/spec/javascripts/sidebar/sidebar_assignees_spec.js
index 865951b2ad7..929ba75e67d 100644
--- a/spec/javascripts/sidebar/sidebar_assignees_spec.js
+++ b/spec/javascripts/sidebar/sidebar_assignees_spec.js
@@ -43,4 +43,16 @@ describe('sidebar assignees', () => {
expect(SidebarMediator.prototype.assignYourself).toHaveBeenCalled();
expect(this.mediator.store.assignees.length).toEqual(1);
});
+
+ it('hides assignees until fetched', (done) => {
+ component = new SidebarAssigneeComponent().$mount(this.sidebarAssigneesEl);
+ const currentAssignee = this.sidebarAssigneesEl.querySelector('.value');
+ expect(currentAssignee).toBe(null);
+
+ component.store.isFetching.assignees = false;
+ Vue.nextTick(() => {
+ expect(component.$el.querySelector('.value')).toBeVisible();
+ done();
+ });
+ });
});
diff --git a/spec/javascripts/sidebar/sidebar_store_spec.js b/spec/javascripts/sidebar/sidebar_store_spec.js
index 29facf483b5..b3fa156eb64 100644
--- a/spec/javascripts/sidebar/sidebar_store_spec.js
+++ b/spec/javascripts/sidebar/sidebar_store_spec.js
@@ -35,6 +35,10 @@ describe('Sidebar store', () => {
SidebarStore.singleton = null;
});
+ it('has default isFetching values', () => {
+ expect(this.store.isFetching.assignees).toBe(true);
+ });
+
it('adds a new assignee', () => {
this.store.addAssignee(assignee);
expect(this.store.assignees.length).toEqual(1);
@@ -67,6 +71,7 @@ describe('Sidebar store', () => {
};
this.store.setAssigneeData(users);
+ expect(this.store.isFetching.assignees).toBe(false);
expect(this.store.assignees.length).toEqual(3);
});
diff --git a/spec/javascripts/vue_mr_widget/stores/get_state_key_spec.js b/spec/javascripts/vue_mr_widget/stores/get_state_key_spec.js
index 9a331d99865..179e42a7cc4 100644
--- a/spec/javascripts/vue_mr_widget/stores/get_state_key_spec.js
+++ b/spec/javascripts/vue_mr_widget/stores/get_state_key_spec.js
@@ -25,6 +25,12 @@ describe('getStateKey', () => {
context.canBeMerged = true;
expect(bound()).toEqual('readyToMerge');
+ context.canMerge = false;
+ expect(bound()).toEqual('notAllowedToMerge');
+
+ context.mergeWhenPipelineSucceeds = true;
+ expect(bound()).toEqual('mergeWhenPipelineSucceeds');
+
context.hasSHAChanged = true;
expect(bound()).toEqual('shaMismatch');
@@ -38,12 +44,6 @@ describe('getStateKey', () => {
context.isPipelineFailed = true;
expect(bound()).toEqual('pipelineFailed');
- context.canMerge = false;
- expect(bound()).toEqual('notAllowedToMerge');
-
- context.mergeWhenPipelineSucceeds = true;
- expect(bound()).toEqual('mergeWhenPipelineSucceeds');
-
data.work_in_progress = true;
expect(bound()).toEqual('workInProgress');
diff --git a/spec/lib/banzai/filter/ascii_doc_post_processing_filter_spec.rb b/spec/lib/banzai/filter/ascii_doc_post_processing_filter_spec.rb
new file mode 100644
index 00000000000..33b812ef425
--- /dev/null
+++ b/spec/lib/banzai/filter/ascii_doc_post_processing_filter_spec.rb
@@ -0,0 +1,15 @@
+require 'spec_helper'
+
+describe Banzai::Filter::AsciiDocPostProcessingFilter, lib: true do
+ include FilterSpecHelper
+
+ it "adds class for elements with data-math-style" do
+ result = filter('<pre data-math-style="inline">some code</pre><div data-math>and</div>').to_html
+ expect(result).to eq('<pre data-math-style="inline" class="code math js-render-math">some code</pre><div data-math>and</div>')
+ end
+
+ it "keeps content when no data-math-style found" do
+ result = filter('<pre>some code</pre><div data-math>and</div>').to_html
+ expect(result).to eq('<pre>some code</pre><div data-math>and</div>')
+ end
+end
diff --git a/spec/lib/banzai/filter/sanitization_filter_spec.rb b/spec/lib/banzai/filter/sanitization_filter_spec.rb
index fdbc65b5e00..fb7862f49a2 100644
--- a/spec/lib/banzai/filter/sanitization_filter_spec.rb
+++ b/spec/lib/banzai/filter/sanitization_filter_spec.rb
@@ -97,6 +97,22 @@ describe Banzai::Filter::SanitizationFilter, lib: true do
expect(filter(act).to_html).to eq exp
end
+ it 'allows `data-math-style` attribute on `code` and `pre` elements' do
+ html = <<-HTML
+ <pre class="code" data-math-style="inline">something</pre>
+ <code class="code" data-math-style="inline">something</code>
+ <div class="code" data-math-style="inline">something</div>
+ HTML
+
+ output = <<-HTML
+ <pre data-math-style="inline">something</pre>
+ <code data-math-style="inline">something</code>
+ <div>something</div>
+ HTML
+
+ expect(filter(html).to_html).to eq(output)
+ end
+
it 'removes `rel` attribute from `a` elements' do
act = %q{<a href="#" rel="nofollow">Link</a>}
exp = %q{<a href="#">Link</a>}
diff --git a/spec/lib/gitlab/asciidoc_spec.rb b/spec/lib/gitlab/asciidoc_spec.rb
index 2c7ebb15fd7..43d52b941ab 100644
--- a/spec/lib/gitlab/asciidoc_spec.rb
+++ b/spec/lib/gitlab/asciidoc_spec.rb
@@ -70,6 +70,31 @@ module Gitlab
expect(output).to include('rel="nofollow noreferrer noopener"')
end
end
+
+ context 'LaTex code' do
+ it 'adds class js-render-math to the output' do
+ input = <<~MD
+ :stem: latexmath
+
+ [stem]
+ ++++
+ \sqrt{4} = 2
+ ++++
+
+ another part
+
+ [latexmath]
+ ++++
+ \beta_x \gamma
+ ++++
+
+ stem:[2+2] is 4
+ MD
+
+ expect(render(input, context)).to include('<pre data-math-style="display" class="code math js-render-math"><code>eta_x gamma</code></pre>')
+ expect(render(input, context)).to include('<p><code data-math-style="inline" class="code math js-render-math">2+2</code> is 4</p>')
+ end
+ end
end
def render(*args)
diff --git a/spec/lib/gitlab/backup/manager_spec.rb b/spec/lib/gitlab/backup/manager_spec.rb
index c59ff7fb290..1c3d2547fec 100644
--- a/spec/lib/gitlab/backup/manager_spec.rb
+++ b/spec/lib/gitlab/backup/manager_spec.rb
@@ -24,8 +24,9 @@ describe Backup::Manager, lib: true do
describe '#remove_old' do
let(:files) do
[
- '1451606400_2016_01_01_gitlab_backup.tar',
- '1451520000_2015_12_31_gitlab_backup.tar',
+ '1451606400_2016_01_01_1.2.3_gitlab_backup.tar',
+ '1451520000_2015_12_31_4.5.6_gitlab_backup.tar',
+ '1451510000_2015_12_30_gitlab_backup.tar',
'1450742400_2015_12_22_gitlab_backup.tar',
'1449878400_gitlab_backup.tar',
'1449014400_gitlab_backup.tar',
@@ -58,6 +59,7 @@ describe Backup::Manager, lib: true do
context 'when there are no files older than keep_time' do
before do
+ # Set to 30 days
allow(Gitlab.config.backup).to receive(:keep_time).and_return(2592000)
subject.remove_old
@@ -74,19 +76,24 @@ describe Backup::Manager, lib: true do
context 'when keep_time is set to remove files' do
before do
+ # Set to 1 second
allow(Gitlab.config.backup).to receive(:keep_time).and_return(1)
subject.remove_old
end
- it 'removes matching files with a human-readable timestamp' do
+ it 'removes matching files with a human-readable versioned timestamp' do
expect(FileUtils).to have_received(:rm).with(files[1])
+ end
+
+ it 'removes matching files with a human-readable non-versioned timestamp' do
expect(FileUtils).to have_received(:rm).with(files[2])
+ expect(FileUtils).to have_received(:rm).with(files[3])
end
it 'removes matching files without a human-readable timestamp' do
- expect(FileUtils).to have_received(:rm).with(files[3])
expect(FileUtils).to have_received(:rm).with(files[4])
+ expect(FileUtils).to have_received(:rm).with(files[5])
end
it 'does not remove files that are not old enough' do
@@ -94,11 +101,11 @@ describe Backup::Manager, lib: true do
end
it 'does not remove non-matching files' do
- expect(FileUtils).not_to have_received(:rm).with(files[5])
+ expect(FileUtils).not_to have_received(:rm).with(files[6])
end
it 'prints a done message' do
- expect(progress).to have_received(:puts).with('done. (4 removed)')
+ expect(progress).to have_received(:puts).with('done. (5 removed)')
end
end
@@ -117,10 +124,11 @@ describe Backup::Manager, lib: true do
expect(FileUtils).to have_received(:rm).with(files[2])
expect(FileUtils).to have_received(:rm).with(files[3])
expect(FileUtils).to have_received(:rm).with(files[4])
+ expect(FileUtils).to have_received(:rm).with(files[5])
end
it 'sets the correct removed count' do
- expect(progress).to have_received(:puts).with('done. (3 removed)')
+ expect(progress).to have_received(:puts).with('done. (4 removed)')
end
it 'prints the error from file that could not be removed' do
@@ -150,7 +158,7 @@ describe Backup::Manager, lib: true do
before do
allow(Dir).to receive(:glob).and_return(
[
- '1451606400_2016_01_01_gitlab_backup.tar',
+ '1451606400_2016_01_01_1.2.3_gitlab_backup.tar',
'1451520000_2015_12_31_gitlab_backup.tar'
]
)
@@ -187,21 +195,21 @@ describe Backup::Manager, lib: true do
before do
allow(Dir).to receive(:glob).and_return(
[
- '1451606400_2016_01_01_gitlab_backup.tar'
+ '1451606400_2016_01_01_1.2.3_gitlab_backup.tar'
]
)
allow(File).to receive(:exist?).and_return(true)
allow(Kernel).to receive(:system).and_return(true)
allow(YAML).to receive(:load_file).and_return(gitlab_version: Gitlab::VERSION)
- stub_env('BACKUP', '1451606400_2016_01_01')
+ stub_env('BACKUP', '1451606400_2016_01_01_1.2.3')
end
it 'unpacks the file' do
subject.unpack
expect(Kernel).to have_received(:system)
- .with("tar", "-xf", "1451606400_2016_01_01_gitlab_backup.tar")
+ .with("tar", "-xf", "1451606400_2016_01_01_1.2.3_gitlab_backup.tar")
expect(progress).to have_received(:puts).with(a_string_matching('done'))
end
end
diff --git a/spec/lib/gitlab/cycle_analytics/events_spec.rb b/spec/lib/gitlab/cycle_analytics/events_spec.rb
index 3610a0354e8..a1b3fe8509e 100644
--- a/spec/lib/gitlab/cycle_analytics/events_spec.rb
+++ b/spec/lib/gitlab/cycle_analytics/events_spec.rb
@@ -126,12 +126,11 @@ describe 'cycle analytics events' do
create(:ci_pipeline,
ref: merge_request.source_branch,
sha: merge_request.diff_head_sha,
- project: context.project)
+ project: context.project,
+ head_pipeline_of: merge_request)
end
before do
- merge_request.update(head_pipeline: pipeline)
-
create(:ci_build, pipeline: pipeline, status: :success, author: user)
create(:ci_build, pipeline: pipeline, status: :success, author: user)
@@ -224,12 +223,11 @@ describe 'cycle analytics events' do
create(:ci_pipeline,
ref: merge_request.source_branch,
sha: merge_request.diff_head_sha,
- project: context.project)
+ project: context.project,
+ head_pipeline_of: merge_request)
end
before do
- merge_request.update(head_pipeline: pipeline)
-
create(:ci_build, pipeline: pipeline, status: :success, author: user)
create(:ci_build, pipeline: pipeline, status: :success, author: user)
diff --git a/spec/lib/gitlab/database/migration_helpers_spec.rb b/spec/lib/gitlab/database/migration_helpers_spec.rb
index dfa3ae9142e..bd5ac6142be 100644
--- a/spec/lib/gitlab/database/migration_helpers_spec.rb
+++ b/spec/lib/gitlab/database/migration_helpers_spec.rb
@@ -247,6 +247,14 @@ describe Gitlab::Database::MigrationHelpers, lib: true do
expect(Project.where(archived: true).count).to eq(1)
end
end
+
+ context 'when the value is Arel.sql (Arel::Nodes::SqlLiteral)' do
+ it 'updates the value as a SQL expression' do
+ model.update_column_in_batches(:projects, :star_count, Arel.sql('1+1'))
+
+ expect(Project.sum(:star_count)).to eq(2 * Project.count)
+ end
+ end
end
describe '#add_column_with_default' do
diff --git a/spec/lib/gitlab/diff/position_tracer_spec.rb b/spec/lib/gitlab/diff/position_tracer_spec.rb
index 4d202a76e1b..93d30b90937 100644
--- a/spec/lib/gitlab/diff/position_tracer_spec.rb
+++ b/spec/lib/gitlab/diff/position_tracer_spec.rb
@@ -61,9 +61,10 @@ describe Gitlab::Diff::PositionTracer, lib: true do
let(:old_diff_refs) { raise NotImplementedError }
let(:new_diff_refs) { raise NotImplementedError }
+ let(:change_diff_refs) { raise NotImplementedError }
let(:old_position) { raise NotImplementedError }
- let(:position_tracer) { described_class.new(repository: project.repository, old_diff_refs: old_diff_refs, new_diff_refs: new_diff_refs) }
+ let(:position_tracer) { described_class.new(project: project, old_diff_refs: old_diff_refs, new_diff_refs: new_diff_refs) }
subject { position_tracer.trace(old_position) }
def diff_refs(base_commit, head_commit)
@@ -77,16 +78,40 @@ describe Gitlab::Diff::PositionTracer, lib: true do
Gitlab::Diff::Position.new(attrs)
end
- def expect_new_position(attrs, new_position = subject)
- if attrs.nil?
- expect(new_position).to be_nil
- else
- expect(new_position).not_to be_nil
+ def expect_new_position(attrs, result = subject)
+ aggregate_failures("expect new position #{attrs.inspect}") do
+ if attrs.nil?
+ expect(result[:outdated]).to be_truthy
+ else
+ expect(result[:outdated]).to be_falsey
- expect(new_position.diff_refs).to eq(new_diff_refs)
+ new_position = result[:position]
+ expect(new_position).not_to be_nil
- attrs.each do |attr, value|
- expect(new_position.send(attr)).to eq(value)
+ expect(new_position.diff_refs).to eq(new_diff_refs)
+
+ attrs.each do |attr, value|
+ expect(new_position.send(attr)).to eq(value)
+ end
+ end
+ end
+ end
+
+ def expect_change_position(attrs, result = subject)
+ aggregate_failures("expect change position #{attrs.inspect}") do
+ expect(result[:outdated]).to be_truthy
+
+ change_position = result[:position]
+ if attrs.nil? || attrs.empty?
+ expect(change_position).to be_nil
+ else
+ expect(change_position).not_to be_nil
+
+ expect(change_position.diff_refs).to eq(change_diff_refs)
+
+ attrs.each do |attr, value|
+ expect(change_position.send(attr)).to eq(value)
+ end
end
end
end
@@ -395,6 +420,7 @@ describe Gitlab::Diff::PositionTracer, lib: true do
context "when that line was changed between the old and the new diff" do
let(:old_diff_refs) { diff_refs(initial_commit, create_file_commit) }
let(:new_diff_refs) { diff_refs(initial_commit, update_line_commit) }
+ let(:change_diff_refs) { diff_refs(create_file_commit, update_line_commit) }
let(:old_position) { position(new_path: file_name, new_line: 2) }
# old diff:
@@ -407,14 +433,20 @@ describe Gitlab::Diff::PositionTracer, lib: true do
# 2 + BB
# 3 + C
- it "returns nil" do
- expect(subject).to be_nil
+ it "returns the position of the change" do
+ expect_change_position(
+ old_path: file_name,
+ new_path: file_name,
+ old_line: 2,
+ new_line: nil
+ )
end
end
context "when that line was deleted between the old and the new diff" do
let(:old_diff_refs) { diff_refs(initial_commit, update_line_commit) }
let(:new_diff_refs) { diff_refs(initial_commit, delete_line_commit) }
+ let(:change_diff_refs) { diff_refs(update_line_commit, delete_line_commit) }
let(:old_position) { position(new_path: file_name, new_line: 3) }
# old diff:
@@ -426,8 +458,13 @@ describe Gitlab::Diff::PositionTracer, lib: true do
# 1 + A
# 2 + BB
- it "returns nil" do
- expect(subject).to be_nil
+ it "returns the position of the change" do
+ expect_change_position(
+ old_path: file_name,
+ new_path: file_name,
+ old_line: 3,
+ new_line: nil
+ )
end
end
end
@@ -512,6 +549,7 @@ describe Gitlab::Diff::PositionTracer, lib: true do
context "when that line was changed between the old and the new diff" do
let(:old_diff_refs) { diff_refs(initial_commit, create_file_commit) }
let(:new_diff_refs) { diff_refs(create_file_commit, update_line_commit) }
+ let(:change_diff_refs) { diff_refs(create_file_commit, update_line_commit) }
let(:old_position) { position(new_path: file_name, new_line: 2) }
# old diff:
@@ -525,14 +563,20 @@ describe Gitlab::Diff::PositionTracer, lib: true do
# 2 + BB
# 3 3 C
- it "returns nil" do
- expect(subject).to be_nil
+ it "returns the position of the change" do
+ expect_change_position(
+ old_path: file_name,
+ new_path: file_name,
+ old_line: 2,
+ new_line: nil
+ )
end
end
context "when that line was deleted between the old and the new diff" do
let(:old_diff_refs) { diff_refs(initial_commit, move_line_commit) }
let(:new_diff_refs) { diff_refs(move_line_commit, delete_line_commit) }
+ let(:change_diff_refs) { diff_refs(move_line_commit, delete_line_commit) }
let(:old_position) { position(new_path: file_name, new_line: 3) }
# old diff:
@@ -545,8 +589,13 @@ describe Gitlab::Diff::PositionTracer, lib: true do
# 2 2 A
# 3 - C
- it "returns nil" do
- expect(subject).to be_nil
+ it "returns the position of the change" do
+ expect_change_position(
+ old_path: file_name,
+ new_path: file_name,
+ old_line: 3,
+ new_line: nil
+ )
end
end
end
@@ -558,6 +607,7 @@ describe Gitlab::Diff::PositionTracer, lib: true do
context "when the file's content was unchanged between the old and the new diff" do
let(:old_diff_refs) { diff_refs(initial_commit, delete_line_commit) }
let(:new_diff_refs) { diff_refs(delete_line_commit, rename_file_commit) }
+ let(:change_diff_refs) { diff_refs(initial_commit, delete_line_commit) }
let(:old_position) { position(new_path: file_name, new_line: 2) }
# old diff:
@@ -569,8 +619,13 @@ describe Gitlab::Diff::PositionTracer, lib: true do
# 1 1 BB
# 2 2 A
- it "returns nil since the line doesn't exist in the new diffs anymore" do
- expect(subject).to be_nil
+ it "returns the position of the change" do
+ expect_change_position(
+ old_path: file_name,
+ new_path: file_name,
+ old_line: nil,
+ new_line: 2
+ )
end
end
@@ -628,6 +683,7 @@ describe Gitlab::Diff::PositionTracer, lib: true do
context "when that line was changed between the old and the new diff" do
let(:old_diff_refs) { diff_refs(initial_commit, delete_line_commit) }
let(:new_diff_refs) { diff_refs(delete_line_commit, update_line_again_commit) }
+ let(:change_diff_refs) { diff_refs(delete_line_commit, update_line_again_commit) }
let(:old_position) { position(new_path: file_name, new_line: 2) }
# old diff:
@@ -640,28 +696,13 @@ describe Gitlab::Diff::PositionTracer, lib: true do
# 2 - A
# 2 + AA
- it "returns nil" do
- expect(subject).to be_nil
- end
- end
-
- context "when that line was deleted between the old and the new diff" do
- let(:old_diff_refs) { diff_refs(initial_commit, delete_line_commit) }
- let(:new_diff_refs) { diff_refs(delete_line_commit, delete_line_again_commit) }
- let(:old_position) { position(new_path: file_name, new_line: 1) }
-
- # old diff:
- # 1 + BB
- # 2 + A
- #
- # new diff:
- # file_name -> new_file_name
- # 1 - BB
- # 2 - A
- # 1 + AA
-
- it "returns nil" do
- expect(subject).to be_nil
+ it "returns the position of the change" do
+ expect_change_position(
+ old_path: file_name,
+ new_path: new_file_name,
+ old_line: 2,
+ new_line: nil
+ )
end
end
end
@@ -673,6 +714,7 @@ describe Gitlab::Diff::PositionTracer, lib: true do
context "when the file's content was unchanged between the old and the new diff" do
let(:old_diff_refs) { diff_refs(initial_commit, delete_line_commit) }
let(:new_diff_refs) { diff_refs(delete_line_commit, delete_file_commit) }
+ let(:change_diff_refs) { diff_refs(delete_line_commit, delete_file_commit) }
let(:old_position) { position(new_path: file_name, new_line: 2) }
# old diff:
@@ -683,8 +725,13 @@ describe Gitlab::Diff::PositionTracer, lib: true do
# 1 - BB
# 2 - A
- it "returns nil" do
- expect(subject).to be_nil
+ it "returns the position of the change" do
+ expect_change_position(
+ old_path: file_name,
+ new_path: file_name,
+ old_line: 2,
+ new_line: nil
+ )
end
end
@@ -692,6 +739,7 @@ describe Gitlab::Diff::PositionTracer, lib: true do
context "when that line was unchanged between the old and the new diff" do
let(:old_diff_refs) { diff_refs(initial_commit, move_line_commit) }
let(:new_diff_refs) { diff_refs(delete_line_commit, delete_file_commit) }
+ let(:change_diff_refs) { diff_refs(move_line_commit, delete_file_commit) }
let(:old_position) { position(new_path: file_name, new_line: 2) }
# old diff:
@@ -703,14 +751,20 @@ describe Gitlab::Diff::PositionTracer, lib: true do
# 1 - BB
# 2 - A
- it "returns nil" do
- expect(subject).to be_nil
+ it "returns the position of the change" do
+ expect_change_position(
+ old_path: file_name,
+ new_path: file_name,
+ old_line: 2,
+ new_line: nil
+ )
end
end
context "when that line was moved between the old and the new diff" do
let(:old_diff_refs) { diff_refs(initial_commit, update_line_commit) }
let(:new_diff_refs) { diff_refs(move_line_commit, delete_file_commit) }
+ let(:change_diff_refs) { diff_refs(update_line_commit, delete_file_commit) }
let(:old_position) { position(new_path: file_name, new_line: 2) }
# old diff:
@@ -723,14 +777,20 @@ describe Gitlab::Diff::PositionTracer, lib: true do
# 2 - A
# 3 - C
- it "returns nil" do
- expect(subject).to be_nil
+ it "returns the position of the change" do
+ expect_change_position(
+ old_path: file_name,
+ new_path: file_name,
+ old_line: 2,
+ new_line: nil
+ )
end
end
context "when that line was changed between the old and the new diff" do
let(:old_diff_refs) { diff_refs(initial_commit, create_file_commit) }
let(:new_diff_refs) { diff_refs(update_line_commit, delete_file_commit) }
+ let(:change_diff_refs) { diff_refs(create_file_commit, delete_file_commit) }
let(:old_position) { position(new_path: file_name, new_line: 2) }
# old diff:
@@ -743,14 +803,20 @@ describe Gitlab::Diff::PositionTracer, lib: true do
# 2 - BB
# 3 - C
- it "returns nil" do
- expect(subject).to be_nil
+ it "returns the position of the change" do
+ expect_change_position(
+ old_path: file_name,
+ new_path: file_name,
+ old_line: 2,
+ new_line: nil
+ )
end
end
context "when that line was deleted between the old and the new diff" do
let(:old_diff_refs) { diff_refs(initial_commit, move_line_commit) }
let(:new_diff_refs) { diff_refs(delete_line_commit, delete_file_commit) }
+ let(:change_diff_refs) { diff_refs(move_line_commit, delete_file_commit) }
let(:old_position) { position(new_path: file_name, new_line: 3) }
# old diff:
@@ -762,8 +828,13 @@ describe Gitlab::Diff::PositionTracer, lib: true do
# 1 - BB
# 2 - A
- it "returns nil" do
- expect(subject).to be_nil
+ it "returns the position of the change" do
+ expect_change_position(
+ old_path: file_name,
+ new_path: file_name,
+ old_line: 3,
+ new_line: nil
+ )
end
end
end
@@ -775,6 +846,7 @@ describe Gitlab::Diff::PositionTracer, lib: true do
context "when the file's content was unchanged between the old and the new diff" do
let(:old_diff_refs) { diff_refs(initial_commit, create_file_commit) }
let(:new_diff_refs) { diff_refs(create_file_commit, create_second_file_commit) }
+ let(:change_diff_refs) { diff_refs(initial_commit, create_file_commit) }
let(:old_position) { position(new_path: file_name, new_line: 2) }
# old diff:
@@ -787,8 +859,13 @@ describe Gitlab::Diff::PositionTracer, lib: true do
# 2 2 B
# 3 3 C
- it "returns nil" do
- expect(subject).to be_nil
+ it "returns the position of the change" do
+ expect_change_position(
+ old_path: file_name,
+ new_path: file_name,
+ old_line: nil,
+ new_line: 2
+ )
end
end
@@ -796,6 +873,7 @@ describe Gitlab::Diff::PositionTracer, lib: true do
context "when that line was unchanged between the old and the new diff" do
let(:old_diff_refs) { diff_refs(initial_commit, create_file_commit) }
let(:new_diff_refs) { diff_refs(update_line_commit, update_second_file_line_commit) }
+ let(:change_diff_refs) { diff_refs(initial_commit, update_line_commit) }
let(:old_position) { position(new_path: file_name, new_line: 1) }
# old diff:
@@ -808,14 +886,20 @@ describe Gitlab::Diff::PositionTracer, lib: true do
# 2 2 BB
# 3 3 C
- it "returns nil" do
- expect(subject).to be_nil
+ it "returns the position of the change" do
+ expect_change_position(
+ old_path: file_name,
+ new_path: file_name,
+ old_line: nil,
+ new_line: 1
+ )
end
end
context "when that line was moved between the old and the new diff" do
let(:old_diff_refs) { diff_refs(initial_commit, update_line_commit) }
let(:new_diff_refs) { diff_refs(move_line_commit, move_second_file_line_commit) }
+ let(:change_diff_refs) { diff_refs(initial_commit, move_line_commit) }
let(:old_position) { position(new_path: file_name, new_line: 2) }
# old diff:
@@ -828,14 +912,20 @@ describe Gitlab::Diff::PositionTracer, lib: true do
# 2 2 A
# 3 3 C
- it "returns nil" do
- expect(subject).to be_nil
+ it "returns the position of the change" do
+ expect_change_position(
+ old_path: file_name,
+ new_path: file_name,
+ old_line: nil,
+ new_line: 1
+ )
end
end
context "when that line was changed between the old and the new diff" do
let(:old_diff_refs) { diff_refs(initial_commit, create_file_commit) }
let(:new_diff_refs) { diff_refs(update_line_commit, update_second_file_line_commit) }
+ let(:change_diff_refs) { diff_refs(create_file_commit, update_second_file_line_commit) }
let(:old_position) { position(new_path: file_name, new_line: 2) }
# old diff:
@@ -848,14 +938,20 @@ describe Gitlab::Diff::PositionTracer, lib: true do
# 2 2 BB
# 3 3 C
- it "returns nil" do
- expect(subject).to be_nil
+ it "returns the position of the change" do
+ expect_change_position(
+ old_path: file_name,
+ new_path: file_name,
+ old_line: 2,
+ new_line: nil
+ )
end
end
context "when that line was deleted between the old and the new diff" do
let(:old_diff_refs) { diff_refs(initial_commit, move_line_commit) }
let(:new_diff_refs) { diff_refs(delete_line_commit, delete_second_file_line_commit) }
+ let(:change_diff_refs) { diff_refs(move_line_commit, delete_second_file_line_commit) }
let(:old_position) { position(new_path: file_name, new_line: 3) }
# old diff:
@@ -867,8 +963,13 @@ describe Gitlab::Diff::PositionTracer, lib: true do
# 1 1 BB
# 2 2 A
- it "returns nil" do
- expect(subject).to be_nil
+ it "returns the position of the change" do
+ expect_change_position(
+ old_path: file_name,
+ new_path: file_name,
+ old_line: 3,
+ new_line: nil
+ )
end
end
end
@@ -957,6 +1058,7 @@ describe Gitlab::Diff::PositionTracer, lib: true do
context "when that line was changed or deleted between the old and the new diff" do
let(:old_diff_refs) { diff_refs(create_file_commit, move_line_commit) }
let(:new_diff_refs) { diff_refs(initial_commit, create_file_commit) }
+ let(:change_diff_refs) { diff_refs(move_line_commit, create_file_commit) }
let(:old_position) { position(old_path: file_name, new_path: file_name, new_line: 1) }
# old diff:
@@ -970,8 +1072,13 @@ describe Gitlab::Diff::PositionTracer, lib: true do
# 2 + B
# 3 + C
- it "returns nil" do
- expect(subject).to be_nil
+ it "returns the position of the change" do
+ expect_change_position(
+ old_path: file_name,
+ new_path: file_name,
+ old_line: 1,
+ new_line: nil
+ )
end
end
end
@@ -980,6 +1087,7 @@ describe Gitlab::Diff::PositionTracer, lib: true do
context "when the position pointed at a deleted line in the old diff" do
let(:old_diff_refs) { diff_refs(create_file_commit, update_line_commit) }
let(:new_diff_refs) { diff_refs(initial_commit, update_line_commit) }
+ let(:change_diff_refs) { diff_refs(create_file_commit, initial_commit) }
let(:old_position) { position(old_path: file_name, new_path: file_name, old_line: 2) }
# old diff:
@@ -993,8 +1101,13 @@ describe Gitlab::Diff::PositionTracer, lib: true do
# 2 + BB
# 3 + C
- it "returns nil" do
- expect(subject).to be_nil
+ it "returns the position of the change" do
+ expect_change_position(
+ old_path: file_name,
+ new_path: file_name,
+ old_line: 2,
+ new_line: nil
+ )
end
end
@@ -1076,6 +1189,7 @@ describe Gitlab::Diff::PositionTracer, lib: true do
context "when that line was changed or deleted between the old and the new diff" do
let(:old_diff_refs) { diff_refs(create_file_commit, move_line_commit) }
let(:new_diff_refs) { diff_refs(initial_commit, delete_line_commit) }
+ let(:change_diff_refs) { diff_refs(move_line_commit, delete_line_commit) }
let(:old_position) { position(old_path: file_name, new_path: file_name, old_line: 3, new_line: 3) }
# old diff:
@@ -1088,8 +1202,13 @@ describe Gitlab::Diff::PositionTracer, lib: true do
# 1 + A
# 2 + B
- it "returns nil" do
- expect(subject).to be_nil
+ it "returns the position of the change" do
+ expect_change_position(
+ old_path: file_name,
+ new_path: file_name,
+ old_line: 3,
+ new_line: nil
+ )
end
end
end
@@ -1182,6 +1301,7 @@ describe Gitlab::Diff::PositionTracer, lib: true do
context "when that line was changed or deleted between the old and the new diff" do
let(:old_diff_refs) { diff_refs(create_file_commit, move_line_commit) }
let(:new_diff_refs) { diff_refs(create_file_commit, update_line_commit) }
+ let(:change_diff_refs) { diff_refs(move_line_commit, update_line_commit) }
let(:old_position) { position(old_path: file_name, new_path: file_name, new_line: 1) }
# old diff:
@@ -1196,8 +1316,13 @@ describe Gitlab::Diff::PositionTracer, lib: true do
# 2 + BB
# 3 3 C
- it "returns nil" do
- expect(subject).to be_nil
+ it "returns the position of the change" do
+ expect_change_position(
+ old_path: file_name,
+ new_path: file_name,
+ old_line: 1,
+ new_line: nil
+ )
end
end
end
@@ -1239,7 +1364,7 @@ describe Gitlab::Diff::PositionTracer, lib: true do
describe "typical use scenarios" do
let(:second_branch_name) { "#{branch_name}-2" }
- def expect_positions(old_attrs, new_attrs)
+ def expect_new_positions(old_attrs, new_attrs)
old_positions = old_attrs.map do |old_attrs|
position(old_attrs)
end
@@ -1248,8 +1373,14 @@ describe Gitlab::Diff::PositionTracer, lib: true do
position_tracer.trace(old_position)
end
- new_positions.zip(new_attrs).each do |new_position, new_attrs|
- expect_new_position(new_attrs, new_position)
+ aggregate_failures do
+ new_positions.zip(new_attrs).each do |new_position, new_attrs|
+ if new_attrs&.delete(:change)
+ expect_change_position(new_attrs, new_position)
+ else
+ expect_new_position(new_attrs, new_position)
+ end
+ end
end
end
@@ -1330,6 +1461,7 @@ describe Gitlab::Diff::PositionTracer, lib: true do
describe "simple push of new commit" do
let(:old_diff_refs) { diff_refs(create_file_commit, update_file_commit) }
let(:new_diff_refs) { diff_refs(create_file_commit, update_file_again_commit) }
+ let(:change_diff_refs) { diff_refs(update_file_commit, update_file_again_commit) }
# old diff:
# 1 1 A
@@ -1368,14 +1500,14 @@ describe Gitlab::Diff::PositionTracer, lib: true do
{ old_path: file_name, new_path: file_name, old_line: 1, new_line: 1 },
{ old_path: file_name, old_line: 2 },
{ old_path: file_name, new_path: file_name, old_line: 3, new_line: 3 },
- { old_path: file_name, old_line: 4, new_line: 4 },
- nil,
+ { new_path: file_name, new_line: 4, change: true },
+ { new_path: file_name, old_line: 3, change: true },
{ old_path: file_name, new_path: file_name, old_line: 5, new_line: 5 },
- { old_path: file_name, old_line: 6 },
+ { new_path: file_name, old_line: 5, change: true },
{ new_path: file_name, new_line: 7 }
]
- expect_positions(old_position_attrs, new_position_attrs)
+ expect_new_positions(old_position_attrs, new_position_attrs)
end
end
@@ -1402,6 +1534,7 @@ describe Gitlab::Diff::PositionTracer, lib: true do
let(:old_diff_refs) { diff_refs(create_file_commit, update_file_commit) }
let(:new_diff_refs) { diff_refs(create_file_commit, second_create_file_commit) }
+ let(:change_diff_refs) { diff_refs(update_file_commit, second_create_file_commit) }
# old diff:
# 1 1 A
@@ -1440,20 +1573,21 @@ describe Gitlab::Diff::PositionTracer, lib: true do
{ old_path: file_name, new_path: file_name, old_line: 1, new_line: 1 },
{ old_path: file_name, old_line: 2 },
{ old_path: file_name, new_path: file_name, old_line: 3, new_line: 3 },
- { old_path: file_name, old_line: 4, new_line: 4 },
- nil,
+ { new_path: file_name, new_line: 4, change: true },
+ { old_path: file_name, old_line: 3, change: true },
{ old_path: file_name, new_path: file_name, old_line: 5, new_line: 5 },
- { old_path: file_name, old_line: 6 },
+ { old_path: file_name, old_line: 5, change: true },
{ new_path: file_name, new_line: 7 }
]
- expect_positions(old_position_attrs, new_position_attrs)
+ expect_new_positions(old_position_attrs, new_position_attrs)
end
end
describe "force push to delete last commit" do
let(:old_diff_refs) { diff_refs(create_file_commit, update_file_again_commit) }
let(:new_diff_refs) { diff_refs(create_file_commit, update_file_commit) }
+ let(:change_diff_refs) { diff_refs(update_file_again_commit, update_file_commit) }
# old diff:
# 1 1 A
@@ -1492,16 +1626,16 @@ describe Gitlab::Diff::PositionTracer, lib: true do
new_position_attrs = [
{ old_path: file_name, new_path: file_name, old_line: 1, new_line: 1 },
{ old_path: file_name, old_line: 2 },
- nil,
+ { old_path: file_name, old_line: 2, change: true },
{ old_path: file_name, new_path: file_name, old_line: 3, new_line: 2 },
- { old_path: file_name, old_line: 4 },
+ { old_path: file_name, old_line: 4, change: true },
{ old_path: file_name, new_path: file_name, old_line: 5, new_line: 4 },
- { old_path: file_name, new_path: file_name, old_line: 6, new_line: 5 },
- nil,
+ { new_path: file_name, new_line: 5, change: true },
+ { old_path: file_name, old_line: 6, change: true },
{ new_path: file_name, new_line: 6 }
]
- expect_positions(old_position_attrs, new_position_attrs)
+ expect_new_positions(old_position_attrs, new_position_attrs)
end
end
@@ -1567,6 +1701,7 @@ describe Gitlab::Diff::PositionTracer, lib: true do
let(:old_diff_refs) { diff_refs(create_file_commit, update_file_again_commit) }
let(:new_diff_refs) { diff_refs(create_file_commit, overwrite_update_file_again_commit) }
+ let(:change_diff_refs) { diff_refs(update_file_again_commit, overwrite_update_file_again_commit) }
# old diff:
# 1 1 A
@@ -1618,7 +1753,7 @@ describe Gitlab::Diff::PositionTracer, lib: true do
{ new_path: file_name, new_line: 10 }, # + G
]
- expect_positions(old_position_attrs, new_position_attrs)
+ expect_new_positions(old_position_attrs, new_position_attrs)
end
end
@@ -1643,6 +1778,7 @@ describe Gitlab::Diff::PositionTracer, lib: true do
let(:old_diff_refs) { diff_refs(create_file_commit, update_file_again_commit) }
let(:new_diff_refs) { diff_refs(create_file_commit, merge_commit) }
+ let(:change_diff_refs) { diff_refs(update_file_again_commit, merge_commit) }
# old diff:
# 1 1 A
@@ -1694,13 +1830,14 @@ describe Gitlab::Diff::PositionTracer, lib: true do
{ new_path: file_name, new_line: 10 }, # + G
]
- expect_positions(old_position_attrs, new_position_attrs)
+ expect_new_positions(old_position_attrs, new_position_attrs)
end
end
describe "changing target branch" do
let(:old_diff_refs) { diff_refs(create_file_commit, update_file_again_commit) }
let(:new_diff_refs) { diff_refs(update_file_commit, update_file_again_commit) }
+ let(:change_diff_refs) { diff_refs(create_file_commit, update_file_commit) }
# old diff:
# 1 1 A
@@ -1739,7 +1876,7 @@ describe Gitlab::Diff::PositionTracer, lib: true do
new_position_attrs = [
{ old_path: file_name, new_path: file_name, old_line: 1, new_line: 1 },
- nil,
+ { old_path: file_name, old_line: 2, change: true },
{ new_path: file_name, new_line: 2 },
{ old_path: file_name, new_path: file_name, old_line: 2, new_line: 3 },
{ new_path: file_name, new_line: 4 },
@@ -1749,7 +1886,7 @@ describe Gitlab::Diff::PositionTracer, lib: true do
{ new_path: file_name, new_line: 7 }
]
- expect_positions(old_position_attrs, new_position_attrs)
+ expect_new_positions(old_position_attrs, new_position_attrs)
end
end
end
diff --git a/spec/lib/gitlab/etag_caching/router_spec.rb b/spec/lib/gitlab/etag_caching/router_spec.rb
index 582ea85a1d6..0418fc0a1e2 100644
--- a/spec/lib/gitlab/etag_caching/router_spec.rb
+++ b/spec/lib/gitlab/etag_caching/router_spec.rb
@@ -83,11 +83,22 @@ describe Gitlab::EtagCaching::Router do
)
result = described_class.match(env)
-
expect(result).to be_present
+
expect(result.name).to eq 'environments'
end
+ it 'matches pipeline#show endpoint' do
+ env = build_env(
+ '/my-group/my-project/pipelines/2.json'
+ )
+
+ result = described_class.match(env)
+
+ expect(result).to be_present
+ expect(result.name).to eq 'project_pipeline'
+ end
+
def build_env(path)
{ 'PATH_INFO' => path }
end
diff --git a/spec/lib/gitlab/import_export/safe_model_attributes.yml b/spec/lib/gitlab/import_export/safe_model_attributes.yml
index c22fba11225..96054c996fd 100644
--- a/spec/lib/gitlab/import_export/safe_model_attributes.yml
+++ b/spec/lib/gitlab/import_export/safe_model_attributes.yml
@@ -54,6 +54,7 @@ Note:
- type
- position
- original_position
+- change_position
- resolved_at
- resolved_by_id
- discussion_id
diff --git a/spec/lib/gitlab/regex_spec.rb b/spec/lib/gitlab/regex_spec.rb
index 72e947f2cc2..a7d1283acb8 100644
--- a/spec/lib/gitlab/regex_spec.rb
+++ b/spec/lib/gitlab/regex_spec.rb
@@ -2,9 +2,377 @@
require 'spec_helper'
describe Gitlab::Regex, lib: true do
+ # Pass in a full path to remove the format segment:
+ # `/ci/lint(.:format)` -> `/ci/lint`
+ def without_format(path)
+ path.split('(', 2)[0]
+ end
+
+ # Pass in a full path and get the last segment before a wildcard
+ # That's not a parameter
+ # `/*namespace_id/:project_id/builds/artifacts/*ref_name_and_path`
+ # -> 'builds/artifacts'
+ def path_before_wildcard(path)
+ path = path.gsub(STARTING_WITH_NAMESPACE, "")
+ path_segments = path.split('/').reject(&:empty?)
+ wildcard_index = path_segments.index { |segment| parameter?(segment) }
+
+ segments_before_wildcard = path_segments[0..wildcard_index - 1]
+
+ segments_before_wildcard.join('/')
+ end
+
+ def parameter?(segment)
+ segment =~ /[*:]/
+ end
+
+ # If the path is reserved. Then no conflicting paths can# be created for any
+ # route using this reserved word.
+ #
+ # Both `builds/artifacts` & `build` are covered by reserving the word
+ # `build`
+ def wildcards_include?(path)
+ described_class::PROJECT_WILDCARD_ROUTES.include?(path) ||
+ described_class::PROJECT_WILDCARD_ROUTES.include?(path.split('/').first)
+ end
+
+ def failure_message(missing_words, constant_name, migration_helper)
+ missing_words = Array(missing_words)
+ <<-MSG
+ Found new routes that could cause conflicts with existing namespaced routes
+ for groups or projects.
+
+ Add <#{missing_words.join(', ')}> to `Gitlab::Regex::#{constant_name}
+ to make sure no projects or namespaces can be created with those paths.
+
+ To rename any existing records with those paths you can use the
+ `Gitlab::Database::RenameReservedpathsMigration::<VERSION>.#{migration_helper}`
+ migration helper.
+
+ Make sure to make a note of the renamed records in the release blog post.
+
+ MSG
+ end
+
+ let(:all_routes) do
+ route_set = Rails.application.routes
+ routes_collection = route_set.routes
+ routes_array = routes_collection.routes
+ routes_array.map { |route| route.path.spec.to_s }
+ end
+
+ let(:routes_without_format) { all_routes.map { |path| without_format(path) } }
+
+ # Routes not starting with `/:` or `/*`
+ # all routes not starting with a param
+ let(:routes_not_starting_in_wildcard) { routes_without_format.select { |p| p !~ %r{^/[:*]} } }
+
+ let(:top_level_words) do
+ routes_not_starting_in_wildcard.map do |route|
+ route.split('/')[1]
+ end.compact.uniq
+ end
+
+ # All routes that start with a namespaced path, that have 1 or more
+ # path-segments before having another wildcard parameter.
+ # - Starting with paths:
+ # - `/*namespace_id/:project_id/`
+ # - `/*namespace_id/:id/`
+ # - Followed by one or more path-parts not starting with `:` or `*`
+ # - Followed by a path-part that includes a wildcard parameter `*`
+ # At the time of writing these routes match: http://rubular.com/r/Rv2pDE5Dvw
+ STARTING_WITH_NAMESPACE = %r{^/\*namespace_id/:(project_)?id}
+ NON_PARAM_PARTS = %r{[^:*][a-z\-_/]*}
+ ANY_OTHER_PATH_PART = %r{[a-z\-_/:]*}
+ WILDCARD_SEGMENT = %r{\*}
+ let(:namespaced_wildcard_routes) do
+ routes_without_format.select do |p|
+ p =~ %r{#{STARTING_WITH_NAMESPACE}/#{NON_PARAM_PARTS}/#{ANY_OTHER_PATH_PART}#{WILDCARD_SEGMENT}}
+ end
+ end
+
+ # This will return all paths that are used in a namespaced route
+ # before another wildcard path:
+ #
+ # /*namespace_id/:project_id/builds/artifacts/*ref_name_and_path
+ # /*namespace_id/:project_id/info/lfs/objects/*oid
+ # /*namespace_id/:project_id/commits/*id
+ # /*namespace_id/:project_id/builds/:build_id/artifacts/file/*path
+ # -> ['builds/artifacts', 'info/lfs/objects', 'commits', 'artifacts/file']
+ let(:all_wildcard_paths) do
+ namespaced_wildcard_routes.map do |route|
+ path_before_wildcard(route)
+ end.uniq
+ end
+
+ STARTING_WITH_GROUP = %r{^/groups/\*(group_)?id/}
+ let(:group_routes) do
+ routes_without_format.select do |path|
+ path =~ STARTING_WITH_GROUP
+ end
+ end
+
+ let(:paths_after_group_id) do
+ group_routes.map do |route|
+ route.gsub(STARTING_WITH_GROUP, '').split('/').first
+ end.uniq
+ end
+
+ describe 'TOP_LEVEL_ROUTES' do
+ it 'includes all the top level namespaces' do
+ failure_block = lambda do
+ missing_words = top_level_words - described_class::TOP_LEVEL_ROUTES
+ failure_message(missing_words, 'TOP_LEVEL_ROUTES', 'rename_root_paths')
+ end
+
+ expect(described_class::TOP_LEVEL_ROUTES)
+ .to include(*top_level_words), failure_block
+ end
+ end
+
+ describe 'GROUP_ROUTES' do
+ it "don't contain a second wildcard" do
+ failure_block = lambda do
+ missing_words = paths_after_group_id - described_class::GROUP_ROUTES
+ failure_message(missing_words, 'GROUP_ROUTES', 'rename_child_paths')
+ end
+
+ expect(described_class::GROUP_ROUTES)
+ .to include(*paths_after_group_id), failure_block
+ end
+ end
+
+ describe 'PROJECT_WILDCARD_ROUTES' do
+ it 'includes all paths that can be used after a namespace/project path' do
+ aggregate_failures do
+ all_wildcard_paths.each do |path|
+ expect(wildcards_include?(path))
+ .to be(true), failure_message(path, 'PROJECT_WILDCARD_ROUTES', 'rename_wildcard_paths')
+ end
+ end
+ end
+ end
+
+ describe '.root_namespace_path_regex' do
+ subject { described_class.root_namespace_path_regex }
+
+ it 'rejects top level routes' do
+ expect(subject).not_to match('admin/')
+ expect(subject).not_to match('api/')
+ expect(subject).not_to match('.well-known/')
+ end
+
+ it 'accepts project wildcard routes' do
+ expect(subject).to match('blob/')
+ expect(subject).to match('edit/')
+ expect(subject).to match('wikis/')
+ end
+
+ it 'accepts group routes' do
+ expect(subject).to match('activity/')
+ expect(subject).to match('group_members/')
+ expect(subject).to match('subgroups/')
+ end
+
+ it 'is not case sensitive' do
+ expect(subject).not_to match('Users/')
+ end
+
+ it 'does not allow extra slashes' do
+ expect(subject).not_to match('/blob/')
+ expect(subject).not_to match('blob//')
+ end
+ end
+
+ describe '.full_namespace_path_regex' do
+ subject { described_class.full_namespace_path_regex }
+
+ context 'at the top level' do
+ context 'when the final level' do
+ it 'rejects top level routes' do
+ expect(subject).not_to match('admin/')
+ expect(subject).not_to match('api/')
+ expect(subject).not_to match('.well-known/')
+ end
+
+ it 'accepts project wildcard routes' do
+ expect(subject).to match('blob/')
+ expect(subject).to match('edit/')
+ expect(subject).to match('wikis/')
+ end
+
+ it 'accepts group routes' do
+ expect(subject).to match('activity/')
+ expect(subject).to match('group_members/')
+ expect(subject).to match('subgroups/')
+ end
+ end
+
+ context 'when more levels follow' do
+ it 'rejects top level routes' do
+ expect(subject).not_to match('admin/more/')
+ expect(subject).not_to match('api/more/')
+ expect(subject).not_to match('.well-known/more/')
+ end
+
+ it 'accepts project wildcard routes' do
+ expect(subject).to match('blob/more/')
+ expect(subject).to match('edit/more/')
+ expect(subject).to match('wikis/more/')
+ expect(subject).to match('environments/folders/')
+ expect(subject).to match('info/lfs/objects/')
+ end
+
+ it 'accepts group routes' do
+ expect(subject).to match('activity/more/')
+ expect(subject).to match('group_members/more/')
+ expect(subject).to match('subgroups/more/')
+ end
+ end
+ end
+
+ context 'at the second level' do
+ context 'when the final level' do
+ it 'accepts top level routes' do
+ expect(subject).to match('root/admin/')
+ expect(subject).to match('root/api/')
+ expect(subject).to match('root/.well-known/')
+ end
+
+ it 'rejects project wildcard routes' do
+ expect(subject).not_to match('root/blob/')
+ expect(subject).not_to match('root/edit/')
+ expect(subject).not_to match('root/wikis/')
+ expect(subject).not_to match('root/environments/folders/')
+ expect(subject).not_to match('root/info/lfs/objects/')
+ end
+
+ it 'rejects group routes' do
+ expect(subject).not_to match('root/activity/')
+ expect(subject).not_to match('root/group_members/')
+ expect(subject).not_to match('root/subgroups/')
+ end
+ end
+
+ context 'when more levels follow' do
+ it 'accepts top level routes' do
+ expect(subject).to match('root/admin/more/')
+ expect(subject).to match('root/api/more/')
+ expect(subject).to match('root/.well-known/more/')
+ end
+
+ it 'rejects project wildcard routes' do
+ expect(subject).not_to match('root/blob/more/')
+ expect(subject).not_to match('root/edit/more/')
+ expect(subject).not_to match('root/wikis/more/')
+ expect(subject).not_to match('root/environments/folders/more/')
+ expect(subject).not_to match('root/info/lfs/objects/more/')
+ end
+
+ it 'rejects group routes' do
+ expect(subject).not_to match('root/activity/more/')
+ expect(subject).not_to match('root/group_members/more/')
+ expect(subject).not_to match('root/subgroups/more/')
+ end
+ end
+ end
+
+ it 'is not case sensitive' do
+ expect(subject).not_to match('root/Blob/')
+ end
+
+ it 'does not allow extra slashes' do
+ expect(subject).not_to match('/root/admin/')
+ expect(subject).not_to match('root/admin//')
+ end
+ end
+
describe '.project_path_regex' do
subject { described_class.project_path_regex }
+ it 'accepts top level routes' do
+ expect(subject).to match('admin/')
+ expect(subject).to match('api/')
+ expect(subject).to match('.well-known/')
+ end
+
+ it 'rejects project wildcard routes' do
+ expect(subject).not_to match('blob/')
+ expect(subject).not_to match('edit/')
+ expect(subject).not_to match('wikis/')
+ expect(subject).not_to match('environments/folders/')
+ expect(subject).not_to match('info/lfs/objects/')
+ end
+
+ it 'accepts group routes' do
+ expect(subject).to match('activity/')
+ expect(subject).to match('group_members/')
+ expect(subject).to match('subgroups/')
+ end
+
+ it 'is not case sensitive' do
+ expect(subject).not_to match('Blob/')
+ end
+
+ it 'does not allow extra slashes' do
+ expect(subject).not_to match('/admin/')
+ expect(subject).not_to match('admin//')
+ end
+ end
+
+ describe '.full_project_path_regex' do
+ subject { described_class.full_project_path_regex }
+
+ it 'accepts top level routes' do
+ expect(subject).to match('root/admin/')
+ expect(subject).to match('root/api/')
+ expect(subject).to match('root/.well-known/')
+ end
+
+ it 'rejects project wildcard routes' do
+ expect(subject).not_to match('root/blob/')
+ expect(subject).not_to match('root/edit/')
+ expect(subject).not_to match('root/wikis/')
+ expect(subject).not_to match('root/environments/folders/')
+ expect(subject).not_to match('root/info/lfs/objects/')
+ end
+
+ it 'accepts group routes' do
+ expect(subject).to match('root/activity/')
+ expect(subject).to match('root/group_members/')
+ expect(subject).to match('root/subgroups/')
+ end
+
+ it 'is not case sensitive' do
+ expect(subject).not_to match('root/Blob/')
+ end
+
+ it 'does not allow extra slashes' do
+ expect(subject).not_to match('/root/admin/')
+ expect(subject).not_to match('root/admin//')
+ end
+ end
+
+ describe '.namespace_regex' do
+ subject { described_class.namespace_regex }
+
+ it { is_expected.to match('gitlab-ce') }
+ it { is_expected.to match('gitlab_git') }
+ it { is_expected.to match('_underscore.js') }
+ it { is_expected.to match('100px.com') }
+ it { is_expected.to match('gitlab.org') }
+ it { is_expected.not_to match('?gitlab') }
+ it { is_expected.not_to match('git lab') }
+ it { is_expected.not_to match('gitlab.git') }
+ it { is_expected.not_to match('gitlab.org.') }
+ it { is_expected.not_to match('gitlab.org/') }
+ it { is_expected.not_to match('/gitlab.org') }
+ it { is_expected.not_to match('gitlab git') }
+ end
+
+ describe '.project_path_format_regex' do
+ subject { described_class.project_path_format_regex }
+
it { is_expected.to match('gitlab-ce') }
it { is_expected.to match('gitlab_git') }
it { is_expected.to match('_underscore.js') }
diff --git a/spec/migrations/upate_retried_for_ci_builds_spec.rb b/spec/migrations/update_retried_for_ci_builds_spec.rb
index 5cdb8a3c7da..3742b4dafe5 100644
--- a/spec/migrations/upate_retried_for_ci_builds_spec.rb
+++ b/spec/migrations/update_retried_for_ci_builds_spec.rb
@@ -1,7 +1,7 @@
require 'spec_helper'
-require Rails.root.join('db', 'post_migrate', '20170503004427_upate_retried_for_ci_build.rb')
+require Rails.root.join('db', 'post_migrate', '20170503004427_update_retried_for_ci_build.rb')
-describe UpateRetriedForCiBuild, truncate: true do
+describe UpdateRetriedForCiBuild, truncate: true do
let(:pipeline) { create(:ci_pipeline) }
let!(:build_old) { create(:ci_build, pipeline: pipeline, name: 'test') }
let!(:build_new) { create(:ci_build, pipeline: pipeline, name: 'test') }
diff --git a/spec/models/commit_status_spec.rb b/spec/models/commit_status_spec.rb
index 6947affcc1e..c50b8bf7b13 100644
--- a/spec/models/commit_status_spec.rb
+++ b/spec/models/commit_status_spec.rb
@@ -36,6 +36,16 @@ describe CommitStatus, :models do
it { is_expected.to eq(commit_status.user) }
end
+ describe 'status state machine' do
+ let!(:commit_status) { create(:commit_status, :running, project: project) }
+
+ it 'invalidates the cache after a transition' do
+ expect(ExpireJobCacheWorker).to receive(:perform_async).with(commit_status.id)
+
+ commit_status.success!
+ end
+ end
+
describe '#started?' do
subject { commit_status.started? }
diff --git a/spec/models/concerns/discussion_on_diff_spec.rb b/spec/models/concerns/discussion_on_diff_spec.rb
index 8571e85627c..f3e148f95f0 100644
--- a/spec/models/concerns/discussion_on_diff_spec.rb
+++ b/spec/models/concerns/discussion_on_diff_spec.rb
@@ -21,4 +21,30 @@ describe DiscussionOnDiff, model: true do
end
end
end
+
+ describe '#line_code_in_diffs' do
+ context 'when the discussion is active in the diff' do
+ let(:diff_refs) { subject.position.diff_refs }
+
+ it 'returns the current line code' do
+ expect(subject.line_code_in_diffs(diff_refs)).to eq(subject.line_code)
+ end
+ end
+
+ context 'when the discussion was created in the diff' do
+ let(:diff_refs) { subject.original_position.diff_refs }
+
+ it 'returns the original line code' do
+ expect(subject.line_code_in_diffs(diff_refs)).to eq(subject.original_line_code)
+ end
+ end
+
+ context 'when the discussion is unrelated to the diff' do
+ let(:diff_refs) { subject.project.commit(RepoHelpers.sample_commit.id).diff_refs }
+
+ it 'returns nil' do
+ expect(subject.line_code_in_diffs(diff_refs)).to be_nil
+ end
+ end
+ end
end
diff --git a/spec/models/cycle_analytics/test_spec.rb b/spec/models/cycle_analytics/test_spec.rb
index d0b919efcf9..fd58bd1d6ad 100644
--- a/spec/models/cycle_analytics/test_spec.rb
+++ b/spec/models/cycle_analytics/test_spec.rb
@@ -13,8 +13,7 @@ describe 'CycleAnalytics#test', feature: true do
data_fn: lambda do |context|
issue = context.create(:issue, project: context.project)
merge_request = context.create_merge_request_closing_issue(issue)
- pipeline = context.create(:ci_pipeline, ref: merge_request.source_branch, sha: merge_request.diff_head_sha, project: context.project)
- merge_request.update(head_pipeline: pipeline)
+ pipeline = context.create(:ci_pipeline, ref: merge_request.source_branch, sha: merge_request.diff_head_sha, project: context.project, head_pipeline_of: merge_request)
{ pipeline: pipeline, issue: issue }
end,
start_time_conditions: [["pipeline is started", -> (context, data) { data[:pipeline].run! }]],
diff --git a/spec/models/diff_discussion_spec.rb b/spec/models/diff_discussion_spec.rb
index 81f338745b1..45b2f6e4beb 100644
--- a/spec/models/diff_discussion_spec.rb
+++ b/spec/models/diff_discussion_spec.rb
@@ -48,7 +48,7 @@ describe DiffDiscussion, model: true do
end
it 'returns the diff ID for the version to show' do
- expect(diff_id: merge_request_diff1.id)
+ expect(subject.merge_request_version_params).to eq(diff_id: merge_request_diff1.id)
end
end
@@ -65,6 +65,11 @@ describe DiffDiscussion, model: true do
let(:diff_note) { create(:diff_note_on_merge_request, noteable: merge_request, project: project, position: position) }
+ before do
+ diff_note.position = diff_note.original_position
+ diff_note.save!
+ end
+
it 'returns the diff ID and start sha of the versions to compare' do
expect(subject.merge_request_version_params).to eq(diff_id: merge_request_diff3.id, start_sha: merge_request_diff1.head_commit_sha)
end
diff --git a/spec/models/merge_request_spec.rb b/spec/models/merge_request_spec.rb
index ce870fcc1d3..da915c49d3c 100644
--- a/spec/models/merge_request_spec.rb
+++ b/spec/models/merge_request_spec.rb
@@ -718,8 +718,7 @@ describe MergeRequest, models: true do
describe '#head_pipeline' do
describe 'when the source project exists' do
it 'returns the latest pipeline' do
- pipeline = create(:ci_empty_pipeline, project: subject.source_project, ref: 'master', status: 'running', sha: "123abc")
- subject.update(head_pipeline: pipeline)
+ pipeline = create(:ci_empty_pipeline, project: subject.source_project, ref: 'master', status: 'running', sha: "123abc", head_pipeline_of: subject)
expect(subject.head_pipeline).to eq(pipeline)
end
@@ -1214,7 +1213,7 @@ describe MergeRequest, models: true do
expect(Notes::DiffPositionUpdateService).to receive(:new).with(
subject.project,
- nil,
+ subject.author,
old_diff_refs: old_diff_refs,
new_diff_refs: commit.diff_refs,
paths: note.position.paths
@@ -1223,7 +1222,7 @@ describe MergeRequest, models: true do
expect_any_instance_of(Notes::DiffPositionUpdateService).to receive(:execute).with(note)
expect_any_instance_of(DiffNote).to receive(:save).once
- subject.reload_diff
+ subject.reload_diff(subject.author)
end
end
@@ -1396,9 +1395,8 @@ describe MergeRequest, models: true do
project: project,
ref: merge_request.source_branch,
sha: merge_request.diff_head_sha,
- status: status)
-
- merge_request.update(head_pipeline: pipeline)
+ status: status,
+ head_pipeline_of: merge_request)
pipeline
end
@@ -1536,4 +1534,36 @@ describe MergeRequest, models: true do
end
end
end
+
+ describe '#version_params_for' do
+ subject { create(:merge_request, importing: true) }
+ let(:project) { subject.project }
+ let!(:merge_request_diff1) { subject.merge_request_diffs.create(head_commit_sha: '6f6d7e7ed97bb5f0054f2b1df789b39ca89b6ff9') }
+ let!(:merge_request_diff2) { subject.merge_request_diffs.create(head_commit_sha: nil) }
+ let!(:merge_request_diff3) { subject.merge_request_diffs.create(head_commit_sha: '5937ac0a7beb003549fc5fd26fc247adbce4a52e') }
+
+ context 'when the diff refs are for an older merge request version' do
+ let(:diff_refs) { merge_request_diff1.diff_refs }
+
+ it 'returns the diff ID for the version to show' do
+ expect(subject.version_params_for(diff_refs)).to eq(diff_id: merge_request_diff1.id)
+ end
+ end
+
+ context 'when the diff refs are for a comparison between merge request versions' do
+ let(:diff_refs) { merge_request_diff3.compare_with(merge_request_diff1.head_commit_sha).diff_refs }
+
+ it 'returns the diff ID and start sha of the versions to compare' do
+ expect(subject.version_params_for(diff_refs)).to eq(diff_id: merge_request_diff3.id, start_sha: merge_request_diff1.head_commit_sha)
+ end
+ end
+
+ context 'when the diff refs are not for a merge request version' do
+ let(:diff_refs) { project.commit(sample_commit.id).diff_refs }
+
+ it 'returns nil' do
+ expect(subject.version_params_for(diff_refs)).to be_nil
+ end
+ end
+ end
end
diff --git a/spec/models/namespace_spec.rb b/spec/models/namespace_spec.rb
index 8624616316c..312302afdbb 100644
--- a/spec/models/namespace_spec.rb
+++ b/spec/models/namespace_spec.rb
@@ -37,7 +37,7 @@ describe Namespace, models: true do
it 'rejects nested paths' do
parent = create(:group, :nested, path: 'environments')
- namespace = build(:project, path: 'folders', namespace: parent)
+ namespace = build(:group, path: 'folders', parent: parent)
expect(namespace).not_to be_valid
end
diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb
index 6a15830a15c..aabdac4bb75 100644
--- a/spec/models/user_spec.rb
+++ b/spec/models/user_spec.rb
@@ -440,6 +440,22 @@ describe User, models: true do
end
end
+ describe 'ensure incoming email token' do
+ it 'has incoming email token' do
+ user = create(:user)
+ expect(user.incoming_email_token).not_to be_blank
+ end
+ end
+
+ describe 'rss token' do
+ it 'ensures an rss token on read' do
+ user = create(:user, rss_token: nil)
+ rss_token = user.rss_token
+ expect(rss_token).not_to be_blank
+ expect(user.reload.rss_token).to eq rss_token
+ end
+ end
+
describe '#recently_sent_password_reset?' do
it 'is false when reset_password_sent_at is nil' do
user = build_stubbed(:user, reset_password_sent_at: nil)
diff --git a/spec/requests/api/internal_spec.rb b/spec/requests/api/internal_spec.rb
index 2ceb4648ece..cf232e7ff69 100644
--- a/spec/requests/api/internal_spec.rb
+++ b/spec/requests/api/internal_spec.rb
@@ -466,86 +466,87 @@ describe API::Internal do
end
end
- describe 'POST /notify_post_receive' do
- let(:valid_params) do
- { project: project.repository.path, secret_token: secret_token }
- end
-
- let(:valid_wiki_params) do
- { project: project.wiki.repository.path, secret_token: secret_token }
- end
-
- before do
- allow(Gitlab.config.gitaly).to receive(:enabled).and_return(true)
- end
-
- it "calls the Gitaly client with the project's repository" do
- expect(Gitlab::GitalyClient::Notifications).
- to receive(:new).with(gitlab_git_repository_with(path: project.repository.path)).
- and_call_original
- expect_any_instance_of(Gitlab::GitalyClient::Notifications).
- to receive(:post_receive)
-
- post api("/internal/notify_post_receive"), valid_params
-
- expect(response).to have_http_status(200)
- end
-
- it "calls the Gitaly client with the wiki's repository if it's a wiki" do
- expect(Gitlab::GitalyClient::Notifications).
- to receive(:new).with(gitlab_git_repository_with(path: project.wiki.repository.path)).
- and_call_original
- expect_any_instance_of(Gitlab::GitalyClient::Notifications).
- to receive(:post_receive)
-
- post api("/internal/notify_post_receive"), valid_wiki_params
-
- expect(response).to have_http_status(200)
- end
-
- it "returns 500 if the gitaly call fails" do
- expect_any_instance_of(Gitlab::GitalyClient::Notifications).
- to receive(:post_receive).and_raise(GRPC::Unavailable)
-
- post api("/internal/notify_post_receive"), valid_params
-
- expect(response).to have_http_status(500)
- end
-
- context 'with a gl_repository parameter' do
- let(:valid_params) do
- { gl_repository: "project-#{project.id}", secret_token: secret_token }
- end
-
- let(:valid_wiki_params) do
- { gl_repository: "wiki-#{project.id}", secret_token: secret_token }
- end
-
- it "calls the Gitaly client with the project's repository" do
- expect(Gitlab::GitalyClient::Notifications).
- to receive(:new).with(gitlab_git_repository_with(path: project.repository.path)).
- and_call_original
- expect_any_instance_of(Gitlab::GitalyClient::Notifications).
- to receive(:post_receive)
-
- post api("/internal/notify_post_receive"), valid_params
-
- expect(response).to have_http_status(200)
- end
-
- it "calls the Gitaly client with the wiki's repository if it's a wiki" do
- expect(Gitlab::GitalyClient::Notifications).
- to receive(:new).with(gitlab_git_repository_with(path: project.wiki.repository.path)).
- and_call_original
- expect_any_instance_of(Gitlab::GitalyClient::Notifications).
- to receive(:post_receive)
-
- post api("/internal/notify_post_receive"), valid_wiki_params
-
- expect(response).to have_http_status(200)
- end
- end
- end
+ # TODO: Uncomment when the end-point is reenabled
+ # describe 'POST /notify_post_receive' do
+ # let(:valid_params) do
+ # { project: project.repository.path, secret_token: secret_token }
+ # end
+ #
+ # let(:valid_wiki_params) do
+ # { project: project.wiki.repository.path, secret_token: secret_token }
+ # end
+ #
+ # before do
+ # allow(Gitlab.config.gitaly).to receive(:enabled).and_return(true)
+ # end
+ #
+ # it "calls the Gitaly client with the project's repository" do
+ # expect(Gitlab::GitalyClient::Notifications).
+ # to receive(:new).with(gitlab_git_repository_with(path: project.repository.path)).
+ # and_call_original
+ # expect_any_instance_of(Gitlab::GitalyClient::Notifications).
+ # to receive(:post_receive)
+ #
+ # post api("/internal/notify_post_receive"), valid_params
+ #
+ # expect(response).to have_http_status(200)
+ # end
+ #
+ # it "calls the Gitaly client with the wiki's repository if it's a wiki" do
+ # expect(Gitlab::GitalyClient::Notifications).
+ # to receive(:new).with(gitlab_git_repository_with(path: project.wiki.repository.path)).
+ # and_call_original
+ # expect_any_instance_of(Gitlab::GitalyClient::Notifications).
+ # to receive(:post_receive)
+ #
+ # post api("/internal/notify_post_receive"), valid_wiki_params
+ #
+ # expect(response).to have_http_status(200)
+ # end
+ #
+ # it "returns 500 if the gitaly call fails" do
+ # expect_any_instance_of(Gitlab::GitalyClient::Notifications).
+ # to receive(:post_receive).and_raise(GRPC::Unavailable)
+ #
+ # post api("/internal/notify_post_receive"), valid_params
+ #
+ # expect(response).to have_http_status(500)
+ # end
+ #
+ # context 'with a gl_repository parameter' do
+ # let(:valid_params) do
+ # { gl_repository: "project-#{project.id}", secret_token: secret_token }
+ # end
+ #
+ # let(:valid_wiki_params) do
+ # { gl_repository: "wiki-#{project.id}", secret_token: secret_token }
+ # end
+ #
+ # it "calls the Gitaly client with the project's repository" do
+ # expect(Gitlab::GitalyClient::Notifications).
+ # to receive(:new).with(gitlab_git_repository_with(path: project.repository.path)).
+ # and_call_original
+ # expect_any_instance_of(Gitlab::GitalyClient::Notifications).
+ # to receive(:post_receive)
+ #
+ # post api("/internal/notify_post_receive"), valid_params
+ #
+ # expect(response).to have_http_status(200)
+ # end
+ #
+ # it "calls the Gitaly client with the wiki's repository if it's a wiki" do
+ # expect(Gitlab::GitalyClient::Notifications).
+ # to receive(:new).with(gitlab_git_repository_with(path: project.wiki.repository.path)).
+ # and_call_original
+ # expect_any_instance_of(Gitlab::GitalyClient::Notifications).
+ # to receive(:post_receive)
+ #
+ # post api("/internal/notify_post_receive"), valid_wiki_params
+ #
+ # expect(response).to have_http_status(200)
+ # end
+ # end
+ # end
def project_with_repo_path(path)
double().tap do |fake_project|
diff --git a/spec/requests/projects/cycle_analytics_events_spec.rb b/spec/requests/projects/cycle_analytics_events_spec.rb
index d92daa345b3..d4d3c9478a0 100644
--- a/spec/requests/projects/cycle_analytics_events_spec.rb
+++ b/spec/requests/projects/cycle_analytics_events_spec.rb
@@ -121,8 +121,7 @@ describe 'cycle analytics events', api: true do
issue.update(milestone: milestone)
mr = create_merge_request_closing_issue(issue, commit_message: "References #{issue.to_reference}")
- pipeline = create(:ci_empty_pipeline, status: 'created', project: project, ref: mr.source_branch, sha: mr.source_branch_sha)
- mr.update(head_pipeline_id: pipeline.id)
+ pipeline = create(:ci_empty_pipeline, status: 'created', project: project, ref: mr.source_branch, sha: mr.source_branch_sha, head_pipeline_of: mr)
pipeline.run
create(:ci_build, pipeline: pipeline, status: :success, author: user)
diff --git a/spec/routing/project_routing_spec.rb b/spec/routing/project_routing_spec.rb
index d5400bbaaf1..a391c046f92 100644
--- a/spec/routing/project_routing_spec.rb
+++ b/spec/routing/project_routing_spec.rb
@@ -462,6 +462,8 @@ describe 'project routing' do
expect(get('/gitlab/gitlabhq/blob/master/app/models/compare.rb')).to route_to('projects/blob#show', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'master/app/models/compare.rb')
expect(get('/gitlab/gitlabhq/blob/master/app/models/diff.js')).to route_to('projects/blob#show', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'master/app/models/diff.js')
expect(get('/gitlab/gitlabhq/blob/master/files.scss')).to route_to('projects/blob#show', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'master/files.scss')
+ expect(get('/gitlab/gitlabhq/blob/master/blob/index.js')).to route_to('projects/blob#show', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'master/blob/index.js')
+ expect(get('/gitlab/gitlabhq/blob/blob/master/blob/index.js')).to route_to('projects/blob#show', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'blob/master/blob/index.js')
end
end
@@ -470,6 +472,8 @@ describe 'project routing' do
it 'to #show' do
expect(get('/gitlab/gitlabhq/tree/master/app/models/project.rb')).to route_to('projects/tree#show', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'master/app/models/project.rb')
expect(get('/gitlab/gitlabhq/tree/master/files.scss')).to route_to('projects/tree#show', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'master/files.scss')
+ expect(get('/gitlab/gitlabhq/tree/master/tree/files')).to route_to('projects/tree#show', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'master/tree/files')
+ expect(get('/gitlab/gitlabhq/tree/tree/master/tree/files')).to route_to('projects/tree#show', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'tree/master/tree/files')
end
end
diff --git a/spec/routing/routing_spec.rb b/spec/routing/routing_spec.rb
index abacc50a371..a62af13cf0c 100644
--- a/spec/routing/routing_spec.rb
+++ b/spec/routing/routing_spec.rb
@@ -151,6 +151,10 @@ describe ProfilesController, "routing" do
expect(put("/profile/reset_private_token")).to route_to('profiles#reset_private_token')
end
+ it "to #reset_rss_token" do
+ expect(put("/profile/reset_rss_token")).to route_to('profiles#reset_rss_token')
+ end
+
it "to #show" do
expect(get("/profile")).to route_to('profiles#show')
end
diff --git a/spec/services/merge_requests/merge_when_pipeline_succeeds_service_spec.rb b/spec/services/merge_requests/merge_when_pipeline_succeeds_service_spec.rb
index 3ef5135e6a3..f17db70faf6 100644
--- a/spec/services/merge_requests/merge_when_pipeline_succeeds_service_spec.rb
+++ b/spec/services/merge_requests/merge_when_pipeline_succeeds_service_spec.rb
@@ -79,11 +79,8 @@ describe MergeRequests::MergeWhenPipelineSucceedsService do
context 'when triggered by pipeline with valid ref and sha' do
let(:triggering_pipeline) do
create(:ci_pipeline, project: project, ref: merge_request_ref,
- sha: merge_request_head, status: 'success')
- end
-
- before do
- mr_merge_if_green_enabled.update(head_pipeline: triggering_pipeline)
+ sha: merge_request_head, status: 'success',
+ head_pipeline_of: mr_merge_if_green_enabled)
end
it "merges all merge requests with merge when the pipeline succeeds enabled" do
@@ -125,11 +122,10 @@ describe MergeRequests::MergeWhenPipelineSucceedsService do
let(:conflict_pipeline) do
create(:ci_pipeline, project: project, ref: mr_conflict.source_branch,
- sha: mr_conflict.diff_head_sha, status: 'success')
+ sha: mr_conflict.diff_head_sha, status: 'success',
+ head_pipeline_of: mr_conflict)
end
- before { mr_conflict.update(head_pipeline: conflict_pipeline) }
-
it 'does not merge the merge request' do
expect(MergeWorker).not_to receive(:perform_async)
diff --git a/spec/services/merge_requests/update_service_spec.rb b/spec/services/merge_requests/update_service_spec.rb
index 860a7798857..d371fc68312 100644
--- a/spec/services/merge_requests/update_service_spec.rb
+++ b/spec/services/merge_requests/update_service_spec.rb
@@ -180,12 +180,13 @@ describe MergeRequests::UpdateService, services: true do
context 'with active pipeline' do
before do
service_mock = double
- pipeline = create(:ci_pipeline_with_one_job,
+ create(
+ :ci_pipeline_with_one_job,
project: project,
- ref: merge_request.source_branch,
- sha: merge_request.diff_head_sha)
-
- merge_request.update(head_pipeline: pipeline)
+ ref: merge_request.source_branch,
+ sha: merge_request.diff_head_sha,
+ head_pipeline_of: merge_request
+ )
expect(MergeRequests::MergeWhenPipelineSucceedsService).to receive(:new).with(project, user).
and_return(service_mock)
diff --git a/spec/services/notes/diff_position_update_service_spec.rb b/spec/services/notes/diff_position_update_service_spec.rb
index d73ae51fbc3..380c296fd3a 100644
--- a/spec/services/notes/diff_position_update_service_spec.rb
+++ b/spec/services/notes/diff_position_update_service_spec.rb
@@ -2,6 +2,7 @@ require 'spec_helper'
describe Notes::DiffPositionUpdateService, services: true do
let(:project) { create(:project, :repository) }
+ let(:current_user) { project.owner }
let(:create_commit) { project.commit("913c66a37b4a45b9769037c55c2d238bd0942d2e") }
let(:modify_commit) { project.commit("874797c3a73b60d2187ed6e2fcabd289ff75171e") }
let(:edit_commit) { project.commit("570e7b2abdd848b95f2f578043fc23bd6f6fd24d") }
@@ -25,7 +26,7 @@ describe Notes::DiffPositionUpdateService, services: true do
subject do
described_class.new(
project,
- nil,
+ current_user,
old_diff_refs: old_diff_refs,
new_diff_refs: new_diff_refs,
paths: [path]
@@ -170,6 +171,23 @@ describe Notes::DiffPositionUpdateService, services: true do
expect(note.original_position).to eq(old_position)
expect(note.position).to eq(old_position)
end
+
+ it 'sets the change position' do
+ subject.execute(note)
+
+ change_position = note.change_position
+ expect(change_position.start_sha).to eq(old_diff_refs.head_sha)
+ expect(change_position.head_sha).to eq(new_diff_refs.head_sha)
+ expect(change_position.old_line).to eq(9)
+ expect(change_position.new_line).to be_nil
+ end
+
+ it 'creates a system note' do
+ expect(SystemNoteService).to receive(:diff_discussion_outdated).with(
+ note.to_discussion, project, current_user, instance_of(Gitlab::Diff::Position))
+
+ subject.execute(note)
+ end
end
end
end
diff --git a/spec/services/projects/propagate_service_template_spec.rb b/spec/services/projects/propagate_service_template_spec.rb
index 90eff3bbc1e..8a6a9f09f74 100644
--- a/spec/services/projects/propagate_service_template_spec.rb
+++ b/spec/services/projects/propagate_service_template_spec.rb
@@ -71,14 +71,18 @@ describe Projects::PropagateServiceTemplate, services: true do
end
describe 'bulk update' do
- it 'creates services for all projects' do
- project_total = 5
+ let(:project_total) { 5 }
+
+ before do
stub_const 'Projects::PropagateServiceTemplate::BATCH_SIZE', 3
project_total.times { create(:empty_project) }
- expect { described_class.propagate(service_template) }.
- to change { Service.count }.by(project_total + 1)
+ described_class.propagate(service_template)
+ end
+
+ it 'creates services for all projects' do
+ expect(Service.all.reload.count).to eq(project_total + 2)
end
end
diff --git a/spec/services/projects/transfer_service_spec.rb b/spec/services/projects/transfer_service_spec.rb
index 29ccce59c53..b957517c715 100644
--- a/spec/services/projects/transfer_service_spec.rb
+++ b/spec/services/projects/transfer_service_spec.rb
@@ -26,6 +26,7 @@ describe Projects::TransferService, services: true do
it { expect(@result).to eq false }
it { expect(project.namespace).to eq(user.namespace) }
+ it { expect(project.errors.messages[:new_namespace].first).to eq 'Please select a new namespace for your project.' }
end
context 'disallow transfering of project with tags' do
diff --git a/spec/services/system_note_service_spec.rb b/spec/services/system_note_service_spec.rb
index 7a9cd7553b1..5a7cfaff7fb 100644
--- a/spec/services/system_note_service_spec.rb
+++ b/spec/services/system_note_service_spec.rb
@@ -1034,4 +1034,35 @@ describe SystemNoteService, services: true do
expect(subject.note).to eq 'resolved all discussions'
end
end
+
+ describe '.diff_discussion_outdated' do
+ let(:discussion) { create(:diff_note_on_merge_request).to_discussion }
+ let(:merge_request) { discussion.noteable }
+ let(:project) { merge_request.source_project }
+ let(:change_position) { discussion.position }
+
+ def reloaded_merge_request
+ MergeRequest.find(merge_request.id)
+ end
+
+ subject { described_class.diff_discussion_outdated(discussion, project, author, change_position) }
+
+ it_behaves_like 'a system note' do
+ let(:expected_noteable) { discussion.first_note.noteable }
+ let(:action) { 'outdated' }
+ end
+
+ it 'creates a new note in the discussion' do
+ # we need to completely rebuild the merge request object, or the `@discussions` on the merge request are not reloaded.
+ expect { subject }.to change { reloaded_merge_request.discussions.first.notes.size }.by(1)
+ end
+
+ it 'links to the diff in the system note' do
+ expect(subject.note).to include('version 1')
+
+ diff_id = merge_request.merge_request_diff.id
+ line_code = change_position.line_code(project.repository)
+ expect(subject.note).to include(diffs_namespace_project_merge_request_url(project.namespace, project, merge_request, diff_id: diff_id, anchor: line_code))
+ end
+ end
end
diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb
index a58f4e664b7..51571ddebe9 100644
--- a/spec/spec_helper.rb
+++ b/spec/spec_helper.rb
@@ -44,7 +44,6 @@ RSpec.configure do |config|
config.include LoginHelpers, type: :feature
config.include SearchHelpers, type: :feature
config.include WaitForRequests, :js
- config.include WaitForAjax, :js
config.include StubConfiguration
config.include EmailHelpers, type: :mailer
config.include TestEnv
diff --git a/spec/support/features/issuable_slash_commands_shared_examples.rb b/spec/support/features/issuable_slash_commands_shared_examples.rb
index ad46b163cd6..fa82dc5e9f9 100644
--- a/spec/support/features/issuable_slash_commands_shared_examples.rb
+++ b/spec/support/features/issuable_slash_commands_shared_examples.rb
@@ -22,7 +22,7 @@ shared_examples 'issuable record that supports slash commands in its description
after do
# Ensure all outstanding Ajax requests are complete to avoid database deadlocks
- wait_for_ajax
+ wait_for_requests
end
describe "new #{issuable_type}", js: true do
@@ -58,7 +58,7 @@ shared_examples 'issuable record that supports slash commands in its description
expect(page).not_to have_content '/label ~bug'
expect(page).not_to have_content '/milestone %"ASAP"'
- wait_for_ajax
+ wait_for_requests
issuable.reload
note = issuable.notes.user.first
diff --git a/spec/support/features/rss_shared_examples.rb b/spec/support/features/rss_shared_examples.rb
index 9a3b0a731ad..1cbb4134995 100644
--- a/spec/support/features/rss_shared_examples.rb
+++ b/spec/support/features/rss_shared_examples.rb
@@ -1,23 +1,23 @@
-shared_examples "an autodiscoverable RSS feed with current_user's private token" do
- it "has an RSS autodiscovery link tag with current_user's private token" do
- expect(page).to have_css("link[type*='atom+xml'][href*='private_token=#{Thread.current[:current_user].private_token}']", visible: false)
+shared_examples "an autodiscoverable RSS feed with current_user's RSS token" do
+ it "has an RSS autodiscovery link tag with current_user's RSS token" do
+ expect(page).to have_css("link[type*='atom+xml'][href*='rss_token=#{Thread.current[:current_user].rss_token}']", visible: false)
end
end
-shared_examples "it has an RSS button with current_user's private token" do
- it "shows the RSS button with current_user's private token" do
- expect(page).to have_css("a:has(.fa-rss)[href*='private_token=#{Thread.current[:current_user].private_token}']")
+shared_examples "it has an RSS button with current_user's RSS token" do
+ it "shows the RSS button with current_user's RSS token" do
+ expect(page).to have_css("a:has(.fa-rss)[href*='rss_token=#{Thread.current[:current_user].rss_token}']")
end
end
-shared_examples "an autodiscoverable RSS feed without a private token" do
- it "has an RSS autodiscovery link tag without a private token" do
- expect(page).to have_css("link[type*='atom+xml']:not([href*='private_token'])", visible: false)
+shared_examples "an autodiscoverable RSS feed without an RSS token" do
+ it "has an RSS autodiscovery link tag without an RSS token" do
+ expect(page).to have_css("link[type*='atom+xml']:not([href*='rss_token'])", visible: false)
end
end
-shared_examples "it has an RSS button without a private token" do
- it "shows the RSS button without a private token" do
- expect(page).to have_css("a:has(.fa-rss):not([href*='private_token'])")
+shared_examples "it has an RSS button without an RSS token" do
+ it "shows the RSS button without an RSS token" do
+ expect(page).to have_css("a:has(.fa-rss):not([href*='rss_token'])")
end
end
diff --git a/spec/support/protected_branches/access_control_ce_shared_examples.rb b/spec/support/protected_branches/access_control_ce_shared_examples.rb
index 7fda4ade665..287d6bb13c3 100644
--- a/spec/support/protected_branches/access_control_ce_shared_examples.rb
+++ b/spec/support/protected_branches/access_control_ce_shared_examples.rb
@@ -38,7 +38,7 @@ RSpec.shared_examples "protected branches > access control > CE" do
end
end
- wait_for_ajax
+ wait_for_requests
expect(ProtectedBranch.last.push_access_levels.map(&:access_level)).to include(access_type_id)
end
@@ -83,7 +83,7 @@ RSpec.shared_examples "protected branches > access control > CE" do
end
end
- wait_for_ajax
+ wait_for_requests
expect(ProtectedBranch.last.merge_access_levels.map(&:access_level)).to include(access_type_id)
end
diff --git a/spec/support/protected_tags/access_control_ce_shared_examples.rb b/spec/support/protected_tags/access_control_ce_shared_examples.rb
index 12622cd548a..1d11512ef82 100644
--- a/spec/support/protected_tags/access_control_ce_shared_examples.rb
+++ b/spec/support/protected_tags/access_control_ce_shared_examples.rb
@@ -39,7 +39,7 @@ RSpec.shared_examples "protected tags > access control > CE" do
end
end
- wait_for_ajax
+ wait_for_requests
expect(ProtectedTag.last.create_access_levels.map(&:access_level)).to include(access_type_id)
end
diff --git a/spec/support/snippets_shared_examples.rb b/spec/support/snippets_shared_examples.rb
index 57dfff3471f..85f0facd5c3 100644
--- a/spec/support/snippets_shared_examples.rb
+++ b/spec/support/snippets_shared_examples.rb
@@ -7,7 +7,7 @@ RSpec.shared_examples 'paginated snippets' do |remote: false|
context 'clicking on the link to the second page' do
before do
click_link('2')
- wait_for_ajax if remote
+ wait_for_requests if remote
end
it 'shows the remaining snippets' do
diff --git a/spec/support/target_branch_helpers.rb b/spec/support/target_branch_helpers.rb
index 3ee8f0f657e..01d1c53fe6c 100644
--- a/spec/support/target_branch_helpers.rb
+++ b/spec/support/target_branch_helpers.rb
@@ -1,7 +1,7 @@
module TargetBranchHelpers
def select_branch(name)
first('button.js-target-branch').click
- wait_for_ajax
+ wait_for_requests
all('a[data-group="Branches"]').find do |el|
el.text == name
end.click
diff --git a/spec/support/time_tracking_shared_examples.rb b/spec/support/time_tracking_shared_examples.rb
index 84ef46ffa27..b407b8097d2 100644
--- a/spec/support/time_tracking_shared_examples.rb
+++ b/spec/support/time_tracking_shared_examples.rb
@@ -8,7 +8,7 @@ shared_examples 'issuable time tracker' do
it 'updates the sidebar component when estimate is added' do
submit_time('/estimate 3w 1d 1h')
- wait_for_ajax
+ wait_for_requests
page.within '.time-tracking-estimate-only-pane' do
expect(page).to have_content '3w 1d 1h'
end
@@ -17,7 +17,7 @@ shared_examples 'issuable time tracker' do
it 'updates the sidebar component when spent is added' do
submit_time('/spend 3w 1d 1h')
- wait_for_ajax
+ wait_for_requests
page.within '.time-tracking-spend-only-pane' do
expect(page).to have_content '3w 1d 1h'
end
@@ -27,7 +27,7 @@ shared_examples 'issuable time tracker' do
submit_time('/estimate 3w 1d 1h')
submit_time('/spend 3w 1d 1h')
- wait_for_ajax
+ wait_for_requests
page.within '.time-tracking-comparison-pane' do
expect(page).to have_content '3w 1d 1h'
end
@@ -81,5 +81,5 @@ end
def submit_time(slash_command)
fill_in 'note[note]', with: slash_command
find('.js-comment-submit-button').trigger('click')
- wait_for_ajax
+ wait_for_requests
end
diff --git a/spec/support/wait_for_ajax.rb b/spec/support/wait_for_ajax.rb
deleted file mode 100644
index 508de2ee8e1..00000000000
--- a/spec/support/wait_for_ajax.rb
+++ /dev/null
@@ -1,18 +0,0 @@
-module WaitForAjax
- def wait_for_ajax
- Timeout.timeout(Capybara.default_max_wait_time) do
- loop until finished_all_ajax_requests?
- end
- end
-
- def finished_all_ajax_requests?
- return true unless javascript_test?
- return true if page.evaluate_script('typeof jQuery === "undefined"')
-
- page.evaluate_script('jQuery.active').zero?
- end
-
- def javascript_test?
- Capybara.current_driver == Capybara.javascript_driver
- end
-end
diff --git a/spec/support/wait_for_requests.rb b/spec/support/wait_for_requests.rb
index d41e83ae128..05ec9026141 100644
--- a/spec/support/wait_for_requests.rb
+++ b/spec/support/wait_for_requests.rb
@@ -1,21 +1,31 @@
-require_relative './wait_for_ajax'
-require_relative './wait_for_vue_resource'
+require_relative './wait_for_requests'
module WaitForRequests
extend self
- include WaitForAjax
- include WaitForVueResource
# This is inspired by http://www.salsify.com/blog/engineering/tearing-capybara-ajax-tests
- def wait_for_requests_complete
+ def block_and_wait_for_requests_complete
Gitlab::Testing::RequestBlockerMiddleware.block_requests!
- wait_for('pending AJAX requests complete') do
+ wait_for('pending requests complete') do
Gitlab::Testing::RequestBlockerMiddleware.num_active_requests.zero?
end
ensure
Gitlab::Testing::RequestBlockerMiddleware.allow_requests!
end
+ def wait_for_requests
+ wait_for('JS requests') { finished_all_requests? }
+ end
+
+ private
+
+ def finished_all_requests?
+ return true unless javascript_test?
+
+ finished_all_ajax_requests? &&
+ finished_all_vue_resource_requests?
+ end
+
# Waits until the passed block returns true
def wait_for(condition_name, max_wait_time: Capybara.default_max_wait_time, polling_interval: 0.01)
wait_until = Time.now + max_wait_time.seconds
@@ -28,10 +38,24 @@ module WaitForRequests
end
end
end
+
+ def finished_all_vue_resource_requests?
+ page.evaluate_script('window.activeVueResources || 0').zero?
+ end
+
+ def finished_all_ajax_requests?
+ return true if page.evaluate_script('typeof jQuery === "undefined"')
+
+ page.evaluate_script('jQuery.active').zero?
+ end
+
+ def javascript_test?
+ Capybara.current_driver == Capybara.javascript_driver
+ end
end
RSpec.configure do |config|
config.after(:each, :js) do
- wait_for_requests_complete
+ block_and_wait_for_requests_complete
end
end
diff --git a/spec/support/wait_for_vue_resource.rb b/spec/support/wait_for_vue_resource.rb
deleted file mode 100644
index 3bb3d9c2e51..00000000000
--- a/spec/support/wait_for_vue_resource.rb
+++ /dev/null
@@ -1,19 +0,0 @@
-module WaitForVueResource
- def wait_for_vue_resource(spinner: true)
- Timeout.timeout(Capybara.default_max_wait_time) do
- loop until finished_all_vue_resource_requests?
- end
- end
-
- private
-
- def finished_all_vue_resource_requests?
- return true unless javascript_test?
-
- page.evaluate_script('window.activeVueResources || 0').zero?
- end
-
- def javascript_test?
- Capybara.current_driver == Capybara.javascript_driver
- end
-end
diff --git a/spec/tasks/tokens_spec.rb b/spec/tasks/tokens_spec.rb
index 19036c7677c..b84137eb365 100644
--- a/spec/tasks/tokens_spec.rb
+++ b/spec/tasks/tokens_spec.rb
@@ -18,4 +18,10 @@ describe 'tokens rake tasks' do
expect { run_rake_task('tokens:reset_all_email') }.to change { user.reload.incoming_email_token }
end
end
+
+ describe 'reset_all_rss task' do
+ it 'invokes create_hooks task' do
+ expect { run_rake_task('tokens:reset_all_rss') }.to change { user.reload.rss_token }
+ end
+ end
end
diff --git a/spec/validators/dynamic_path_validator_spec.rb b/spec/validators/dynamic_path_validator_spec.rb
index b114bfc1bca..03e23781d1b 100644
--- a/spec/validators/dynamic_path_validator_spec.rb
+++ b/spec/validators/dynamic_path_validator_spec.rb
@@ -3,246 +3,46 @@ require 'spec_helper'
describe DynamicPathValidator do
let(:validator) { described_class.new(attributes: [:path]) }
- # Pass in a full path to remove the format segment:
- # `/ci/lint(.:format)` -> `/ci/lint`
- def without_format(path)
- path.split('(', 2)[0]
- end
-
- # Pass in a full path and get the last segment before a wildcard
- # That's not a parameter
- # `/*namespace_id/:project_id/builds/artifacts/*ref_name_and_path`
- # -> 'builds/artifacts'
- def path_before_wildcard(path)
- path = path.gsub(STARTING_WITH_NAMESPACE, "")
- path_segments = path.split('/').reject(&:empty?)
- wildcard_index = path_segments.index { |segment| parameter?(segment) }
-
- segments_before_wildcard = path_segments[0..wildcard_index - 1]
-
- segments_before_wildcard.join('/')
- end
-
- def parameter?(segment)
- segment =~ /[*:]/
- end
-
- # If the path is reserved. Then no conflicting paths can# be created for any
- # route using this reserved word.
- #
- # Both `builds/artifacts` & `build` are covered by reserving the word
- # `build`
- def wildcards_include?(path)
- described_class::WILDCARD_ROUTES.include?(path) ||
- described_class::WILDCARD_ROUTES.include?(path.split('/').first)
- end
-
- def failure_message(missing_words, constant_name, migration_helper)
- missing_words = Array(missing_words)
- <<-MSG
- Found new routes that could cause conflicts with existing namespaced routes
- for groups or projects.
+ describe '#path_valid_for_record?' do
+ context 'for project' do
+ it 'calls valid_project_path?' do
+ project = build(:project, path: 'activity')
- Add <#{missing_words.join(', ')}> to `DynamicPathValidator::#{constant_name}
- to make sure no projects or namespaces can be created with those paths.
+ expect(described_class).to receive(:valid_project_path?).with(project.full_path).and_call_original
- To rename any existing records with those paths you can use the
- `Gitlab::Database::RenameReservedpathsMigration::<VERSION>.#{migration_helper}`
- migration helper.
-
- Make sure to make a note of the renamed records in the release blog post.
-
- MSG
- end
-
- let(:all_routes) do
- Rails.application.routes.routes.routes.
- map { |r| r.path.spec.to_s }
- end
-
- let(:routes_without_format) { all_routes.map { |path| without_format(path) } }
-
- # Routes not starting with `/:` or `/*`
- # all routes not starting with a param
- let(:routes_not_starting_in_wildcard) { routes_without_format.select { |p| p !~ %r{^/[:*]} } }
-
- let(:top_level_words) do
- routes_not_starting_in_wildcard.map do |route|
- route.split('/')[1]
- end.compact.uniq
- end
-
- # All routes that start with a namespaced path, that have 1 or more
- # path-segments before having another wildcard parameter.
- # - Starting with paths:
- # - `/*namespace_id/:project_id/`
- # - `/*namespace_id/:id/`
- # - Followed by one or more path-parts not starting with `:` or `*`
- # - Followed by a path-part that includes a wildcard parameter `*`
- # At the time of writing these routes match: http://rubular.com/r/Rv2pDE5Dvw
- STARTING_WITH_NAMESPACE = %r{^/\*namespace_id/:(project_)?id}
- NON_PARAM_PARTS = %r{[^:*][a-z\-_/]*}
- ANY_OTHER_PATH_PART = %r{[a-z\-_/:]*}
- WILDCARD_SEGMENT = %r{\*}
- let(:namespaced_wildcard_routes) do
- routes_without_format.select do |p|
- p =~ %r{#{STARTING_WITH_NAMESPACE}/#{NON_PARAM_PARTS}/#{ANY_OTHER_PATH_PART}#{WILDCARD_SEGMENT}}
- end
- end
-
- # This will return all paths that are used in a namespaced route
- # before another wildcard path:
- #
- # /*namespace_id/:project_id/builds/artifacts/*ref_name_and_path
- # /*namespace_id/:project_id/info/lfs/objects/*oid
- # /*namespace_id/:project_id/commits/*id
- # /*namespace_id/:project_id/builds/:build_id/artifacts/file/*path
- # -> ['builds/artifacts', 'info/lfs/objects', 'commits', 'artifacts/file']
- let(:all_wildcard_paths) do
- namespaced_wildcard_routes.map do |route|
- path_before_wildcard(route)
- end.uniq
- end
-
- STARTING_WITH_GROUP = %r{^/groups/\*(group_)?id/}
- let(:group_routes) do
- routes_without_format.select do |path|
- path =~ STARTING_WITH_GROUP
- end
- end
-
- let(:paths_after_group_id) do
- group_routes.map do |route|
- route.gsub(STARTING_WITH_GROUP, '').split('/').first
- end.uniq
- end
-
- describe 'TOP_LEVEL_ROUTES' do
- it 'includes all the top level namespaces' do
- failure_block = lambda do
- missing_words = top_level_words - described_class::TOP_LEVEL_ROUTES
- failure_message(missing_words, 'TOP_LEVEL_ROUTES', 'rename_root_paths')
+ expect(validator.path_valid_for_record?(project, 'activity')).to be_truthy
end
-
- expect(described_class::TOP_LEVEL_ROUTES)
- .to include(*top_level_words), failure_block
end
- end
- describe 'GROUP_ROUTES' do
- it "don't contain a second wildcard" do
- failure_block = lambda do
- missing_words = paths_after_group_id - described_class::GROUP_ROUTES
- failure_message(missing_words, 'GROUP_ROUTES', 'rename_child_paths')
- end
+ context 'for group' do
+ it 'calls valid_namespace_path?' do
+ group = build(:group, :nested, path: 'activity')
- expect(described_class::GROUP_ROUTES)
- .to include(*paths_after_group_id), failure_block
- end
- end
+ expect(described_class).to receive(:valid_namespace_path?).with(group.full_path).and_call_original
- describe 'WILDCARD_ROUTES' do
- it 'includes all paths that can be used after a namespace/project path' do
- aggregate_failures do
- all_wildcard_paths.each do |path|
- expect(wildcards_include?(path))
- .to be(true), failure_message(path, 'WILDCARD_ROUTES', 'rename_wildcard_paths')
- end
+ expect(validator.path_valid_for_record?(group, 'activity')).to be_falsey
end
end
- end
- describe '.without_reserved_wildcard_paths_regex' do
- subject { described_class.without_reserved_wildcard_paths_regex }
+ context 'for user' do
+ it 'calls valid_namespace_path?' do
+ user = build(:user, username: 'activity')
- it 'rejects paths starting with a reserved top level' do
- expect(subject).not_to match('dashboard/hello/world')
- expect(subject).not_to match('dashboard')
- end
+ expect(described_class).to receive(:valid_namespace_path?).with(user.full_path).and_call_original
- it 'matches valid paths with a toplevel word in a different place' do
- expect(subject).to match('parent/dashboard/project-path')
- end
-
- it 'rejects paths containing a wildcard reserved word' do
- expect(subject).not_to match('hello/edit')
- expect(subject).not_to match('hello/edit/in-the-middle')
- expect(subject).not_to match('foo/bar1/refs/master/logs_tree')
- end
-
- it 'matches valid paths' do
- expect(subject).to match('parent/child/project-path')
- end
- end
-
- describe '.regex_excluding_child_paths' do
- let(:subject) { described_class.without_reserved_child_paths_regex }
-
- it 'rejects paths containing a child reserved word' do
- expect(subject).not_to match('hello/group_members')
- expect(subject).not_to match('hello/activity/in-the-middle')
- expect(subject).not_to match('foo/bar1/refs/master/logs_tree')
- end
-
- it 'allows a child path on the top level' do
- expect(subject).to match('activity/foo')
- expect(subject).to match('avatar')
- end
- end
-
- describe ".valid?" do
- it 'is not case sensitive' do
- expect(described_class.valid?("Users")).to be_falsey
- end
-
- it "isn't valid when the top level is reserved" do
- test_path = 'u/should-be-a/reserved-word'
-
- expect(described_class.valid?(test_path)).to be_falsey
- end
-
- it "isn't valid if any of the path segments is reserved" do
- test_path = 'the-wildcard/wikis/is-not-allowed'
-
- expect(described_class.valid?(test_path)).to be_falsey
- end
-
- it "is valid if the path doesn't contain reserved words" do
- test_path = 'there-are/no-wildcards/in-this-path'
-
- expect(described_class.valid?(test_path)).to be_truthy
- end
-
- it 'allows allows a child path on the last spot' do
- test_path = 'there/can-be-a/project-called/labels'
-
- expect(described_class.valid?(test_path)).to be_truthy
- end
-
- it 'rejects a child path somewhere else' do
- test_path = 'there/can-be-no/labels/group'
-
- expect(described_class.valid?(test_path)).to be_falsey
+ expect(validator.path_valid_for_record?(user, 'activity')).to be_truthy
+ end
end
- it 'rejects paths that are in an incorrect format' do
- test_path = 'incorrect/format.git'
-
- expect(described_class.valid?(test_path)).to be_falsey
- end
- end
+ context 'for user namespace' do
+ it 'calls valid_namespace_path?' do
+ user = create(:user, username: 'activity')
+ namespace = user.namespace
- describe '#path_reserved_for_record?' do
- it 'reserves a sub-group named activity' do
- group = build(:group, :nested, path: 'activity')
+ expect(described_class).to receive(:valid_namespace_path?).with(namespace.full_path).and_call_original
- expect(validator.path_reserved_for_record?(group, 'activity')).to be_truthy
- end
-
- it "doesn't reserve a project called activity" do
- project = build(:project, path: 'activity')
-
- expect(validator.path_reserved_for_record?(project, 'activity')).to be_falsey
+ expect(validator.path_valid_for_record?(namespace, 'activity')).to be_truthy
+ end
end
end
diff --git a/spec/workers/expire_job_cache_worker_spec.rb b/spec/workers/expire_job_cache_worker_spec.rb
new file mode 100644
index 00000000000..1b614342a18
--- /dev/null
+++ b/spec/workers/expire_job_cache_worker_spec.rb
@@ -0,0 +1,31 @@
+require 'spec_helper'
+
+describe ExpireJobCacheWorker do
+ set(:pipeline) { create(:ci_empty_pipeline) }
+ let(:project) { pipeline.project }
+ subject { described_class.new }
+
+ describe '#perform' do
+ context 'with a job in the pipeline' do
+ let(:job) { create(:ci_build, pipeline: pipeline) }
+
+ it 'invalidates Etag caching for the job path' do
+ pipeline_path = "/#{project.full_path}/pipelines/#{pipeline.id}.json"
+ job_path = "/#{project.full_path}/builds/#{job.id}.json"
+
+ expect_any_instance_of(Gitlab::EtagCaching::Store).to receive(:touch).with(pipeline_path)
+ expect_any_instance_of(Gitlab::EtagCaching::Store).to receive(:touch).with(job_path)
+
+ subject.perform(job.id)
+ end
+ end
+
+ context 'when there is no job in the pipeline' do
+ it 'does not change the etag store' do
+ expect(Gitlab::EtagCaching::Store).not_to receive(:new)
+
+ subject.perform(9999)
+ end
+ end
+ end
+end
diff --git a/spec/workers/expire_pipeline_cache_worker_spec.rb b/spec/workers/expire_pipeline_cache_worker_spec.rb
index ceba604dea2..28e5b706803 100644
--- a/spec/workers/expire_pipeline_cache_worker_spec.rb
+++ b/spec/workers/expire_pipeline_cache_worker_spec.rb
@@ -10,9 +10,11 @@ describe ExpirePipelineCacheWorker do
it 'invalidates Etag caching for project pipelines path' do
pipelines_path = "/#{project.full_path}/pipelines.json"
new_mr_pipelines_path = "/#{project.full_path}/merge_requests/new.json"
+ pipeline_path = "/#{project.full_path}/pipelines/#{pipeline.id}.json"
expect_any_instance_of(Gitlab::EtagCaching::Store).to receive(:touch).with(pipelines_path)
expect_any_instance_of(Gitlab::EtagCaching::Store).to receive(:touch).with(new_mr_pipelines_path)
+ expect_any_instance_of(Gitlab::EtagCaching::Store).to receive(:touch).with(pipeline_path)
subject.perform(pipeline.id)
end