Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/spec
diff options
context:
space:
mode:
authorKamil Trzciński <ayufan@ayufan.eu>2018-02-28 22:46:53 +0300
committerKamil Trzciński <ayufan@ayufan.eu>2018-02-28 22:46:53 +0300
commit45d2c31643017807cb3fc66c0be6e9cad9964faf (patch)
treeb3564188de7323969c6ba0a7ec4c86a3b6cce2ac /spec
parent87f11d2cf539d9539b439b54355f0dadaf4ebf76 (diff)
parent4b92efd90cedaa0aff218d11fdce279701128bea (diff)
Merge commit '4b92efd90cedaa0aff218d11fdce279701128bea' into object-storage-ee-to-ce-backport
Diffstat (limited to 'spec')
-rw-r--r--spec/controllers/admin/users_controller_spec.rb2
-rw-r--r--spec/controllers/boards/issues_controller_spec.rb6
-rw-r--r--spec/controllers/boards/lists_controller_spec.rb4
-rw-r--r--spec/controllers/dashboard/milestones_controller_spec.rb2
-rw-r--r--spec/controllers/dashboard/todos_controller_spec.rb2
-rw-r--r--spec/controllers/dashboard_controller_spec.rb2
-rw-r--r--spec/controllers/groups/group_members_controller_spec.rb19
-rw-r--r--spec/controllers/groups/milestones_controller_spec.rb2
-rw-r--r--spec/controllers/notification_settings_controller_spec.rb2
-rw-r--r--spec/controllers/projects/artifacts_controller_spec.rb2
-rw-r--r--spec/controllers/projects/avatars_controller_spec.rb2
-rw-r--r--spec/controllers/projects/blame_controller_spec.rb2
-rw-r--r--spec/controllers/projects/blob_controller_spec.rb8
-rw-r--r--spec/controllers/projects/boards_controller_spec.rb12
-rw-r--r--spec/controllers/projects/branches_controller_spec.rb18
-rw-r--r--spec/controllers/projects/clusters/applications_controller_spec.rb2
-rw-r--r--spec/controllers/projects/clusters/gcp_controller_spec.rb18
-rw-r--r--spec/controllers/projects/clusters_controller_spec.rb28
-rw-r--r--spec/controllers/projects/commits_controller_spec.rb2
-rw-r--r--spec/controllers/projects/compare_controller_spec.rb2
-rw-r--r--spec/controllers/projects/cycle_analytics_controller_spec.rb2
-rw-r--r--spec/controllers/projects/deploy_keys_controller_spec.rb4
-rw-r--r--spec/controllers/projects/deployments_controller_spec.rb2
-rw-r--r--spec/controllers/projects/discussions_controller_spec.rb4
-rw-r--r--spec/controllers/projects/find_file_controller_spec.rb2
-rw-r--r--spec/controllers/projects/forks_controller_spec.rb2
-rw-r--r--spec/controllers/projects/graphs_controller_spec.rb2
-rw-r--r--spec/controllers/projects/group_links_controller_spec.rb2
-rw-r--r--spec/controllers/projects/hooks_controller_spec.rb2
-rw-r--r--spec/controllers/projects/imports_controller_spec.rb4
-rw-r--r--spec/controllers/projects/issues_controller_spec.rb53
-rw-r--r--spec/controllers/projects/jobs_controller_spec.rb2
-rw-r--r--spec/controllers/projects/labels_controller_spec.rb2
-rw-r--r--spec/controllers/projects/mattermosts_controller_spec.rb2
-rw-r--r--spec/controllers/projects/merge_requests/creations_controller_spec.rb4
-rw-r--r--spec/controllers/projects/merge_requests/diffs_controller_spec.rb2
-rw-r--r--spec/controllers/projects/merge_requests_controller_spec.rb76
-rw-r--r--spec/controllers/projects/milestones_controller_spec.rb2
-rw-r--r--spec/controllers/projects/notes_controller_spec.rb14
-rw-r--r--spec/controllers/projects/pipeline_schedules_controller_spec.rb67
-rw-r--r--spec/controllers/projects/pipelines_controller_spec.rb13
-rw-r--r--spec/controllers/projects/project_members_controller_spec.rb46
-rw-r--r--spec/controllers/projects/refs_controller_spec.rb2
-rw-r--r--spec/controllers/projects/releases_controller_spec.rb2
-rw-r--r--spec/controllers/projects/repositories_controller_spec.rb2
-rw-r--r--spec/controllers/projects/services_controller_spec.rb38
-rw-r--r--spec/controllers/projects/settings/ci_cd_controller_spec.rb49
-rw-r--r--spec/controllers/projects/settings/integrations_controller_spec.rb2
-rw-r--r--spec/controllers/projects/templates_controller_spec.rb2
-rw-r--r--spec/controllers/projects/todos_controller_spec.rb4
-rw-r--r--spec/controllers/projects/tree_controller_spec.rb2
-rw-r--r--spec/controllers/projects/variables_controller_spec.rb2
-rw-r--r--spec/controllers/projects_controller_spec.rb6
-rw-r--r--spec/controllers/uploads_controller_spec.rb8
-rw-r--r--spec/controllers/users_controller_spec.rb4
-rw-r--r--spec/factories/abuse_reports.rb2
-rw-r--r--spec/factories/appearances.rb4
-rw-r--r--spec/factories/application_settings.rb2
-rw-r--r--spec/factories/award_emoji.rb2
-rw-r--r--spec/factories/boards.rb2
-rw-r--r--spec/factories/broadcast_messages.rb2
-rw-r--r--spec/factories/chat_names.rb2
-rw-r--r--spec/factories/chat_teams.rb2
-rw-r--r--spec/factories/ci/build_trace_section_names.rb2
-rw-r--r--spec/factories/ci/builds.rb27
-rw-r--r--spec/factories/ci/group_variables.rb2
-rw-r--r--spec/factories/ci/job_artifacts.rb2
-rw-r--r--spec/factories/ci/pipeline_schedule.rb2
-rw-r--r--spec/factories/ci/pipeline_schedule_variables.rb2
-rw-r--r--spec/factories/ci/pipeline_variables.rb2
-rw-r--r--spec/factories/ci/pipelines.rb2
-rw-r--r--spec/factories/ci/runner_projects.rb2
-rw-r--r--spec/factories/ci/runners.rb2
-rw-r--r--spec/factories/ci/stages.rb2
-rw-r--r--spec/factories/ci/trigger_requests.rb2
-rw-r--r--spec/factories/ci/triggers.rb2
-rw-r--r--spec/factories/ci/variables.rb2
-rw-r--r--spec/factories/clusters/applications/helm.rb7
-rw-r--r--spec/factories/clusters/applications/ingress.rb35
-rw-r--r--spec/factories/clusters/clusters.rb2
-rw-r--r--spec/factories/clusters/platforms/kubernetes.rb2
-rw-r--r--spec/factories/clusters/providers/gcp.rb2
-rw-r--r--spec/factories/commit_statuses.rb2
-rw-r--r--spec/factories/commits.rb21
-rw-r--r--spec/factories/container_repositories.rb2
-rw-r--r--spec/factories/conversational_development_index_metrics.rb2
-rw-r--r--spec/factories/deploy_keys_projects.rb2
-rw-r--r--spec/factories/deployments.rb2
-rw-r--r--spec/factories/emails.rb2
-rw-r--r--spec/factories/environments.rb2
-rw-r--r--spec/factories/events.rb2
-rw-r--r--spec/factories/file_uploaders.rb2
-rw-r--r--spec/factories/fork_network_members.rb2
-rw-r--r--spec/factories/fork_networks.rb2
-rw-r--r--spec/factories/forked_project_links.rb2
-rw-r--r--spec/factories/gitaly/commit.rb2
-rw-r--r--spec/factories/gitaly/commit_author.rb2
-rw-r--r--spec/factories/gpg_key_subkeys.rb2
-rw-r--r--spec/factories/gpg_keys.rb2
-rw-r--r--spec/factories/gpg_signature.rb2
-rw-r--r--spec/factories/group_custom_attributes.rb2
-rw-r--r--spec/factories/group_members.rb2
-rw-r--r--spec/factories/groups.rb2
-rw-r--r--spec/factories/identities.rb2
-rw-r--r--spec/factories/instance_configuration.rb2
-rw-r--r--spec/factories/issues.rb3
-rw-r--r--spec/factories/keys.rb65
-rw-r--r--spec/factories/label_links.rb2
-rw-r--r--spec/factories/label_priorities.rb2
-rw-r--r--spec/factories/labels.rb2
-rw-r--r--spec/factories/lfs_objects.rb2
-rw-r--r--spec/factories/lfs_objects_projects.rb2
-rw-r--r--spec/factories/lists.rb2
-rw-r--r--spec/factories/merge_requests.rb2
-rw-r--r--spec/factories/merge_requests_closing_issues.rb2
-rw-r--r--spec/factories/milestones.rb2
-rw-r--r--spec/factories/namespaces.rb2
-rw-r--r--spec/factories/notes.rb2
-rw-r--r--spec/factories/notification_settings.rb2
-rw-r--r--spec/factories/oauth_access_grants.rb2
-rw-r--r--spec/factories/oauth_access_tokens.rb2
-rw-r--r--spec/factories/oauth_applications.rb2
-rw-r--r--spec/factories/pages_domains.rb2
-rw-r--r--spec/factories/personal_access_tokens.rb2
-rw-r--r--spec/factories/project_auto_devops.rb2
-rw-r--r--spec/factories/project_custom_attributes.rb2
-rw-r--r--spec/factories/project_group_links.rb2
-rw-r--r--spec/factories/project_hooks.rb2
-rw-r--r--spec/factories/project_members.rb2
-rw-r--r--spec/factories/project_statistics.rb2
-rw-r--r--spec/factories/project_wikis.rb2
-rw-r--r--spec/factories/projects.rb2
-rw-r--r--spec/factories/protected_branches.rb2
-rw-r--r--spec/factories/protected_tags.rb2
-rw-r--r--spec/factories/releases.rb2
-rw-r--r--spec/factories/sent_notifications.rb2
-rw-r--r--spec/factories/sequences.rb2
-rw-r--r--spec/factories/service_hooks.rb2
-rw-r--r--spec/factories/services.rb3
-rw-r--r--spec/factories/snippets.rb2
-rw-r--r--spec/factories/spam_logs.rb2
-rw-r--r--spec/factories/subscriptions.rb2
-rw-r--r--spec/factories/system_hooks.rb2
-rw-r--r--spec/factories/system_note_metadata.rb2
-rw-r--r--spec/factories/timelogs.rb4
-rw-r--r--spec/factories/todos.rb2
-rw-r--r--spec/factories/trending_project.rb2
-rw-r--r--spec/factories/u2f_registrations.rb2
-rw-r--r--spec/factories/uploads.rb2
-rw-r--r--spec/factories/user_agent_details.rb2
-rw-r--r--spec/factories/user_custom_attributes.rb2
-rw-r--r--spec/factories/users.rb6
-rw-r--r--spec/factories/web_hook_log.rb2
-rw-r--r--spec/factories/wiki_directories.rb2
-rw-r--r--spec/factories/wiki_pages.rb2
-rw-r--r--spec/factories_spec.rb2
-rw-r--r--spec/features/admin/admin_projects_spec.rb6
-rw-r--r--spec/features/admin/admin_runners_spec.rb14
-rw-r--r--spec/features/admin/admin_system_info_spec.rb12
-rw-r--r--spec/features/atom/dashboard_issues_spec.rb4
-rw-r--r--spec/features/atom/dashboard_spec.rb2
-rw-r--r--spec/features/atom/issues_spec.rb2
-rw-r--r--spec/features/atom/users_spec.rb2
-rw-r--r--spec/features/auto_deploy_spec.rb4
-rw-r--r--spec/features/boards/add_issues_modal_spec.rb2
-rw-r--r--spec/features/boards/boards_spec.rb6
-rw-r--r--spec/features/boards/issue_ordering_spec.rb2
-rw-r--r--spec/features/boards/keyboard_shortcut_spec.rb36
-rw-r--r--spec/features/boards/modal_filter_spec.rb6
-rw-r--r--spec/features/boards/new_issue_spec.rb2
-rw-r--r--spec/features/calendar_spec.rb2
-rw-r--r--spec/features/commits_spec.rb10
-rw-r--r--spec/features/copy_as_gfm_spec.rb96
-rw-r--r--spec/features/cycle_analytics_spec.rb2
-rw-r--r--spec/features/dashboard/activity_spec.rb7
-rw-r--r--spec/features/dashboard/archived_projects_spec.rb4
-rw-r--r--spec/features/dashboard/datetime_on_tooltips_spec.rb4
-rw-r--r--spec/features/dashboard/groups_list_spec.rb8
-rw-r--r--spec/features/dashboard/issues_spec.rb2
-rw-r--r--spec/features/dashboard/milestones_spec.rb2
-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/todos/todos_filtering_spec.rb8
-rw-r--r--spec/features/dashboard/todos/todos_sorting_spec.rb2
-rw-r--r--spec/features/dashboard/todos/todos_spec.rb2
-rw-r--r--spec/features/dashboard/user_filters_projects_spec.rb4
-rw-r--r--spec/features/explore/groups_spec.rb87
-rw-r--r--spec/features/global_search_spec.rb2
-rw-r--r--spec/features/group_variables_spec.rb2
-rw-r--r--spec/features/groups/labels/user_sees_links_to_issuables.rb15
-rw-r--r--spec/features/groups/members/manage_members.rb21
-rw-r--r--spec/features/groups/show_spec.rb16
-rw-r--r--spec/features/help_pages_spec.rb26
-rw-r--r--spec/features/invites_spec.rb97
-rw-r--r--spec/features/issues/award_emoji_spec.rb2
-rw-r--r--spec/features/issues/bulk_assignment_labels_spec.rb2
-rw-r--r--spec/features/issues/create_issue_for_discussions_in_merge_request_spec.rb4
-rw-r--r--spec/features/issues/create_issue_for_single_discussion_in_merge_request_spec.rb4
-rw-r--r--spec/features/issues/filtered_search/dropdown_assignee_spec.rb8
-rw-r--r--spec/features/issues/filtered_search/dropdown_author_spec.rb8
-rw-r--r--spec/features/issues/filtered_search/dropdown_emoji_spec.rb2
-rw-r--r--spec/features/issues/filtered_search/dropdown_hint_spec.rb7
-rw-r--r--spec/features/issues/filtered_search/dropdown_milestone_spec.rb2
-rw-r--r--spec/features/issues/filtered_search/search_bar_spec.rb2
-rw-r--r--spec/features/issues/form_spec.rb4
-rw-r--r--spec/features/issues/gfm_autocomplete_spec.rb4
-rw-r--r--spec/features/issues/issue_detail_spec.rb2
-rw-r--r--spec/features/issues/issue_sidebar_spec.rb6
-rw-r--r--spec/features/issues/keyboard_shortcut_spec.rb36
-rw-r--r--spec/features/issues/move_spec.rb10
-rw-r--r--spec/features/issues/notes_on_issues_spec.rb2
-rw-r--r--spec/features/issues/spam_issues_spec.rb2
-rw-r--r--spec/features/issues/todo_spec.rb2
-rw-r--r--spec/features/issues/update_issues_spec.rb2
-rw-r--r--spec/features/issues/user_uses_slash_commands_spec.rb12
-rw-r--r--spec/features/issues_spec.rb1007
-rw-r--r--spec/features/markdown_spec.rb2
-rw-r--r--spec/features/merge_requests/assign_issues_spec.rb2
-rw-r--r--spec/features/merge_requests/check_if_mergeable_with_unresolved_discussions_spec.rb2
-rw-r--r--spec/features/merge_requests/cherry_pick_spec.rb2
-rw-r--r--spec/features/merge_requests/closes_issues_spec.rb2
-rw-r--r--spec/features/merge_requests/conflicts_spec.rb4
-rw-r--r--spec/features/merge_requests/create_new_mr_spec.rb2
-rw-r--r--spec/features/merge_requests/created_from_fork_spec.rb2
-rw-r--r--spec/features/merge_requests/deleted_source_branch_spec.rb2
-rw-r--r--spec/features/merge_requests/diff_notes_avatars_spec.rb2
-rw-r--r--spec/features/merge_requests/diff_notes_resolve_spec.rb6
-rw-r--r--spec/features/merge_requests/edit_mr_spec.rb2
-rw-r--r--spec/features/merge_requests/filter_by_milestone_spec.rb2
-rw-r--r--spec/features/merge_requests/form_spec.rb6
-rw-r--r--spec/features/merge_requests/image_diff_notes_spec.rb (renamed from spec/features/merge_requests/image_diff_notes.rb)59
-rw-r--r--spec/features/merge_requests/merge_commit_message_toggle_spec.rb2
-rw-r--r--spec/features/merge_requests/merge_immediately_with_pipeline_spec.rb2
-rw-r--r--spec/features/merge_requests/mini_pipeline_graph_spec.rb8
-rw-r--r--spec/features/merge_requests/only_allow_merge_if_build_succeeds_spec.rb2
-rw-r--r--spec/features/merge_requests/pipelines_spec.rb2
-rw-r--r--spec/features/merge_requests/reset_filters_spec.rb2
-rw-r--r--spec/features/merge_requests/target_branch_spec.rb2
-rw-r--r--spec/features/merge_requests/update_merge_requests_spec.rb2
-rw-r--r--spec/features/merge_requests/user_posts_notes_spec.rb15
-rw-r--r--spec/features/merge_requests/user_uses_slash_commands_spec.rb10
-rw-r--r--spec/features/merge_requests/widget_deployments_spec.rb2
-rw-r--r--spec/features/merge_requests/widget_spec.rb2
-rw-r--r--spec/features/merge_requests/wip_message_spec.rb2
-rw-r--r--spec/features/milestone_spec.rb6
-rw-r--r--spec/features/password_reset_spec.rb19
-rw-r--r--spec/features/profile_spec.rb4
-rw-r--r--spec/features/profiles/keys_spec.rb1
-rw-r--r--spec/features/profiles/oauth_applications_spec.rb8
-rw-r--r--spec/features/profiles/user_visits_notifications_tab_spec.rb2
-rw-r--r--spec/features/profiles/user_visits_profile_preferences_page_spec.rb12
-rw-r--r--spec/features/projects/activity/rss_spec.rb2
-rw-r--r--spec/features/projects/badges/coverage_spec.rb2
-rw-r--r--spec/features/projects/badges/list_spec.rb2
-rw-r--r--spec/features/projects/blobs/edit_spec.rb8
-rw-r--r--spec/features/projects/branches/download_buttons_spec.rb2
-rw-r--r--spec/features/projects/branches_spec.rb4
-rw-r--r--spec/features/projects/clusters/applications_spec.rb2
-rw-r--r--spec/features/projects/clusters/gcp_spec.rb155
-rw-r--r--spec/features/projects/clusters/interchangeability_spec.rb2
-rw-r--r--spec/features/projects/clusters/user_spec.rb8
-rw-r--r--spec/features/projects/clusters_spec.rb25
-rw-r--r--spec/features/projects/commit/builds_spec.rb2
-rw-r--r--spec/features/projects/commit/cherry_pick_spec.rb2
-rw-r--r--spec/features/projects/commits/rss_spec.rb2
-rw-r--r--spec/features/projects/compare_spec.rb2
-rw-r--r--spec/features/projects/deploy_keys_spec.rb2
-rw-r--r--spec/features/projects/developer_views_empty_project_instructions_spec.rb2
-rw-r--r--spec/features/projects/edit_spec.rb2
-rw-r--r--spec/features/projects/environments/environment_spec.rb2
-rw-r--r--spec/features/projects/environments/environments_spec.rb2
-rw-r--r--spec/features/projects/features_visibility_spec.rb6
-rw-r--r--spec/features/projects/files/browse_files_spec.rb2
-rw-r--r--spec/features/projects/files/creating_a_file_spec.rb2
-rw-r--r--spec/features/projects/files/dockerfile_dropdown_spec.rb2
-rw-r--r--spec/features/projects/files/download_buttons_spec.rb2
-rw-r--r--spec/features/projects/files/edit_file_soft_wrap_spec.rb2
-rw-r--r--spec/features/projects/files/editing_a_file_spec.rb2
-rw-r--r--spec/features/projects/files/files_sort_submodules_with_folders_spec.rb2
-rw-r--r--spec/features/projects/files/find_file_keyboard_spec.rb2
-rw-r--r--spec/features/projects/files/gitignore_dropdown_spec.rb2
-rw-r--r--spec/features/projects/files/gitlab_ci_yml_dropdown_spec.rb2
-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.rb8
-rw-r--r--spec/features/projects/files/template_type_dropdown_spec.rb2
-rw-r--r--spec/features/projects/files/undo_template_spec.rb2
-rw-r--r--spec/features/projects/guest_navigation_menu_spec.rb2
-rw-r--r--spec/features/projects/import_export/export_file_spec.rb2
-rw-r--r--spec/features/projects/import_export/test_project_export.tar.gzbin343232 -> 343092 bytes
-rw-r--r--spec/features/projects/issuable_templates_spec.rb12
-rw-r--r--spec/features/projects/issues/rss_spec.rb2
-rw-r--r--spec/features/projects/jobs/user_browses_job_spec.rb2
-rw-r--r--spec/features/projects/jobs_spec.rb30
-rw-r--r--spec/features/projects/labels/subscription_spec.rb2
-rw-r--r--spec/features/projects/labels/update_prioritization_spec.rb2
-rw-r--r--spec/features/projects/labels/user_sees_links_to_issuables.rb75
-rw-r--r--spec/features/projects/main/download_buttons_spec.rb2
-rw-r--r--spec/features/projects/main/rss_spec.rb2
-rw-r--r--spec/features/projects/members/anonymous_user_sees_members_spec.rb2
-rw-r--r--spec/features/projects/members/group_members_spec.rb2
-rw-r--r--spec/features/projects/members/groups_with_access_list_spec.rb2
-rw-r--r--spec/features/projects/members/master_adds_member_with_expiration_date_spec.rb2
-rw-r--r--spec/features/projects/members/master_manages_access_requests_spec.rb2
-rw-r--r--spec/features/projects/members/member_cannot_request_access_to_his_project_spec.rb2
-rw-r--r--spec/features/projects/members/member_leaves_project_spec.rb2
-rw-r--r--spec/features/projects/merge_requests/list_spec.rb2
-rw-r--r--spec/features/projects/pages_spec.rb2
-rw-r--r--spec/features/projects/pipelines/pipeline_spec.rb6
-rw-r--r--spec/features/projects/pipelines/pipelines_spec.rb36
-rw-r--r--spec/features/projects/ref_switcher_spec.rb78
-rw-r--r--spec/features/projects/services/user_activates_jira_spec.rb2
-rw-r--r--spec/features/projects/services/user_activates_mattermost_slash_command_spec.rb2
-rw-r--r--spec/features/projects/services/user_activates_slack_slash_command_spec.rb2
-rw-r--r--spec/features/projects/settings/integration_settings_spec.rb2
-rw-r--r--spec/features/projects/settings/merge_requests_settings_spec.rb2
-rw-r--r--spec/features/projects/settings/pipelines_settings_spec.rb2
-rw-r--r--spec/features/projects/settings/repository_settings_spec.rb4
-rw-r--r--spec/features/projects/settings/visibility_settings_spec.rb2
-rw-r--r--spec/features/projects/snippets/create_snippet_spec.rb2
-rw-r--r--spec/features/projects/snippets/show_spec.rb2
-rw-r--r--spec/features/projects/tags/download_buttons_spec.rb2
-rw-r--r--spec/features/projects/tree/create_directory_spec.rb28
-rw-r--r--spec/features/projects/tree/create_file_spec.rb18
-rw-r--r--spec/features/projects/tree/rss_spec.rb2
-rw-r--r--spec/features/projects/tree/tree_show_spec.rb28
-rw-r--r--spec/features/projects/tree/upload_file_spec.rb9
-rw-r--r--spec/features/projects/user_browses_files_spec.rb2
-rw-r--r--spec/features/projects/user_creates_directory_spec.rb4
-rw-r--r--spec/features/projects/user_creates_files_spec.rb6
-rw-r--r--spec/features/projects/user_deletes_files_spec.rb4
-rw-r--r--spec/features/projects/user_edits_files_spec.rb8
-rw-r--r--spec/features/projects/user_replaces_files_spec.rb4
-rw-r--r--spec/features/projects/user_uploads_files_spec.rb4
-rw-r--r--spec/features/projects/wiki/markdown_preview_spec.rb2
-rw-r--r--spec/features/projects/wiki/user_views_wiki_in_project_page_spec.rb2
-rw-r--r--spec/features/projects_spec.rb6
-rw-r--r--spec/features/runners_spec.rb20
-rw-r--r--spec/features/signed_commits_spec.rb6
-rw-r--r--spec/features/tags/master_creates_tag_spec.rb2
-rw-r--r--spec/features/tags/master_deletes_tag_spec.rb2
-rw-r--r--spec/features/tags/master_updates_tag_spec.rb2
-rw-r--r--spec/features/tags/master_views_tags_spec.rb5
-rw-r--r--spec/features/triggers_spec.rb6
-rw-r--r--spec/features/u2f_spec.rb2
-rw-r--r--spec/features/variables_spec.rb6
-rw-r--r--spec/finders/access_requests_finder_spec.rb4
-rw-r--r--spec/finders/group_descendants_finder_spec.rb35
-rw-r--r--spec/finders/group_projects_finder_spec.rb4
-rw-r--r--spec/finders/issues_finder_spec.rb6
-rw-r--r--spec/finders/labels_finder_spec.rb14
-rw-r--r--spec/finders/merge_requests_finder_spec.rb8
-rw-r--r--spec/finders/move_to_project_finder_spec.rb34
-rw-r--r--spec/finders/notes_finder_spec.rb6
-rw-r--r--spec/finders/personal_projects_finder_spec.rb2
-rw-r--r--spec/finders/snippets_finder_spec.rb4
-rw-r--r--spec/finders/todos_finder_spec.rb2
-rw-r--r--spec/fixtures/api/schemas/contributor.json18
-rw-r--r--spec/fixtures/api/schemas/contributors.json4
-rw-r--r--spec/fixtures/api/schemas/entities/merge_request_basic.json1
-rw-r--r--spec/fixtures/api/schemas/entities/merge_request_metrics.json21
-rw-r--r--spec/fixtures/api/schemas/entities/merge_request_widget.json (renamed from spec/fixtures/api/schemas/entities/merge_request.json)22
-rw-r--r--spec/fixtures/api/schemas/entities/user.json17
-rw-r--r--spec/fixtures/api/schemas/public_api/v4/board.json86
-rw-r--r--spec/fixtures/api/schemas/public_api/v4/boards.json4
-rw-r--r--spec/fixtures/api/schemas/public_api/v4/pages_domain/basic.json3
-rw-r--r--spec/fixtures/api/schemas/public_api/v4/user/basic.json2
-rw-r--r--spec/fixtures/emails/valid_new_merge_request.eml2
-rw-r--r--spec/fixtures/emails/valid_new_merge_request_no_description.eml18
-rw-r--r--spec/fixtures/markdown.md.erb17
-rw-r--r--spec/helpers/labels_helper_spec.rb63
-rw-r--r--spec/helpers/markup_helper_spec.rb2
-rw-r--r--spec/helpers/members_helper_spec.rb8
-rw-r--r--spec/helpers/notes_helper_spec.rb22
-rw-r--r--spec/helpers/preferences_helper_spec.rb74
-rw-r--r--spec/helpers/runners_helper_spec.rb6
-rw-r--r--spec/helpers/tree_helper_spec.rb1
-rw-r--r--spec/javascripts/activities_spec.js5
-rw-r--r--spec/javascripts/behaviors/secret_values_spec.js146
-rw-r--r--spec/javascripts/blob/notebook/index_spec.js48
-rw-r--r--spec/javascripts/boards/board_blank_state_spec.js23
-rw-r--r--spec/javascripts/boards/board_card_spec.js13
-rw-r--r--spec/javascripts/boards/board_list_spec.js14
-rw-r--r--spec/javascripts/boards/board_new_issue_spec.js23
-rw-r--r--spec/javascripts/boards/boards_store_spec.js16
-rw-r--r--spec/javascripts/boards/components/board_spec.js3
-rw-r--r--spec/javascripts/boards/issue_card_spec.js3
-rw-r--r--spec/javascripts/boards/issue_spec.js4
-rw-r--r--spec/javascripts/boards/list_spec.js22
-rw-r--r--spec/javascripts/boards/mock_data.js35
-rw-r--r--spec/javascripts/clusters/components/applications_spec.js5
-rw-r--r--spec/javascripts/clusters/services/mock_data.js6
-rw-r--r--spec/javascripts/clusters/stores/clusters_store_spec.js7
-rw-r--r--spec/javascripts/collapsed_sidebar_todo_spec.js3
-rw-r--r--spec/javascripts/commits_spec.js1
-rw-r--r--spec/javascripts/datetime_utility_spec.js186
-rw-r--r--spec/javascripts/deploy_keys/components/key_spec.js3
-rw-r--r--spec/javascripts/filtered_search/filtered_search_manager_spec.js24
-rw-r--r--spec/javascripts/fixtures/pipelines.html.haml4
-rw-r--r--spec/javascripts/fly_out_nav_spec.js4
-rw-r--r--spec/javascripts/gl_dropdown_spec.js6
-rw-r--r--spec/javascripts/graphs/stat_graph_contributors_graph_spec.js12
-rw-r--r--spec/javascripts/graphs/stat_graph_contributors_spec.js26
-rw-r--r--spec/javascripts/groups/components/app_spec.js6
-rw-r--r--spec/javascripts/groups/components/group_item_spec.js6
-rw-r--r--spec/javascripts/groups/components/item_actions_spec.js30
-rw-r--r--spec/javascripts/groups/components/item_caret_spec.js10
-rw-r--r--spec/javascripts/groups/components/item_stats_spec.js55
-rw-r--r--spec/javascripts/groups/components/item_stats_value_spec.js81
-rw-r--r--spec/javascripts/groups/components/item_type_icon_spec.js8
-rw-r--r--spec/javascripts/groups/mock_data.js22
-rw-r--r--spec/javascripts/helpers/locale_helper.js11
-rw-r--r--spec/javascripts/image_diff/helpers/badge_helper_spec.js9
-rw-r--r--spec/javascripts/issue_show/components/app_spec.js177
-rw-r--r--spec/javascripts/issue_show/components/description_spec.js30
-rw-r--r--spec/javascripts/issue_show/mock_data.js10
-rw-r--r--spec/javascripts/issue_spec.js2
-rw-r--r--spec/javascripts/job_spec.js26
-rw-r--r--spec/javascripts/jobs/header_spec.js3
-rw-r--r--spec/javascripts/lib/utils/text_utility_spec.js10
-rw-r--r--spec/javascripts/lib/utils/tick_formats_spec.js40
-rw-r--r--spec/javascripts/line_highlighter_spec.js3
-rw-r--r--spec/javascripts/locale/index_spec.js35
-rw-r--r--spec/javascripts/merge_request_notes_spec.js4
-rw-r--r--spec/javascripts/merge_request_spec.js3
-rw-r--r--spec/javascripts/merge_request_tabs_spec.js50
-rw-r--r--spec/javascripts/monitoring/graph/deployment_spec.js121
-rw-r--r--spec/javascripts/monitoring/graph/flag_spec.js114
-rw-r--r--spec/javascripts/monitoring/graph_spec.js14
-rw-r--r--spec/javascripts/monitoring/mock_data.js10
-rw-r--r--spec/javascripts/notes/components/comment_form_spec.js (renamed from spec/javascripts/notes/components/issue_comment_form_spec.js)2
-rw-r--r--spec/javascripts/notes/components/note_app_spec.js (renamed from spec/javascripts/notes/components/issue_note_app_spec.js)81
-rw-r--r--spec/javascripts/notes/components/note_body_spec.js (renamed from spec/javascripts/notes/components/issue_note_body_spec.js)2
-rw-r--r--spec/javascripts/notes/components/note_form_spec.js (renamed from spec/javascripts/notes/components/issue_note_form_spec.js)2
-rw-r--r--spec/javascripts/notes/components/noteable_discussion_spec.js (renamed from spec/javascripts/notes/components/issue_discussion_spec.js)4
-rw-r--r--spec/javascripts/notes/components/noteable_note_spec.js (renamed from spec/javascripts/notes/components/issue_note_spec.js)17
-rw-r--r--spec/javascripts/notes/mock_data.js332
-rw-r--r--spec/javascripts/notes_spec.js16
-rw-r--r--spec/javascripts/pager_spec.js15
-rw-r--r--spec/javascripts/pipelines/graph/job_component_spec.js31
-rw-r--r--spec/javascripts/pipelines/nav_controls_spec.js27
-rw-r--r--spec/javascripts/profile/account/components/delete_account_modal_spec.js8
-rw-r--r--spec/javascripts/projects/project_new_spec.js32
-rw-r--r--spec/javascripts/repo/components/commit_sidebar/list_collapsed_spec.js4
-rw-r--r--spec/javascripts/repo/components/commit_sidebar/list_item_spec.js2
-rw-r--r--spec/javascripts/repo/components/commit_sidebar/list_spec.js29
-rw-r--r--spec/javascripts/repo/components/ide_context_bar_spec.js49
-rw-r--r--spec/javascripts/repo/components/ide_repo_tree_spec.js (renamed from spec/javascripts/repo/components/repo_sidebar_spec.js)29
-rw-r--r--spec/javascripts/repo/components/ide_side_bar_spec.js43
-rw-r--r--spec/javascripts/repo/components/ide_spec.js (renamed from spec/javascripts/repo/components/repo_spec.js)16
-rw-r--r--spec/javascripts/repo/components/new_branch_form_spec.js4
-rw-r--r--spec/javascripts/repo/components/new_dropdown/index_spec.js30
-rw-r--r--spec/javascripts/repo/components/new_dropdown/modal_spec.js149
-rw-r--r--spec/javascripts/repo/components/new_dropdown/upload_spec.js81
-rw-r--r--spec/javascripts/repo/components/repo_commit_section_spec.js38
-rw-r--r--spec/javascripts/repo/components/repo_edit_button_spec.js8
-rw-r--r--spec/javascripts/repo/components/repo_editor_spec.js6
-rw-r--r--spec/javascripts/repo/components/repo_file_buttons_spec.js4
-rw-r--r--spec/javascripts/repo/components/repo_file_spec.js28
-rw-r--r--spec/javascripts/repo/components/repo_loading_file_spec.js5
-rw-r--r--spec/javascripts/repo/components/repo_prev_directory_spec.js4
-rw-r--r--spec/javascripts/repo/components/repo_preview_spec.js4
-rw-r--r--spec/javascripts/repo/components/repo_tab_spec.js10
-rw-r--r--spec/javascripts/repo/components/repo_tabs_spec.js4
-rw-r--r--spec/javascripts/repo/helpers.js5
-rw-r--r--spec/javascripts/repo/lib/common/disposable_spec.js2
-rw-r--r--spec/javascripts/repo/lib/common/model_manager_spec.js4
-rw-r--r--spec/javascripts/repo/lib/common/model_spec.js4
-rw-r--r--spec/javascripts/repo/lib/decorations/controller_spec.js8
-rw-r--r--spec/javascripts/repo/lib/diff/controller_spec.js12
-rw-r--r--spec/javascripts/repo/lib/diff/diff_spec.js2
-rw-r--r--spec/javascripts/repo/lib/editor_options_spec.js2
-rw-r--r--spec/javascripts/repo/lib/editor_spec.js4
-rw-r--r--spec/javascripts/repo/monaco_loader_spec.js2
-rw-r--r--spec/javascripts/repo/stores/actions/branch_spec.js20
-rw-r--r--spec/javascripts/repo/stores/actions/file_spec.js75
-rw-r--r--spec/javascripts/repo/stores/actions/tree_spec.js325
-rw-r--r--spec/javascripts/repo/stores/actions_spec.js102
-rw-r--r--spec/javascripts/repo/stores/getters_spec.js38
-rw-r--r--spec/javascripts/repo/stores/mutations/branch_spec.js6
-rw-r--r--spec/javascripts/repo/stores/mutations/file_spec.js4
-rw-r--r--spec/javascripts/repo/stores/mutations/tree_spec.js4
-rw-r--r--spec/javascripts/repo/stores/mutations_spec.js40
-rw-r--r--spec/javascripts/repo/stores/utils_spec.js43
-rw-r--r--spec/javascripts/right_sidebar_spec.js5
-rw-r--r--spec/javascripts/search_autocomplete_spec.js7
-rw-r--r--spec/javascripts/sidebar/sidebar_assignees_spec.js45
-rw-r--r--spec/javascripts/sidebar/sidebar_mediator_spec.js5
-rw-r--r--spec/javascripts/sidebar/sidebar_subscriptions_spec.js9
-rw-r--r--spec/javascripts/syntax_highlight_spec.js74
-rw-r--r--spec/javascripts/test_bundle.js12
-rw-r--r--spec/javascripts/todos_spec.js5
-rw-r--r--spec/javascripts/vue_mr_widget/components/mr_widget_deployment_spec.js14
-rw-r--r--spec/javascripts/vue_mr_widget/components/mr_widget_memory_usage_spec.js4
-rw-r--r--spec/javascripts/vue_mr_widget/components/mr_widget_rebase_spec.js115
-rw-r--r--spec/javascripts/vue_mr_widget/components/states/mr_widget_closed_spec.js13
-rw-r--r--spec/javascripts/vue_mr_widget/components/states/mr_widget_merge_when_pipeline_succeeds_spec.js10
-rw-r--r--spec/javascripts/vue_mr_widget/components/states/mr_widget_merged_spec.js17
-rw-r--r--spec/javascripts/vue_mr_widget/components/states/mr_widget_ready_to_merge_spec.js13
-rw-r--r--spec/javascripts/vue_mr_widget/components/states/mr_widget_wip_spec.js4
-rw-r--r--spec/javascripts/vue_mr_widget/mock_data.js9
-rw-r--r--spec/javascripts/vue_mr_widget/mr_widget_options_spec.js33
-rw-r--r--spec/javascripts/vue_mr_widget/stores/mr_widget_store_spec.js13
-rw-r--r--spec/javascripts/vue_shared/components/commit_spec.js4
-rw-r--r--spec/javascripts/vue_shared/components/expand_button_spec.js32
-rw-r--r--spec/javascripts/vue_shared/components/file_icon_spec.js83
-rw-r--r--spec/javascripts/vue_shared/components/header_ci_component_spec.js81
-rw-r--r--spec/javascripts/vue_shared/components/modal_spec.js66
-rw-r--r--spec/javascripts/vue_shared/components/panel_resizer_spec.js59
-rw-r--r--spec/javascripts/vue_shared/components/table_pagination_spec.js16
-rw-r--r--spec/javascripts/vue_shared/components/time_ago_tooltip_spec.js6
-rw-r--r--spec/javascripts/vue_shared/components/toggle_button_spec.js14
-rw-r--r--spec/lib/banzai/filter/mermaid_filter_spec.rb4
-rw-r--r--spec/lib/banzai/filter/redactor_filter_spec.rb6
-rw-r--r--spec/lib/banzai/filter/relative_link_filter_spec.rb86
-rw-r--r--spec/lib/banzai/filter/table_of_contents_filter_spec.rb7
-rw-r--r--spec/lib/banzai/filter/upload_link_filter_spec.rb133
-rw-r--r--spec/lib/banzai/filter/user_reference_filter_spec.rb6
-rw-r--r--spec/lib/banzai/reference_parser/user_parser_spec.rb6
-rw-r--r--spec/lib/gitlab/action_rate_limiter_spec.rb29
-rw-r--r--spec/lib/gitlab/background_migration/create_fork_network_memberships_range_spec.rb27
-rw-r--r--spec/lib/gitlab/background_migration/delete_conflicting_redirect_routes_range_spec.rb13
-rw-r--r--spec/lib/gitlab/background_migration/migrate_events_to_push_event_payloads_spec.rb19
-rw-r--r--spec/lib/gitlab/background_migration/populate_fork_networks_range_spec.rb19
-rw-r--r--spec/lib/gitlab/background_migration/populate_merge_request_metrics_with_events_data_spec.rb124
-rw-r--r--spec/lib/gitlab/background_migration/populate_untracked_uploads_spec.rb17
-rw-r--r--spec/lib/gitlab/background_migration/prepare_untracked_uploads_spec.rb13
-rw-r--r--spec/lib/gitlab/bare_repository_import/importer_spec.rb40
-rw-r--r--spec/lib/gitlab/bare_repository_import/repository_spec.rb129
-rw-r--r--spec/lib/gitlab/checks/project_moved_spec.rb87
-rw-r--r--spec/lib/gitlab/ci/ansi2html_spec.rb4
-rw-r--r--spec/lib/gitlab/ci/pipeline/chain/build_spec.rb57
-rw-r--r--spec/lib/gitlab/ci/pipeline/chain/command_spec.rb185
-rw-r--r--spec/lib/gitlab/ci/pipeline/chain/create_spec.rb6
-rw-r--r--spec/lib/gitlab/ci/pipeline/chain/sequence_spec.rb2
-rw-r--r--spec/lib/gitlab/ci/pipeline/chain/skip_spec.rb9
-rw-r--r--spec/lib/gitlab/ci/pipeline/chain/validate/abilities_spec.rb5
-rw-r--r--spec/lib/gitlab/ci/pipeline/chain/validate/config_spec.rb7
-rw-r--r--spec/lib/gitlab/ci/pipeline/chain/validate/repository_spec.rb26
-rw-r--r--spec/lib/gitlab/ci/status/build/common_spec.rb2
-rw-r--r--spec/lib/gitlab/ci/status/external/common_spec.rb2
-rw-r--r--spec/lib/gitlab/ci/status/external/factory_spec.rb2
-rw-r--r--spec/lib/gitlab/ci/status/pipeline/common_spec.rb2
-rw-r--r--spec/lib/gitlab/ci/status/pipeline/factory_spec.rb2
-rw-r--r--spec/lib/gitlab/ci/status/stage/common_spec.rb2
-rw-r--r--spec/lib/gitlab/ci/status/stage/factory_spec.rb2
-rw-r--r--spec/lib/gitlab/closing_issue_extractor_spec.rb6
-rw-r--r--spec/lib/gitlab/cycle_analytics/base_event_fetcher_spec.rb2
-rw-r--r--spec/lib/gitlab/cycle_analytics/events_spec.rb14
-rw-r--r--spec/lib/gitlab/cycle_analytics/permissions_spec.rb8
-rw-r--r--spec/lib/gitlab/database/migration_helpers_spec.rb101
-rw-r--r--spec/lib/gitlab/diff/file_collection/merge_request_diff_spec.rb14
-rw-r--r--spec/lib/gitlab/diff/file_spec.rb25
-rw-r--r--spec/lib/gitlab/email/handler/create_merge_request_handler_spec.rb12
-rw-r--r--spec/lib/gitlab/email/handler/create_note_handler_spec.rb4
-rw-r--r--spec/lib/gitlab/encoding_helper_spec.rb32
-rw-r--r--spec/lib/gitlab/exclusive_lease_spec.rb15
-rw-r--r--spec/lib/gitlab/gfm/reference_rewriter_spec.rb2
-rw-r--r--spec/lib/gitlab/git/blob_spec.rb19
-rw-r--r--spec/lib/gitlab/git/commit_spec.rb6
-rw-r--r--spec/lib/gitlab/git/gitlab_projects_spec.rb337
-rw-r--r--spec/lib/gitlab/git/repository_spec.rb352
-rw-r--r--spec/lib/gitlab/git_access_spec.rb70
-rw-r--r--spec/lib/gitlab/git_access_wiki_spec.rb4
-rw-r--r--spec/lib/gitlab/gitaly_client/conflicts_service_spec.rb90
-rw-r--r--spec/lib/gitlab/gitaly_client/remote_service_spec.rb47
-rw-r--r--spec/lib/gitlab/gitaly_client_spec.rb14
-rw-r--r--spec/lib/gitlab/github_import/importer/repository_importer_spec.rb8
-rw-r--r--spec/lib/gitlab/google_code_import/importer_spec.rb2
-rw-r--r--spec/lib/gitlab/identifier_spec.rb4
-rw-r--r--spec/lib/gitlab/import_export/project.json770
-rw-r--r--spec/lib/gitlab/import_export/project_tree_restorer_spec.rb26
-rw-r--r--spec/lib/gitlab/import_export/project_tree_saver_spec.rb14
-rw-r--r--spec/lib/gitlab/import_export/repo_saver_spec.rb2
-rw-r--r--spec/lib/gitlab/import_export/safe_model_attributes.yml1
-rw-r--r--spec/lib/gitlab/import_export/wiki_repo_saver_spec.rb2
-rw-r--r--spec/lib/gitlab/kubernetes/helm/api_spec.rb (renamed from spec/lib/gitlab/kubernetes/helm_spec.rb)49
-rw-r--r--spec/lib/gitlab/kubernetes/helm/install_command_spec.rb111
-rw-r--r--spec/lib/gitlab/kubernetes/helm/pod_spec.rb86
-rw-r--r--spec/lib/gitlab/ldap/adapter_spec.rb10
-rw-r--r--spec/lib/gitlab/ldap/person_spec.rb73
-rw-r--r--spec/lib/gitlab/ldap/user_spec.rb15
-rw-r--r--spec/lib/gitlab/metrics/method_call_spec.rb43
-rw-r--r--spec/lib/gitlab/metrics/samplers/influx_sampler_spec.rb23
-rw-r--r--spec/lib/gitlab/metrics/samplers/ruby_sampler_spec.rb23
-rw-r--r--spec/lib/gitlab/metrics/system_spec.rb8
-rw-r--r--spec/lib/gitlab/o_auth/user_spec.rb70
-rw-r--r--spec/lib/gitlab/project_authorizations_spec.rb2
-rw-r--r--spec/lib/gitlab/project_search_results_spec.rb8
-rw-r--r--spec/lib/gitlab/reference_extractor_spec.rb23
-rw-r--r--spec/lib/gitlab/search_results_spec.rb42
-rw-r--r--spec/lib/gitlab/shell_spec.rb175
-rw-r--r--spec/lib/gitlab/sidekiq_config_spec.rb23
-rw-r--r--spec/lib/gitlab/sidekiq_versioning/manager_spec.rb22
-rw-r--r--spec/lib/gitlab/sidekiq_versioning_spec.rb44
-rw-r--r--spec/lib/gitlab/slash_commands/issue_new_spec.rb2
-rw-r--r--spec/lib/gitlab/slash_commands/issue_search_spec.rb2
-rw-r--r--spec/lib/gitlab/slash_commands/issue_show_spec.rb2
-rw-r--r--spec/lib/gitlab/tcp_checker_spec.rb32
-rw-r--r--spec/lib/gitlab/user_access_spec.rb36
-rw-r--r--spec/lib/gitlab/utils/strong_memoize_spec.rb12
-rw-r--r--spec/lib/gitlab/view/presenter/factory_spec.rb8
-rw-r--r--spec/lib/gitlab/visibility_level_spec.rb27
-rw-r--r--spec/lib/google_api/cloud_platform/client_spec.rb24
-rw-r--r--spec/mailers/notify_spec.rb48
-rw-r--r--spec/migrations/clean_up_for_members_spec.rb78
-rw-r--r--spec/migrations/delete_conflicting_redirect_routes_spec.rb22
-rw-r--r--spec/migrations/issues_moved_to_id_foreign_key_spec.rb25
-rw-r--r--spec/migrations/migrate_gcp_clusters_to_new_clusters_architectures_spec.rb43
-rw-r--r--spec/migrations/migrate_kubernetes_service_to_new_clusters_architectures_spec.rb312
-rw-r--r--spec/migrations/migrate_stage_id_reference_in_background_spec.rb6
-rw-r--r--spec/migrations/migrate_stages_statuses_spec.rb6
-rw-r--r--spec/migrations/normalize_ldap_extern_uids_spec.rb6
-rw-r--r--spec/migrations/remove_assignee_id_from_issue_spec.rb37
-rw-r--r--spec/migrations/schedule_create_gpg_key_subkeys_from_gpg_keys_spec.rb12
-rw-r--r--spec/migrations/schedule_merge_request_diff_migrations_spec.rb6
-rw-r--r--spec/migrations/schedule_merge_request_diff_migrations_take_two_spec.rb6
-rw-r--r--spec/migrations/schedule_merge_request_latest_merge_request_diff_id_migrations_spec.rb6
-rw-r--r--spec/migrations/schedule_populate_merge_request_metrics_with_events_data_spec.rb24
-rw-r--r--spec/migrations/track_untracked_uploads_spec.rb12
-rw-r--r--spec/models/ability_spec.rb6
-rw-r--r--spec/models/blob_viewer/package_json_spec.rb47
-rw-r--r--spec/models/ci/build_spec.rb69
-rw-r--r--spec/models/ci/pipeline_spec.rb18
-rw-r--r--spec/models/ci/runner_spec.rb28
-rw-r--r--spec/models/ci/trigger_spec.rb2
-rw-r--r--spec/models/clusters/applications/helm_spec.rb12
-rw-r--r--spec/models/clusters/applications/ingress_spec.rb102
-rw-r--r--spec/models/clusters/applications/prometheus_spec.rb16
-rw-r--r--spec/models/clusters/cluster_spec.rb10
-rw-r--r--spec/models/commit_spec.rb44
-rw-r--r--spec/models/concerns/blocks_json_serialization_spec.rb17
-rw-r--r--spec/models/concerns/cache_markdown_field_spec.rb20
-rw-r--r--spec/models/concerns/deployment_platform_spec.rb73
-rw-r--r--spec/models/concerns/issuable_spec.rb4
-rw-r--r--spec/models/concerns/mentionable_spec.rb4
-rw-r--r--spec/models/concerns/milestoneish_spec.rb8
-rw-r--r--spec/models/concerns/resolvable_discussion_spec.rb2
-rw-r--r--spec/models/deployment_spec.rb8
-rw-r--r--spec/models/diff_discussion_spec.rb48
-rw-r--r--spec/models/diff_note_spec.rb16
-rw-r--r--spec/models/event_spec.rb20
-rw-r--r--spec/models/generic_commit_status_spec.rb2
-rw-r--r--spec/models/hooks/system_hook_spec.rb4
-rw-r--r--spec/models/identity_spec.rb27
-rw-r--r--spec/models/issue_collection_spec.rb2
-rw-r--r--spec/models/issue_spec.rb40
-rw-r--r--spec/models/member_spec.rb8
-rw-r--r--spec/models/members/project_member_spec.rb8
-rw-r--r--spec/models/merge_request/metrics_spec.rb15
-rw-r--r--spec/models/merge_request_spec.rb126
-rw-r--r--spec/models/namespace_spec.rb50
-rw-r--r--spec/models/note_spec.rb24
-rw-r--r--spec/models/project_feature_spec.rb2
-rw-r--r--spec/models/project_services/jira_service_spec.rb20
-rw-r--r--spec/models/project_services/kubernetes_service_spec.rb101
-rw-r--r--spec/models/project_services/pipelines_email_service_spec.rb2
-rw-r--r--spec/models/project_spec.rb132
-rw-r--r--spec/models/project_team_spec.rb4
-rw-r--r--spec/models/repository_spec.rb233
-rw-r--r--spec/models/route_spec.rb104
-rw-r--r--spec/models/service_spec.rb26
-rw-r--r--spec/models/user_spec.rb107
-rw-r--r--spec/policies/ci/build_policy_spec.rb4
-rw-r--r--spec/policies/ci/pipeline_schedule_policy_spec.rb92
-rw-r--r--spec/policies/ci/trigger_policy_spec.rb10
-rw-r--r--spec/policies/issue_policy_spec.rb12
-rw-r--r--spec/policies/project_snippet_policy_spec.rb6
-rw-r--r--spec/presenters/group_member_presenter_spec.rb138
-rw-r--r--spec/presenters/merge_request_presenter_spec.rb71
-rw-r--r--spec/presenters/project_member_presenter_spec.rb138
-rw-r--r--spec/requests/api/access_requests_spec.rb4
-rw-r--r--spec/requests/api/award_emoji_spec.rb2
-rw-r--r--spec/requests/api/boards_spec.rb179
-rw-r--r--spec/requests/api/deployments_spec.rb2
-rw-r--r--spec/requests/api/environments_spec.rb2
-rw-r--r--spec/requests/api/files_spec.rb2
-rw-r--r--spec/requests/api/groups_spec.rb64
-rw-r--r--spec/requests/api/internal_spec.rb64
-rw-r--r--spec/requests/api/issues_spec.rb34
-rw-r--r--spec/requests/api/jobs_spec.rb6
-rw-r--r--spec/requests/api/labels_spec.rb2
-rw-r--r--spec/requests/api/members_spec.rb4
-rw-r--r--spec/requests/api/merge_request_diffs_spec.rb2
-rw-r--r--spec/requests/api/merge_requests_spec.rb38
-rw-r--r--spec/requests/api/notes_spec.rb6
-rw-r--r--spec/requests/api/pages_domains_spec.rb1
-rw-r--r--spec/requests/api/pipelines_spec.rb4
-rw-r--r--spec/requests/api/project_hooks_spec.rb6
-rw-r--r--spec/requests/api/project_milestones_spec.rb4
-rw-r--r--spec/requests/api/projects_spec.rb10
-rw-r--r--spec/requests/api/repositories_spec.rb22
-rw-r--r--spec/requests/api/services_spec.rb10
-rw-r--r--spec/requests/api/tags_spec.rb38
-rw-r--r--spec/requests/api/todos_spec.rb6
-rw-r--r--spec/requests/api/v3/award_emoji_spec.rb2
-rw-r--r--spec/requests/api/v3/boards_spec.rb4
-rw-r--r--spec/requests/api/v3/commits_spec.rb8
-rw-r--r--spec/requests/api/v3/deployments_spec.rb2
-rw-r--r--spec/requests/api/v3/environments_spec.rb2
-rw-r--r--spec/requests/api/v3/files_spec.rb2
-rw-r--r--spec/requests/api/v3/groups_spec.rb2
-rw-r--r--spec/requests/api/v3/issues_spec.rb8
-rw-r--r--spec/requests/api/v3/labels_spec.rb2
-rw-r--r--spec/requests/api/v3/members_spec.rb4
-rw-r--r--spec/requests/api/v3/merge_request_diffs_spec.rb2
-rw-r--r--spec/requests/api/v3/merge_requests_spec.rb12
-rw-r--r--spec/requests/api/v3/milestones_spec.rb8
-rw-r--r--spec/requests/api/v3/notes_spec.rb6
-rw-r--r--spec/requests/api/v3/pipelines_spec.rb4
-rw-r--r--spec/requests/api/v3/project_hooks_spec.rb6
-rw-r--r--spec/requests/api/v3/projects_spec.rb10
-rw-r--r--spec/requests/api/v3/services_spec.rb4
-rw-r--r--spec/requests/api/v3/todos_spec.rb4
-rw-r--r--spec/requests/api/wikis_spec.rb30
-rw-r--r--spec/requests/git_http_spec.rb24
-rw-r--r--spec/requests/lfs_http_spec.rb30
-rw-r--r--spec/requests/projects/cycle_analytics_events_spec.rb2
-rw-r--r--spec/rubocop/cop/active_record_dependent_spec.rb33
-rw-r--r--spec/rubocop/cop/active_record_serialize_spec.rb33
-rw-r--r--spec/rubocop/cop/custom_error_class_spec.rb111
-rw-r--r--spec/rubocop/cop/gem_fetcher_spec.rb46
-rw-r--r--spec/rubocop/cop/gitlab/module_with_instance_variables_spec.rb157
-rw-r--r--spec/rubocop/cop/in_batches_spec.rb19
-rw-r--r--spec/rubocop/cop/include_sidekiq_worker_spec.rb33
-rw-r--r--spec/rubocop/cop/line_break_after_guard_clauses_spec.rb160
-rw-r--r--spec/rubocop/cop/migration/add_concurrent_foreign_key_spec.rb6
-rw-r--r--spec/rubocop/cop/migration/add_concurrent_index_spec.rb6
-rw-r--r--spec/rubocop/cop/migration/add_timestamps_spec.rb12
-rw-r--r--spec/rubocop/cop/migration/datetime_spec.rb16
-rw-r--r--spec/rubocop/cop/migration/hash_index_spec.rb8
-rw-r--r--spec/rubocop/cop/migration/remove_column_spec.rb68
-rw-r--r--spec/rubocop/cop/migration/remove_concurrent_index_spec.rb6
-rw-r--r--spec/rubocop/cop/migration/remove_index_spec.rb4
-rw-r--r--spec/rubocop/cop/migration/reversible_add_column_with_default_spec.rb6
-rw-r--r--spec/rubocop/cop/migration/safer_boolean_column_spec.rb10
-rw-r--r--spec/rubocop/cop/migration/timestamps_spec.rb12
-rw-r--r--spec/rubocop/cop/migration/update_column_in_batches_spec.rb8
-rw-r--r--spec/rubocop/cop/migration/update_large_table_spec.rb10
-rw-r--r--spec/rubocop/cop/polymorphic_associations_spec.rb33
-rw-r--r--spec/rubocop/cop/project_path_helper_spec.rb6
-rw-r--r--spec/rubocop/cop/redirect_with_status_spec.rb86
-rw-r--r--spec/rubocop/cop/rspec/env_assignment_spec.rb6
-rw-r--r--spec/rubocop/cop/rspec/single_line_hook_spec.rb66
-rw-r--r--spec/rubocop/cop/rspec/verbose_include_metadata_spec.rb53
-rw-r--r--spec/rubocop/cop/sidekiq_options_queue_spec.rb28
-rw-r--r--spec/serializers/cluster_application_entity_spec.rb4
-rw-r--r--spec/serializers/event_entity_spec.rb13
-rw-r--r--spec/serializers/group_child_entity_spec.rb12
-rw-r--r--spec/serializers/merge_request_serializer_spec.rb48
-rw-r--r--spec/serializers/merge_request_widget_entity_spec.rb (renamed from spec/serializers/merge_request_entity_spec.rb)120
-rw-r--r--spec/serializers/pipeline_serializer_spec.rb6
-rw-r--r--spec/services/boards/issues/create_service_spec.rb2
-rw-r--r--spec/services/boards/issues/list_service_spec.rb2
-rw-r--r--spec/services/boards/issues/move_service_spec.rb2
-rw-r--r--spec/services/boards/lists/create_service_spec.rb2
-rw-r--r--spec/services/boards/lists/generate_service_spec.rb2
-rw-r--r--spec/services/check_gcp_project_billing_service_spec.rb31
-rw-r--r--spec/services/ci/create_pipeline_service_spec.rb31
-rw-r--r--spec/services/ci/register_job_service_spec.rb40
-rw-r--r--spec/services/ci/retry_build_service_spec.rb2
-rw-r--r--spec/services/ci/stop_environments_service_spec.rb6
-rw-r--r--spec/services/clusters/applications/check_installation_progress_service_spec.rb4
-rw-r--r--spec/services/clusters/applications/install_service_spec.rb12
-rw-r--r--spec/services/clusters/applications/schedule_installation_service_spec.rb2
-rw-r--r--spec/services/create_deployment_service_spec.rb2
-rw-r--r--spec/services/delete_branch_service_spec.rb2
-rw-r--r--spec/services/discussions/resolve_service_spec.rb2
-rw-r--r--spec/services/files/delete_service_spec.rb64
-rw-r--r--spec/services/files/multi_service_spec.rb144
-rw-r--r--spec/services/files/update_service_spec.rb2
-rw-r--r--spec/services/git_push_service_spec.rb12
-rw-r--r--spec/services/git_tag_push_service_spec.rb2
-rw-r--r--spec/services/issuable/bulk_update_service_spec.rb8
-rw-r--r--spec/services/issuable/destroy_service_spec.rb16
-rw-r--r--spec/services/issues/build_service_spec.rb2
-rw-r--r--spec/services/issues/close_service_spec.rb6
-rw-r--r--spec/services/issues/create_service_spec.rb18
-rw-r--r--spec/services/issues/move_service_spec.rb28
-rw-r--r--spec/services/issues/reopen_service_spec.rb4
-rw-r--r--spec/services/issues/resolve_discussions_spec.rb2
-rw-r--r--spec/services/issues/update_service_spec.rb12
-rw-r--r--spec/services/labels/find_or_create_service_spec.rb2
-rw-r--r--spec/services/members/approve_access_request_service_spec.rb2
-rw-r--r--spec/services/members/authorized_destroy_service_spec.rb6
-rw-r--r--spec/services/members/create_service_spec.rb2
-rw-r--r--spec/services/members/destroy_service_spec.rb4
-rw-r--r--spec/services/merge_requests/assign_issues_service_spec.rb2
-rw-r--r--spec/services/merge_requests/build_service_spec.rb2
-rw-r--r--spec/services/merge_requests/close_service_spec.rb19
-rw-r--r--spec/services/merge_requests/conflicts/list_service_spec.rb26
-rw-r--r--spec/services/merge_requests/conflicts/resolve_service_spec.rb22
-rw-r--r--spec/services/merge_requests/create_from_issue_service_spec.rb12
-rw-r--r--spec/services/merge_requests/create_service_spec.rb18
-rw-r--r--spec/services/merge_requests/ff_merge_service_spec.rb4
-rw-r--r--spec/services/merge_requests/merge_service_spec.rb8
-rw-r--r--spec/services/merge_requests/post_merge_service_spec.rb15
-rw-r--r--spec/services/merge_requests/rebase_service_spec.rb134
-rw-r--r--spec/services/merge_requests/refresh_service_spec.rb4
-rw-r--r--spec/services/merge_requests/reopen_service_spec.rb19
-rw-r--r--spec/services/merge_requests/update_service_spec.rb10
-rw-r--r--spec/services/milestones/close_service_spec.rb2
-rw-r--r--spec/services/milestones/create_service_spec.rb2
-rw-r--r--spec/services/milestones/destroy_service_spec.rb2
-rw-r--r--spec/services/notes/create_service_spec.rb2
-rw-r--r--spec/services/notes/destroy_service_spec.rb16
-rw-r--r--spec/services/notes/post_process_service_spec.rb2
-rw-r--r--spec/services/notes/quick_actions_service_spec.rb10
-rw-r--r--spec/services/notes/update_service_spec.rb6
-rw-r--r--spec/services/projects/autocomplete_service_spec.rb4
-rw-r--r--spec/services/projects/create_service_spec.rb6
-rw-r--r--spec/services/projects/fork_service_spec.rb17
-rw-r--r--spec/services/projects/hashed_storage/migrate_repository_service_spec.rb8
-rw-r--r--spec/services/projects/transfer_service_spec.rb12
-rw-r--r--spec/services/projects/unlink_fork_service_spec.rb20
-rw-r--r--spec/services/projects/update_service_spec.rb2
-rw-r--r--spec/services/protected_branches/create_service_spec.rb16
-rw-r--r--spec/services/quick_actions/interpret_service_spec.rb12
-rw-r--r--spec/services/reset_project_cache_service_spec.rb28
-rw-r--r--spec/services/search/snippet_service_spec.rb2
-rw-r--r--spec/services/system_note_service_spec.rb36
-rw-r--r--spec/services/todo_service_spec.rb33
-rw-r--r--spec/services/update_merge_request_metrics_service_spec.rb42
-rw-r--r--spec/services/users/destroy_service_spec.rb17
-rw-r--r--spec/services/users/keys_count_service_spec.rb10
-rw-r--r--spec/spec_helper.rb2
-rw-r--r--spec/support/api/boards_shared_examples.rb180
-rw-r--r--spec/support/api/milestones_shared_examples.rb4
-rw-r--r--spec/support/api/time_tracking_shared_examples.rb6
-rw-r--r--spec/support/api/v3/time_tracking_shared_examples.rb6
-rw-r--r--spec/support/background_migrations_matchers.rb15
-rw-r--r--spec/support/batch_loader.rb5
-rw-r--r--spec/support/capybara.rb3
-rw-r--r--spec/support/cluster_application_spec.rb105
-rw-r--r--spec/support/cookie_helper.rb4
-rw-r--r--spec/support/factory_girl.rb2
-rw-r--r--spec/support/features/issuable_slash_commands_shared_examples.rb2
-rw-r--r--spec/support/google_api/cloud_platform_helpers.rb6
-rw-r--r--spec/support/markdown_feature.rb6
-rw-r--r--spec/support/mentionable_shared_examples.rb2
-rw-r--r--spec/support/reference_parser_shared_examples.rb2
-rw-r--r--spec/support/services/issuable_create_service_slash_commands_shared_examples.rb4
-rw-r--r--spec/support/services_shared_context.rb8
-rw-r--r--spec/support/shared_examples/requests/api/issuable_participants_examples.rb29
-rw-r--r--spec/support/stub_configuration.rb3
-rw-r--r--spec/support/stub_env.rb2
-rw-r--r--spec/support/test_env.rb13
-rw-r--r--spec/support/updating_mentions_shared_examples.rb2
-rw-r--r--spec/tasks/gitlab/git_rake_spec.rb38
-rw-r--r--spec/uploaders/records_uploads_spec.rb2
-rw-r--r--spec/views/events/event/_push.html.haml_spec.rb55
-rw-r--r--spec/views/projects/imports/new.html.haml_spec.rb2
-rw-r--r--spec/views/projects/jobs/show.html.haml_spec.rb4
-rw-r--r--spec/views/projects/merge_requests/show.html.haml_spec.rb2
-rw-r--r--spec/views/projects/tree/_blob_item.html.haml_spec.rb40
-rw-r--r--spec/views/projects/tree/show.html.haml_spec.rb1
-rw-r--r--spec/views/shared/notes/_form.html.haml_spec.rb2
-rw-r--r--spec/workers/background_migration_worker_spec.rb23
-rw-r--r--spec/workers/check_gcp_project_billing_worker_spec.rb61
-rw-r--r--spec/workers/concerns/application_worker_spec.rb8
-rw-r--r--spec/workers/concerns/cluster_queue_spec.rb2
-rw-r--r--spec/workers/concerns/cronjob_queue_spec.rb2
-rw-r--r--spec/workers/concerns/gitlab/github_import/queue_spec.rb2
-rw-r--r--spec/workers/concerns/pipeline_queue_spec.rb11
-rw-r--r--spec/workers/concerns/project_import_options_spec.rb40
-rw-r--r--spec/workers/concerns/repository_check_queue_spec.rb2
-rw-r--r--spec/workers/every_sidekiq_worker_spec.rb31
-rw-r--r--spec/workers/merge_worker_spec.rb2
-rw-r--r--spec/workers/rebase_worker_spec.rb27
-rw-r--r--spec/workers/repository_fork_worker_spec.rb29
-rw-r--r--spec/workers/repository_import_worker_spec.rb24
-rw-r--r--spec/workers/run_pipeline_schedule_worker_spec.rb39
-rw-r--r--spec/workers/stuck_merge_jobs_worker_spec.rb6
-rw-r--r--spec/workers/update_head_pipeline_for_merge_request_worker_spec.rb2
873 files changed, 13122 insertions, 5724 deletions
diff --git a/spec/controllers/admin/users_controller_spec.rb b/spec/controllers/admin/users_controller_spec.rb
index f044a068938..f350641a643 100644
--- a/spec/controllers/admin/users_controller_spec.rb
+++ b/spec/controllers/admin/users_controller_spec.rb
@@ -13,7 +13,7 @@ describe Admin::UsersController do
let!(:issue) { create(:issue, author: user) }
before do
- project.team << [user, :developer]
+ project.add_developer(user)
end
it 'deletes user and ghosts their contributions' do
diff --git a/spec/controllers/boards/issues_controller_spec.rb b/spec/controllers/boards/issues_controller_spec.rb
index 44d504d5852..79bbc29e80d 100644
--- a/spec/controllers/boards/issues_controller_spec.rb
+++ b/spec/controllers/boards/issues_controller_spec.rb
@@ -13,8 +13,8 @@ describe Boards::IssuesController do
let!(:list2) { create(:list, board: board, label: development, position: 1) }
before do
- project.team << [user, :master]
- project.team << [guest, :guest]
+ project.add_master(user)
+ project.add_guest(guest)
end
describe 'GET index' do
@@ -221,7 +221,7 @@ describe Boards::IssuesController do
let(:guest) { create(:user) }
before do
- project.team << [guest, :guest]
+ project.add_guest(guest)
end
it 'returns a forbidden 403 response' do
diff --git a/spec/controllers/boards/lists_controller_spec.rb b/spec/controllers/boards/lists_controller_spec.rb
index a2b432af23a..71d45a22d91 100644
--- a/spec/controllers/boards/lists_controller_spec.rb
+++ b/spec/controllers/boards/lists_controller_spec.rb
@@ -7,8 +7,8 @@ describe Boards::ListsController do
let(:guest) { create(:user) }
before do
- project.team << [user, :master]
- project.team << [guest, :guest]
+ project.add_master(user)
+ project.add_guest(guest)
end
describe 'GET index' do
diff --git a/spec/controllers/dashboard/milestones_controller_spec.rb b/spec/controllers/dashboard/milestones_controller_spec.rb
index 2f3d7be9abe..60547db82b6 100644
--- a/spec/controllers/dashboard/milestones_controller_spec.rb
+++ b/spec/controllers/dashboard/milestones_controller_spec.rb
@@ -17,7 +17,7 @@ describe Dashboard::MilestonesController do
before do
sign_in(user)
- project.team << [user, :master]
+ project.add_master(user)
end
it_behaves_like 'milestone tabs'
diff --git a/spec/controllers/dashboard/todos_controller_spec.rb b/spec/controllers/dashboard/todos_controller_spec.rb
index f9faa4fa59a..b4a731fd3a3 100644
--- a/spec/controllers/dashboard/todos_controller_spec.rb
+++ b/spec/controllers/dashboard/todos_controller_spec.rb
@@ -8,7 +8,7 @@ describe Dashboard::TodosController do
before do
sign_in(user)
- project.team << [user, :developer]
+ project.add_developer(user)
end
describe 'GET #index' do
diff --git a/spec/controllers/dashboard_controller_spec.rb b/spec/controllers/dashboard_controller_spec.rb
index 566d8515198..97c2c3fb940 100644
--- a/spec/controllers/dashboard_controller_spec.rb
+++ b/spec/controllers/dashboard_controller_spec.rb
@@ -5,7 +5,7 @@ describe DashboardController do
let(:project) { create(:project) }
before do
- project.team << [user, :master]
+ project.add_master(user)
sign_in(user)
end
diff --git a/spec/controllers/groups/group_members_controller_spec.rb b/spec/controllers/groups/group_members_controller_spec.rb
index 9c6d584f59b..362d5cc4514 100644
--- a/spec/controllers/groups/group_members_controller_spec.rb
+++ b/spec/controllers/groups/group_members_controller_spec.rb
@@ -62,6 +62,25 @@ describe Groups::GroupMembersController do
end
end
+ describe 'PUT update' do
+ let(:requester) { create(:group_member, :access_request, group: group) }
+
+ before do
+ group.add_owner(user)
+ sign_in(user)
+ end
+
+ Gitlab::Access.options.each do |label, value|
+ it "can change the access level to #{label}" do
+ xhr :put, :update, group_member: { access_level: value },
+ group_id: group,
+ id: requester
+
+ expect(requester.reload.human_access).to eq(label)
+ end
+ end
+ end
+
describe 'DELETE destroy' do
let(:member) { create(:group_member, :developer, group: group) }
diff --git a/spec/controllers/groups/milestones_controller_spec.rb b/spec/controllers/groups/milestones_controller_spec.rb
index c1aba46be04..733386500ca 100644
--- a/spec/controllers/groups/milestones_controller_spec.rb
+++ b/spec/controllers/groups/milestones_controller_spec.rb
@@ -28,7 +28,7 @@ describe Groups::MilestonesController do
before do
sign_in(user)
group.add_owner(user)
- project.team << [user, :master]
+ project.add_master(user)
end
describe '#index' do
diff --git a/spec/controllers/notification_settings_controller_spec.rb b/spec/controllers/notification_settings_controller_spec.rb
index 9014b8b5084..e133950e684 100644
--- a/spec/controllers/notification_settings_controller_spec.rb
+++ b/spec/controllers/notification_settings_controller_spec.rb
@@ -6,7 +6,7 @@ describe NotificationSettingsController do
let(:user) { create(:user) }
before do
- project.team << [user, :developer]
+ project.add_developer(user)
end
describe '#create' do
diff --git a/spec/controllers/projects/artifacts_controller_spec.rb b/spec/controllers/projects/artifacts_controller_spec.rb
index 581b3e4e4ab..46d618fa682 100644
--- a/spec/controllers/projects/artifacts_controller_spec.rb
+++ b/spec/controllers/projects/artifacts_controller_spec.rb
@@ -111,6 +111,8 @@ describe Projects::ArtifactsController do
end
describe 'GET raw' do
+ subject { get(:raw, namespace_id: project.namespace, project_id: project, job_id: job, path: path) }
+
context 'when the file exists' do
let(:path) { 'ci_artifacts.txt' }
diff --git a/spec/controllers/projects/avatars_controller_spec.rb b/spec/controllers/projects/avatars_controller_spec.rb
index f5ea097af8b..3bbe168f6d5 100644
--- a/spec/controllers/projects/avatars_controller_spec.rb
+++ b/spec/controllers/projects/avatars_controller_spec.rb
@@ -6,7 +6,7 @@ describe Projects::AvatarsController do
before do
sign_in(user)
- project.team << [user, :master]
+ project.add_master(user)
controller.instance_variable_set(:@project, project)
end
diff --git a/spec/controllers/projects/blame_controller_spec.rb b/spec/controllers/projects/blame_controller_spec.rb
index 54282aa4001..88d4f4e9cd0 100644
--- a/spec/controllers/projects/blame_controller_spec.rb
+++ b/spec/controllers/projects/blame_controller_spec.rb
@@ -7,7 +7,7 @@ describe Projects::BlameController do
before do
sign_in(user)
- project.team << [user, :master]
+ project.add_master(user)
controller.instance_variable_set(:@project, project)
end
diff --git a/spec/controllers/projects/blob_controller_spec.rb b/spec/controllers/projects/blob_controller_spec.rb
index 6a1c07b4a0b..00a7df6ccc8 100644
--- a/spec/controllers/projects/blob_controller_spec.rb
+++ b/spec/controllers/projects/blob_controller_spec.rb
@@ -89,7 +89,7 @@ describe Projects::BlobController do
end
before do
- project.team << [user, :master]
+ project.add_master(user)
sign_in(user)
end
@@ -147,7 +147,7 @@ describe Projects::BlobController do
let(:developer) { create(:user) }
before do
- project.team << [developer, :developer]
+ project.add_developer(developer)
sign_in(developer)
get :edit, default_params
end
@@ -161,7 +161,7 @@ describe Projects::BlobController do
let(:master) { create(:user) }
before do
- project.team << [master, :master]
+ project.add_master(master)
sign_in(master)
get :edit, default_params
end
@@ -190,7 +190,7 @@ describe Projects::BlobController do
end
before do
- project.team << [user, :master]
+ project.add_master(user)
sign_in(user)
end
diff --git a/spec/controllers/projects/boards_controller_spec.rb b/spec/controllers/projects/boards_controller_spec.rb
index d6ccb92c54b..4d765229bde 100644
--- a/spec/controllers/projects/boards_controller_spec.rb
+++ b/spec/controllers/projects/boards_controller_spec.rb
@@ -5,7 +5,7 @@ describe Projects::BoardsController do
let(:user) { create(:user) }
before do
- project.team << [user, :master]
+ project.add_master(user)
sign_in(user)
end
@@ -55,6 +55,16 @@ describe Projects::BoardsController do
end
end
+ context 'issues are disabled' do
+ let(:project) { create(:project, :issues_disabled) }
+
+ it 'returns a not found 404 response' do
+ list_boards
+
+ expect(response).to have_gitlab_http_status(404)
+ end
+ end
+
def list_boards(format: :html)
get :index, namespace_id: project.namespace,
project_id: project,
diff --git a/spec/controllers/projects/branches_controller_spec.rb b/spec/controllers/projects/branches_controller_spec.rb
index d731200f70f..734396ddf7b 100644
--- a/spec/controllers/projects/branches_controller_spec.rb
+++ b/spec/controllers/projects/branches_controller_spec.rb
@@ -6,8 +6,8 @@ describe Projects::BranchesController do
let(:developer) { create(:user) }
before do
- project.team << [user, :master]
- project.team << [user, :developer]
+ project.add_master(user)
+ project.add_developer(user)
allow(project).to receive(:branches).and_return(['master', 'foo/bar/baz'])
allow(project).to receive(:tags).and_return(['v1.0.0', 'v2.0.0'])
@@ -148,6 +148,20 @@ describe Projects::BranchesController do
end
end
+ context 'when create branch service fails' do
+ let(:branch) { "./invalid-branch-name" }
+
+ it "doesn't post a system note" do
+ expect(SystemNoteService).not_to receive(:new_issue_branch)
+
+ post :create,
+ namespace_id: project.namespace,
+ project_id: project,
+ branch_name: branch,
+ issue_iid: issue.iid
+ end
+ end
+
context 'without issue feature access' do
before do
project.update!(visibility_level: Gitlab::VisibilityLevel::PUBLIC)
diff --git a/spec/controllers/projects/clusters/applications_controller_spec.rb b/spec/controllers/projects/clusters/applications_controller_spec.rb
index 8b460646059..99fdff5f846 100644
--- a/spec/controllers/projects/clusters/applications_controller_spec.rb
+++ b/spec/controllers/projects/clusters/applications_controller_spec.rb
@@ -52,7 +52,7 @@ describe Projects::Clusters::ApplicationsController do
context 'when application is already installing' do
before do
- create(:cluster_applications_helm, :installing, cluster: cluster)
+ create(:clusters_applications_helm, :installing, cluster: cluster)
end
it 'returns 400' do
diff --git a/spec/controllers/projects/clusters/gcp_controller_spec.rb b/spec/controllers/projects/clusters/gcp_controller_spec.rb
index ee7928beb7e..be19fa93183 100644
--- a/spec/controllers/projects/clusters/gcp_controller_spec.rb
+++ b/spec/controllers/projects/clusters/gcp_controller_spec.rb
@@ -17,7 +17,6 @@ describe Projects::Clusters::GcpController do
context 'when omniauth has been configured' do
let(:key) { 'secret-key' }
-
let(:session_key_for_redirect_uri) do
GoogleApi::CloudPlatform::Client.session_key_for_redirect_uri(key)
end
@@ -78,6 +77,8 @@ describe Projects::Clusters::GcpController do
end
it 'has new object' do
+ expect(controller).to receive(:authorize_google_project_billing)
+
go
expect(assigns(:cluster)).to be_an_instance_of(Clusters::Cluster)
@@ -138,7 +139,11 @@ describe Projects::Clusters::GcpController do
stub_google_api_validate_token
end
- context 'when creates a cluster on gke' do
+ context 'when google project billing is enabled' do
+ before do
+ stub_google_project_billing_status
+ end
+
it 'creates a new cluster' do
expect(ClusterProvisionWorker).to receive(:perform_async)
expect { go }.to change { Clusters::Cluster.count }
@@ -148,6 +153,15 @@ describe Projects::Clusters::GcpController do
expect(project.clusters.first).to be_kubernetes
end
end
+
+ context 'when google project billing is not enabled' do
+ it 'renders the cluster form with an error' do
+ go
+
+ expect(response).to set_flash[:error]
+ expect(response).to render_template('new')
+ end
+ end
end
context 'when access token is expired' do
diff --git a/spec/controllers/projects/clusters_controller_spec.rb b/spec/controllers/projects/clusters_controller_spec.rb
index 280b7e4d8b9..a3b13647c92 100644
--- a/spec/controllers/projects/clusters_controller_spec.rb
+++ b/spec/controllers/projects/clusters_controller_spec.rb
@@ -27,13 +27,6 @@ describe Projects::ClustersController do
expect(assigns(:clusters)).to match_array([enabled_cluster, disabled_cluster])
end
- it 'assigns counters to correct values' do
- go
-
- expect(assigns(:active_count)).to eq(1)
- expect(assigns(:inactive_count)).to eq(1)
- end
-
context 'when page is specified' do
let(:last_page) { project.clusters.page.total_pages }
@@ -48,20 +41,6 @@ describe Projects::ClustersController do
expect(assigns(:clusters).current_page).to eq(last_page)
end
end
-
- context 'when only enabled clusters are requested' do
- it 'returns only enabled clusters' do
- get :index, namespace_id: project.namespace, project_id: project, scope: 'active'
- expect(assigns(:clusters)).to all(have_attributes(enabled: true))
- end
- end
-
- context 'when only disabled clusters are requested' do
- it 'returns only disabled clusters' do
- get :index, namespace_id: project.namespace, project_id: project, scope: 'inactive'
- expect(assigns(:clusters)).to all(have_attributes(enabled: false))
- end
- end
end
context 'when project does not have a cluster' do
@@ -74,13 +53,6 @@ describe Projects::ClustersController do
expect(response).to render_template(:index, partial: :empty_state)
expect(assigns(:clusters)).to eq([])
end
-
- it 'assigns counters to zero' do
- go
-
- expect(assigns(:active_count)).to eq(0)
- expect(assigns(:inactive_count)).to eq(0)
- end
end
end
diff --git a/spec/controllers/projects/commits_controller_spec.rb b/spec/controllers/projects/commits_controller_spec.rb
index c459d732507..73fb90d73ec 100644
--- a/spec/controllers/projects/commits_controller_spec.rb
+++ b/spec/controllers/projects/commits_controller_spec.rb
@@ -6,7 +6,7 @@ describe Projects::CommitsController do
before do
sign_in(user)
- project.team << [user, :master]
+ project.add_master(user)
end
describe "GET show" do
diff --git a/spec/controllers/projects/compare_controller_spec.rb b/spec/controllers/projects/compare_controller_spec.rb
index fe5818da0bc..046ce027965 100644
--- a/spec/controllers/projects/compare_controller_spec.rb
+++ b/spec/controllers/projects/compare_controller_spec.rb
@@ -8,7 +8,7 @@ describe Projects::CompareController do
before do
sign_in(user)
- project.team << [user, :master]
+ project.add_master(user)
end
it 'compare shows some diffs' do
diff --git a/spec/controllers/projects/cycle_analytics_controller_spec.rb b/spec/controllers/projects/cycle_analytics_controller_spec.rb
index 6fae52edbad..7c708a418a7 100644
--- a/spec/controllers/projects/cycle_analytics_controller_spec.rb
+++ b/spec/controllers/projects/cycle_analytics_controller_spec.rb
@@ -6,7 +6,7 @@ describe Projects::CycleAnalyticsController do
before do
sign_in(user)
- project.team << [user, :master]
+ project.add_master(user)
end
describe 'cycle analytics not set up flag' do
diff --git a/spec/controllers/projects/deploy_keys_controller_spec.rb b/spec/controllers/projects/deploy_keys_controller_spec.rb
index c3208357694..97db69427e9 100644
--- a/spec/controllers/projects/deploy_keys_controller_spec.rb
+++ b/spec/controllers/projects/deploy_keys_controller_spec.rb
@@ -5,7 +5,7 @@ describe Projects::DeployKeysController do
let(:user) { create(:user) }
before do
- project.team << [user, :master]
+ project.add_master(user)
sign_in(user)
end
@@ -48,7 +48,7 @@ describe Projects::DeployKeysController do
end
before do
- project2.team << [user, :developer]
+ project2.add_developer(user)
end
it 'returns json in a correct format' do
diff --git a/spec/controllers/projects/deployments_controller_spec.rb b/spec/controllers/projects/deployments_controller_spec.rb
index 3164fd5c143..73e7921fab7 100644
--- a/spec/controllers/projects/deployments_controller_spec.rb
+++ b/spec/controllers/projects/deployments_controller_spec.rb
@@ -8,7 +8,7 @@ describe Projects::DeploymentsController do
let(:environment) { create(:environment, name: 'production', project: project) }
before do
- project.team << [user, :master]
+ project.add_master(user)
sign_in(user)
end
diff --git a/spec/controllers/projects/discussions_controller_spec.rb b/spec/controllers/projects/discussions_controller_spec.rb
index 3bf676637a2..00328d3ea51 100644
--- a/spec/controllers/projects/discussions_controller_spec.rb
+++ b/spec/controllers/projects/discussions_controller_spec.rb
@@ -31,7 +31,7 @@ describe Projects::DiscussionsController do
context "when the user is authorized to resolve the discussion" do
before do
- project.team << [user, :developer]
+ project.add_developer(user)
end
context "when the discussion is not resolvable" do
@@ -92,7 +92,7 @@ describe Projects::DiscussionsController do
context "when the user is authorized to resolve the discussion" do
before do
- project.team << [user, :developer]
+ project.add_developer(user)
end
context "when the discussion is not resolvable" do
diff --git a/spec/controllers/projects/find_file_controller_spec.rb b/spec/controllers/projects/find_file_controller_spec.rb
index 6a5433bcc9c..505fe82851a 100644
--- a/spec/controllers/projects/find_file_controller_spec.rb
+++ b/spec/controllers/projects/find_file_controller_spec.rb
@@ -7,7 +7,7 @@ describe Projects::FindFileController do
before do
sign_in(user)
- project.team << [user, :master]
+ project.add_master(user)
controller.instance_variable_set(:@project, project)
end
diff --git a/spec/controllers/projects/forks_controller_spec.rb b/spec/controllers/projects/forks_controller_spec.rb
index 1bedb8ebdff..c4b32dc3a09 100644
--- a/spec/controllers/projects/forks_controller_spec.rb
+++ b/spec/controllers/projects/forks_controller_spec.rb
@@ -51,7 +51,7 @@ describe Projects::ForksController do
context 'when user is a member of the Project' do
before do
- forked_project.team << [project.creator, :developer]
+ forked_project.add_developer(project.creator)
end
it 'sees the project listed' do
diff --git a/spec/controllers/projects/graphs_controller_spec.rb b/spec/controllers/projects/graphs_controller_spec.rb
index 5af03ae118c..c3605555fe7 100644
--- a/spec/controllers/projects/graphs_controller_spec.rb
+++ b/spec/controllers/projects/graphs_controller_spec.rb
@@ -6,7 +6,7 @@ describe Projects::GraphsController do
before do
sign_in(user)
- project.team << [user, :master]
+ project.add_master(user)
end
describe 'GET languages' do
diff --git a/spec/controllers/projects/group_links_controller_spec.rb b/spec/controllers/projects/group_links_controller_spec.rb
index f8c792cd0f0..5bfc3d31401 100644
--- a/spec/controllers/projects/group_links_controller_spec.rb
+++ b/spec/controllers/projects/group_links_controller_spec.rb
@@ -7,7 +7,7 @@ describe Projects::GroupLinksController do
let(:user) { create(:user) }
before do
- project.team << [user, :master]
+ project.add_master(user)
sign_in(user)
end
diff --git a/spec/controllers/projects/hooks_controller_spec.rb b/spec/controllers/projects/hooks_controller_spec.rb
index 07174660f46..aba70c6d4c1 100644
--- a/spec/controllers/projects/hooks_controller_spec.rb
+++ b/spec/controllers/projects/hooks_controller_spec.rb
@@ -5,7 +5,7 @@ describe Projects::HooksController do
let(:user) { create(:user) }
before do
- project.team << [user, :master]
+ project.add_master(user)
sign_in(user)
end
diff --git a/spec/controllers/projects/imports_controller_spec.rb b/spec/controllers/projects/imports_controller_spec.rb
index 2a5ec6d584b..7fb4c1b7425 100644
--- a/spec/controllers/projects/imports_controller_spec.rb
+++ b/spec/controllers/projects/imports_controller_spec.rb
@@ -9,7 +9,7 @@ describe Projects::ImportsController do
before do
sign_in(user)
- project.team << [user, :master]
+ project.add_master(user)
end
it 'renders template' do
@@ -30,7 +30,7 @@ describe Projects::ImportsController do
before do
sign_in(user)
- project.team << [user, :master]
+ project.add_master(user)
end
context 'when import is in progress' do
diff --git a/spec/controllers/projects/issues_controller_spec.rb b/spec/controllers/projects/issues_controller_spec.rb
index 4dbbaecdd6d..6b7db947216 100644
--- a/spec/controllers/projects/issues_controller_spec.rb
+++ b/spec/controllers/projects/issues_controller_spec.rb
@@ -37,7 +37,7 @@ describe Projects::IssuesController do
context 'internal issue tracker' do
before do
sign_in(user)
- project.team << [user, :developer]
+ project.add_developer(user)
end
it_behaves_like "issuables list meta-data", :issue
@@ -69,7 +69,7 @@ describe Projects::IssuesController do
before do
sign_in(user)
- project.team << [user, :developer]
+ project.add_developer(user)
allow(Kaminari.config).to receive(:default_per_page).and_return(1)
end
@@ -116,7 +116,7 @@ describe Projects::IssuesController do
context 'internal issue tracker' do
before do
sign_in(user)
- project.team << [user, :developer]
+ project.add_developer(user)
end
it 'builds a new issue' do
@@ -127,7 +127,7 @@ describe Projects::IssuesController do
it 'fills in an issue for a merge request' do
project_with_repository = create(:project, :repository)
- project_with_repository.team << [user, :developer]
+ project_with_repository.add_developer(user)
mr = create(:merge_request_with_diff_notes, source_project: project_with_repository)
get :new, namespace_id: project_with_repository.namespace, project_id: project_with_repository, merge_request_to_resolve_discussions_of: mr.iid
@@ -153,7 +153,7 @@ describe Projects::IssuesController do
before do
sign_in(user)
- project.team << [user, :developer]
+ project.add_developer(user)
external = double
allow(project).to receive(:external_issue_tracker).and_return(external)
@@ -272,6 +272,20 @@ describe Projects::IssuesController do
expect(response).to have_http_status(:ok)
expect(issue.reload.title).to eq('New title')
end
+
+ context 'when Akismet is enabled and the issue is identified as spam' do
+ before do
+ stub_application_setting(recaptcha_enabled: true)
+ allow_any_instance_of(SpamService).to receive(:check_for_spam?).and_return(true)
+ allow_any_instance_of(AkismetService).to receive(:spam?).and_return(true)
+ end
+
+ it 'renders json with recaptcha_html' do
+ subject
+
+ expect(JSON.parse(response.body)).to have_key('recaptcha_html')
+ end
+ end
end
context 'when user does not have access to update issue' do
@@ -315,7 +329,7 @@ describe Projects::IssuesController do
it 'does not list confidential issues for project members with guest role' do
sign_in(member)
- project.team << [member, :guest]
+ project.add_guest(member)
get_issues
@@ -340,7 +354,7 @@ describe Projects::IssuesController do
it 'lists confidential issues for project members' do
sign_in(member)
- project.team << [member, :developer]
+ project.add_developer(member)
get_issues
@@ -380,7 +394,7 @@ describe Projects::IssuesController do
it 'returns 404 for project members with guest role' do
sign_in(member)
- project.team << [member, :guest]
+ project.add_guest(member)
go(id: unescaped_parameter_value.to_param)
expect(response).to have_gitlab_http_status :not_found
@@ -402,7 +416,7 @@ describe Projects::IssuesController do
it "returns #{http_status[:success]} for project members" do
sign_in(member)
- project.team << [member, :developer]
+ project.add_developer(member)
go(id: unescaped_parameter_value.to_param)
expect(response).to have_gitlab_http_status http_status[:success]
@@ -436,7 +450,7 @@ describe Projects::IssuesController do
before do
sign_in(user)
- project.team << [user, :developer]
+ project.add_developer(user)
end
it_behaves_like 'restricted action', success: 200
@@ -504,17 +518,16 @@ describe Projects::IssuesController do
expect(spam_logs.first.recaptcha_verified).to be_falsey
end
- it 'renders json errors' do
+ it 'renders recaptcha_html json response' do
update_issue
- expect(json_response)
- .to eql("errors" => ["Your issue has been recognized as spam. Please, change the content or solve the reCAPTCHA to proceed."])
+ expect(json_response).to have_key('recaptcha_html')
end
- it 'returns 422 status' do
+ it 'returns 200 status' do
update_issue
- expect(response).to have_gitlab_http_status(422)
+ expect(response).to have_gitlab_http_status(200)
end
end
@@ -581,7 +594,7 @@ describe Projects::IssuesController do
let(:deleted_user) { create(:user) }
before do
- project.team << [user, :developer]
+ project.add_developer(user)
issue.update!(last_edited_by: deleted_user, last_edited_at: Time.now)
@@ -625,7 +638,7 @@ describe Projects::IssuesController do
def post_new_issue(issue_attrs = {}, additional_params = {})
sign_in(user)
project = create(:project, :public)
- project.team << [user, :developer]
+ project.add_developer(user)
post :create, {
namespace_id: project.namespace.to_param,
@@ -642,7 +655,7 @@ describe Projects::IssuesController do
let(:project) { merge_request.source_project }
before do
- project.team << [user, :master]
+ project.add_master(user)
sign_in user
end
@@ -816,7 +829,7 @@ describe Projects::IssuesController do
def post_spam
admin = create(:admin)
create(:user_agent_detail, subject: issue)
- project.team << [admin, :master]
+ project.add_master(admin)
sign_in(admin)
post :mark_as_spam, {
namespace_id: project.namespace,
@@ -861,7 +874,7 @@ describe Projects::IssuesController do
end
it 'delegates the update of the todos count cache to TodoService' do
- expect_any_instance_of(TodoService).to receive(:destroy_issuable).with(issue, owner).once
+ expect_any_instance_of(TodoService).to receive(:destroy_target).with(issue).once
delete :destroy, namespace_id: project.namespace, project_id: project, id: issue.iid
end
diff --git a/spec/controllers/projects/jobs_controller_spec.rb b/spec/controllers/projects/jobs_controller_spec.rb
index 7490f8fefce..e6a4e7c8257 100644
--- a/spec/controllers/projects/jobs_controller_spec.rb
+++ b/spec/controllers/projects/jobs_controller_spec.rb
@@ -374,7 +374,7 @@ describe Projects::JobsController do
let(:role) { :master }
before do
- project.team << [user, role]
+ project.add_role(user, role)
sign_in(user)
post_erase
diff --git a/spec/controllers/projects/labels_controller_spec.rb b/spec/controllers/projects/labels_controller_spec.rb
index cf83f2f3265..452d7e23983 100644
--- a/spec/controllers/projects/labels_controller_spec.rb
+++ b/spec/controllers/projects/labels_controller_spec.rb
@@ -6,7 +6,7 @@ describe Projects::LabelsController do
let(:user) { create(:user) }
before do
- project.team << [user, :master]
+ project.add_master(user)
sign_in(user)
end
diff --git a/spec/controllers/projects/mattermosts_controller_spec.rb b/spec/controllers/projects/mattermosts_controller_spec.rb
index 33d48ff94d1..c5ac0be27bb 100644
--- a/spec/controllers/projects/mattermosts_controller_spec.rb
+++ b/spec/controllers/projects/mattermosts_controller_spec.rb
@@ -5,7 +5,7 @@ describe Projects::MattermostsController do
let!(:user) { create(:user) }
before do
- project.team << [user, :master]
+ project.add_master(user)
sign_in(user)
end
diff --git a/spec/controllers/projects/merge_requests/creations_controller_spec.rb b/spec/controllers/projects/merge_requests/creations_controller_spec.rb
index 7fdddc02fd3..7e2366847f4 100644
--- a/spec/controllers/projects/merge_requests/creations_controller_spec.rb
+++ b/spec/controllers/projects/merge_requests/creations_controller_spec.rb
@@ -6,7 +6,7 @@ describe Projects::MergeRequests::CreationsController do
let(:fork_project) { create(:forked_project_with_submodules) }
before do
- fork_project.team << [user, :master]
+ fork_project.add_master(user)
sign_in(user)
end
@@ -86,7 +86,7 @@ describe Projects::MergeRequests::CreationsController do
let(:other_project) { create(:project, :repository) }
before do
- other_project.team << [user, :master]
+ other_project.add_master(user)
end
context 'when the path exists in the diff' do
diff --git a/spec/controllers/projects/merge_requests/diffs_controller_spec.rb b/spec/controllers/projects/merge_requests/diffs_controller_spec.rb
index ba97ccfbbd4..5d297c654bf 100644
--- a/spec/controllers/projects/merge_requests/diffs_controller_spec.rb
+++ b/spec/controllers/projects/merge_requests/diffs_controller_spec.rb
@@ -151,7 +151,7 @@ describe Projects::MergeRequests::DiffsController do
let(:other_project) { create(:project) }
before do
- other_project.team << [user, :master]
+ other_project.add_master(user)
diff_for_path(old_path: existing_path, new_path: existing_path, project_id: other_project)
end
diff --git a/spec/controllers/projects/merge_requests_controller_spec.rb b/spec/controllers/projects/merge_requests_controller_spec.rb
index 51d5d6a52b3..c8cc6b374f6 100644
--- a/spec/controllers/projects/merge_requests_controller_spec.rb
+++ b/spec/controllers/projects/merge_requests_controller_spec.rb
@@ -91,11 +91,19 @@ describe Projects::MergeRequestsController do
end
end
- context 'without basic serializer param' do
- it 'renders the merge request in the json format' do
- go(format: :json)
+ context 'with widget serializer param' do
+ it 'renders widget MR entity as json' do
+ go(serializer: 'widget', format: :json)
- expect(response).to match_response_schema('entities/merge_request')
+ expect(response).to match_response_schema('entities/merge_request_widget')
+ end
+ end
+
+ context 'when no serialiser was passed' do
+ it 'renders widget MR entity as json' do
+ go(serializer: nil, format: :json)
+
+ expect(response).to match_response_schema('entities/merge_request_widget')
end
end
end
@@ -468,7 +476,7 @@ describe Projects::MergeRequestsController do
end
it 'delegates the update of the todos count cache to TodoService' do
- expect_any_instance_of(TodoService).to receive(:destroy_issuable).with(merge_request, owner).once
+ expect_any_instance_of(TodoService).to receive(:destroy_target).with(merge_request).once
delete :destroy, namespace_id: project.namespace, project_id: project, id: merge_request.iid
end
@@ -676,4 +684,62 @@ describe Projects::MergeRequestsController do
format: :json
end
end
+
+ describe 'POST #rebase' do
+ let(:viewer) { user }
+
+ def post_rebase
+ post :rebase, namespace_id: project.namespace, project_id: project, id: merge_request
+ end
+
+ def expect_rebase_worker_for(user)
+ expect(RebaseWorker).to receive(:perform_async).with(merge_request.id, user.id)
+ end
+
+ context 'successfully' do
+ it 'enqeues a RebaseWorker' do
+ expect_rebase_worker_for(viewer)
+
+ post_rebase
+
+ expect(response.status).to eq(200)
+ end
+ end
+
+ context 'with a forked project' do
+ let(:fork_project) { create(:project, :repository, forked_from_project: project) }
+ let(:fork_owner) { fork_project.owner }
+
+ before do
+ merge_request.update!(source_project: fork_project)
+ fork_project.add_reporter(user)
+ end
+
+ context 'user cannot push to source branch' do
+ it 'returns 404' do
+ expect_rebase_worker_for(viewer).never
+
+ post_rebase
+
+ expect(response.status).to eq(404)
+ end
+ end
+
+ context 'user can push to source branch' do
+ before do
+ project.add_reporter(fork_owner)
+
+ sign_in(fork_owner)
+ end
+
+ it 'returns 200' do
+ expect_rebase_worker_for(fork_owner)
+
+ post_rebase
+
+ expect(response.status).to eq(200)
+ end
+ end
+ end
+ end
end
diff --git a/spec/controllers/projects/milestones_controller_spec.rb b/spec/controllers/projects/milestones_controller_spec.rb
index 209979e642d..00cf464ec5b 100644
--- a/spec/controllers/projects/milestones_controller_spec.rb
+++ b/spec/controllers/projects/milestones_controller_spec.rb
@@ -11,7 +11,7 @@ describe Projects::MilestonesController do
before do
sign_in(user)
- project.team << [user, :master]
+ project.add_master(user)
controller.instance_variable_set(:@project, project)
end
diff --git a/spec/controllers/projects/notes_controller_spec.rb b/spec/controllers/projects/notes_controller_spec.rb
index 37e9f863fc4..de132dfaa21 100644
--- a/spec/controllers/projects/notes_controller_spec.rb
+++ b/spec/controllers/projects/notes_controller_spec.rb
@@ -32,7 +32,7 @@ describe Projects::NotesController do
before do
sign_in(user)
- project.team << [user, :developer]
+ project.add_developer(user)
end
it 'passes last_fetched_at from headers to NotesFinder' do
@@ -351,7 +351,7 @@ describe Projects::NotesController do
before do
sign_in(note.author)
- project.team << [note.author, :developer]
+ project.add_developer(note.author)
end
it "updates the note" do
@@ -372,7 +372,7 @@ describe Projects::NotesController do
context 'user is the author of a note' do
before do
sign_in(note.author)
- project.team << [note.author, :developer]
+ project.add_developer(note.author)
end
it "returns status 200 for html" do
@@ -389,7 +389,7 @@ describe Projects::NotesController do
context 'user is not the author of a note' do
before do
sign_in(user)
- project.team << [user, :developer]
+ project.add_developer(user)
end
it "returns status 404" do
@@ -403,7 +403,7 @@ describe Projects::NotesController do
describe 'POST toggle_award_emoji' do
before do
sign_in(user)
- project.team << [user, :developer]
+ project.add_developer(user)
end
it "toggles the award emoji" do
@@ -445,7 +445,7 @@ describe Projects::NotesController do
context "when the user is authorized to resolve the note" do
before do
- project.team << [user, :developer]
+ project.add_developer(user)
end
context "when the note is not resolvable" do
@@ -506,7 +506,7 @@ describe Projects::NotesController do
context "when the user is authorized to resolve the note" do
before do
- project.team << [user, :developer]
+ project.add_developer(user)
end
context "when the note is not resolvable" do
diff --git a/spec/controllers/projects/pipeline_schedules_controller_spec.rb b/spec/controllers/projects/pipeline_schedules_controller_spec.rb
index 4e52e261920..966ffdf6996 100644
--- a/spec/controllers/projects/pipeline_schedules_controller_spec.rb
+++ b/spec/controllers/projects/pipeline_schedules_controller_spec.rb
@@ -3,10 +3,12 @@ require 'spec_helper'
describe Projects::PipelineSchedulesController do
include AccessMatchersForController
- set(:project) { create(:project, :public) }
- let!(:pipeline_schedule) { create(:ci_pipeline_schedule, project: project) }
+ set(:project) { create(:project, :public, :repository) }
+ set(:pipeline_schedule) { create(:ci_pipeline_schedule, project: project) }
describe 'GET #index' do
+ render_views
+
let(:scope) { nil }
let!(:inactive_pipeline_schedule) do
create(:ci_pipeline_schedule, :inactive, project: project)
@@ -96,7 +98,7 @@ describe Projects::PipelineSchedulesController do
end
end
- context 'when variables_attributes has two variables and duplicted' do
+ context 'when variables_attributes has two variables and duplicated' do
let(:schedule) do
basic_param.merge({
variables_attributes: [{ key: 'AAA', value: 'AAA123' }, { key: 'AAA', value: 'BBB123' }]
@@ -364,6 +366,65 @@ describe Projects::PipelineSchedulesController do
end
end
+ describe 'POST #play', :clean_gitlab_redis_cache do
+ set(:user) { create(:user) }
+ let(:ref) { 'master' }
+
+ before do
+ project.add_developer(user)
+
+ sign_in(user)
+ end
+
+ context 'when an anonymous user makes the request' do
+ before do
+ sign_out(user)
+ end
+
+ it 'does not allow pipeline to be executed' do
+ expect(RunPipelineScheduleWorker).not_to receive(:perform_async)
+
+ post :play, namespace_id: project.namespace.to_param, project_id: project, id: pipeline_schedule.id
+
+ expect(response).to have_gitlab_http_status(404)
+ end
+ end
+
+ context 'when a developer makes the request' do
+ it 'executes a new pipeline' do
+ expect(RunPipelineScheduleWorker).to receive(:perform_async).with(pipeline_schedule.id, user.id).and_return('job-123')
+
+ post :play, namespace_id: project.namespace.to_param, project_id: project, id: pipeline_schedule.id
+
+ expect(flash[:notice]).to start_with 'Successfully scheduled a pipeline to run'
+ expect(response).to have_gitlab_http_status(302)
+ end
+
+ it 'prevents users from scheduling the same pipeline repeatedly' do
+ 2.times do
+ post :play, namespace_id: project.namespace.to_param, project_id: project, id: pipeline_schedule.id
+ end
+
+ expect(flash.to_a.size).to eq(2)
+ expect(flash[:alert]).to eq 'You cannot play this scheduled pipeline at the moment. Please wait a minute.'
+ expect(response).to have_gitlab_http_status(302)
+ end
+ end
+
+ context 'when a developer attempts to schedule a protected ref' do
+ it 'does not allow pipeline to be executed' do
+ create(:protected_branch, project: project, name: ref)
+ protected_schedule = create(:ci_pipeline_schedule, project: project, ref: ref)
+
+ expect(RunPipelineScheduleWorker).not_to receive(:perform_async)
+
+ post :play, namespace_id: project.namespace.to_param, project_id: project, id: protected_schedule.id
+
+ expect(response).to have_gitlab_http_status(404)
+ end
+ end
+ end
+
describe 'DELETE #destroy' do
set(:user) { create(:user) }
diff --git a/spec/controllers/projects/pipelines_controller_spec.rb b/spec/controllers/projects/pipelines_controller_spec.rb
index 1604a2da485..35ac999cc65 100644
--- a/spec/controllers/projects/pipelines_controller_spec.rb
+++ b/spec/controllers/projects/pipelines_controller_spec.rb
@@ -17,13 +17,10 @@ describe Projects::PipelinesController do
describe 'GET index.json' do
before do
- branch_head = project.commit
- parent = branch_head.parent
-
- create(:ci_empty_pipeline, status: 'pending', project: project, sha: branch_head.id)
- create(:ci_empty_pipeline, status: 'running', project: project, sha: branch_head.id)
- create(:ci_empty_pipeline, status: 'created', project: project, sha: parent.id)
- create(:ci_empty_pipeline, status: 'success', project: project, sha: parent.id)
+ %w(pending running created success).each_with_index do |status, index|
+ sha = project.commit("HEAD~#{index}")
+ create(:ci_empty_pipeline, status: status, project: project, sha: sha)
+ end
end
subject do
@@ -46,7 +43,7 @@ describe Projects::PipelinesController do
context 'when performing gitaly calls', :request_store do
it 'limits the Gitaly requests' do
- expect { subject }.to change { Gitlab::GitalyClient.get_request_count }.by(8)
+ expect { subject }.to change { Gitlab::GitalyClient.get_request_count }.by(3)
end
end
end
diff --git a/spec/controllers/projects/project_members_controller_spec.rb b/spec/controllers/projects/project_members_controller_spec.rb
index a34dc27a5ed..46b08a03b19 100644
--- a/spec/controllers/projects/project_members_controller_spec.rb
+++ b/spec/controllers/projects/project_members_controller_spec.rb
@@ -21,7 +21,7 @@ describe Projects::ProjectMembersController do
context 'when user does not have enough rights' do
before do
- project.team << [user, :developer]
+ project.add_developer(user)
end
it 'returns 404' do
@@ -37,7 +37,7 @@ describe Projects::ProjectMembersController do
context 'when user has enough rights' do
before do
- project.team << [user, :master]
+ project.add_master(user)
end
it 'adds user to members' do
@@ -66,6 +66,26 @@ describe Projects::ProjectMembersController do
end
end
+ describe 'PUT update' do
+ let(:requester) { create(:project_member, :access_request, project: project) }
+
+ before do
+ project.add_master(user)
+ sign_in(user)
+ end
+
+ Gitlab::Access.options.each do |label, value|
+ it "can change the access level to #{label}" do
+ xhr :put, :update, project_member: { access_level: value },
+ namespace_id: project.namespace,
+ project_id: project,
+ id: requester
+
+ expect(requester.reload.human_access).to eq(label)
+ end
+ end
+ end
+
describe 'DELETE destroy' do
let(:member) { create(:project_member, :developer, project: project) }
@@ -86,7 +106,7 @@ describe Projects::ProjectMembersController do
context 'when member is found' do
context 'when user does not have enough rights' do
before do
- project.team << [user, :developer]
+ project.add_developer(user)
end
it 'returns 404' do
@@ -101,7 +121,7 @@ describe Projects::ProjectMembersController do
context 'when user has enough rights' do
before do
- project.team << [user, :master]
+ project.add_master(user)
end
it '[HTML] removes user from members' do
@@ -144,7 +164,7 @@ describe Projects::ProjectMembersController do
context 'when member is found' do
context 'and is not an owner' do
before do
- project.team << [user, :developer]
+ project.add_developer(user)
end
it 'removes user from members' do
@@ -161,7 +181,7 @@ describe Projects::ProjectMembersController do
let(:project) { create(:project, namespace: user.namespace) }
before do
- project.team << [user, :master]
+ project.add_master(user)
end
it 'cannot remove himself from the project' do
@@ -228,7 +248,7 @@ describe Projects::ProjectMembersController do
context 'when member is found' do
context 'when user does not have enough rights' do
before do
- project.team << [user, :developer]
+ project.add_developer(user)
end
it 'returns 404' do
@@ -243,7 +263,7 @@ describe Projects::ProjectMembersController do
context 'when user has enough rights' do
before do
- project.team << [user, :master]
+ project.add_master(user)
end
it 'adds user to members' do
@@ -265,8 +285,8 @@ describe Projects::ProjectMembersController do
let(:member) { create(:user) }
before do
- project.team << [user, :master]
- another_project.team << [member, :guest]
+ project.add_master(user)
+ another_project.add_guest(member)
sign_in(user)
end
@@ -280,7 +300,7 @@ describe Projects::ProjectMembersController do
context 'when user can access source project members' do
before do
- another_project.team << [user, :guest]
+ another_project.add_guest(user)
end
include_context 'import applied'
@@ -312,7 +332,7 @@ describe Projects::ProjectMembersController do
context 'when creating owner' do
before do
- project.team << [user, :master]
+ project.add_master(user)
sign_in(user)
end
@@ -328,7 +348,7 @@ describe Projects::ProjectMembersController do
context 'when create master' do
before do
- project.team << [user, :master]
+ project.add_master(user)
sign_in(user)
end
diff --git a/spec/controllers/projects/refs_controller_spec.rb b/spec/controllers/projects/refs_controller_spec.rb
index 748ae040928..ceaffd92623 100644
--- a/spec/controllers/projects/refs_controller_spec.rb
+++ b/spec/controllers/projects/refs_controller_spec.rb
@@ -6,7 +6,7 @@ describe Projects::RefsController do
before do
sign_in(user)
- project.team << [user, :developer]
+ project.add_developer(user)
end
describe 'GET #logs_tree' do
diff --git a/spec/controllers/projects/releases_controller_spec.rb b/spec/controllers/projects/releases_controller_spec.rb
index 358f26dfb02..fc1619acec6 100644
--- a/spec/controllers/projects/releases_controller_spec.rb
+++ b/spec/controllers/projects/releases_controller_spec.rb
@@ -7,7 +7,7 @@ describe Projects::ReleasesController do
let!(:tag) { release.tag }
before do
- project.team << [user, :developer]
+ project.add_developer(user)
sign_in(user)
end
diff --git a/spec/controllers/projects/repositories_controller_spec.rb b/spec/controllers/projects/repositories_controller_spec.rb
index 8b777eb68ca..04d16e98913 100644
--- a/spec/controllers/projects/repositories_controller_spec.rb
+++ b/spec/controllers/projects/repositories_controller_spec.rb
@@ -17,7 +17,7 @@ describe Projects::RepositoriesController do
let(:user) { create(:user) }
before do
- project.team << [user, :developer]
+ project.add_developer(user)
sign_in(user)
end
diff --git a/spec/controllers/projects/services_controller_spec.rb b/spec/controllers/projects/services_controller_spec.rb
index a907da2b60f..847ac6f2be0 100644
--- a/spec/controllers/projects/services_controller_spec.rb
+++ b/spec/controllers/projects/services_controller_spec.rb
@@ -9,7 +9,7 @@ describe Projects::ServicesController do
before do
sign_in(user)
- project.team << [user, :master]
+ project.add_master(user)
end
describe '#test' do
@@ -114,5 +114,41 @@ describe Projects::ServicesController do
expect(flash[:notice]).to eq 'HipChat settings saved, but not activated.'
end
end
+
+ context 'with a deprecated service' do
+ let(:service) { create(:kubernetes_service, project: project) }
+
+ before do
+ put :update,
+ namespace_id: project.namespace, project_id: project, id: service.to_param, service: { namespace: 'updated_namespace' }
+ end
+
+ it 'should not update the service' do
+ service.reload
+ expect(service.namespace).not_to eq('updated_namespace')
+ end
+ end
+ end
+
+ describe "GET #edit" do
+ before do
+ get :edit, namespace_id: project.namespace, project_id: project, id: service_id
+ end
+
+ context 'with approved services' do
+ let(:service_id) { 'jira' }
+
+ it 'should render edit page' do
+ expect(response).to be_success
+ end
+ end
+
+ context 'with a deprecated service' do
+ let(:service_id) { 'kubernetes' }
+
+ it 'should render edit page' do
+ expect(response).to be_success
+ end
+ end
end
end
diff --git a/spec/controllers/projects/settings/ci_cd_controller_spec.rb b/spec/controllers/projects/settings/ci_cd_controller_spec.rb
index b8fe0f46f57..0202149f335 100644
--- a/spec/controllers/projects/settings/ci_cd_controller_spec.rb
+++ b/spec/controllers/projects/settings/ci_cd_controller_spec.rb
@@ -5,7 +5,7 @@ describe Projects::Settings::CiCdController do
let(:user) { create(:user) }
before do
- project.team << [user, :master]
+ project.add_master(user)
sign_in(user)
end
@@ -17,4 +17,51 @@ describe Projects::Settings::CiCdController do
expect(response).to render_template(:show)
end
end
+
+ describe '#reset_cache' do
+ before do
+ sign_in(user)
+
+ project.add_master(user)
+
+ allow(ResetProjectCacheService).to receive_message_chain(:new, :execute).and_return(true)
+ end
+
+ subject { post :reset_cache, namespace_id: project.namespace, project_id: project }
+
+ it 'calls reset project cache service' do
+ expect(ResetProjectCacheService).to receive_message_chain(:new, :execute)
+
+ subject
+ end
+
+ it 'redirects to project pipelines path' do
+ subject
+
+ expect(response).to have_gitlab_http_status(:redirect)
+ expect(response).to redirect_to(project_pipelines_path(project))
+ end
+
+ context 'when service returns successfully' do
+ it 'sets the flash notice variable' do
+ subject
+
+ expect(controller).to set_flash[:notice]
+ expect(controller).not_to set_flash[:error]
+ end
+ end
+
+ context 'when service does not return successfully' do
+ before do
+ allow(ResetProjectCacheService).to receive_message_chain(:new, :execute).and_return(false)
+ end
+
+ it 'sets the flash error variable' do
+ subject
+
+ expect(controller).not_to set_flash[:notice]
+ expect(controller).to set_flash[:error]
+ end
+ end
+ end
end
diff --git a/spec/controllers/projects/settings/integrations_controller_spec.rb b/spec/controllers/projects/settings/integrations_controller_spec.rb
index 3068837f394..77df9a6f567 100644
--- a/spec/controllers/projects/settings/integrations_controller_spec.rb
+++ b/spec/controllers/projects/settings/integrations_controller_spec.rb
@@ -5,7 +5,7 @@ describe Projects::Settings::IntegrationsController do
let(:user) { create(:user) }
before do
- project.team << [user, :master]
+ project.add_master(user)
sign_in(user)
end
diff --git a/spec/controllers/projects/templates_controller_spec.rb b/spec/controllers/projects/templates_controller_spec.rb
index 70e7f9ca96e..8fcfa3c9ecd 100644
--- a/spec/controllers/projects/templates_controller_spec.rb
+++ b/spec/controllers/projects/templates_controller_spec.rb
@@ -8,7 +8,7 @@ describe Projects::TemplatesController do
let(:body) { JSON.parse(response.body) }
before do
- project.team << [user, :developer]
+ project.add_developer(user)
sign_in(user)
end
diff --git a/spec/controllers/projects/todos_controller_spec.rb b/spec/controllers/projects/todos_controller_spec.rb
index 4622e27e60f..e2524be7724 100644
--- a/spec/controllers/projects/todos_controller_spec.rb
+++ b/spec/controllers/projects/todos_controller_spec.rb
@@ -20,7 +20,7 @@ describe Projects::TodosController do
context 'when authorized' do
before do
sign_in(user)
- project.team << [user, :developer]
+ project.add_developer(user)
end
it 'creates todo for issue' do
@@ -88,7 +88,7 @@ describe Projects::TodosController do
context 'when authorized' do
before do
sign_in(user)
- project.team << [user, :developer]
+ project.add_developer(user)
end
it 'creates todo for merge request' do
diff --git a/spec/controllers/projects/tree_controller_spec.rb b/spec/controllers/projects/tree_controller_spec.rb
index 65b821c9486..d3188f054cf 100644
--- a/spec/controllers/projects/tree_controller_spec.rb
+++ b/spec/controllers/projects/tree_controller_spec.rb
@@ -7,7 +7,7 @@ describe Projects::TreeController do
before do
sign_in(user)
- project.team << [user, :master]
+ project.add_master(user)
controller.instance_variable_set(:@project, project)
end
diff --git a/spec/controllers/projects/variables_controller_spec.rb b/spec/controllers/projects/variables_controller_spec.rb
index d065cd00d00..9fde6544215 100644
--- a/spec/controllers/projects/variables_controller_spec.rb
+++ b/spec/controllers/projects/variables_controller_spec.rb
@@ -6,7 +6,7 @@ describe Projects::VariablesController do
before do
sign_in(user)
- project.team << [user, :master]
+ project.add_master(user)
end
describe 'POST #create' do
diff --git a/spec/controllers/projects_controller_spec.rb b/spec/controllers/projects_controller_spec.rb
index e61187fb518..5202ffdd8bb 100644
--- a/spec/controllers/projects_controller_spec.rb
+++ b/spec/controllers/projects_controller_spec.rb
@@ -102,7 +102,7 @@ describe ProjectsController do
render_views
before do
- project.team << [user, :developer]
+ project.add_developer(user)
project.project_feature.update_attribute(:repository_access_level, ProjectFeature::DISABLED)
end
@@ -437,7 +437,7 @@ describe ProjectsController do
before do
sign_in(user)
- project.team << [user, :developer]
+ project.add_developer(user)
allow(Gitlab.config.incoming_email).to receive(:enabled).and_return(true)
end
@@ -465,7 +465,7 @@ describe ProjectsController do
before do
sign_in(user)
- project.team << [user, :developer]
+ project.add_developer(user)
allow(Gitlab.config.incoming_email).to receive(:enabled).and_return(true)
end
diff --git a/spec/controllers/uploads_controller_spec.rb b/spec/controllers/uploads_controller_spec.rb
index 7e42e43345c..b1f601a19e5 100644
--- a/spec/controllers/uploads_controller_spec.rb
+++ b/spec/controllers/uploads_controller_spec.rb
@@ -265,13 +265,13 @@ describe UploadsController do
context "when the user has access to the project" do
before do
- project.team << [user, :master]
+ project.add_master(user)
end
context "when the user is blocked" do
before do
user.block
- project.team << [user, :master]
+ project.add_master(user)
end
it "redirects to the sign in page" do
@@ -465,13 +465,13 @@ describe UploadsController do
context "when the user has access to the project" do
before do
- project.team << [user, :master]
+ project.add_master(user)
end
context "when the user is blocked" do
before do
user.block
- project.team << [user, :master]
+ project.add_master(user)
end
it "redirects to the sign in page" do
diff --git a/spec/controllers/users_controller_spec.rb b/spec/controllers/users_controller_spec.rb
index 01ab59aa363..2898c4b119e 100644
--- a/spec/controllers/users_controller_spec.rb
+++ b/spec/controllers/users_controller_spec.rb
@@ -91,7 +91,7 @@ describe UsersController do
before do
sign_in(user)
- project.team << [user, :developer]
+ project.add_developer(user)
push_data = Gitlab::DataBuilder::Push.build_sample(project, user)
@@ -117,7 +117,7 @@ describe UsersController do
allow_any_instance_of(User).to receive(:contributed_projects_ids).and_return([project.id])
sign_in(user)
- project.team << [user, :developer]
+ project.add_developer(user)
end
it 'assigns @calendar_date' do
diff --git a/spec/factories/abuse_reports.rb b/spec/factories/abuse_reports.rb
index 8f6422a7825..021971ac421 100644
--- a/spec/factories/abuse_reports.rb
+++ b/spec/factories/abuse_reports.rb
@@ -1,4 +1,4 @@
-FactoryGirl.define do
+FactoryBot.define do
factory :abuse_report do
reporter factory: :user
user
diff --git a/spec/factories/appearances.rb b/spec/factories/appearances.rb
index 860973024c9..5f9c57c0c8d 100644
--- a/spec/factories/appearances.rb
+++ b/spec/factories/appearances.rb
@@ -1,6 +1,6 @@
-# Read about factories at https://github.com/thoughtbot/factory_girl
+# Read about factories at https://github.com/thoughtbot/factory_bot
-FactoryGirl.define do
+FactoryBot.define do
factory :appearance do
title "MepMep"
description "This is my Community Edition instance"
diff --git a/spec/factories/application_settings.rb b/spec/factories/application_settings.rb
index aef65e724c2..3ecc90b6573 100644
--- a/spec/factories/application_settings.rb
+++ b/spec/factories/application_settings.rb
@@ -1,4 +1,4 @@
-FactoryGirl.define do
+FactoryBot.define do
factory :application_setting do
end
end
diff --git a/spec/factories/award_emoji.rb b/spec/factories/award_emoji.rb
index 4b858df52c9..a0abbbce686 100644
--- a/spec/factories/award_emoji.rb
+++ b/spec/factories/award_emoji.rb
@@ -1,4 +1,4 @@
-FactoryGirl.define do
+FactoryBot.define do
factory :award_emoji do
name "thumbsup"
user
diff --git a/spec/factories/boards.rb b/spec/factories/boards.rb
index 1ec042a6ab4..1e125237ae8 100644
--- a/spec/factories/boards.rb
+++ b/spec/factories/boards.rb
@@ -1,4 +1,4 @@
-FactoryGirl.define do
+FactoryBot.define do
factory :board do
project
diff --git a/spec/factories/broadcast_messages.rb b/spec/factories/broadcast_messages.rb
index c2fdf89213a..9a65e7f8a3f 100644
--- a/spec/factories/broadcast_messages.rb
+++ b/spec/factories/broadcast_messages.rb
@@ -1,4 +1,4 @@
-FactoryGirl.define do
+FactoryBot.define do
factory :broadcast_message do
message "MyText"
starts_at 1.day.ago
diff --git a/spec/factories/chat_names.rb b/spec/factories/chat_names.rb
index 9a0be1a4598..56993e5da18 100644
--- a/spec/factories/chat_names.rb
+++ b/spec/factories/chat_names.rb
@@ -1,4 +1,4 @@
-FactoryGirl.define do
+FactoryBot.define do
factory :chat_name, class: ChatName do
user factory: :user
service factory: :service
diff --git a/spec/factories/chat_teams.rb b/spec/factories/chat_teams.rb
index ffedf69a69b..d048c46d6b6 100644
--- a/spec/factories/chat_teams.rb
+++ b/spec/factories/chat_teams.rb
@@ -1,4 +1,4 @@
-FactoryGirl.define do
+FactoryBot.define do
factory :chat_team, class: ChatTeam do
sequence(:team_id) { |n| "abcdefghijklm#{n}" }
namespace factory: :group
diff --git a/spec/factories/ci/build_trace_section_names.rb b/spec/factories/ci/build_trace_section_names.rb
index 1c16225f0e5..ce07e716dde 100644
--- a/spec/factories/ci/build_trace_section_names.rb
+++ b/spec/factories/ci/build_trace_section_names.rb
@@ -1,4 +1,4 @@
-FactoryGirl.define do
+FactoryBot.define do
factory :ci_build_trace_section_name, class: Ci::BuildTraceSectionName do
sequence(:name) { |n| "section_#{n}" }
project factory: :project
diff --git a/spec/factories/ci/builds.rb b/spec/factories/ci/builds.rb
index c868525cbc0..6f66468570f 100644
--- a/spec/factories/ci/builds.rb
+++ b/spec/factories/ci/builds.rb
@@ -1,18 +1,16 @@
include ActionDispatch::TestProcess
-FactoryGirl.define do
+FactoryBot.define do
factory :ci_build, class: Ci::Build do
name 'test'
stage 'test'
stage_idx 0
ref 'master'
tag false
- status 'pending'
- created_at 'Di 29. Okt 09:50:00 CET 2013'
- started_at 'Di 29. Okt 09:51:28 CET 2013'
- finished_at 'Di 29. Okt 09:53:28 CET 2013'
commands 'ls -a'
protected false
+ created_at 'Di 29. Okt 09:50:00 CET 2013'
+ pending
options do
{
@@ -29,23 +27,37 @@ FactoryGirl.define do
pipeline factory: :ci_pipeline
+ trait :started do
+ started_at 'Di 29. Okt 09:51:28 CET 2013'
+ end
+
+ trait :finished do
+ started
+ finished_at 'Di 29. Okt 09:53:28 CET 2013'
+ end
+
trait :success do
+ finished
status 'success'
end
trait :failed do
+ finished
status 'failed'
end
trait :canceled do
+ finished
status 'canceled'
end
trait :skipped do
+ started
status 'skipped'
end
trait :running do
+ started
status 'running'
end
@@ -114,11 +126,6 @@ FactoryGirl.define do
build.project ||= build.pipeline.project
end
- factory :ci_not_started_build do
- started_at nil
- finished_at nil
- end
-
trait :tag do
tag true
end
diff --git a/spec/factories/ci/group_variables.rb b/spec/factories/ci/group_variables.rb
index 565ced9eb1a..64716842b12 100644
--- a/spec/factories/ci/group_variables.rb
+++ b/spec/factories/ci/group_variables.rb
@@ -1,4 +1,4 @@
-FactoryGirl.define do
+FactoryBot.define do
factory :ci_group_variable, class: Ci::GroupVariable do
sequence(:key) { |n| "VARIABLE_#{n}" }
value 'VARIABLE_VALUE'
diff --git a/spec/factories/ci/job_artifacts.rb b/spec/factories/ci/job_artifacts.rb
index 39185249695..436735e7ed3 100644
--- a/spec/factories/ci/job_artifacts.rb
+++ b/spec/factories/ci/job_artifacts.rb
@@ -1,6 +1,6 @@
include ActionDispatch::TestProcess
-FactoryGirl.define do
+FactoryBot.define do
factory :ci_job_artifact, class: Ci::JobArtifact do
job factory: :ci_build
file_type :archive
diff --git a/spec/factories/ci/pipeline_schedule.rb b/spec/factories/ci/pipeline_schedule.rb
index 564fef6833b..b2b79807429 100644
--- a/spec/factories/ci/pipeline_schedule.rb
+++ b/spec/factories/ci/pipeline_schedule.rb
@@ -1,4 +1,4 @@
-FactoryGirl.define do
+FactoryBot.define do
factory :ci_pipeline_schedule, class: Ci::PipelineSchedule do
cron '0 1 * * *'
cron_timezone Gitlab::Ci::CronParser::VALID_SYNTAX_SAMPLE_TIME_ZONE
diff --git a/spec/factories/ci/pipeline_schedule_variables.rb b/spec/factories/ci/pipeline_schedule_variables.rb
index ca64d1aada0..8d29118e310 100644
--- a/spec/factories/ci/pipeline_schedule_variables.rb
+++ b/spec/factories/ci/pipeline_schedule_variables.rb
@@ -1,4 +1,4 @@
-FactoryGirl.define do
+FactoryBot.define do
factory :ci_pipeline_schedule_variable, class: Ci::PipelineScheduleVariable do
sequence(:key) { |n| "VARIABLE_#{n}" }
value 'VARIABLE_VALUE'
diff --git a/spec/factories/ci/pipeline_variables.rb b/spec/factories/ci/pipeline_variables.rb
index 7c1a7faec08..b18055d7b3a 100644
--- a/spec/factories/ci/pipeline_variables.rb
+++ b/spec/factories/ci/pipeline_variables.rb
@@ -1,4 +1,4 @@
-FactoryGirl.define do
+FactoryBot.define do
factory :ci_pipeline_variable, class: Ci::PipelineVariable do
sequence(:key) { |n| "VARIABLE_#{n}" }
value 'VARIABLE_VALUE'
diff --git a/spec/factories/ci/pipelines.rb b/spec/factories/ci/pipelines.rb
index f994c2df821..51a767e5b93 100644
--- a/spec/factories/ci/pipelines.rb
+++ b/spec/factories/ci/pipelines.rb
@@ -1,4 +1,4 @@
-FactoryGirl.define do
+FactoryBot.define do
factory :ci_empty_pipeline, class: Ci::Pipeline do
source :push
ref 'master'
diff --git a/spec/factories/ci/runner_projects.rb b/spec/factories/ci/runner_projects.rb
index fa76d0ecd8c..f605e90ceed 100644
--- a/spec/factories/ci/runner_projects.rb
+++ b/spec/factories/ci/runner_projects.rb
@@ -1,4 +1,4 @@
-FactoryGirl.define do
+FactoryBot.define do
factory :ci_runner_project, class: Ci::RunnerProject do
runner factory: :ci_runner
project
diff --git a/spec/factories/ci/runners.rb b/spec/factories/ci/runners.rb
index 88bb755d068..34b8b246d0f 100644
--- a/spec/factories/ci/runners.rb
+++ b/spec/factories/ci/runners.rb
@@ -1,4 +1,4 @@
-FactoryGirl.define do
+FactoryBot.define do
factory :ci_runner, class: Ci::Runner do
sequence(:description) { |n| "My runner#{n}" }
diff --git a/spec/factories/ci/stages.rb b/spec/factories/ci/stages.rb
index b2ded945738..25309033571 100644
--- a/spec/factories/ci/stages.rb
+++ b/spec/factories/ci/stages.rb
@@ -1,4 +1,4 @@
-FactoryGirl.define do
+FactoryBot.define do
factory :ci_stage, class: Ci::LegacyStage do
skip_create
diff --git a/spec/factories/ci/trigger_requests.rb b/spec/factories/ci/trigger_requests.rb
index 40b8848920e..0e9fc3d0014 100644
--- a/spec/factories/ci/trigger_requests.rb
+++ b/spec/factories/ci/trigger_requests.rb
@@ -1,4 +1,4 @@
-FactoryGirl.define do
+FactoryBot.define do
factory :ci_trigger_request, class: Ci::TriggerRequest do
trigger factory: :ci_trigger
end
diff --git a/spec/factories/ci/triggers.rb b/spec/factories/ci/triggers.rb
index 3734c7040c0..742d9efba2d 100644
--- a/spec/factories/ci/triggers.rb
+++ b/spec/factories/ci/triggers.rb
@@ -1,4 +1,4 @@
-FactoryGirl.define do
+FactoryBot.define do
factory :ci_trigger_without_token, class: Ci::Trigger do
owner
diff --git a/spec/factories/ci/variables.rb b/spec/factories/ci/variables.rb
index d8fd513ffcf..3d014b9b54f 100644
--- a/spec/factories/ci/variables.rb
+++ b/spec/factories/ci/variables.rb
@@ -1,4 +1,4 @@
-FactoryGirl.define do
+FactoryBot.define do
factory :ci_variable, class: Ci::Variable do
sequence(:key) { |n| "VARIABLE_#{n}" }
value 'VARIABLE_VALUE'
diff --git a/spec/factories/clusters/applications/helm.rb b/spec/factories/clusters/applications/helm.rb
index fab37195113..775fbb3d27b 100644
--- a/spec/factories/clusters/applications/helm.rb
+++ b/spec/factories/clusters/applications/helm.rb
@@ -1,5 +1,5 @@
-FactoryGirl.define do
- factory :cluster_applications_helm, class: Clusters::Applications::Helm do
+FactoryBot.define do
+ factory :clusters_applications_helm, class: Clusters::Applications::Helm do
cluster factory: %i(cluster provided_by_gcp)
trait :not_installable do
@@ -31,5 +31,8 @@ FactoryGirl.define do
installing
updated_at ClusterWaitForAppInstallationWorker::TIMEOUT.ago
end
+
+ factory :clusters_applications_ingress, class: Clusters::Applications::Ingress
+ factory :clusters_applications_prometheus, class: Clusters::Applications::Prometheus
end
end
diff --git a/spec/factories/clusters/applications/ingress.rb b/spec/factories/clusters/applications/ingress.rb
deleted file mode 100644
index b103a980655..00000000000
--- a/spec/factories/clusters/applications/ingress.rb
+++ /dev/null
@@ -1,35 +0,0 @@
-FactoryGirl.define do
- factory :cluster_applications_ingress, class: Clusters::Applications::Ingress do
- cluster factory: %i(cluster provided_by_gcp)
-
- trait :not_installable do
- status(-2)
- end
-
- trait :installable do
- status 0
- end
-
- trait :scheduled do
- status 1
- end
-
- trait :installing do
- status 2
- end
-
- trait :installed do
- status 3
- end
-
- trait :errored do
- status(-1)
- status_reason 'something went wrong'
- end
-
- trait :timeouted do
- installing
- updated_at ClusterWaitForAppInstallationWorker::TIMEOUT.ago
- end
- end
-end
diff --git a/spec/factories/clusters/clusters.rb b/spec/factories/clusters/clusters.rb
index 9e73a19e856..20d5580f0c2 100644
--- a/spec/factories/clusters/clusters.rb
+++ b/spec/factories/clusters/clusters.rb
@@ -1,4 +1,4 @@
-FactoryGirl.define do
+FactoryBot.define do
factory :cluster, class: Clusters::Cluster do
user
name 'test-cluster'
diff --git a/spec/factories/clusters/platforms/kubernetes.rb b/spec/factories/clusters/platforms/kubernetes.rb
index 8b3e6ff35fa..89f6ddebf6a 100644
--- a/spec/factories/clusters/platforms/kubernetes.rb
+++ b/spec/factories/clusters/platforms/kubernetes.rb
@@ -1,4 +1,4 @@
-FactoryGirl.define do
+FactoryBot.define do
factory :cluster_platform_kubernetes, class: Clusters::Platforms::Kubernetes do
cluster
namespace nil
diff --git a/spec/factories/clusters/providers/gcp.rb b/spec/factories/clusters/providers/gcp.rb
index a815410512a..a002ab28519 100644
--- a/spec/factories/clusters/providers/gcp.rb
+++ b/spec/factories/clusters/providers/gcp.rb
@@ -1,4 +1,4 @@
-FactoryGirl.define do
+FactoryBot.define do
factory :cluster_provider_gcp, class: Clusters::Providers::Gcp do
cluster
gcp_project_id 'test-gcp-project'
diff --git a/spec/factories/commit_statuses.rb b/spec/factories/commit_statuses.rb
index abbe37df90e..ce5fbc343ee 100644
--- a/spec/factories/commit_statuses.rb
+++ b/spec/factories/commit_statuses.rb
@@ -1,4 +1,4 @@
-FactoryGirl.define do
+FactoryBot.define do
factory :commit_status, class: CommitStatus do
name 'default'
stage 'test'
diff --git a/spec/factories/commits.rb b/spec/factories/commits.rb
index f4f12a095fc..84a8bc56640 100644
--- a/spec/factories/commits.rb
+++ b/spec/factories/commits.rb
@@ -1,16 +1,29 @@
require_relative '../support/repo_helpers'
-FactoryGirl.define do
+FactoryBot.define do
factory :commit do
- git_commit RepoHelpers.sample_commit
+ transient do
+ author nil
+ end
+
+ git_commit do
+ commit = RepoHelpers.sample_commit
+
+ if author
+ commit.author_email = author.email
+ commit.author_name = author.name
+ end
+
+ commit
+ end
project
initialize_with do
new(git_commit, project)
end
- after(:build) do |commit|
- allow(commit).to receive(:author).and_return build(:author)
+ after(:build) do |commit, evaluator|
+ allow(commit).to receive(:author).and_return(evaluator.author || build(:author))
end
trait :without_author do
diff --git a/spec/factories/container_repositories.rb b/spec/factories/container_repositories.rb
index 3fcad9fd4b3..62a89a12ef5 100644
--- a/spec/factories/container_repositories.rb
+++ b/spec/factories/container_repositories.rb
@@ -1,4 +1,4 @@
-FactoryGirl.define do
+FactoryBot.define do
factory :container_repository do
name 'test_container_image'
project
diff --git a/spec/factories/conversational_development_index_metrics.rb b/spec/factories/conversational_development_index_metrics.rb
index 3806c43ba15..abf37fb861e 100644
--- a/spec/factories/conversational_development_index_metrics.rb
+++ b/spec/factories/conversational_development_index_metrics.rb
@@ -1,4 +1,4 @@
-FactoryGirl.define do
+FactoryBot.define do
factory :conversational_development_index_metric, class: ConversationalDevelopmentIndex::Metric do
leader_issues 9.256
instance_issues 1.234
diff --git a/spec/factories/deploy_keys_projects.rb b/spec/factories/deploy_keys_projects.rb
index 27cece487bd..30a6d468ed3 100644
--- a/spec/factories/deploy_keys_projects.rb
+++ b/spec/factories/deploy_keys_projects.rb
@@ -1,4 +1,4 @@
-FactoryGirl.define do
+FactoryBot.define do
factory :deploy_keys_project do
deploy_key
project
diff --git a/spec/factories/deployments.rb b/spec/factories/deployments.rb
index 0dd1238d6e2..9d7d5e56611 100644
--- a/spec/factories/deployments.rb
+++ b/spec/factories/deployments.rb
@@ -1,4 +1,4 @@
-FactoryGirl.define do
+FactoryBot.define do
factory :deployment, class: Deployment do
sha '97de212e80737a608d939f648d959671fb0a0142'
ref 'master'
diff --git a/spec/factories/emails.rb b/spec/factories/emails.rb
index c9ab87a15aa..4dc7961060a 100644
--- a/spec/factories/emails.rb
+++ b/spec/factories/emails.rb
@@ -1,4 +1,4 @@
-FactoryGirl.define do
+FactoryBot.define do
factory :email do
user
email { generate(:email_alias) }
diff --git a/spec/factories/environments.rb b/spec/factories/environments.rb
index 9034476d094..b5db57d5148 100644
--- a/spec/factories/environments.rb
+++ b/spec/factories/environments.rb
@@ -1,4 +1,4 @@
-FactoryGirl.define do
+FactoryBot.define do
factory :environment, class: Environment do
sequence(:name) { |n| "environment#{n}" }
diff --git a/spec/factories/events.rb b/spec/factories/events.rb
index ad9f7e2caef..ed275243ac9 100644
--- a/spec/factories/events.rb
+++ b/spec/factories/events.rb
@@ -1,4 +1,4 @@
-FactoryGirl.define do
+FactoryBot.define do
factory :event do
project
author factory: :user
diff --git a/spec/factories/file_uploaders.rb b/spec/factories/file_uploaders.rb
index 622571390d2..8404985bfea 100644
--- a/spec/factories/file_uploaders.rb
+++ b/spec/factories/file_uploaders.rb
@@ -1,4 +1,4 @@
-FactoryGirl.define do
+FactoryBot.define do
factory :file_uploader do
skip_create
diff --git a/spec/factories/fork_network_members.rb b/spec/factories/fork_network_members.rb
index 509c4e1fa1c..850e3f158f1 100644
--- a/spec/factories/fork_network_members.rb
+++ b/spec/factories/fork_network_members.rb
@@ -1,4 +1,4 @@
-FactoryGirl.define do
+FactoryBot.define do
factory :fork_network_member do
association :project
association :fork_network
diff --git a/spec/factories/fork_networks.rb b/spec/factories/fork_networks.rb
index f42d36f3d19..813b1943eb2 100644
--- a/spec/factories/fork_networks.rb
+++ b/spec/factories/fork_networks.rb
@@ -1,4 +1,4 @@
-FactoryGirl.define do
+FactoryBot.define do
factory :fork_network do
association :root_project, factory: :project
end
diff --git a/spec/factories/forked_project_links.rb b/spec/factories/forked_project_links.rb
index 9b34651a4ae..bc59fea81ec 100644
--- a/spec/factories/forked_project_links.rb
+++ b/spec/factories/forked_project_links.rb
@@ -1,4 +1,4 @@
-FactoryGirl.define do
+FactoryBot.define do
factory :forked_project_link do
association :forked_to_project, factory: [:project, :repository]
association :forked_from_project, factory: [:project, :repository]
diff --git a/spec/factories/gitaly/commit.rb b/spec/factories/gitaly/commit.rb
index e7966cee77b..5034c3d0e48 100644
--- a/spec/factories/gitaly/commit.rb
+++ b/spec/factories/gitaly/commit.rb
@@ -1,4 +1,4 @@
-FactoryGirl.define do
+FactoryBot.define do
sequence(:gitaly_commit_id) { Digest::SHA1.hexdigest(Time.now.to_f.to_s) }
factory :gitaly_commit, class: Gitaly::GitCommit do
diff --git a/spec/factories/gitaly/commit_author.rb b/spec/factories/gitaly/commit_author.rb
index 341873a2002..aaf634ce08b 100644
--- a/spec/factories/gitaly/commit_author.rb
+++ b/spec/factories/gitaly/commit_author.rb
@@ -1,4 +1,4 @@
-FactoryGirl.define do
+FactoryBot.define do
factory :gitaly_commit_author, class: Gitaly::CommitAuthor do
skip_create
diff --git a/spec/factories/gpg_key_subkeys.rb b/spec/factories/gpg_key_subkeys.rb
index 66ecb44d84b..57eaaee345f 100644
--- a/spec/factories/gpg_key_subkeys.rb
+++ b/spec/factories/gpg_key_subkeys.rb
@@ -1,6 +1,6 @@
require_relative '../support/gpg_helpers'
-FactoryGirl.define do
+FactoryBot.define do
factory :gpg_key_subkey do
gpg_key
diff --git a/spec/factories/gpg_keys.rb b/spec/factories/gpg_keys.rb
index 93218e5763e..b8aabf74221 100644
--- a/spec/factories/gpg_keys.rb
+++ b/spec/factories/gpg_keys.rb
@@ -1,6 +1,6 @@
require_relative '../support/gpg_helpers'
-FactoryGirl.define do
+FactoryBot.define do
factory :gpg_key do
key GpgHelpers::User1.public_key
user
diff --git a/spec/factories/gpg_signature.rb b/spec/factories/gpg_signature.rb
index e9798ff6a41..4620caff823 100644
--- a/spec/factories/gpg_signature.rb
+++ b/spec/factories/gpg_signature.rb
@@ -1,6 +1,6 @@
require_relative '../support/gpg_helpers'
-FactoryGirl.define do
+FactoryBot.define do
factory :gpg_signature do
commit_sha { Digest::SHA1.hexdigest(SecureRandom.hex) }
project
diff --git a/spec/factories/group_custom_attributes.rb b/spec/factories/group_custom_attributes.rb
index 7ff5f376e8b..d2f45d5d3ce 100644
--- a/spec/factories/group_custom_attributes.rb
+++ b/spec/factories/group_custom_attributes.rb
@@ -1,4 +1,4 @@
-FactoryGirl.define do
+FactoryBot.define do
factory :group_custom_attribute do
group
sequence(:key) { |n| "key#{n}" }
diff --git a/spec/factories/group_members.rb b/spec/factories/group_members.rb
index 32cbfe28a60..1c2214e9481 100644
--- a/spec/factories/group_members.rb
+++ b/spec/factories/group_members.rb
@@ -1,4 +1,4 @@
-FactoryGirl.define do
+FactoryBot.define do
factory :group_member do
access_level { GroupMember::OWNER }
group
diff --git a/spec/factories/groups.rb b/spec/factories/groups.rb
index 52f76b094a3..1512f5a0e58 100644
--- a/spec/factories/groups.rb
+++ b/spec/factories/groups.rb
@@ -1,4 +1,4 @@
-FactoryGirl.define do
+FactoryBot.define do
factory :group, class: Group, parent: :namespace do
sequence(:name) { |n| "group#{n}" }
path { name.downcase.gsub(/\s/, '_') }
diff --git a/spec/factories/identities.rb b/spec/factories/identities.rb
index 26ef6f18698..122d0d42938 100644
--- a/spec/factories/identities.rb
+++ b/spec/factories/identities.rb
@@ -1,4 +1,4 @@
-FactoryGirl.define do
+FactoryBot.define do
factory :identity do
provider 'ldapmain'
extern_uid 'my-ldap-id'
diff --git a/spec/factories/instance_configuration.rb b/spec/factories/instance_configuration.rb
index 406c7c3caf1..31866a9c221 100644
--- a/spec/factories/instance_configuration.rb
+++ b/spec/factories/instance_configuration.rb
@@ -1,4 +1,4 @@
-FactoryGirl.define do
+FactoryBot.define do
factory :instance_configuration do
skip_create
end
diff --git a/spec/factories/issues.rb b/spec/factories/issues.rb
index 7c3b80198f9..71dc169c6a2 100644
--- a/spec/factories/issues.rb
+++ b/spec/factories/issues.rb
@@ -1,4 +1,4 @@
-FactoryGirl.define do
+FactoryBot.define do
factory :issue do
title { generate(:title) }
author
@@ -14,6 +14,7 @@ FactoryGirl.define do
trait :closed do
state :closed
+ closed_at { Time.now }
end
factory :closed_issue, traits: [:closed]
diff --git a/spec/factories/keys.rb b/spec/factories/keys.rb
index 3f7c794b14a..552b4b7e06e 100644
--- a/spec/factories/keys.rb
+++ b/spec/factories/keys.rb
@@ -1,6 +1,6 @@
require_relative '../support/helpers/key_generator_helper'
-FactoryGirl.define do
+FactoryBot.define do
factory :key do
title
key { Spec::Support::Helpers::KeyGeneratorHelper.new(1024).generate + ' dummy@gitlab.com' }
@@ -21,12 +21,14 @@ FactoryGirl.define do
factory :rsa_key_2048 do
key do
- 'ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDFf6RYK3qu/RKF/3ndJmL5xgMLp3O9' \
- '6x8lTay+QGZ0+9FnnAXMdUqBq/ZU6d/gyMB4IaW3nHzM1w049++yAB6UPCzMB8Uo27K5' \
- '/jyZCtj7Vm9PFNjF/8am1kp46c/SeYicQgQaSBdzIW3UDEa1Ef68qroOlvpi9PYZ/tA7' \
- 'M0YP0K5PXX+E36zaIRnJVMPT3f2k+GnrxtjafZrwFdpOP/Fol5BQLBgcsyiU+LM1SuaC' \
- 'rzd8c9vyaTA1CxrkxaZh+buAi0PmdDtaDrHd42gqZkXCKavyvgM5o2CkQ5LJHCgzpXy0' \
- '5qNFzmThBSkb+XtoxbyagBiGbVZtSVow6Xa7qewz= dummy@gitlab.com'
+ <<~KEY.delete("\n")
+ ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDFf6RYK3qu/RKF/3ndJmL5xgMLp3O9
+ 6x8lTay+QGZ0+9FnnAXMdUqBq/ZU6d/gyMB4IaW3nHzM1w049++yAB6UPCzMB8Uo27K5
+ /jyZCtj7Vm9PFNjF/8am1kp46c/SeYicQgQaSBdzIW3UDEa1Ef68qroOlvpi9PYZ/tA7
+ M0YP0K5PXX+E36zaIRnJVMPT3f2k+GnrxtjafZrwFdpOP/Fol5BQLBgcsyiU+LM1SuaC
+ rzd8c9vyaTA1CxrkxaZh+buAi0PmdDtaDrHd42gqZkXCKavyvgM5o2CkQ5LJHCgzpXy0
+ 5qNFzmThBSkb+XtoxbyagBiGbVZtSVow6Xa7qewz= dummy@gitlab.com
+ KEY
end
factory :rsa_deploy_key_2048, class: 'DeployKey'
@@ -34,37 +36,44 @@ FactoryGirl.define do
factory :dsa_key_2048 do
key do
- 'ssh-dss AAAAB3NzaC1kc3MAAAEBAO/3/NPLA/zSFkMOCaTtGo+uos1flfQ5f038Uk+G' \
- 'Y9AeLGzX+Srhw59GdVXmOQLYBrOt5HdGwqYcmLnE2VurUGmhtfeO5H+3p5pGJbkS0Gxp' \
- 'YH1HRO9lWsncF3Hh1w4lYsDjkclDiSTdfTuN8F4Kb3DXNnVSCieeonp+B25F/CXagyTQ' \
- '/pvNmHFeYgGCVdnBtFdi+xfxaZ8NKdPrGggzokbKHElDZQ4Xo5EpdcyLajgM7nB2r2Rz' \
- 'OrmeaevKi5lV68ehRa9Yyrb7vxvwiwBwOgqR/mnN7Gnaq1jUdmJY+ct04Qwx37f5jvhv' \
- '5gA4U40SGMoiHM8RFIN7Ksz0jsyX73MAAAAVALRWOfjfzHpK7KLz4iqDvvTUAevJAAAB' \
- 'AEa9NZ+6y9iQ5erGsdfLTXFrhSefTG0NhghoO/5IFkSGfd8V7kzTvCHaFrcfpEA5kP8t' \
- 'poeOG0TASB6tgGOxm1Bq4Wncry5RORBPJlAVpDGRcvZ931ddH7IgltEInS6za2uH6F/1' \
- 'M1QfKePSLr6xJ1ZLYfP0Og5KTp1x6yMQvfwV0a+XdA+EPgaJWLWp/pWwKWa0oLUgjsIH' \
- 'MYzuOGh5c708uZrmkzqvgtW2NgXhcIroRgynT3IfI2lP2rqqb3uuuE/qH5UCUFO+Dc3H' \
- 'nAFNeQDT/M25AERdPYBAY5a+iPjIgO+jT7BfmfByT+AZTqZySrCyc7nNZL3YgGLK0l6A' \
- '1GgAAAEBAN9FpFOdIXE+YEZhKl1vPmbcn+b1y5zOl6N4x1B7Q8pD/pLMziWROIS8uLzb' \
- 'aZ0sMIWezHIkxuo1iROMeT+jtCubn7ragaN6AX7nMpxYUH9+mYZZs/fyElt6wCviVhTI' \
- 'zM+u7VdQsnZttOOlQfogHdL+SpeAft0DsfJjlcgQnsLlHQKv6aPqCPYUST2nE7RyW/Ex' \
- 'PrMxLtOWt0/j8RYHbwwqvyeZqBz3ESBgrS9c5tBdBfauwYUV/E7gPLOU3OZFw9ue7o+z' \
- 'wzoTZqW6Xouy5wtWvSLQSLT5XwOslmQz8QMBxD0AQyDfEFGsBCWzmbTgKv9uqrBjubsS' \
- 'Taja+Cf9kMo== dummy@gitlab.com'
+ <<~KEY.delete("\n")
+ ssh-dss AAAAB3NzaC1kc3MAAAEBAO/3/NPLA/zSFkMOCaTtGo+uos1flfQ5f038Uk+G
+ Y9AeLGzX+Srhw59GdVXmOQLYBrOt5HdGwqYcmLnE2VurUGmhtfeO5H+3p5pGJbkS0Gxp
+ YH1HRO9lWsncF3Hh1w4lYsDjkclDiSTdfTuN8F4Kb3DXNnVSCieeonp+B25F/CXagyTQ
+ /pvNmHFeYgGCVdnBtFdi+xfxaZ8NKdPrGggzokbKHElDZQ4Xo5EpdcyLajgM7nB2r2Rz
+ OrmeaevKi5lV68ehRa9Yyrb7vxvwiwBwOgqR/mnN7Gnaq1jUdmJY+ct04Qwx37f5jvhv
+ 5gA4U40SGMoiHM8RFIN7Ksz0jsyX73MAAAAVALRWOfjfzHpK7KLz4iqDvvTUAevJAAAB
+ AEa9NZ+6y9iQ5erGsdfLTXFrhSefTG0NhghoO/5IFkSGfd8V7kzTvCHaFrcfpEA5kP8t
+ poeOG0TASB6tgGOxm1Bq4Wncry5RORBPJlAVpDGRcvZ931ddH7IgltEInS6za2uH6F/1
+ M1QfKePSLr6xJ1ZLYfP0Og5KTp1x6yMQvfwV0a+XdA+EPgaJWLWp/pWwKWa0oLUgjsIH
+ MYzuOGh5c708uZrmkzqvgtW2NgXhcIroRgynT3IfI2lP2rqqb3uuuE/qH5UCUFO+Dc3H
+ nAFNeQDT/M25AERdPYBAY5a+iPjIgO+jT7BfmfByT+AZTqZySrCyc7nNZL3YgGLK0l6A
+ 1GgAAAEBAN9FpFOdIXE+YEZhKl1vPmbcn+b1y5zOl6N4x1B7Q8pD/pLMziWROIS8uLzb
+ aZ0sMIWezHIkxuo1iROMeT+jtCubn7ragaN6AX7nMpxYUH9+mYZZs/fyElt6wCviVhTI
+ zM+u7VdQsnZttOOlQfogHdL+SpeAft0DsfJjlcgQnsLlHQKv6aPqCPYUST2nE7RyW/Ex
+ PrMxLtOWt0/j8RYHbwwqvyeZqBz3ESBgrS9c5tBdBfauwYUV/E7gPLOU3OZFw9ue7o+z
+ wzoTZqW6Xouy5wtWvSLQSLT5XwOslmQz8QMBxD0AQyDfEFGsBCWzmbTgKv9uqrBjubsS
+ Taja+Cf9kMo== dummy@gitlab.com
+ KEY
end
end
factory :ecdsa_key_256 do
key do
- 'ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYA' \
- 'AABBBJZmkzTgY0fiCQ+DVReyH/fFwTFz0XoR3RUO0u+199H19KFw7mNPxRSMOVS7tEtO' \
- 'Nj3Q7FcZXfqthHvgAzDiHsc= dummy@gitlab.com'
+ <<~KEY.delete("\n")
+ ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYA
+ AABBBJZmkzTgY0fiCQ+DVReyH/fFwTFz0XoR3RUO0u+199H19KFw7mNPxRSMOVS7tEtO
+ Nj3Q7FcZXfqthHvgAzDiHsc= dummy@gitlab.com
+ KEY
end
end
factory :ed25519_key_256 do
key do
- 'ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIETnVTgzqC1gatgSlC4zH6aYt2CAQzgJOhDRvf59ohL6 dummy@gitlab.com'
+ <<~KEY.delete("\n")
+ ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIETnVTgzqC1gatgSlC4zH6aYt2CAQzgJ
+ OhDRvf59ohL6 dummy@gitlab.com
+ KEY
end
end
end
diff --git a/spec/factories/label_links.rb b/spec/factories/label_links.rb
index 3580174e873..007847d9cf4 100644
--- a/spec/factories/label_links.rb
+++ b/spec/factories/label_links.rb
@@ -1,4 +1,4 @@
-FactoryGirl.define do
+FactoryBot.define do
factory :label_link do
label
target factory: :issue
diff --git a/spec/factories/label_priorities.rb b/spec/factories/label_priorities.rb
index 7430466fc57..c4824faad53 100644
--- a/spec/factories/label_priorities.rb
+++ b/spec/factories/label_priorities.rb
@@ -1,4 +1,4 @@
-FactoryGirl.define do
+FactoryBot.define do
factory :label_priority do
project
label
diff --git a/spec/factories/labels.rb b/spec/factories/labels.rb
index 416317d677b..f759b6d499d 100644
--- a/spec/factories/labels.rb
+++ b/spec/factories/labels.rb
@@ -1,4 +1,4 @@
-FactoryGirl.define do
+FactoryBot.define do
trait :base_label do
title { generate(:label_title) }
color "#990000"
diff --git a/spec/factories/lfs_objects.rb b/spec/factories/lfs_objects.rb
index 477fab9e964..8eb709022ce 100644
--- a/spec/factories/lfs_objects.rb
+++ b/spec/factories/lfs_objects.rb
@@ -1,6 +1,6 @@
include ActionDispatch::TestProcess
-FactoryGirl.define do
+FactoryBot.define do
factory :lfs_object do
sequence(:oid) { |n| "b68143e6463773b1b6c6fd009a76c32aeec041faff32ba2ed42fd7f708a%05x" % n }
size 499013
diff --git a/spec/factories/lfs_objects_projects.rb b/spec/factories/lfs_objects_projects.rb
index 1ed0355c8e4..c225387a5de 100644
--- a/spec/factories/lfs_objects_projects.rb
+++ b/spec/factories/lfs_objects_projects.rb
@@ -1,4 +1,4 @@
-FactoryGirl.define do
+FactoryBot.define do
factory :lfs_objects_project do
lfs_object
project
diff --git a/spec/factories/lists.rb b/spec/factories/lists.rb
index 48142d3c49b..210c58b21e9 100644
--- a/spec/factories/lists.rb
+++ b/spec/factories/lists.rb
@@ -1,4 +1,4 @@
-FactoryGirl.define do
+FactoryBot.define do
factory :list do
board
label
diff --git a/spec/factories/merge_requests.rb b/spec/factories/merge_requests.rb
index cc6cef63b47..40558c88d15 100644
--- a/spec/factories/merge_requests.rb
+++ b/spec/factories/merge_requests.rb
@@ -1,4 +1,4 @@
-FactoryGirl.define do
+FactoryBot.define do
factory :merge_request do
title { generate(:title) }
author
diff --git a/spec/factories/merge_requests_closing_issues.rb b/spec/factories/merge_requests_closing_issues.rb
index fdbdc00cad7..ee0606a72b6 100644
--- a/spec/factories/merge_requests_closing_issues.rb
+++ b/spec/factories/merge_requests_closing_issues.rb
@@ -1,4 +1,4 @@
-FactoryGirl.define do
+FactoryBot.define do
factory :merge_requests_closing_issues do
issue
merge_request
diff --git a/spec/factories/milestones.rb b/spec/factories/milestones.rb
index b5298b2f969..f95632e7187 100644
--- a/spec/factories/milestones.rb
+++ b/spec/factories/milestones.rb
@@ -1,4 +1,4 @@
-FactoryGirl.define do
+FactoryBot.define do
factory :milestone do
title
diff --git a/spec/factories/namespaces.rb b/spec/factories/namespaces.rb
index 1b1fc4ce80d..f94b09cff15 100644
--- a/spec/factories/namespaces.rb
+++ b/spec/factories/namespaces.rb
@@ -1,4 +1,4 @@
-FactoryGirl.define do
+FactoryBot.define do
factory :namespace do
sequence(:name) { |n| "namespace#{n}" }
path { name.downcase.gsub(/\s/, '_') }
diff --git a/spec/factories/notes.rb b/spec/factories/notes.rb
index 471bfb3213a..707ecbd6be5 100644
--- a/spec/factories/notes.rb
+++ b/spec/factories/notes.rb
@@ -2,7 +2,7 @@ require_relative '../support/repo_helpers'
include ActionDispatch::TestProcess
-FactoryGirl.define do
+FactoryBot.define do
factory :note do
project
note { generate(:title) }
diff --git a/spec/factories/notification_settings.rb b/spec/factories/notification_settings.rb
index e9171528d86..5116ef33f5d 100644
--- a/spec/factories/notification_settings.rb
+++ b/spec/factories/notification_settings.rb
@@ -1,4 +1,4 @@
-FactoryGirl.define do
+FactoryBot.define do
factory :notification_setting do
source factory: :project
user
diff --git a/spec/factories/oauth_access_grants.rb b/spec/factories/oauth_access_grants.rb
index 543b3e99274..9e6af24c4eb 100644
--- a/spec/factories/oauth_access_grants.rb
+++ b/spec/factories/oauth_access_grants.rb
@@ -1,4 +1,4 @@
-FactoryGirl.define do
+FactoryBot.define do
factory :oauth_access_grant do
resource_owner_id { create(:user).id }
application
diff --git a/spec/factories/oauth_access_tokens.rb b/spec/factories/oauth_access_tokens.rb
index a46bc1d8ce8..eabfd6cd830 100644
--- a/spec/factories/oauth_access_tokens.rb
+++ b/spec/factories/oauth_access_tokens.rb
@@ -1,4 +1,4 @@
-FactoryGirl.define do
+FactoryBot.define do
factory :oauth_access_token do
resource_owner
application
diff --git a/spec/factories/oauth_applications.rb b/spec/factories/oauth_applications.rb
index c7ede40f240..4427da1d6c7 100644
--- a/spec/factories/oauth_applications.rb
+++ b/spec/factories/oauth_applications.rb
@@ -1,4 +1,4 @@
-FactoryGirl.define do
+FactoryBot.define do
factory :oauth_application, class: 'Doorkeeper::Application', aliases: [:application] do
sequence(:name) { |n| "OAuth App #{n}" }
uid { Doorkeeper::OAuth::Helpers::UniqueToken.generate }
diff --git a/spec/factories/pages_domains.rb b/spec/factories/pages_domains.rb
index 6d2e45f41ba..61b04708da2 100644
--- a/spec/factories/pages_domains.rb
+++ b/spec/factories/pages_domains.rb
@@ -1,4 +1,4 @@
-FactoryGirl.define do
+FactoryBot.define do
factory :pages_domain, class: 'PagesDomain' do
domain 'my.domain.com'
diff --git a/spec/factories/personal_access_tokens.rb b/spec/factories/personal_access_tokens.rb
index 06acaff6cd0..1b12f84d7b8 100644
--- a/spec/factories/personal_access_tokens.rb
+++ b/spec/factories/personal_access_tokens.rb
@@ -1,4 +1,4 @@
-FactoryGirl.define do
+FactoryBot.define do
factory :personal_access_token do
user
token { SecureRandom.hex(50) }
diff --git a/spec/factories/project_auto_devops.rb b/spec/factories/project_auto_devops.rb
index 8d124dc2381..5ce1988c76f 100644
--- a/spec/factories/project_auto_devops.rb
+++ b/spec/factories/project_auto_devops.rb
@@ -1,4 +1,4 @@
-FactoryGirl.define do
+FactoryBot.define do
factory :project_auto_devops do
project
enabled true
diff --git a/spec/factories/project_custom_attributes.rb b/spec/factories/project_custom_attributes.rb
index 5eedeb86304..099d2d7ff19 100644
--- a/spec/factories/project_custom_attributes.rb
+++ b/spec/factories/project_custom_attributes.rb
@@ -1,4 +1,4 @@
-FactoryGirl.define do
+FactoryBot.define do
factory :project_custom_attribute do
project
sequence(:key) { |n| "key#{n}" }
diff --git a/spec/factories/project_group_links.rb b/spec/factories/project_group_links.rb
index e73cc05f9d7..d5ace9425a0 100644
--- a/spec/factories/project_group_links.rb
+++ b/spec/factories/project_group_links.rb
@@ -1,4 +1,4 @@
-FactoryGirl.define do
+FactoryBot.define do
factory :project_group_link do
project
group
diff --git a/spec/factories/project_hooks.rb b/spec/factories/project_hooks.rb
index accae636a3a..493b7bc021c 100644
--- a/spec/factories/project_hooks.rb
+++ b/spec/factories/project_hooks.rb
@@ -1,4 +1,4 @@
-FactoryGirl.define do
+FactoryBot.define do
factory :project_hook do
url { generate(:url) }
enable_ssl_verification false
diff --git a/spec/factories/project_members.rb b/spec/factories/project_members.rb
index 9cf3a1e8e8a..4260f52498d 100644
--- a/spec/factories/project_members.rb
+++ b/spec/factories/project_members.rb
@@ -1,4 +1,4 @@
-FactoryGirl.define do
+FactoryBot.define do
factory :project_member do
user
project
diff --git a/spec/factories/project_statistics.rb b/spec/factories/project_statistics.rb
index 6c2ed7c6581..2d0f698475d 100644
--- a/spec/factories/project_statistics.rb
+++ b/spec/factories/project_statistics.rb
@@ -1,4 +1,4 @@
-FactoryGirl.define do
+FactoryBot.define do
factory :project_statistics do
project
diff --git a/spec/factories/project_wikis.rb b/spec/factories/project_wikis.rb
index 38fcab7466d..89d8248f9f4 100644
--- a/spec/factories/project_wikis.rb
+++ b/spec/factories/project_wikis.rb
@@ -1,4 +1,4 @@
-FactoryGirl.define do
+FactoryBot.define do
factory :project_wiki do
skip_create
diff --git a/spec/factories/projects.rb b/spec/factories/projects.rb
index 4034e7905ad..d0f3911f730 100644
--- a/spec/factories/projects.rb
+++ b/spec/factories/projects.rb
@@ -1,6 +1,6 @@
require_relative '../support/test_env'
-FactoryGirl.define do
+FactoryBot.define do
# Project without repository
#
# Project does not have bare repository.
diff --git a/spec/factories/protected_branches.rb b/spec/factories/protected_branches.rb
index fe0cbfc4444..39460834d06 100644
--- a/spec/factories/protected_branches.rb
+++ b/spec/factories/protected_branches.rb
@@ -1,4 +1,4 @@
-FactoryGirl.define do
+FactoryBot.define do
factory :protected_branch do
name
project
diff --git a/spec/factories/protected_tags.rb b/spec/factories/protected_tags.rb
index 225588b23cc..df9c8b3cb63 100644
--- a/spec/factories/protected_tags.rb
+++ b/spec/factories/protected_tags.rb
@@ -1,4 +1,4 @@
-FactoryGirl.define do
+FactoryBot.define do
factory :protected_tag do
name
project
diff --git a/spec/factories/releases.rb b/spec/factories/releases.rb
index 74497dc82c0..d80c65cf8bb 100644
--- a/spec/factories/releases.rb
+++ b/spec/factories/releases.rb
@@ -1,4 +1,4 @@
-FactoryGirl.define do
+FactoryBot.define do
factory :release do
tag "v1.1.0"
description "Awesome release"
diff --git a/spec/factories/sent_notifications.rb b/spec/factories/sent_notifications.rb
index c2febf5b438..80872067233 100644
--- a/spec/factories/sent_notifications.rb
+++ b/spec/factories/sent_notifications.rb
@@ -1,4 +1,4 @@
-FactoryGirl.define do
+FactoryBot.define do
factory :sent_notification do
project
recipient factory: :user
diff --git a/spec/factories/sequences.rb b/spec/factories/sequences.rb
index c0232ba5bf6..f2b6e7a11f9 100644
--- a/spec/factories/sequences.rb
+++ b/spec/factories/sequences.rb
@@ -1,4 +1,4 @@
-FactoryGirl.define do
+FactoryBot.define do
sequence(:username) { |n| "user#{n}" }
sequence(:name) { |n| "John Doe#{n}" }
sequence(:email) { |n| "user#{n}@example.org" }
diff --git a/spec/factories/service_hooks.rb b/spec/factories/service_hooks.rb
index e3f88ab8fcc..c907862b4f6 100644
--- a/spec/factories/service_hooks.rb
+++ b/spec/factories/service_hooks.rb
@@ -1,4 +1,4 @@
-FactoryGirl.define do
+FactoryBot.define do
factory :service_hook do
url { generate(:url) }
service
diff --git a/spec/factories/services.rb b/spec/factories/services.rb
index ccf63f3ffa4..110ef33c6f7 100644
--- a/spec/factories/services.rb
+++ b/spec/factories/services.rb
@@ -1,4 +1,4 @@
-FactoryGirl.define do
+FactoryBot.define do
factory :service do
project
type 'Service'
@@ -18,6 +18,7 @@ FactoryGirl.define do
factory :kubernetes_service do
project
+ type 'KubernetesService'
active true
properties({
api_url: 'https://kubernetes.example.com',
diff --git a/spec/factories/snippets.rb b/spec/factories/snippets.rb
index 075bccd7f94..2ab9a56d255 100644
--- a/spec/factories/snippets.rb
+++ b/spec/factories/snippets.rb
@@ -1,4 +1,4 @@
-FactoryGirl.define do
+FactoryBot.define do
factory :snippet do
author
title { generate(:title) }
diff --git a/spec/factories/spam_logs.rb b/spec/factories/spam_logs.rb
index e369f9f13e9..a467f850a80 100644
--- a/spec/factories/spam_logs.rb
+++ b/spec/factories/spam_logs.rb
@@ -1,4 +1,4 @@
-FactoryGirl.define do
+FactoryBot.define do
factory :spam_log do
user
sequence(:source_ip) { |n| "42.42.42.#{n % 255}" }
diff --git a/spec/factories/subscriptions.rb b/spec/factories/subscriptions.rb
index 1ae7fc9f384..a4bc4e87b0a 100644
--- a/spec/factories/subscriptions.rb
+++ b/spec/factories/subscriptions.rb
@@ -1,4 +1,4 @@
-FactoryGirl.define do
+FactoryBot.define do
factory :subscription do
user
project
diff --git a/spec/factories/system_hooks.rb b/spec/factories/system_hooks.rb
index 841e1e293e8..9e00eeb6ef1 100644
--- a/spec/factories/system_hooks.rb
+++ b/spec/factories/system_hooks.rb
@@ -1,4 +1,4 @@
-FactoryGirl.define do
+FactoryBot.define do
factory :system_hook do
url { generate(:url) }
end
diff --git a/spec/factories/system_note_metadata.rb b/spec/factories/system_note_metadata.rb
index f487a2d7e4a..e913068da40 100644
--- a/spec/factories/system_note_metadata.rb
+++ b/spec/factories/system_note_metadata.rb
@@ -1,4 +1,4 @@
-FactoryGirl.define do
+FactoryBot.define do
factory :system_note_metadata do
note
action 'merge'
diff --git a/spec/factories/timelogs.rb b/spec/factories/timelogs.rb
index 6f1545418eb..af34b0681e2 100644
--- a/spec/factories/timelogs.rb
+++ b/spec/factories/timelogs.rb
@@ -1,6 +1,6 @@
-# Read about factories at https://github.com/thoughtbot/factory_girl
+# Read about factories at https://github.com/thoughtbot/factory_bot
-FactoryGirl.define do
+FactoryBot.define do
factory :timelog do
time_spent 3600
user
diff --git a/spec/factories/todos.rb b/spec/factories/todos.rb
index 4975befbfe3..6a6de665dd1 100644
--- a/spec/factories/todos.rb
+++ b/spec/factories/todos.rb
@@ -1,4 +1,4 @@
-FactoryGirl.define do
+FactoryBot.define do
factory :todo do
project
author
diff --git a/spec/factories/trending_project.rb b/spec/factories/trending_project.rb
index 246176611dc..f7c634fd21f 100644
--- a/spec/factories/trending_project.rb
+++ b/spec/factories/trending_project.rb
@@ -1,4 +1,4 @@
-FactoryGirl.define do
+FactoryBot.define do
# TrendingProject
factory :trending_project, class: 'TrendingProject' do
project
diff --git a/spec/factories/u2f_registrations.rb b/spec/factories/u2f_registrations.rb
index df92b079581..26090b08966 100644
--- a/spec/factories/u2f_registrations.rb
+++ b/spec/factories/u2f_registrations.rb
@@ -1,4 +1,4 @@
-FactoryGirl.define do
+FactoryBot.define do
factory :u2f_registration do
certificate { FFaker::BaconIpsum.characters(728) }
key_handle { FFaker::BaconIpsum.characters(86) }
diff --git a/spec/factories/uploads.rb b/spec/factories/uploads.rb
index e18f1a6bd4a..c39500faea1 100644
--- a/spec/factories/uploads.rb
+++ b/spec/factories/uploads.rb
@@ -1,4 +1,4 @@
-FactoryGirl.define do
+FactoryBot.define do
factory :upload do
model { build(:project) }
path { "uploads/-/system/project/avatar/avatar.jpg" }
diff --git a/spec/factories/user_agent_details.rb b/spec/factories/user_agent_details.rb
index 9763cc0cf15..7183a8e1140 100644
--- a/spec/factories/user_agent_details.rb
+++ b/spec/factories/user_agent_details.rb
@@ -1,4 +1,4 @@
-FactoryGirl.define do
+FactoryBot.define do
factory :user_agent_detail do
ip_address '127.0.0.1'
user_agent 'AppleWebKit/537.36'
diff --git a/spec/factories/user_custom_attributes.rb b/spec/factories/user_custom_attributes.rb
index 278cf290d4f..a184a2e0f17 100644
--- a/spec/factories/user_custom_attributes.rb
+++ b/spec/factories/user_custom_attributes.rb
@@ -1,4 +1,4 @@
-FactoryGirl.define do
+FactoryBot.define do
factory :user_custom_attribute do
user
sequence(:key) { |n| "key#{n}" }
diff --git a/spec/factories/users.rb b/spec/factories/users.rb
index 4000cd085b7..e62e0b263ca 100644
--- a/spec/factories/users.rb
+++ b/spec/factories/users.rb
@@ -1,4 +1,4 @@
-FactoryGirl.define do
+FactoryBot.define do
factory :user, aliases: [:author, :assignee, :recipient, :owner, :resource_owner] do
email { generate(:email) }
name { generate(:name) }
@@ -58,6 +58,10 @@ FactoryGirl.define do
end
end
+ trait :readme do
+ project_view :readme
+ end
+
factory :omniauth_user do
transient do
extern_uid '123456'
diff --git a/spec/factories/web_hook_log.rb b/spec/factories/web_hook_log.rb
index 230b3f6b26e..17837260a4b 100644
--- a/spec/factories/web_hook_log.rb
+++ b/spec/factories/web_hook_log.rb
@@ -1,4 +1,4 @@
-FactoryGirl.define do
+FactoryBot.define do
factory :web_hook_log do
web_hook factory: :project_hook
trigger 'push_hooks'
diff --git a/spec/factories/wiki_directories.rb b/spec/factories/wiki_directories.rb
index 3b4cfc380b8..b105c82b19d 100644
--- a/spec/factories/wiki_directories.rb
+++ b/spec/factories/wiki_directories.rb
@@ -1,4 +1,4 @@
-FactoryGirl.define do
+FactoryBot.define do
factory :wiki_directory do
skip_create
diff --git a/spec/factories/wiki_pages.rb b/spec/factories/wiki_pages.rb
index 4105f59e289..2335b5118dd 100644
--- a/spec/factories/wiki_pages.rb
+++ b/spec/factories/wiki_pages.rb
@@ -1,6 +1,6 @@
require 'ostruct'
-FactoryGirl.define do
+FactoryBot.define do
factory :wiki_page do
transient do
attrs do
diff --git a/spec/factories_spec.rb b/spec/factories_spec.rb
index 09b3c0b0994..66b71d0f556 100644
--- a/spec/factories_spec.rb
+++ b/spec/factories_spec.rb
@@ -1,7 +1,7 @@
require 'spec_helper'
describe 'factories' do
- FactoryGirl.factories.each do |factory|
+ FactoryBot.factories.each do |factory|
describe "#{factory.name} factory" do
it 'does not raise error when built' do
expect { build(factory.name) }.not_to raise_error
diff --git a/spec/features/admin/admin_projects_spec.rb b/spec/features/admin/admin_projects_spec.rb
index 94b12105088..d02ac6c2e2a 100644
--- a/spec/features/admin/admin_projects_spec.rb
+++ b/spec/features/admin/admin_projects_spec.rb
@@ -88,7 +88,7 @@ describe "Admin::Projects" do
describe 'add admin himself to a project' do
before do
- project.team << [user, :master]
+ project.add_master(user)
end
it 'adds admin a to a project as developer', :js do
@@ -110,8 +110,8 @@ describe "Admin::Projects" do
describe 'admin remove himself from a project' do
before do
- project.team << [user, :master]
- project.team << [current_user, :developer]
+ project.add_master(user)
+ project.add_developer(current_user)
end
it 'removes admin from the project' do
diff --git a/spec/features/admin/admin_runners_spec.rb b/spec/features/admin/admin_runners_spec.rb
index e3bb16af38a..c1c54177167 100644
--- a/spec/features/admin/admin_runners_spec.rb
+++ b/spec/features/admin/admin_runners_spec.rb
@@ -13,8 +13,8 @@ describe "Admin Runners" do
context "when there are runners" do
before do
- runner = FactoryGirl.create(:ci_runner, contacted_at: Time.now)
- FactoryGirl.create(:ci_build, pipeline: pipeline, runner_id: runner.id)
+ runner = FactoryBot.create(:ci_runner, contacted_at: Time.now)
+ FactoryBot.create(:ci_build, pipeline: pipeline, runner_id: runner.id)
visit admin_runners_path
end
@@ -25,8 +25,8 @@ describe "Admin Runners" do
describe 'search' do
before do
- FactoryGirl.create :ci_runner, description: 'runner-foo'
- FactoryGirl.create :ci_runner, description: 'runner-bar'
+ FactoryBot.create :ci_runner, description: 'runner-foo'
+ FactoryBot.create :ci_runner, description: 'runner-bar'
end
it 'shows correct runner when description matches' do
@@ -62,11 +62,11 @@ describe "Admin Runners" do
end
describe "Runner show page" do
- let(:runner) { FactoryGirl.create :ci_runner }
+ let(:runner) { FactoryBot.create :ci_runner }
before do
- @project1 = FactoryGirl.create(:project)
- @project2 = FactoryGirl.create(:project)
+ @project1 = FactoryBot.create(:project)
+ @project2 = FactoryBot.create(:project)
visit admin_runner_path(runner)
end
diff --git a/spec/features/admin/admin_system_info_spec.rb b/spec/features/admin/admin_system_info_spec.rb
index 1fd1cda694a..5a989319d5b 100644
--- a/spec/features/admin/admin_system_info_spec.rb
+++ b/spec/features/admin/admin_system_info_spec.rb
@@ -18,8 +18,8 @@ describe 'Admin System Info' do
it 'shows system info page' do
expect(page).to have_content 'CPU 2 cores'
- expect(page).to have_content 'Memory 4 GB / 16 GB'
- expect(page).to have_content 'Disks'
+ expect(page).to have_content 'Memory Usage 4 GB / 16 GB'
+ expect(page).to have_content 'Disk Usage'
expect(page).to have_content 'Uptime'
end
end
@@ -33,8 +33,8 @@ describe 'Admin System Info' do
it 'shows system info page with no CPU info' do
expect(page).to have_content 'CPU Unable to collect CPU info'
- expect(page).to have_content 'Memory 4 GB / 16 GB'
- expect(page).to have_content 'Disks'
+ expect(page).to have_content 'Memory Usage 4 GB / 16 GB'
+ expect(page).to have_content 'Disk Usage'
expect(page).to have_content 'Uptime'
end
end
@@ -48,8 +48,8 @@ describe 'Admin System Info' do
it 'shows system info page with no CPU info' do
expect(page).to have_content 'CPU 2 cores'
- expect(page).to have_content 'Memory Unable to collect memory info'
- expect(page).to have_content 'Disks'
+ expect(page).to have_content 'Memory Usage Unable to collect memory info'
+ expect(page).to have_content 'Disk Usage'
expect(page).to have_content 'Uptime'
end
end
diff --git a/spec/features/atom/dashboard_issues_spec.rb b/spec/features/atom/dashboard_issues_spec.rb
index 89c9d377003..d673bac4995 100644
--- a/spec/features/atom/dashboard_issues_spec.rb
+++ b/spec/features/atom/dashboard_issues_spec.rb
@@ -8,8 +8,8 @@ describe "Dashboard Issues Feed" do
let!(:project2) { create(:project) }
before do
- project1.team << [user, :master]
- project2.team << [user, :master]
+ project1.add_master(user)
+ project2.add_master(user)
end
describe "atom feed" do
diff --git a/spec/features/atom/dashboard_spec.rb b/spec/features/atom/dashboard_spec.rb
index 2c0c331b6db..c6683bb3bc9 100644
--- a/spec/features/atom/dashboard_spec.rb
+++ b/spec/features/atom/dashboard_spec.rb
@@ -26,7 +26,7 @@ describe "Dashboard Feed" do
let(:note) { create(:note, noteable: issue, author: user, note: 'Bug confirmed', project: project) }
before do
- project.team << [user, :master]
+ project.add_master(user)
issue_event(issue, user)
note_event(note, user)
visit dashboard_projects_path(:atom, rss_token: user.rss_token)
diff --git a/spec/features/atom/issues_spec.rb b/spec/features/atom/issues_spec.rb
index 4102ac0588a..525ce23aa56 100644
--- a/spec/features/atom/issues_spec.rb
+++ b/spec/features/atom/issues_spec.rb
@@ -9,7 +9,7 @@ describe 'Issues Feed' do
let!(:issue) { create(:issue, author: user, assignees: [assignee], project: project) }
before do
- project.team << [user, :developer]
+ project.add_developer(user)
group.add_developer(user)
end
diff --git a/spec/features/atom/users_spec.rb b/spec/features/atom/users_spec.rb
index 2b934d81674..782f42aab04 100644
--- a/spec/features/atom/users_spec.rb
+++ b/spec/features/atom/users_spec.rb
@@ -47,7 +47,7 @@ describe "User Feed" do
let!(:push_event_payload) { create(:push_event_payload, event: push_event) }
before do
- project.team << [user, :master]
+ project.add_master(user)
issue_event(issue, user)
note_event(note, user)
merge_request_event(merge_request, user)
diff --git a/spec/features/auto_deploy_spec.rb b/spec/features/auto_deploy_spec.rb
index 7a395f62511..9aef68b7156 100644
--- a/spec/features/auto_deploy_spec.rb
+++ b/spec/features/auto_deploy_spec.rb
@@ -52,7 +52,7 @@ describe 'Auto deploy' do
context 'when user configured kubernetes from Integration > Kubernetes' do
before do
create :kubernetes_service, project: project
- project.team << [user, :master]
+ project.add_master(user)
sign_in user
end
@@ -65,7 +65,7 @@ describe 'Auto deploy' do
context 'when user configured kubernetes from CI/CD > Clusters' do
before do
create(:cluster, :provided_by_gcp, projects: [project])
- project.team << [user, :master]
+ project.add_master(user)
sign_in user
end
diff --git a/spec/features/boards/add_issues_modal_spec.rb b/spec/features/boards/add_issues_modal_spec.rb
index e4cfcea45a5..18901a954cc 100644
--- a/spec/features/boards/add_issues_modal_spec.rb
+++ b/spec/features/boards/add_issues_modal_spec.rb
@@ -12,7 +12,7 @@ describe 'Issue Boards add issue modal', :js do
let!(:issue2) { create(:issue, project: project, title: 'hij', description: 'klm') }
before do
- project.team << [user, :master]
+ project.add_master(user)
sign_in(user)
diff --git a/spec/features/boards/boards_spec.rb b/spec/features/boards/boards_spec.rb
index e8d779f5772..3876d1c76d7 100644
--- a/spec/features/boards/boards_spec.rb
+++ b/spec/features/boards/boards_spec.rb
@@ -11,8 +11,8 @@ describe 'Issue Boards', :js do
let!(:user2) { create(:user) }
before do
- project.team << [user, :master]
- project.team << [user2, :master]
+ project.add_master(user)
+ project.add_master(user2)
set_cookie('sidebar_collapsed', 'true')
@@ -551,7 +551,7 @@ describe 'Issue Boards', :js do
let(:user_guest) { create(:user) }
before do
- project.team << [user_guest, :guest]
+ project.add_guest(user_guest)
sign_out(:user)
sign_in(user_guest)
visit project_board_path(project, board)
diff --git a/spec/features/boards/issue_ordering_spec.rb b/spec/features/boards/issue_ordering_spec.rb
index 4cbb48e2e6e..5abd02dbb48 100644
--- a/spec/features/boards/issue_ordering_spec.rb
+++ b/spec/features/boards/issue_ordering_spec.rb
@@ -13,7 +13,7 @@ describe 'Issue Boards', :js do
let!(:issue3) { create(:labeled_issue, project: project, title: 'testing 3', labels: [label], relative_position: 1) }
before do
- project.team << [user, :master]
+ project.add_master(user)
sign_in(user)
end
diff --git a/spec/features/boards/keyboard_shortcut_spec.rb b/spec/features/boards/keyboard_shortcut_spec.rb
index 435de3861cf..d820a59aa16 100644
--- a/spec/features/boards/keyboard_shortcut_spec.rb
+++ b/spec/features/boards/keyboard_shortcut_spec.rb
@@ -1,20 +1,38 @@
require 'rails_helper'
describe 'Issue Boards shortcut', :js do
- let(:project) { create(:project) }
+ context 'issues are enabled' do
+ let(:project) { create(:project) }
- before do
- create(:board, project: project)
+ before do
+ create(:board, project: project)
- sign_in(create(:admin))
+ sign_in(create(:admin))
- visit project_path(project)
+ visit project_path(project)
+ end
+
+ it 'takes user to issue board index' do
+ find('body').native.send_keys('gb')
+ expect(page).to have_selector('.boards-list')
+
+ wait_for_requests
+ end
end
- it 'takes user to issue board index' do
- find('body').native.send_keys('gb')
- expect(page).to have_selector('.boards-list')
+ context 'issues are not enabled' do
+ let(:project) { create(:project, :issues_disabled) }
+
+ before do
+ sign_in(create(:admin))
+
+ visit project_path(project)
+ end
+
+ it 'does not take user to the issue board index' do
+ find('body').native.send_keys('gb')
- wait_for_requests
+ expect(page).to have_selector("body[data-page='projects:show']")
+ end
end
end
diff --git a/spec/features/boards/modal_filter_spec.rb b/spec/features/boards/modal_filter_spec.rb
index 422d96175f7..5907bb0840f 100644
--- a/spec/features/boards/modal_filter_spec.rb
+++ b/spec/features/boards/modal_filter_spec.rb
@@ -10,7 +10,7 @@ describe 'Issue Boards add issue modal filtering', :js do
let!(:issue1) { create(:issue, project: project) }
before do
- project.team << [user, :master]
+ project.add_master(user)
sign_in(user)
end
@@ -76,7 +76,7 @@ describe 'Issue Boards add issue modal filtering', :js do
let!(:issue) { create(:issue, project: project, author: user2) }
before do
- project.team << [user2, :developer]
+ project.add_developer(user2)
visit_board
end
@@ -99,7 +99,7 @@ describe 'Issue Boards add issue modal filtering', :js do
let!(:issue) { create(:issue, project: project, assignees: [user2]) }
before do
- project.team << [user2, :developer]
+ project.add_developer(user2)
visit_board
end
diff --git a/spec/features/boards/new_issue_spec.rb b/spec/features/boards/new_issue_spec.rb
index 5ac4d87e90b..6769acb7c9c 100644
--- a/spec/features/boards/new_issue_spec.rb
+++ b/spec/features/boards/new_issue_spec.rb
@@ -8,7 +8,7 @@ describe 'Issue Boards new issue', :js do
context 'authorized user' do
before do
- project.team << [user, :master]
+ project.add_master(user)
sign_in(user)
diff --git a/spec/features/calendar_spec.rb b/spec/features/calendar_spec.rb
index a9530becb65..70faf28e09d 100644
--- a/spec/features/calendar_spec.rb
+++ b/spec/features/calendar_spec.rb
@@ -12,7 +12,7 @@ feature 'Contributions Calendar', :js do
issue_params = { title: issue_title }
def get_cell_color_selector(contributions)
- activity_colors = %w[#ededed #acd5f2 #7fa8c9 #527ba0 #254e77]
+ activity_colors = ["#ededed", "rgb(172, 213, 242)", "rgb(127, 168, 201)", "rgb(82, 123, 160)", "rgb(37, 78, 119)"]
# We currently don't actually test the cases with contributions >= 20
activity_colors_index =
if contributions > 0 && contributions < 10
diff --git a/spec/features/commits_spec.rb b/spec/features/commits_spec.rb
index c870910c8ea..a28b8905b65 100644
--- a/spec/features/commits_spec.rb
+++ b/spec/features/commits_spec.rb
@@ -26,7 +26,7 @@ describe 'Commits' do
let!(:status) { create(:generic_commit_status, pipeline: pipeline) }
before do
- project.team << [user, :reporter]
+ project.add_reporter(user)
end
describe 'Commit builds' do
@@ -51,7 +51,7 @@ describe 'Commits' do
context 'when logged as developer' do
before do
- project.team << [user, :developer]
+ project.add_developer(user)
end
describe 'Project commits' do
@@ -145,7 +145,7 @@ describe 'Commits' do
context "when logged as reporter" do
before do
- project.team << [user, :reporter]
+ project.add_reporter(user)
build.update_attributes(legacy_artifacts_file: artifacts_file)
visit pipeline_path(pipeline)
end
@@ -188,7 +188,7 @@ describe 'Commits' do
let(:branch_name) { 'master' }
before do
- project.team << [user, :master]
+ project.add_master(user)
sign_in(user)
visit project_commits_path(project, branch_name)
end
@@ -197,7 +197,7 @@ describe 'Commits' do
commits = project.repository.commits(branch_name)
commits.each do |commit|
- expect(page).to have_content("committed #{commit.committed_date.strftime("%b %d, %Y")}")
+ expect(page).to have_content("authored #{commit.authored_date.strftime("%b %d, %Y")}")
end
end
diff --git a/spec/features/copy_as_gfm_spec.rb b/spec/features/copy_as_gfm_spec.rb
index 1fcb8d5bc67..d8f1a919522 100644
--- a/spec/features/copy_as_gfm_spec.rb
+++ b/spec/features/copy_as_gfm_spec.rb
@@ -285,6 +285,102 @@ describe 'Copy as GFM', :js do
end
verify(
+ 'MermaidFilter: mermaid as converted from GFM to HTML',
+
+ <<-GFM.strip_heredoc
+ ```mermaid
+ graph TD;
+ A-->B;
+ ```
+ GFM
+ )
+
+ aggregate_failures('MermaidFilter: mermaid as transformed from HTML to SVG') do
+ gfm = <<-GFM.strip_heredoc
+ ```mermaid
+ graph TD;
+ A-->B;
+ ```
+ GFM
+
+ html = <<-HTML.strip_heredoc
+ <svg id="mermaidChart1" xmlns="http://www.w3.org/2000/svg" height="100%" viewBox="0 0 87.234375 174" style="max-width:87.234375px;" class="mermaid">
+ <style>
+ .mermaid {
+ /* Flowchart variables */
+ /* Sequence Diagram variables */
+ /* Gantt chart variables */
+ /** Section styling */
+ /* Grid and axis */
+ /* Today line */
+ /* Task styling */
+ /* Default task */
+ /* Specific task settings for the sections*/
+ /* Active task */
+ /* Completed task */
+ /* Tasks on the critical line */
+ }
+ </style>
+ <g>
+ <g class="output">
+ <g class="clusters"></g>
+ <g class="edgePaths">
+ <g class="edgePath" style="opacity: 1;">
+ <path class="path" d="M33.6171875,52L33.6171875,77L33.6171875,102" marker-end="url(#arrowhead65)" style="fill:none"></path>
+ <defs>
+ <marker id="arrowhead65" viewBox="0 0 10 10" refX="9" refY="5" markerUnits="strokeWidth" markerWidth="8" markerHeight="6" orient="auto">
+ <path d="M 0 0 L 10 5 L 0 10 z" class="arrowheadPath" style="stroke-width: 1; stroke-dasharray: 1, 0;"></path>
+ </marker>
+ </defs>
+ </g>
+ </g>
+ <g class="edgeLabels">
+ <g class="edgeLabel" style="opacity: 1;" transform="">
+ <g transform="translate(0,0)" class="label">
+ <foreignObject width="0" height="0">
+ <div xmlns="http://www.w3.org/1999/xhtml" style="display: inline-block; white-space: nowrap;">
+ <span class="edgeLabel"></span>
+ </div>
+ </foreignObject>
+ </g>
+ </g>
+ </g>
+ <g class="nodes">
+ <g class="node" id="A" transform="translate(33.6171875,36)" style="opacity: 1;">
+ <rect rx="0" ry="0" x="-13.6171875" y="-16" width="27.234375" height="32"></rect>
+ <g class="label" transform="translate(0,0)">
+ <g transform="translate(-3.6171875,-6)">
+ <foreignObject width="7.234375" height="12">
+ <div xmlns="http://www.w3.org/1999/xhtml" style="display: inline-block; white-space: nowrap;">A</div>
+ </foreignObject>
+ </g>
+ </g>
+ </g>
+ <g class="node" id="B" transform="translate(33.6171875,118)" style="opacity: 1;">
+ <rect rx="0" ry="0" x="-13.6171875" y="-16" width="27.234375" height="32">
+ </rect>
+ <g class="label" transform="translate(0,0)">
+ <g transform="translate(-3.6171875,-6)">
+ <foreignObject width="7.234375" height="12">
+ <div xmlns="http://www.w3.org/1999/xhtml" style="display: inline-block; white-space: nowrap;">B</div>
+ </foreignObject>
+ </g>
+ </g>
+ </g>
+ </g>
+ </g>
+ </g>
+ <text class="source" display="none">graph TD;
+ A--&gt;B;
+ </text>
+ </svg>
+ HTML
+
+ output_gfm = html_to_gfm(html)
+ expect(output_gfm.strip).to eq(gfm.strip)
+ end
+
+ verify(
'SanitizationFilter',
<<-GFM.strip_heredoc
diff --git a/spec/features/cycle_analytics_spec.rb b/spec/features/cycle_analytics_spec.rb
index 177cd50dd72..d36954954b6 100644
--- a/spec/features/cycle_analytics_spec.rb
+++ b/spec/features/cycle_analytics_spec.rb
@@ -95,7 +95,7 @@ feature 'Cycle Analytics', :js do
before do
user.update_attribute(:preferred_language, 'es')
- project.team << [user, :master]
+ project.add_master(user)
sign_in(user)
visit project_cycle_analytics_path(project)
wait_for_requests
diff --git a/spec/features/dashboard/activity_spec.rb b/spec/features/dashboard/activity_spec.rb
index bd115785646..a74a8aac2b2 100644
--- a/spec/features/dashboard/activity_spec.rb
+++ b/spec/features/dashboard/activity_spec.rb
@@ -24,6 +24,7 @@ feature 'Dashboard > Activity' do
end
let(:note) { create(:note, project: project, noteable: merge_request) }
+ let(:milestone) { create(:milestone, :active, project: project, title: '1.0') }
let!(:push_event) do
event = create(:push_event, project: project, author: user)
@@ -54,6 +55,10 @@ feature 'Dashboard > Activity' do
create(:event, :commented, project: project, target: note, author: user)
end
+ let!(:milestone_event) do
+ create(:event, :closed, project: project, target: milestone, author: user)
+ end
+
before do
project.add_master(user)
@@ -68,6 +73,7 @@ feature 'Dashboard > Activity' do
expect(page).to have_content('accepted')
expect(page).to have_content('closed')
expect(page).to have_content('commented on')
+ expect(page).to have_content('closed milestone')
end
end
@@ -107,6 +113,7 @@ feature 'Dashboard > Activity' do
expect(page).not_to have_content('accepted')
expect(page).to have_content('closed')
expect(page).not_to have_content('commented on')
+ expect(page).to have_content('closed milestone')
end
end
diff --git a/spec/features/dashboard/archived_projects_spec.rb b/spec/features/dashboard/archived_projects_spec.rb
index e8d699ff5e0..b36231fd78b 100644
--- a/spec/features/dashboard/archived_projects_spec.rb
+++ b/spec/features/dashboard/archived_projects_spec.rb
@@ -6,8 +6,8 @@ RSpec.describe 'Dashboard Archived Project' do
let(:archived_project) { create(:project, :archived) }
before do
- project.team << [user, :master]
- archived_project.team << [user, :master]
+ project.add_master(user)
+ archived_project.add_master(user)
sign_in(user)
diff --git a/spec/features/dashboard/datetime_on_tooltips_spec.rb b/spec/features/dashboard/datetime_on_tooltips_spec.rb
index 349f9a47112..089c388636d 100644
--- a/spec/features/dashboard/datetime_on_tooltips_spec.rb
+++ b/spec/features/dashboard/datetime_on_tooltips_spec.rb
@@ -8,7 +8,7 @@ feature 'Tooltips on .timeago dates', :js do
context 'on the activity tab' do
before do
- project.team << [user, :master]
+ project.add_master(user)
Event.create( project: project, author_id: user.id, action: Event::JOINED,
updated_at: created_date, created_at: created_date)
@@ -27,7 +27,7 @@ feature 'Tooltips on .timeago dates', :js do
context 'on the snippets tab' do
before do
- project.team << [user, :master]
+ project.add_master(user)
create(:snippet, author: user, updated_at: created_date, created_at: created_date)
sign_in user
diff --git a/spec/features/dashboard/groups_list_spec.rb b/spec/features/dashboard/groups_list_spec.rb
index d92c002b4e7..a71020002dc 100644
--- a/spec/features/dashboard/groups_list_spec.rb
+++ b/spec/features/dashboard/groups_list_spec.rb
@@ -94,22 +94,14 @@ feature 'Dashboard Groups page', :js do
end
it 'can toggle parent group' do
- # Collapsed by default
- expect(page).not_to have_selector("#group-#{group.id} .fa-caret-down", count: 1)
- expect(page).to have_selector("#group-#{group.id} .fa-caret-right")
-
# expand
click_group_caret(group)
- expect(page).to have_selector("#group-#{group.id} .fa-caret-down")
- expect(page).not_to have_selector("#group-#{group.id} .fa-caret-right", count: 1)
expect(page).to have_selector("#group-#{group.id} #group-#{subgroup.id}")
# collapse
click_group_caret(group)
- expect(page).not_to have_selector("#group-#{group.id} .fa-caret-down", count: 1)
- expect(page).to have_selector("#group-#{group.id} .fa-caret-right")
expect(page).not_to have_selector("#group-#{group.id} #group-#{subgroup.id}")
end
end
diff --git a/spec/features/dashboard/issues_spec.rb b/spec/features/dashboard/issues_spec.rb
index 5b4c00b3c7e..54652e2d849 100644
--- a/spec/features/dashboard/issues_spec.rb
+++ b/spec/features/dashboard/issues_spec.rb
@@ -12,7 +12,7 @@ RSpec.describe 'Dashboard Issues' do
let!(:other_issue) { create :issue, project: project }
before do
- [project, project_with_issues_disabled].each { |project| project.team << [current_user, :master] }
+ [project, project_with_issues_disabled].each { |project| project.add_master(current_user) }
sign_in(current_user)
visit issues_dashboard_path(assignee_id: current_user.id)
end
diff --git a/spec/features/dashboard/milestones_spec.rb b/spec/features/dashboard/milestones_spec.rb
index 41d37376cfb..7787772a958 100644
--- a/spec/features/dashboard/milestones_spec.rb
+++ b/spec/features/dashboard/milestones_spec.rb
@@ -16,7 +16,7 @@ feature 'Dashboard > Milestones' do
let(:project) { create(:project, namespace: user.namespace) }
let!(:milestone) { create(:milestone, project: project) }
before do
- project.team << [user, :master]
+ project.add_master(user)
sign_in(user)
visit dashboard_milestones_path
end
diff --git a/spec/features/dashboard/project_member_activity_index_spec.rb b/spec/features/dashboard/project_member_activity_index_spec.rb
index 8f96899fb4f..6c3093607b0 100644
--- a/spec/features/dashboard/project_member_activity_index_spec.rb
+++ b/spec/features/dashboard/project_member_activity_index_spec.rb
@@ -5,7 +5,7 @@ feature 'Project member activity', :js do
let(:project) { create(:project, :public, name: 'x', namespace: user.namespace) }
before do
- project.team << [user, :master]
+ project.add_master(user)
end
def visit_activities_and_wait_with_event(event_type)
diff --git a/spec/features/dashboard/projects_spec.rb b/spec/features/dashboard/projects_spec.rb
index fbf8b5c0db6..586c7b48d0b 100644
--- a/spec/features/dashboard/projects_spec.rb
+++ b/spec/features/dashboard/projects_spec.rb
@@ -6,7 +6,7 @@ feature 'Dashboard Projects' do
let(:project2) { create(:project, :public, name: 'Community project') }
before do
- project.team << [user, :developer]
+ project.add_developer(user)
sign_in(user)
end
diff --git a/spec/features/dashboard/todos/todos_filtering_spec.rb b/spec/features/dashboard/todos/todos_filtering_spec.rb
index ad0f132da8c..2fc34301d51 100644
--- a/spec/features/dashboard/todos/todos_filtering_spec.rb
+++ b/spec/features/dashboard/todos/todos_filtering_spec.rb
@@ -15,8 +15,8 @@ feature 'Dashboard > User filters todos', :js do
create(:todo, user: user_1, author: user_2, project: project_1, target: issue, action: 1)
create(:todo, user: user_1, author: user_1, project: project_2, target: merge_request, action: 2)
- project_1.team << [user_1, :developer]
- project_2.team << [user_1, :developer]
+ project_1.add_developer(user_1)
+ project_2.add_developer(user_1)
sign_in(user_1)
visit dashboard_todos_path
end
@@ -66,8 +66,8 @@ feature 'Dashboard > User filters todos', :js do
create(:todo, user: user_1, author: user_3, project: project_1, target: issue, action: 1, state: :done)
create(:todo, user: user_1, author: user_4, project: project_2, target: merge_request, action: 2, state: :done)
- project_1.team << [user_3, :developer]
- project_2.team << [user_4, :developer]
+ project_1.add_developer(user_3)
+ project_2.add_developer(user_4)
visit dashboard_todos_path(state: 'done')
diff --git a/spec/features/dashboard/todos/todos_sorting_spec.rb b/spec/features/dashboard/todos/todos_sorting_spec.rb
index b7d39a872b0..10e3ad843fd 100644
--- a/spec/features/dashboard/todos/todos_sorting_spec.rb
+++ b/spec/features/dashboard/todos/todos_sorting_spec.rb
@@ -9,7 +9,7 @@ feature 'Dashboard > User sorts todos' do
let(:label_3) { create(:label, title: 'label_3', project: project, priority: 3) }
before do
- project.team << [user, :developer]
+ project.add_developer(user)
end
context 'sort options' do
diff --git a/spec/features/dashboard/todos/todos_spec.rb b/spec/features/dashboard/todos/todos_spec.rb
index 6f916078b1a..94133c62b5c 100644
--- a/spec/features/dashboard/todos/todos_spec.rb
+++ b/spec/features/dashboard/todos/todos_spec.rb
@@ -13,7 +13,7 @@ feature 'Dashboard Todos' do
end
it 'shows "All done" message' do
- expect(page).to have_content 'Todos let you see what you should do next.'
+ expect(page).to have_content 'Todos let you see what you should do next'
end
end
diff --git a/spec/features/dashboard/user_filters_projects_spec.rb b/spec/features/dashboard/user_filters_projects_spec.rb
index c352b6ded14..92f4d4b854c 100644
--- a/spec/features/dashboard/user_filters_projects_spec.rb
+++ b/spec/features/dashboard/user_filters_projects_spec.rb
@@ -7,14 +7,14 @@ describe 'Dashboard > User filters projects' do
let(:project2) { create(:project, name: 'Treasure', namespace: user2.namespace) }
before do
- project.team << [user, :master]
+ project.add_master(user)
sign_in(user)
end
describe 'filtering personal projects' do
before do
- project2.team << [user, :developer]
+ project2.add_developer(user)
visit dashboard_projects_path
end
diff --git a/spec/features/explore/groups_spec.rb b/spec/features/explore/groups_spec.rb
new file mode 100644
index 00000000000..e4ef47d88dd
--- /dev/null
+++ b/spec/features/explore/groups_spec.rb
@@ -0,0 +1,87 @@
+require 'spec_helper'
+
+describe 'Explore Groups', :js do
+ let(:user) { create :user }
+ let(:group) { create :group }
+ let!(:private_project) do
+ create :project, :private, namespace: group do |project|
+ create(:issue, project: internal_project)
+ create(:merge_request, source_project: project, target_project: project)
+ end
+ end
+
+ let!(:internal_project) do
+ create :project, :internal, namespace: group do |project|
+ create(:issue, project: project)
+ create(:merge_request, source_project: project, target_project: project)
+ end
+ end
+
+ let!(:public_project) do
+ create(:project, :public, namespace: group) do |project|
+ create(:issue, project: project)
+ create(:merge_request, source_project: project, target_project: project)
+ end
+ end
+
+ shared_examples 'renders public and internal projects' do
+ it do
+ visit_page
+ expect(page).to have_content(public_project.name)
+ expect(page).to have_content(internal_project.name)
+ expect(page).not_to have_content(private_project.name)
+ end
+ end
+
+ shared_examples 'renders only public project' do
+ it do
+ visit_page
+ expect(page).to have_content(public_project.name)
+ expect(page).not_to have_content(internal_project.name)
+ expect(page).not_to have_content(private_project.name)
+ end
+ end
+
+ shared_examples 'renders group in public groups area' do
+ it do
+ visit explore_groups_path
+ expect(page).to have_content(group.name)
+ end
+ end
+
+ context 'when signed in' do
+ before do
+ sign_in(user)
+ end
+
+ it_behaves_like 'renders public and internal projects' do
+ subject(:visit_page) { visit group_path(group) }
+ end
+
+ it_behaves_like 'renders public and internal projects' do
+ subject(:visit_page) { visit issues_group_path(group) }
+ end
+
+ it_behaves_like 'renders public and internal projects' do
+ subject(:visit_page) { visit merge_requests_group_path(group) }
+ end
+
+ it_behaves_like 'renders group in public groups area'
+ end
+
+ context 'when signed out' do
+ it_behaves_like 'renders only public project' do
+ subject(:visit_page) { visit group_path(group) }
+ end
+
+ it_behaves_like 'renders only public project' do
+ subject(:visit_page) { visit issues_group_path(group) }
+ end
+
+ it_behaves_like 'renders only public project' do
+ subject(:visit_page) { visit merge_requests_group_path(group) }
+ end
+
+ it_behaves_like 'renders group in public groups area'
+ end
+end
diff --git a/spec/features/global_search_spec.rb b/spec/features/global_search_spec.rb
index f04e13adba7..4f575613848 100644
--- a/spec/features/global_search_spec.rb
+++ b/spec/features/global_search_spec.rb
@@ -5,7 +5,7 @@ feature 'Global search' do
let(:project) { create(:project, namespace: user.namespace) }
before do
- project.team << [user, :master]
+ project.add_master(user)
sign_in(user)
end
diff --git a/spec/features/group_variables_spec.rb b/spec/features/group_variables_spec.rb
index d2d0be35f1c..e9b375f4c94 100644
--- a/spec/features/group_variables_spec.rb
+++ b/spec/features/group_variables_spec.rb
@@ -24,7 +24,7 @@ feature 'Group variables', :js do
expect(find(".variable-value")).to have_content('******')
expect(find(".variable-protected")).to have_content('Yes')
end
- click_on 'Reveal Values'
+ click_on 'Reveal value'
page.within('.variables-table') do
expect(find(".variable-value")).to have_content('AAA123')
end
diff --git a/spec/features/groups/labels/user_sees_links_to_issuables.rb b/spec/features/groups/labels/user_sees_links_to_issuables.rb
new file mode 100644
index 00000000000..5d6290d2109
--- /dev/null
+++ b/spec/features/groups/labels/user_sees_links_to_issuables.rb
@@ -0,0 +1,15 @@
+require 'spec_helper'
+
+feature 'Groups > Labels > User sees links to issuables' do
+ set(:group) { create(:group, :public) }
+
+ before do
+ create(:group_label, group: group, title: 'bug')
+ visit group_labels_path(group)
+ end
+
+ scenario 'shows links to MRs and issues' do
+ expect(page).to have_link('view merge requests')
+ expect(page).to have_link('view open issues')
+ end
+end
diff --git a/spec/features/groups/members/manage_members.rb b/spec/features/groups/members/manage_members.rb
index da1e17225db..21f7b4999ad 100644
--- a/spec/features/groups/members/manage_members.rb
+++ b/spec/features/groups/members/manage_members.rb
@@ -38,6 +38,27 @@ feature 'Groups > Members > Manage members' do
end
end
+ scenario 'do not disclose email addresses', :js do
+ group.add_owner(user1)
+ create(:user, email: 'undisclosed_email@gitlab.com', name: "Jane 'invisible' Doe")
+
+ visit group_group_members_path(group)
+
+ find('.select2-container').click
+ select_input = find('.select2-input')
+
+ select_input.send_keys('@gitlab.com')
+ wait_for_requests
+
+ expect(page).to have_content('No matches found')
+
+ select_input.native.clear
+ select_input.send_keys('undisclosed_email@gitlab.com')
+ wait_for_requests
+
+ expect(page).to have_content("Jane 'invisible' Doe")
+ end
+
scenario 'remove user from group', :js do
group.add_owner(user1)
group.add_developer(user2)
diff --git a/spec/features/groups/show_spec.rb b/spec/features/groups/show_spec.rb
index 7fc2b383749..ceccc471405 100644
--- a/spec/features/groups/show_spec.rb
+++ b/spec/features/groups/show_spec.rb
@@ -55,4 +55,20 @@ feature 'Group show page' do
end
end
end
+
+ context 'group has a project with emoji in description', :js do
+ let(:user) { create(:user) }
+ let!(:project) { create(:project, description: ':smile:', namespace: group) }
+
+ before do
+ group.add_owner(user)
+ sign_in(user)
+ visit path
+ end
+
+ it 'shows the project info' do
+ expect(page).to have_content(project.title)
+ expect(page).to have_selector('gl-emoji[data-name="smile"]')
+ end
+ end
end
diff --git a/spec/features/help_pages_spec.rb b/spec/features/help_pages_spec.rb
index 93be3b066ee..0d04ed612c2 100644
--- a/spec/features/help_pages_spec.rb
+++ b/spec/features/help_pages_spec.rb
@@ -32,12 +32,30 @@ describe 'Help Pages' do
it_behaves_like 'help page', prefix: '/gitlab'
end
+
+ context 'quick link shortcuts', :js do
+ before do
+ visit help_path
+ end
+
+ it 'focuses search bar' do
+ find('.js-trigger-search-bar').click
+
+ expect(page).to have_selector('#search:focus')
+ end
+
+ it 'opens shortcuts help dialog' do
+ find('.js-trigger-shortcut').click
+
+ expect(page).to have_selector('#modal-shortcuts')
+ end
+ end
end
context 'in a production environment with version check enabled', :js do
before do
allow(Rails.env).to receive(:production?) { true }
- allow_any_instance_of(ApplicationSetting).to receive(:version_check_enabled) { true }
+ stub_application_setting(version_check_enabled: true)
allow_any_instance_of(VersionCheck).to receive(:url) { '/version-check-url' }
sign_in(create(:user))
@@ -56,9 +74,9 @@ describe 'Help Pages' do
describe 'when help page is customized' do
before do
- allow_any_instance_of(ApplicationSetting).to receive(:help_page_hide_commercial_content?) { true }
- allow_any_instance_of(ApplicationSetting).to receive(:help_page_text) { "My Custom Text" }
- allow_any_instance_of(ApplicationSetting).to receive(:help_page_support_url) { "http://example.com/help" }
+ stub_application_setting(help_page_hide_commercial_content: true,
+ help_page_text: 'My Custom Text',
+ help_page_support_url: 'http://example.com/help')
sign_in(create(:user))
visit help_path
diff --git a/spec/features/invites_spec.rb b/spec/features/invites_spec.rb
new file mode 100644
index 00000000000..e4be6193b8b
--- /dev/null
+++ b/spec/features/invites_spec.rb
@@ -0,0 +1,97 @@
+require 'spec_helper'
+
+describe 'Invites' do
+ let(:user) { create(:user) }
+ let(:owner) { create(:user, name: 'John Doe') }
+ let(:group) { create(:group, name: 'Owned') }
+ let(:project) { create(:project, :repository, namespace: group) }
+ let(:invite) { group.group_members.invite.last }
+
+ before do
+ project.add_master(owner)
+ group.add_user(owner, Gitlab::Access::OWNER)
+ group.add_developer('user@example.com', owner)
+ invite.generate_invite_token!
+ end
+
+ context 'when signed out' do
+ before do
+ visit invite_path(invite.raw_invite_token)
+ end
+
+ it 'renders sign in page with sign in notice' do
+ expect(current_path).to eq(new_user_session_path)
+ expect(page).to have_content('To accept this invitation, sign in')
+ end
+
+ it 'sign in and redirects to invitation page' do
+ fill_in 'user_login', with: user.email
+ fill_in 'user_password', with: user.password
+ check 'user_remember_me'
+ click_button 'Sign in'
+
+ expect(current_path).to eq(invite_path(invite.raw_invite_token))
+ expect(page).to have_content(
+ 'You have been invited by John Doe to join group Owned as Developer.'
+ )
+ expect(page).to have_link('Accept invitation')
+ expect(page).to have_link('Decline')
+ end
+ end
+
+ context 'when signed in as an exists member' do
+ before do
+ sign_in(owner)
+ end
+
+ it 'shows message user already a member' do
+ visit invite_path(invite.raw_invite_token)
+ expect(page).to have_content('However, you are already a member of this group.')
+ end
+ end
+
+ describe 'accepting the invitation' do
+ before do
+ sign_in(user)
+ visit invite_path(invite.raw_invite_token)
+ end
+
+ it 'grants access and redirects to group page' do
+ page.click_link 'Accept invitation'
+ expect(current_path).to eq(group_path(group))
+ expect(page).to have_content(
+ 'You have been granted Developer access to group Owned.'
+ )
+ end
+ end
+
+ describe 'declining the application' do
+ context 'when signed in' do
+ before do
+ sign_in(user)
+ visit invite_path(invite.raw_invite_token)
+ end
+
+ it 'declines application and redirects to dashboard' do
+ page.click_link 'Decline'
+ expect(current_path).to eq(dashboard_projects_path)
+ expect(page).to have_content(
+ 'You have declined the invitation to join group Owned.'
+ )
+ end
+ end
+
+ context 'when signed out' do
+ before do
+ visit decline_invite_path(invite.raw_invite_token)
+ end
+
+ it 'declines application and redirects to sign in page' do
+ expect(current_path).to eq(new_user_session_path)
+ expect(page).to have_content(
+ 'You have declined the invitation to join group Owned.'
+ )
+ end
+ end
+ end
+end
diff --git a/spec/features/issues/award_emoji_spec.rb b/spec/features/issues/award_emoji_spec.rb
index 850b35c4467..1131e1711bf 100644
--- a/spec/features/issues/award_emoji_spec.rb
+++ b/spec/features/issues/award_emoji_spec.rb
@@ -11,7 +11,7 @@ describe 'Awards Emoji' do
context 'authorized user' do
before do
- project.team << [user, :master]
+ project.add_master(user)
sign_in(user)
end
diff --git a/spec/features/issues/bulk_assignment_labels_spec.rb b/spec/features/issues/bulk_assignment_labels_spec.rb
index fa4d3a55c62..587ece22ec7 100644
--- a/spec/features/issues/bulk_assignment_labels_spec.rb
+++ b/spec/features/issues/bulk_assignment_labels_spec.rb
@@ -11,7 +11,7 @@ feature 'Issues > Labels bulk assignment' do
context 'as an allowed user', :js do
before do
- project.team << [user, :master]
+ project.add_master(user)
sign_in user
end
diff --git a/spec/features/issues/create_issue_for_discussions_in_merge_request_spec.rb b/spec/features/issues/create_issue_for_discussions_in_merge_request_spec.rb
index 822ba48e005..e0466aaf422 100644
--- a/spec/features/issues/create_issue_for_discussions_in_merge_request_spec.rb
+++ b/spec/features/issues/create_issue_for_discussions_in_merge_request_spec.rb
@@ -8,7 +8,7 @@ feature 'Resolving all open discussions in a merge request from an issue', :js d
describe 'as a user with access to the project' do
before do
- project.team << [user, :master]
+ project.add_master(user)
sign_in user
visit project_merge_request_path(project, merge_request)
end
@@ -81,7 +81,7 @@ feature 'Resolving all open discussions in a merge request from an issue', :js d
describe 'as a reporter' do
before do
- project.team << [user, :reporter]
+ project.add_reporter(user)
sign_in user
visit new_project_issue_path(project, merge_request_to_resolve_discussions_of: merge_request.iid)
end
diff --git a/spec/features/issues/create_issue_for_single_discussion_in_merge_request_spec.rb b/spec/features/issues/create_issue_for_single_discussion_in_merge_request_spec.rb
index f0bed85595c..34beb282bad 100644
--- a/spec/features/issues/create_issue_for_single_discussion_in_merge_request_spec.rb
+++ b/spec/features/issues/create_issue_for_single_discussion_in_merge_request_spec.rb
@@ -8,7 +8,7 @@ feature 'Resolve an open discussion in a merge request by creating an issue' do
describe 'As a user with access to the project' do
before do
- project.team << [user, :master]
+ project.add_master(user)
sign_in user
visit project_merge_request_path(project, merge_request)
end
@@ -65,7 +65,7 @@ feature 'Resolve an open discussion in a merge request by creating an issue' do
describe 'as a reporter' do
before do
- project.team << [user, :reporter]
+ project.add_reporter(user)
sign_in user
visit new_project_issue_path(project, merge_request_to_resolve_discussions_of: merge_request.iid,
discussion_to_resolve: discussion.id)
diff --git a/spec/features/issues/filtered_search/dropdown_assignee_spec.rb b/spec/features/issues/filtered_search/dropdown_assignee_spec.rb
index 2e4a25ee15d..cbd0949c192 100644
--- a/spec/features/issues/filtered_search/dropdown_assignee_spec.rb
+++ b/spec/features/issues/filtered_search/dropdown_assignee_spec.rb
@@ -20,9 +20,9 @@ describe 'Dropdown assignee', :js do
end
before do
- project.team << [user, :master]
- project.team << [user_john, :master]
- project.team << [user_jacob, :master]
+ project.add_master(user)
+ project.add_master(user_john)
+ project.add_master(user_jacob)
sign_in(user)
create(:issue, project: project)
@@ -222,7 +222,7 @@ describe 'Dropdown assignee', :js do
expect(initial_size).to be > 0
new_user = create(:user)
- project.team << [new_user, :master]
+ project.add_master(new_user)
find('.filtered-search-box .clear-search').click
filtered_search.set('assignee')
filtered_search.send_keys(':')
diff --git a/spec/features/issues/filtered_search/dropdown_author_spec.rb b/spec/features/issues/filtered_search/dropdown_author_spec.rb
index 2fb5e7cdba4..70b4f11410d 100644
--- a/spec/features/issues/filtered_search/dropdown_author_spec.rb
+++ b/spec/features/issues/filtered_search/dropdown_author_spec.rb
@@ -28,9 +28,9 @@ describe 'Dropdown author', :js do
end
before do
- project.team << [user, :master]
- project.team << [user_john, :master]
- project.team << [user_jacob, :master]
+ project.add_master(user)
+ project.add_master(user_john)
+ project.add_master(user_jacob)
sign_in(user)
create(:issue, project: project)
@@ -195,7 +195,7 @@ describe 'Dropdown author', :js do
expect(initial_size).to be > 0
new_user = create(:user)
- project.team << [new_user, :master]
+ project.add_master(new_user)
find('.filtered-search-box .clear-search').click
filtered_search.set('author')
send_keys_to_filtered_search(':')
diff --git a/spec/features/issues/filtered_search/dropdown_emoji_spec.rb b/spec/features/issues/filtered_search/dropdown_emoji_spec.rb
index 8db435634fd..436625a6f7b 100644
--- a/spec/features/issues/filtered_search/dropdown_emoji_spec.rb
+++ b/spec/features/issues/filtered_search/dropdown_emoji_spec.rb
@@ -28,7 +28,7 @@ describe 'Dropdown emoji', :js do
end
before do
- project.team << [user, :master]
+ project.add_master(user)
create_list(:award_emoji, 2, user: user, name: 'thumbsup')
create_list(:award_emoji, 1, user: user, name: 'thumbsdown')
create_list(:award_emoji, 3, user: user, name: 'star')
diff --git a/spec/features/issues/filtered_search/dropdown_hint_spec.rb b/spec/features/issues/filtered_search/dropdown_hint_spec.rb
index 0183495a1db..ef40dddfd3a 100644
--- a/spec/features/issues/filtered_search/dropdown_hint_spec.rb
+++ b/spec/features/issues/filtered_search/dropdown_hint_spec.rb
@@ -13,7 +13,7 @@ describe 'Dropdown hint', :js do
end
before do
- project.team << [user, :master]
+ project.add_master(user)
create(:issue, project: project)
end
@@ -176,6 +176,7 @@ describe 'Dropdown hint', :js do
it 'reuses existing author text' do
filtered_search.send_keys('author:')
filtered_search.send_keys(:backspace)
+ filtered_search.send_keys(:backspace)
click_hint('author')
expect_tokens([{ name: 'author' }])
@@ -185,6 +186,7 @@ describe 'Dropdown hint', :js do
it 'reuses existing assignee text' do
filtered_search.send_keys('assignee:')
filtered_search.send_keys(:backspace)
+ filtered_search.send_keys(:backspace)
click_hint('assignee')
expect_tokens([{ name: 'assignee' }])
@@ -194,6 +196,7 @@ describe 'Dropdown hint', :js do
it 'reuses existing milestone text' do
filtered_search.send_keys('milestone:')
filtered_search.send_keys(:backspace)
+ filtered_search.send_keys(:backspace)
click_hint('milestone')
expect_tokens([{ name: 'milestone' }])
@@ -203,6 +206,7 @@ describe 'Dropdown hint', :js do
it 'reuses existing label text' do
filtered_search.send_keys('label:')
filtered_search.send_keys(:backspace)
+ filtered_search.send_keys(:backspace)
click_hint('label')
expect_tokens([{ name: 'label' }])
@@ -212,6 +216,7 @@ describe 'Dropdown hint', :js do
it 'reuses existing emoji text' do
filtered_search.send_keys('my-reaction:')
filtered_search.send_keys(:backspace)
+ filtered_search.send_keys(:backspace)
click_hint('my-reaction')
expect_tokens([{ name: 'my-reaction' }])
diff --git a/spec/features/issues/filtered_search/dropdown_milestone_spec.rb b/spec/features/issues/filtered_search/dropdown_milestone_spec.rb
index 031eb06723a..94710c2f71f 100644
--- a/spec/features/issues/filtered_search/dropdown_milestone_spec.rb
+++ b/spec/features/issues/filtered_search/dropdown_milestone_spec.rb
@@ -29,7 +29,7 @@ describe 'Dropdown milestone', :js do
end
before do
- project.team << [user, :master]
+ project.add_master(user)
sign_in(user)
create(:issue, project: project)
diff --git a/spec/features/issues/filtered_search/search_bar_spec.rb b/spec/features/issues/filtered_search/search_bar_spec.rb
index 88688422dc7..268590da599 100644
--- a/spec/features/issues/filtered_search/search_bar_spec.rb
+++ b/spec/features/issues/filtered_search/search_bar_spec.rb
@@ -8,7 +8,7 @@ describe 'Search bar', :js do
let(:filtered_search) { find('.filtered-search') }
before do
- project.team << [user, :master]
+ project.add_master(user)
sign_in(user)
create(:issue, project: project)
diff --git a/spec/features/issues/form_spec.rb b/spec/features/issues/form_spec.rb
index 2db6f9a2982..faf14be4818 100644
--- a/spec/features/issues/form_spec.rb
+++ b/spec/features/issues/form_spec.rb
@@ -13,8 +13,8 @@ describe 'New/edit issue', :js do
let!(:issue) { create(:issue, project: project, assignees: [user], milestone: milestone) }
before do
- project.team << [user, :master]
- project.team << [user2, :master]
+ project.add_master(user)
+ project.add_master(user2)
sign_in(user)
end
diff --git a/spec/features/issues/gfm_autocomplete_spec.rb b/spec/features/issues/gfm_autocomplete_spec.rb
index c31b636d67f..f2624f55c86 100644
--- a/spec/features/issues/gfm_autocomplete_spec.rb
+++ b/spec/features/issues/gfm_autocomplete_spec.rb
@@ -7,7 +7,7 @@ feature 'GFM autocomplete', :js do
let(:issue) { create(:issue, project: project) }
before do
- project.team << [user, :master]
+ project.add_master(user)
sign_in(user)
visit project_issue_path(project, issue)
@@ -15,7 +15,7 @@ feature 'GFM autocomplete', :js do
end
it 'updates issue descripton with GFM reference' do
- find('.issuable-edit').click
+ find('.js-issuable-edit').click
simulate_input('#issue-description', "@#{user.name[0...3]}")
diff --git a/spec/features/issues/issue_detail_spec.rb b/spec/features/issues/issue_detail_spec.rb
index 4224a8fe5d4..babb0285590 100644
--- a/spec/features/issues/issue_detail_spec.rb
+++ b/spec/features/issues/issue_detail_spec.rb
@@ -24,7 +24,7 @@ feature 'Issue Detail', :js do
visit project_issue_path(project, issue)
wait_for_requests
- click_link 'Edit'
+ page.find('.js-issuable-edit').click
fill_in 'issuable-title', with: 'issue title'
click_button 'Save'
wait_for_requests
diff --git a/spec/features/issues/issue_sidebar_spec.rb b/spec/features/issues/issue_sidebar_spec.rb
index a9de52bd8d5..a5c9d0bde5d 100644
--- a/spec/features/issues/issue_sidebar_spec.rb
+++ b/spec/features/issues/issue_sidebar_spec.rb
@@ -18,7 +18,7 @@ feature 'Issue Sidebar' do
let(:issue2) { create(:issue, project: project, author: user2) }
before do
- project.team << [user, :developer]
+ project.add_developer(user)
visit_issue(project, issue2)
find('.block.assignee .edit-link').click
@@ -78,7 +78,7 @@ feature 'Issue Sidebar' do
context 'as a allowed user' do
before do
- project.team << [user, :developer]
+ project.add_developer(user)
visit_issue(project, issue)
end
@@ -156,7 +156,7 @@ feature 'Issue Sidebar' do
context 'as a guest' do
before do
- project.team << [user, :guest]
+ project.add_guest(user)
visit_issue(project, issue)
end
diff --git a/spec/features/issues/keyboard_shortcut_spec.rb b/spec/features/issues/keyboard_shortcut_spec.rb
new file mode 100644
index 00000000000..961de9d3d25
--- /dev/null
+++ b/spec/features/issues/keyboard_shortcut_spec.rb
@@ -0,0 +1,36 @@
+require 'rails_helper'
+
+describe 'Issues shortcut', :js do
+ context 'New Issue shortcut' do
+ context 'issues are enabled' do
+ let(:project) { create(:project) }
+
+ before do
+ sign_in(create(:admin))
+
+ visit project_path(project)
+ end
+
+ it 'takes user to the new issue page' do
+ find('body').native.send_keys('i')
+ expect(page).to have_selector('#new_issue')
+ end
+ end
+
+ context 'issues are not enabled' do
+ let(:project) { create(:project, :issues_disabled) }
+
+ before do
+ sign_in(create(:admin))
+
+ visit project_path(project)
+ end
+
+ it 'does not take user to the new issue page' do
+ find('body').native.send_keys('i')
+
+ expect(page).to have_selector("body[data-page='projects:show']")
+ end
+ end
+ end
+end
diff --git a/spec/features/issues/move_spec.rb b/spec/features/issues/move_spec.rb
index 17035b5501c..076a02150a4 100644
--- a/spec/features/issues/move_spec.rb
+++ b/spec/features/issues/move_spec.rb
@@ -13,7 +13,7 @@ feature 'issue move to another project' do
context 'user does not have permission to move issue' do
background do
- old_project.team << [user, :guest]
+ old_project.add_guest(user)
visit issue_path(issue)
end
@@ -31,8 +31,8 @@ feature 'issue move to another project' do
let(:cross_reference) { old_project.to_reference(new_project) }
background do
- old_project.team << [user, :reporter]
- new_project.team << [user, :reporter]
+ old_project.add_reporter(user)
+ new_project.add_reporter(user)
visit issue_path(issue)
end
@@ -50,7 +50,7 @@ feature 'issue move to another project' do
end
scenario 'searching project dropdown', :js do
- new_project_search.team << [user, :reporter]
+ new_project_search.add_reporter(user)
find('.js-move-issue').click
wait_for_requests
@@ -66,7 +66,7 @@ feature 'issue move to another project' do
context 'user does not have permission to move the issue to a project', :js do
let!(:private_project) { create(:project, :private) }
let(:another_project) { create(:project) }
- background { another_project.team << [user, :guest] }
+ background { another_project.add_guest(user) }
scenario 'browsing projects in projects select' do
find('.js-move-issue').click
diff --git a/spec/features/issues/notes_on_issues_spec.rb b/spec/features/issues/notes_on_issues_spec.rb
index 05c93a19253..f08c73f947c 100644
--- a/spec/features/issues/notes_on_issues_spec.rb
+++ b/spec/features/issues/notes_on_issues_spec.rb
@@ -8,7 +8,7 @@ describe 'Create notes on issues', :js do
let(:note_text) { "Check #{mention.to_reference}" }
before do
- project.team << [user, :developer]
+ project.add_developer(user)
sign_in(user)
visit project_issue_path(project, issue)
diff --git a/spec/features/issues/spam_issues_spec.rb b/spec/features/issues/spam_issues_spec.rb
index d25231d624c..53706ef84bc 100644
--- a/spec/features/issues/spam_issues_spec.rb
+++ b/spec/features/issues/spam_issues_spec.rb
@@ -17,7 +17,7 @@ describe 'New issue', :js do
recaptcha_private_key: 'test private key'
)
- project.team << [user, :master]
+ project.add_master(user)
sign_in(user)
end
diff --git a/spec/features/issues/todo_spec.rb b/spec/features/issues/todo_spec.rb
index 29a2d38ae18..8e6493bbd93 100644
--- a/spec/features/issues/todo_spec.rb
+++ b/spec/features/issues/todo_spec.rb
@@ -6,7 +6,7 @@ feature 'Manually create a todo item from issue', :js do
let!(:user) { create(:user)}
before do
- project.team << [user, :master]
+ project.add_master(user)
sign_in(user)
visit project_issue_path(project, issue)
end
diff --git a/spec/features/issues/update_issues_spec.rb b/spec/features/issues/update_issues_spec.rb
index bcc6e9bab0f..7d6edc171f8 100644
--- a/spec/features/issues/update_issues_spec.rb
+++ b/spec/features/issues/update_issues_spec.rb
@@ -6,7 +6,7 @@ feature 'Multiple issue updating from issues#index', :js do
let!(:user) { create(:user)}
before do
- project.team << [user, :master]
+ project.add_master(user)
sign_in(user)
end
diff --git a/spec/features/issues/user_uses_slash_commands_spec.rb b/spec/features/issues/user_uses_slash_commands_spec.rb
index c4c06ed514b..e711a191db2 100644
--- a/spec/features/issues/user_uses_slash_commands_spec.rb
+++ b/spec/features/issues/user_uses_slash_commands_spec.rb
@@ -12,7 +12,7 @@ feature 'Issues > User uses quick actions', :js do
let(:project) { create(:project, :public) }
before do
- project.team << [user, :master]
+ project.add_master(user)
sign_in(user)
visit project_issue_path(project, issue)
end
@@ -50,7 +50,7 @@ feature 'Issues > User uses quick actions', :js do
context 'when the current user cannot update the due date' do
let(:guest) { create(:user) }
before do
- project.team << [guest, :guest]
+ project.add_guest(guest)
gitlab_sign_out
sign_in(guest)
visit project_issue_path(project, issue)
@@ -90,7 +90,7 @@ feature 'Issues > User uses quick actions', :js do
context 'when the current user cannot update the due date' do
let(:guest) { create(:user) }
before do
- project.team << [guest, :guest]
+ project.add_guest(guest)
gitlab_sign_out
sign_in(guest)
visit project_issue_path(project, issue)
@@ -138,7 +138,7 @@ feature 'Issues > User uses quick actions', :js do
context 'when the current user cannot update the issue' do
let(:guest) { create(:user) }
before do
- project.team << [guest, :guest]
+ project.add_guest(guest)
gitlab_sign_out
sign_in(guest)
visit project_issue_path(project, issue)
@@ -163,7 +163,7 @@ feature 'Issues > User uses quick actions', :js do
let(:target_project) { create(:project, :public) }
before do
- target_project.team << [user, :master]
+ target_project.add_master(user)
sign_in(user)
visit project_issue_path(project, issue)
end
@@ -220,7 +220,7 @@ feature 'Issues > User uses quick actions', :js do
let(:wontfix_target) { create(:label, project: target_project, title: 'wontfix') }
before do
- target_project.team << [user, :master]
+ target_project.add_master(user)
sign_in(user)
visit project_issue_path(project, issue)
end
diff --git a/spec/features/issues_spec.rb b/spec/features/issues_spec.rb
index 852d9e368aa..314bd19f586 100644
--- a/spec/features/issues_spec.rb
+++ b/spec/features/issues_spec.rb
@@ -8,729 +8,754 @@ describe 'Issues' do
let(:user) { create(:user) }
let(:project) { create(:project, :public) }
- before do
- sign_in(user)
- user2 = create(:user)
-
- project.team << [[user, user2], :developer]
- end
+ describe 'while user is signed out' do
+ describe 'empty state' do
+ it 'user sees empty state' do
+ visit project_issues_path(project)
- describe 'Edit issue' do
- let!(:issue) do
- create(:issue,
- author: user,
- assignees: [user],
- project: project)
+ expect(page).to have_content('Register / Sign In')
+ expect(page).to have_content('The Issue Tracker is the place to add things that need to be improved or solved in a project.')
+ expect(page).to have_content('You can register or sign in to create issues for this project.')
+ end
end
+ end
+ describe 'while user is signed in' do
before do
- visit edit_project_issue_path(project, issue)
- find('.js-zen-enter').click
- end
-
- it 'opens new issue popup' do
- expect(page).to have_content("Issue ##{issue.iid}")
- end
- end
+ sign_in(user)
+ user2 = create(:user)
- describe 'Editing issue assignee' do
- let!(:issue) do
- create(:issue,
- author: user,
- assignees: [user],
- project: project)
+ project.add_developer(user)
+ project.add_developer(user2)
end
- it 'allows user to select unassigned', :js do
- visit edit_project_issue_path(project, issue)
-
- expect(page).to have_content "Assignee #{user.name}"
+ describe 'empty state' do
+ it 'user sees empty state' do
+ visit project_issues_path(project)
- first('.js-user-search').click
- click_link 'Unassigned'
+ expect(page).to have_content('The Issue Tracker is the place to add things that need to be improved or solved in a project')
+ expect(page).to have_content('Issues can be bugs, tasks or ideas to be discussed. Also, issues are searchable and filterable.')
+ expect(page).to have_content('New issue')
+ end
+ end
- click_button 'Save changes'
+ describe 'Edit issue' do
+ let!(:issue) do
+ create(:issue,
+ author: user,
+ assignees: [user],
+ project: project)
+ end
- page.within('.assignee') do
- expect(page).to have_content 'No assignee - assign yourself'
+ before do
+ visit edit_project_issue_path(project, issue)
+ find('.js-zen-enter').click
end
- expect(issue.reload.assignees).to be_empty
+ it 'opens new issue popup' do
+ expect(page).to have_content("Issue ##{issue.iid}")
+ end
end
- end
- describe 'due date', :js do
- context 'on new form' do
- before do
- visit new_project_issue_path(project)
+ describe 'Editing issue assignee' do
+ let!(:issue) do
+ create(:issue,
+ author: user,
+ assignees: [user],
+ project: project)
end
- it 'saves with due date' do
- date = Date.today.at_beginning_of_month
-
- fill_in 'issue_title', with: 'bug 345'
- fill_in 'issue_description', with: 'bug description'
- find('#issuable-due-date').click
+ it 'allows user to select unassigned', :js do
+ visit edit_project_issue_path(project, issue)
- page.within '.pika-single' do
- click_button date.day
- end
+ expect(page).to have_content "Assignee #{user.name}"
- expect(find('#issuable-due-date').value).to eq date.to_s
+ first('.js-user-search').click
+ click_link 'Unassigned'
- click_button 'Submit issue'
+ click_button 'Save changes'
- page.within '.issuable-sidebar' do
- expect(page).to have_content date.to_s(:medium)
+ page.within('.assignee') do
+ expect(page).to have_content 'No assignee - assign yourself'
end
+
+ expect(issue.reload.assignees).to be_empty
end
end
- context 'on edit form' do
- let(:issue) { create(:issue, author: user, project: project, due_date: Date.today.at_beginning_of_month.to_s) }
+ describe 'due date', :js do
+ context 'on new form' do
+ before do
+ visit new_project_issue_path(project)
+ end
- before do
- visit edit_project_issue_path(project, issue)
- end
+ it 'saves with due date' do
+ date = Date.today.at_beginning_of_month
- it 'saves with due date' do
- date = Date.today.at_beginning_of_month
+ fill_in 'issue_title', with: 'bug 345'
+ fill_in 'issue_description', with: 'bug description'
+ find('#issuable-due-date').click
- expect(find('#issuable-due-date').value).to eq date.to_s
+ page.within '.pika-single' do
+ click_button date.day
+ end
- date = date.tomorrow
+ expect(find('#issuable-due-date').value).to eq date.to_s
- fill_in 'issue_title', with: 'bug 345'
- fill_in 'issue_description', with: 'bug description'
- find('#issuable-due-date').click
+ click_button 'Submit issue'
- page.within '.pika-single' do
- click_button date.day
+ page.within '.issuable-sidebar' do
+ expect(page).to have_content date.to_s(:medium)
+ end
end
+ end
- expect(find('#issuable-due-date').value).to eq date.to_s
+ context 'on edit form' do
+ let(:issue) { create(:issue, author: user, project: project, due_date: Date.today.at_beginning_of_month.to_s) }
- click_button 'Save changes'
-
- page.within '.issuable-sidebar' do
- expect(page).to have_content date.to_s(:medium)
+ before do
+ visit edit_project_issue_path(project, issue)
end
- end
- it 'warns about version conflict' do
- issue.update(title: "New title")
+ it 'saves with due date' do
+ date = Date.today.at_beginning_of_month
- fill_in 'issue_title', with: 'bug 345'
- fill_in 'issue_description', with: 'bug description'
+ expect(find('#issuable-due-date').value).to eq date.to_s
- click_button 'Save changes'
+ date = date.tomorrow
- expect(page).to have_content 'Someone edited the issue the same time you did'
- end
- end
- end
+ fill_in 'issue_title', with: 'bug 345'
+ fill_in 'issue_description', with: 'bug description'
+ find('#issuable-due-date').click
+
+ page.within '.pika-single' do
+ click_button date.day
+ end
- describe 'Issue info' do
- it 'links to current issue in breadcrubs' do
- issue = create(:issue, project: project)
+ expect(find('#issuable-due-date').value).to eq date.to_s
- visit project_issue_path(project, issue)
+ click_button 'Save changes'
- expect(find('.breadcrumbs-sub-title a')[:href]).to end_with(issue_path(issue))
- end
+ page.within '.issuable-sidebar' do
+ expect(page).to have_content date.to_s(:medium)
+ end
+ end
- it 'excludes award_emoji from comment count' do
- issue = create(:issue, author: user, assignees: [user], project: project, title: 'foobar')
- create(:award_emoji, awardable: issue)
+ it 'warns about version conflict' do
+ issue.update(title: "New title")
- visit project_issues_path(project, assignee_id: user.id)
+ fill_in 'issue_title', with: 'bug 345'
+ fill_in 'issue_description', with: 'bug description'
- expect(page).to have_content 'foobar'
- expect(page.all('.no-comments').first.text).to eq "0"
- end
- end
+ click_button 'Save changes'
- describe 'Filter issue' do
- before do
- %w(foobar barbaz gitlab).each do |title|
- create(:issue,
- author: user,
- assignees: [user],
- project: project,
- title: title)
+ expect(page).to have_content 'Someone edited the issue the same time you did'
+ end
end
-
- @issue = Issue.find_by(title: 'foobar')
- @issue.milestone = create(:milestone, project: project)
- @issue.assignees = []
- @issue.save
end
- let(:issue) { @issue }
+ describe 'Issue info' do
+ it 'links to current issue in breadcrubs' do
+ issue = create(:issue, project: project)
- it 'allows filtering by issues with no specified assignee' do
- visit project_issues_path(project, assignee_id: IssuableFinder::NONE)
+ visit project_issue_path(project, issue)
- expect(page).to have_content 'foobar'
- expect(page).not_to have_content 'barbaz'
- expect(page).not_to have_content 'gitlab'
- end
+ expect(find('.breadcrumbs-sub-title a')[:href]).to end_with(issue_path(issue))
+ end
- it 'allows filtering by a specified assignee' do
- visit project_issues_path(project, assignee_id: user.id)
+ it 'excludes award_emoji from comment count' do
+ issue = create(:issue, author: user, assignees: [user], project: project, title: 'foobar')
+ create(:award_emoji, awardable: issue)
- expect(page).not_to have_content 'foobar'
- expect(page).to have_content 'barbaz'
- expect(page).to have_content 'gitlab'
- end
- end
+ visit project_issues_path(project, assignee_id: user.id)
- describe 'filter issue' do
- titles = %w[foo bar baz]
- titles.each_with_index do |title, index|
- let!(title.to_sym) do
- create(:issue, title: title,
- project: project,
- created_at: Time.now - (index * 60))
+ expect(page).to have_content 'foobar'
+ expect(page.all('.no-comments').first.text).to eq "0"
end
end
- let(:newer_due_milestone) { create(:milestone, due_date: '2013-12-11') }
- let(:later_due_milestone) { create(:milestone, due_date: '2013-12-12') }
- it 'sorts by newest' do
- visit project_issues_path(project, sort: sort_value_created_date)
+ describe 'Filter issue' do
+ before do
+ %w(foobar barbaz gitlab).each do |title|
+ create(:issue,
+ author: user,
+ assignees: [user],
+ project: project,
+ title: title)
+ end
- expect(first_issue).to include('foo')
- expect(last_issue).to include('baz')
- end
+ @issue = Issue.find_by(title: 'foobar')
+ @issue.milestone = create(:milestone, project: project)
+ @issue.assignees = []
+ @issue.save
+ end
- it 'sorts by most recently updated' do
- baz.updated_at = Time.now + 100
- baz.save
- visit project_issues_path(project, sort: sort_value_recently_updated)
+ let(:issue) { @issue }
- expect(first_issue).to include('baz')
- end
+ it 'allows filtering by issues with no specified assignee' do
+ visit project_issues_path(project, assignee_id: IssuableFinder::NONE)
- describe 'sorting by due date' do
- before do
- foo.update(due_date: 1.day.from_now)
- bar.update(due_date: 6.days.from_now)
+ expect(page).to have_content 'foobar'
+ expect(page).not_to have_content 'barbaz'
+ expect(page).not_to have_content 'gitlab'
end
- it 'sorts by due date' do
- visit project_issues_path(project, sort: sort_value_due_date)
+ it 'allows filtering by a specified assignee' do
+ visit project_issues_path(project, assignee_id: user.id)
- expect(first_issue).to include('foo')
+ expect(page).not_to have_content 'foobar'
+ expect(page).to have_content 'barbaz'
+ expect(page).to have_content 'gitlab'
end
+ end
- it 'sorts by due date by excluding nil due dates' do
- bar.update(due_date: nil)
+ describe 'filter issue' do
+ titles = %w[foo bar baz]
+ titles.each_with_index do |title, index|
+ let!(title.to_sym) do
+ create(:issue, title: title,
+ project: project,
+ created_at: Time.now - (index * 60))
+ end
+ end
+ let(:newer_due_milestone) { create(:milestone, due_date: '2013-12-11') }
+ let(:later_due_milestone) { create(:milestone, due_date: '2013-12-12') }
- visit project_issues_path(project, sort: sort_value_due_date)
+ it 'sorts by newest' do
+ visit project_issues_path(project, sort: sort_value_created_date)
expect(first_issue).to include('foo')
+ expect(last_issue).to include('baz')
end
- context 'with a filter on labels' do
- let(:label) { create(:label, project: project) }
+ it 'sorts by most recently updated' do
+ baz.updated_at = Time.now + 100
+ baz.save
+ visit project_issues_path(project, sort: sort_value_recently_updated)
+ expect(first_issue).to include('baz')
+ end
+
+ describe 'sorting by due date' do
before do
- create(:label_link, label: label, target: foo)
+ foo.update(due_date: 1.day.from_now)
+ bar.update(due_date: 6.days.from_now)
+ end
+
+ it 'sorts by due date' do
+ visit project_issues_path(project, sort: sort_value_due_date)
+
+ expect(first_issue).to include('foo')
end
- it 'sorts by least recently due date by excluding nil due dates' do
+ it 'sorts by due date by excluding nil due dates' do
bar.update(due_date: nil)
- visit project_issues_path(project, label_names: [label.name], sort: sort_value_due_date_later)
+ visit project_issues_path(project, sort: sort_value_due_date)
expect(first_issue).to include('foo')
end
- end
- end
- describe 'filtering by due date' do
- before do
- foo.update(due_date: 1.day.from_now)
- bar.update(due_date: 6.days.from_now)
- end
+ context 'with a filter on labels' do
+ let(:label) { create(:label, project: project) }
+
+ before do
+ create(:label_link, label: label, target: foo)
+ end
+
+ it 'sorts by least recently due date by excluding nil due dates' do
+ bar.update(due_date: nil)
- it 'filters by none' do
- visit project_issues_path(project, due_date: Issue::NoDueDate.name)
+ visit project_issues_path(project, label_names: [label.name], sort: sort_value_due_date_later)
- page.within '.issues-holder' do
- expect(page).not_to have_content('foo')
- expect(page).not_to have_content('bar')
- expect(page).to have_content('baz')
+ expect(first_issue).to include('foo')
+ end
end
end
- it 'filters by any' do
- visit project_issues_path(project, due_date: Issue::AnyDueDate.name)
+ describe 'filtering by due date' do
+ before do
+ foo.update(due_date: 1.day.from_now)
+ bar.update(due_date: 6.days.from_now)
+ end
+
+ it 'filters by none' do
+ visit project_issues_path(project, due_date: Issue::NoDueDate.name)
- page.within '.issues-holder' do
- expect(page).to have_content('foo')
- expect(page).to have_content('bar')
- expect(page).to have_content('baz')
+ page.within '.issues-holder' do
+ expect(page).not_to have_content('foo')
+ expect(page).not_to have_content('bar')
+ expect(page).to have_content('baz')
+ end
+ end
+
+ it 'filters by any' do
+ visit project_issues_path(project, due_date: Issue::AnyDueDate.name)
+
+ page.within '.issues-holder' do
+ expect(page).to have_content('foo')
+ expect(page).to have_content('bar')
+ expect(page).to have_content('baz')
+ end
end
- end
- it 'filters by due this week' do
- foo.update(due_date: Date.today.beginning_of_week + 2.days)
- bar.update(due_date: Date.today.end_of_week)
- baz.update(due_date: Date.today - 8.days)
+ it 'filters by due this week' do
+ foo.update(due_date: Date.today.beginning_of_week + 2.days)
+ bar.update(due_date: Date.today.end_of_week)
+ baz.update(due_date: Date.today - 8.days)
- visit project_issues_path(project, due_date: Issue::DueThisWeek.name)
+ visit project_issues_path(project, due_date: Issue::DueThisWeek.name)
- page.within '.issues-holder' do
- expect(page).to have_content('foo')
- expect(page).to have_content('bar')
- expect(page).not_to have_content('baz')
+ page.within '.issues-holder' do
+ expect(page).to have_content('foo')
+ expect(page).to have_content('bar')
+ expect(page).not_to have_content('baz')
+ end
end
- end
- it 'filters by due this month' do
- foo.update(due_date: Date.today.beginning_of_month + 2.days)
- bar.update(due_date: Date.today.end_of_month)
- baz.update(due_date: Date.today - 50.days)
+ it 'filters by due this month' do
+ foo.update(due_date: Date.today.beginning_of_month + 2.days)
+ bar.update(due_date: Date.today.end_of_month)
+ baz.update(due_date: Date.today - 50.days)
- visit project_issues_path(project, due_date: Issue::DueThisMonth.name)
+ visit project_issues_path(project, due_date: Issue::DueThisMonth.name)
- page.within '.issues-holder' do
- expect(page).to have_content('foo')
- expect(page).to have_content('bar')
- expect(page).not_to have_content('baz')
+ page.within '.issues-holder' do
+ expect(page).to have_content('foo')
+ expect(page).to have_content('bar')
+ expect(page).not_to have_content('baz')
+ end
end
- end
- it 'filters by overdue' do
- foo.update(due_date: Date.today + 2.days)
- bar.update(due_date: Date.today + 20.days)
- baz.update(due_date: Date.yesterday)
+ it 'filters by overdue' do
+ foo.update(due_date: Date.today + 2.days)
+ bar.update(due_date: Date.today + 20.days)
+ baz.update(due_date: Date.yesterday)
- visit project_issues_path(project, due_date: Issue::Overdue.name)
+ visit project_issues_path(project, due_date: Issue::Overdue.name)
- page.within '.issues-holder' do
- expect(page).not_to have_content('foo')
- expect(page).not_to have_content('bar')
- expect(page).to have_content('baz')
+ page.within '.issues-holder' do
+ expect(page).not_to have_content('foo')
+ expect(page).not_to have_content('bar')
+ expect(page).to have_content('baz')
+ end
end
end
- end
- describe 'sorting by milestone' do
- before do
- foo.milestone = newer_due_milestone
- foo.save
- bar.milestone = later_due_milestone
- bar.save
- end
+ describe 'sorting by milestone' do
+ before do
+ foo.milestone = newer_due_milestone
+ foo.save
+ bar.milestone = later_due_milestone
+ bar.save
+ end
- it 'sorts by milestone' do
- visit project_issues_path(project, sort: sort_value_milestone)
+ it 'sorts by milestone' do
+ visit project_issues_path(project, sort: sort_value_milestone)
- expect(first_issue).to include('foo')
- expect(last_issue).to include('baz')
+ expect(first_issue).to include('foo')
+ expect(last_issue).to include('baz')
+ end
end
- end
- describe 'combine filter and sort' do
- let(:user2) { create(:user) }
+ describe 'combine filter and sort' do
+ let(:user2) { create(:user) }
- before do
- foo.assignees << user2
- foo.save
- bar.assignees << user2
- bar.save
- end
+ before do
+ foo.assignees << user2
+ foo.save
+ bar.assignees << user2
+ bar.save
+ end
- it 'sorts with a filter applied' do
- visit project_issues_path(project, sort: sort_value_created_date, assignee_id: user2.id)
+ it 'sorts with a filter applied' do
+ visit project_issues_path(project, sort: sort_value_created_date, assignee_id: user2.id)
- expect(first_issue).to include('foo')
- expect(last_issue).to include('bar')
- expect(page).not_to have_content('baz')
+ expect(first_issue).to include('foo')
+ expect(last_issue).to include('bar')
+ expect(page).not_to have_content('baz')
+ end
end
end
- end
- describe 'when I want to reset my incoming email token' do
- let(:project1) { create(:project, namespace: user.namespace) }
- let!(:issue) { create(:issue, project: project1) }
+ describe 'when I want to reset my incoming email token' do
+ let(:project1) { create(:project, namespace: user.namespace) }
+ let!(:issue) { create(:issue, project: project1) }
- before do
- stub_incoming_email_setting(enabled: true, address: "p+%{key}@gl.ab")
- project1.team << [user, :master]
- visit namespace_project_issues_path(user.namespace, project1)
- end
+ before do
+ stub_incoming_email_setting(enabled: true, address: "p+%{key}@gl.ab")
+ project1.add_master(user)
+ visit namespace_project_issues_path(user.namespace, project1)
+ end
- it 'changes incoming email address token', :js do
- find('.issuable-email-modal-btn').click
- previous_token = find('input#issuable_email').value
- find('.incoming-email-token-reset').click
+ it 'changes incoming email address token', :js do
+ find('.issuable-email-modal-btn').click
+ previous_token = find('input#issuable_email').value
+ find('.incoming-email-token-reset').click
- wait_for_requests
+ wait_for_requests
- expect(page).to have_no_field('issuable_email', with: previous_token)
- new_token = project1.new_issuable_address(user.reload, 'issue')
- expect(page).to have_field(
- 'issuable_email',
- with: new_token
- )
+ expect(page).to have_no_field('issuable_email', with: previous_token)
+ new_token = project1.new_issuable_address(user.reload, 'issue')
+ expect(page).to have_field(
+ 'issuable_email',
+ with: new_token
+ )
+ end
end
- end
- describe 'update labels from issue#show', :js do
- let(:issue) { create(:issue, project: project, author: user, assignees: [user]) }
- let!(:label) { create(:label, project: project) }
+ describe 'update labels from issue#show', :js do
+ let(:issue) { create(:issue, project: project, author: user, assignees: [user]) }
+ let!(:label) { create(:label, project: project) }
- before do
- visit project_issue_path(project, issue)
- end
+ before do
+ visit project_issue_path(project, issue)
+ end
- it 'will not send ajax request when no data is changed' do
- page.within '.labels' do
- click_link 'Edit'
+ it 'will not send ajax request when no data is changed' do
+ page.within '.labels' do
+ click_link 'Edit'
- find('.dropdown-menu-close', match: :first).click
+ find('.dropdown-menu-close', match: :first).click
- expect(page).not_to have_selector('.block-loading')
+ expect(page).not_to have_selector('.block-loading')
+ end
end
end
- end
- describe 'update assignee from issue#show' do
- let(:issue) { create(:issue, project: project, author: user, assignees: [user]) }
+ describe 'update assignee from issue#show' do
+ let(:issue) { create(:issue, project: project, author: user, assignees: [user]) }
- context 'by authorized user' do
- it 'allows user to select unassigned', :js do
- visit project_issue_path(project, issue)
+ context 'by authorized user' do
+ it 'allows user to select unassigned', :js do
+ visit project_issue_path(project, issue)
- page.within('.assignee') do
- expect(page).to have_content "#{user.name}"
+ page.within('.assignee') do
+ expect(page).to have_content "#{user.name}"
- click_link 'Edit'
- click_link 'Unassigned'
- first('.title').click
- expect(page).to have_content 'No assignee'
- end
+ click_link 'Edit'
+ click_link 'Unassigned'
+ first('.title').click
+ expect(page).to have_content 'No assignee'
+ end
- # wait_for_requests does not work with vue-resource at the moment
- sleep 1
+ # wait_for_requests does not work with vue-resource at the moment
+ sleep 1
- expect(issue.reload.assignees).to be_empty
- end
+ expect(issue.reload.assignees).to be_empty
+ end
- it 'allows user to select an assignee', :js do
- issue2 = create(:issue, project: project, author: user)
- visit project_issue_path(project, issue2)
+ it 'allows user to select an assignee', :js do
+ issue2 = create(:issue, project: project, author: user)
+ visit project_issue_path(project, issue2)
- page.within('.assignee') do
- expect(page).to have_content "No assignee"
- end
+ page.within('.assignee') do
+ expect(page).to have_content "No assignee"
+ end
- page.within '.assignee' do
- click_link 'Edit'
- end
+ page.within '.assignee' do
+ click_link 'Edit'
+ end
- page.within '.dropdown-menu-user' do
- click_link user.name
- end
+ page.within '.dropdown-menu-user' do
+ click_link user.name
+ end
- page.within('.assignee') do
- expect(page).to have_content user.name
+ page.within('.assignee') do
+ expect(page).to have_content user.name
+ end
end
- end
- it 'allows user to unselect themselves', :js do
- issue2 = create(:issue, project: project, author: user)
- visit project_issue_path(project, issue2)
+ it 'allows user to unselect themselves', :js do
+ issue2 = create(:issue, project: project, author: user)
+ visit project_issue_path(project, issue2)
- page.within '.assignee' do
- click_link 'Edit'
- click_link user.name
+ page.within '.assignee' do
+ click_link 'Edit'
+ click_link user.name
- page.within '.value .author' do
- expect(page).to have_content user.name
- end
+ page.within '.value .author' do
+ expect(page).to have_content user.name
+ end
- click_link 'Edit'
- click_link user.name
+ click_link 'Edit'
+ click_link user.name
- page.within '.value .assign-yourself' do
- expect(page).to have_content "No assignee"
+ page.within '.value .assign-yourself' do
+ expect(page).to have_content "No assignee"
+ end
end
end
end
- end
- context 'by unauthorized user' do
- let(:guest) { create(:user) }
+ context 'by unauthorized user' do
+ let(:guest) { create(:user) }
- before do
- project.team << [[guest], :guest]
- end
+ before do
+ project.add_guest(guest)
+ end
- it 'shows assignee text', :js do
- sign_out(:user)
- sign_in(guest)
+ it 'shows assignee text', :js do
+ sign_out(:user)
+ sign_in(guest)
- visit project_issue_path(project, issue)
- expect(page).to have_content issue.assignees.first.name
+ visit project_issue_path(project, issue)
+ expect(page).to have_content issue.assignees.first.name
+ end
end
end
- end
- describe 'update milestone from issue#show' do
- let!(:issue) { create(:issue, project: project, author: user) }
- let!(:milestone) { create(:milestone, project: project) }
+ describe 'update milestone from issue#show' do
+ let!(:issue) { create(:issue, project: project, author: user) }
+ let!(:milestone) { create(:milestone, project: project) }
- context 'by authorized user' do
- it 'allows user to select unassigned', :js do
- visit project_issue_path(project, issue)
+ context 'by authorized user' do
+ it 'allows user to select unassigned', :js do
+ visit project_issue_path(project, issue)
- page.within('.milestone') do
- expect(page).to have_content "None"
- end
+ page.within('.milestone') do
+ expect(page).to have_content "None"
+ end
- find('.block.milestone .edit-link').click
- sleep 2 # wait for ajax stuff to complete
- first('.dropdown-content li').click
- sleep 2
- page.within('.milestone') do
- expect(page).to have_content 'None'
+ find('.block.milestone .edit-link').click
+ sleep 2 # wait for ajax stuff to complete
+ first('.dropdown-content li').click
+ sleep 2
+ page.within('.milestone') do
+ expect(page).to have_content 'None'
+ end
+
+ expect(issue.reload.milestone).to be_nil
end
- expect(issue.reload.milestone).to be_nil
- end
+ it 'allows user to de-select milestone', :js do
+ visit project_issue_path(project, issue)
- it 'allows user to de-select milestone', :js do
- visit project_issue_path(project, issue)
+ page.within('.milestone') do
+ click_link 'Edit'
+ click_link milestone.title
- page.within('.milestone') do
- click_link 'Edit'
- click_link milestone.title
-
- page.within '.value' do
- expect(page).to have_content milestone.title
- end
+ page.within '.value' do
+ expect(page).to have_content milestone.title
+ end
- click_link 'Edit'
- click_link milestone.title
+ click_link 'Edit'
+ click_link milestone.title
- page.within '.value' do
- expect(page).to have_content 'None'
+ page.within '.value' do
+ expect(page).to have_content 'None'
+ end
end
end
end
- end
- context 'by unauthorized user' do
- let(:guest) { create(:user) }
+ context 'by unauthorized user' do
+ let(:guest) { create(:user) }
- before do
- project.team << [guest, :guest]
- issue.milestone = milestone
- issue.save
- end
+ before do
+ project.add_guest(guest)
+ issue.milestone = milestone
+ issue.save
+ end
- it 'shows milestone text', :js do
- sign_out(:user)
- sign_in(guest)
+ it 'shows milestone text', :js do
+ sign_out(:user)
+ sign_in(guest)
- visit project_issue_path(project, issue)
- expect(page).to have_content milestone.title
+ visit project_issue_path(project, issue)
+ expect(page).to have_content milestone.title
+ end
end
end
- end
- describe 'new issue' do
- let!(:issue) { create(:issue, project: project) }
+ describe 'new issue' do
+ let!(:issue) { create(:issue, project: project) }
- context 'by unauthenticated user' do
- before do
- sign_out(:user)
- end
+ context 'by unauthenticated user' do
+ before do
+ sign_out(:user)
+ end
- it 'redirects to signin then back to new issue after signin' do
- visit project_issues_path(project)
+ it 'redirects to signin then back to new issue after signin' do
+ visit project_issues_path(project)
- page.within '.nav-controls' do
- click_link 'New issue'
- end
+ page.within '.nav-controls' do
+ click_link 'New issue'
+ end
- expect(current_path).to eq new_user_session_path
+ expect(current_path).to eq new_user_session_path
- gitlab_sign_in(create(:user))
+ gitlab_sign_in(create(:user))
- expect(current_path).to eq new_project_issue_path(project)
+ expect(current_path).to eq new_project_issue_path(project)
+ end
end
- end
- context 'dropzone upload file', :js do
- before do
- visit new_project_issue_path(project)
- end
+ context 'dropzone upload file', :js do
+ before do
+ visit new_project_issue_path(project)
+ end
- it 'uploads file when dragging into textarea' do
- dropzone_file Rails.root.join('spec', 'fixtures', 'banana_sample.gif')
+ it 'uploads file when dragging into textarea' do
+ dropzone_file Rails.root.join('spec', 'fixtures', 'banana_sample.gif')
- expect(page.find_field("issue_description").value).to have_content 'banana_sample'
- end
+ expect(page.find_field("issue_description").value).to have_content 'banana_sample'
+ end
- it "doesn't add double newline to end of a single attachment markdown" do
- dropzone_file Rails.root.join('spec', 'fixtures', 'banana_sample.gif')
+ it "doesn't add double newline to end of a single attachment markdown" do
+ dropzone_file Rails.root.join('spec', 'fixtures', 'banana_sample.gif')
- expect(page.find_field("issue_description").value).not_to match /\n\n$/
- end
+ expect(page.find_field("issue_description").value).not_to match /\n\n$/
+ end
- it "cancels a file upload correctly" do
- slow_requests do
- dropzone_file([Rails.root.join('spec', 'fixtures', 'dk.png')], 0, false)
+ it "cancels a file upload correctly" do
+ slow_requests do
+ dropzone_file([Rails.root.join('spec', 'fixtures', 'dk.png')], 0, false)
- click_button 'Cancel'
- end
+ click_button 'Cancel'
+ end
- expect(page).to have_button('Attach a file')
- expect(page).not_to have_button('Cancel')
- expect(page).not_to have_selector('.uploading-progress-container', visible: true)
+ expect(page).to have_button('Attach a file')
+ expect(page).not_to have_button('Cancel')
+ expect(page).not_to have_selector('.uploading-progress-container', visible: true)
+ end
end
- end
- context 'form filled by URL parameters' do
- let(:project) { create(:project, :public, :repository) }
+ context 'form filled by URL parameters' do
+ let(:project) { create(:project, :public, :repository) }
- before do
- project.repository.create_file(
- user,
- '.gitlab/issue_templates/bug.md',
- 'this is a test "bug" template',
- message: 'added issue template',
- branch_name: 'master')
-
- visit new_project_issue_path(project, issuable_template: 'bug')
- end
+ before do
+ project.repository.create_file(
+ user,
+ '.gitlab/issue_templates/bug.md',
+ 'this is a test "bug" template',
+ message: 'added issue template',
+ branch_name: 'master')
+
+ visit new_project_issue_path(project, issuable_template: 'bug')
+ end
- it 'fills in template' do
- expect(find('.js-issuable-selector .dropdown-toggle-text')).to have_content('bug')
+ it 'fills in template' do
+ expect(find('.js-issuable-selector .dropdown-toggle-text')).to have_content('bug')
+ end
end
end
- end
- describe 'new issue by email' do
- shared_examples 'show the email in the modal' do
- let(:issue) { create(:issue, project: project) }
+ describe 'new issue by email' do
+ shared_examples 'show the email in the modal' do
+ let(:issue) { create(:issue, project: project) }
- before do
- project.issues << issue
- stub_incoming_email_setting(enabled: true, address: "p+%{key}@gl.ab")
+ before do
+ project.issues << issue
+ stub_incoming_email_setting(enabled: true, address: "p+%{key}@gl.ab")
- visit project_issues_path(project)
- click_button('Email a new issue')
- end
+ visit project_issues_path(project)
+ click_button('Email a new issue')
+ end
- it 'click the button to show modal for the new email' do
- page.within '#issuable-email-modal' do
- email = project.new_issuable_address(user, 'issue')
+ it 'click the button to show modal for the new email' do
+ page.within '#issuable-email-modal' do
+ email = project.new_issuable_address(user, 'issue')
- expect(page).to have_selector("input[value='#{email}']")
+ expect(page).to have_selector("input[value='#{email}']")
+ end
end
end
- end
- context 'with existing issues' do
- let!(:issue) { create(:issue, project: project, author: user) }
+ context 'with existing issues' do
+ let!(:issue) { create(:issue, project: project, author: user) }
- it_behaves_like 'show the email in the modal'
- end
+ it_behaves_like 'show the email in the modal'
+ end
- context 'without existing issues' do
- it_behaves_like 'show the email in the modal'
+ context 'without existing issues' do
+ it_behaves_like 'show the email in the modal'
+ end
end
- end
- describe 'due date' do
- context 'update due on issue#show', :js do
- let(:issue) { create(:issue, project: project, author: user, assignees: [user]) }
+ describe 'due date' do
+ context 'update due on issue#show', :js do
+ let(:issue) { create(:issue, project: project, author: user, assignees: [user]) }
- before do
- visit project_issue_path(project, issue)
- end
+ before do
+ visit project_issue_path(project, issue)
+ end
- it 'adds due date to issue' do
- date = Date.today.at_beginning_of_month + 2.days
+ it 'adds due date to issue' do
+ date = Date.today.at_beginning_of_month + 2.days
- page.within '.due_date' do
- click_link 'Edit'
+ page.within '.due_date' do
+ click_link 'Edit'
- page.within '.pika-single' do
- click_button date.day
- end
+ page.within '.pika-single' do
+ click_button date.day
+ end
- wait_for_requests
+ wait_for_requests
- expect(find('.value').text).to have_content date.strftime('%b %-d, %Y')
+ expect(find('.value').text).to have_content date.strftime('%b %-d, %Y')
+ end
end
- end
- it 'removes due date from issue' do
- date = Date.today.at_beginning_of_month + 2.days
+ it 'removes due date from issue' do
+ date = Date.today.at_beginning_of_month + 2.days
- page.within '.due_date' do
- click_link 'Edit'
+ page.within '.due_date' do
+ click_link 'Edit'
- page.within '.pika-single' do
- click_button date.day
- end
+ page.within '.pika-single' do
+ click_button date.day
+ end
- wait_for_requests
+ wait_for_requests
- expect(page).to have_no_content 'No due date'
+ expect(page).to have_no_content 'No due date'
- click_link 'remove due date'
- expect(page).to have_content 'No due date'
+ click_link 'remove due date'
+ expect(page).to have_content 'No due date'
+ end
end
end
end
- end
- describe 'title issue#show', :js do
- it 'updates the title', :js do
- issue = create(:issue, author: user, assignees: [user], project: project, title: 'new title')
+ describe 'title issue#show', :js do
+ it 'updates the title', :js do
+ issue = create(:issue, author: user, assignees: [user], project: project, title: 'new title')
- visit project_issue_path(project, issue)
+ visit project_issue_path(project, issue)
- expect(page).to have_text("new title")
+ expect(page).to have_text("new title")
- issue.update(title: "updated title")
+ issue.update(title: "updated title")
- wait_for_requests
- expect(page).to have_text("updated title")
+ wait_for_requests
+ expect(page).to have_text("updated title")
+ end
end
- end
- describe 'confidential issue#show', :js do
- it 'shows confidential sibebar information as confidential and can be turned off' do
- issue = create(:issue, :confidential, project: project)
+ describe 'confidential issue#show', :js do
+ it 'shows confidential sibebar information as confidential and can be turned off' do
+ issue = create(:issue, :confidential, project: project)
- visit project_issue_path(project, issue)
+ visit project_issue_path(project, issue)
- expect(page).to have_css('.issuable-note-warning')
- expect(find('.issuable-sidebar-item.confidentiality')).to have_css('.is-active')
- expect(find('.issuable-sidebar-item.confidentiality')).not_to have_css('.not-active')
+ expect(page).to have_css('.issuable-note-warning')
+ expect(find('.issuable-sidebar-item.confidentiality')).to have_css('.is-active')
+ expect(find('.issuable-sidebar-item.confidentiality')).not_to have_css('.not-active')
- find('.confidential-edit').click
- expect(page).to have_css('.sidebar-item-warning-message')
+ find('.confidential-edit').click
+ expect(page).to have_css('.sidebar-item-warning-message')
- within('.sidebar-item-warning-message') do
- find('.btn-close').click
- end
+ within('.sidebar-item-warning-message') do
+ find('.btn-close').click
+ end
- wait_for_requests
+ wait_for_requests
- visit project_issue_path(project, issue)
+ visit project_issue_path(project, issue)
- expect(page).not_to have_css('.is-active')
+ expect(page).not_to have_css('.is-active')
+ end
end
end
end
diff --git a/spec/features/markdown_spec.rb b/spec/features/markdown_spec.rb
index e285befc66f..a2b78a5e021 100644
--- a/spec/features/markdown_spec.rb
+++ b/spec/features/markdown_spec.rb
@@ -71,7 +71,7 @@ describe 'GitLab Markdown' do
it 'parses mermaid code block' do
aggregate_failures do
- expect(doc).to have_selector('pre.code.js-render-mermaid')
+ expect(doc).to have_selector('pre[lang=mermaid] > code.js-render-mermaid')
end
end
diff --git a/spec/features/merge_requests/assign_issues_spec.rb b/spec/features/merge_requests/assign_issues_spec.rb
index d49d145f254..b2d64a62b4f 100644
--- a/spec/features/merge_requests/assign_issues_spec.rb
+++ b/spec/features/merge_requests/assign_issues_spec.rb
@@ -9,7 +9,7 @@ feature 'Merge request issue assignment', :js do
let(:service) { MergeRequests::AssignIssuesService.new(merge_request, user, user, project) }
before do
- project.team << [user, :developer]
+ project.add_developer(user)
end
def visit_merge_request(current_user = nil)
diff --git a/spec/features/merge_requests/check_if_mergeable_with_unresolved_discussions_spec.rb b/spec/features/merge_requests/check_if_mergeable_with_unresolved_discussions_spec.rb
index fbbfe7942be..892c32c8806 100644
--- a/spec/features/merge_requests/check_if_mergeable_with_unresolved_discussions_spec.rb
+++ b/spec/features/merge_requests/check_if_mergeable_with_unresolved_discussions_spec.rb
@@ -7,7 +7,7 @@ feature 'Check if mergeable with unresolved discussions', :js do
before do
sign_in user
- project.team << [user, :master]
+ project.add_master(user)
end
context 'when project.only_allow_merge_if_all_discussions_are_resolved == true' do
diff --git a/spec/features/merge_requests/cherry_pick_spec.rb b/spec/features/merge_requests/cherry_pick_spec.rb
index 48f370c3ad4..205e38337d1 100644
--- a/spec/features/merge_requests/cherry_pick_spec.rb
+++ b/spec/features/merge_requests/cherry_pick_spec.rb
@@ -8,7 +8,7 @@ describe 'Cherry-pick Merge Requests', :js do
before do
sign_in user
- project.team << [user, :master]
+ project.add_master(user)
end
context "Viewing a merged merge request" do
diff --git a/spec/features/merge_requests/closes_issues_spec.rb b/spec/features/merge_requests/closes_issues_spec.rb
index 4dd4e40f52c..55de9a01ed5 100644
--- a/spec/features/merge_requests/closes_issues_spec.rb
+++ b/spec/features/merge_requests/closes_issues_spec.rb
@@ -18,7 +18,7 @@ feature 'Merge Request closing issues message', :js do
let(:merge_request_title) { 'Merge Request Title' }
before do
- project.team << [user, :master]
+ project.add_master(user)
sign_in user
diff --git a/spec/features/merge_requests/conflicts_spec.rb b/spec/features/merge_requests/conflicts_spec.rb
index 4e2963c116d..05d99a2dff2 100644
--- a/spec/features/merge_requests/conflicts_spec.rb
+++ b/spec/features/merge_requests/conflicts_spec.rb
@@ -88,7 +88,7 @@ feature 'Merge request conflict resolution', :js do
context 'can be resolved in the UI' do
before do
- project.team << [user, :developer]
+ project.add_developer(user)
sign_in(user)
end
@@ -175,7 +175,7 @@ feature 'Merge request conflict resolution', :js do
let(:merge_request) { create_merge_request(source_branch) }
before do
- project.team << [user, :developer]
+ project.add_developer(user)
sign_in(user)
visit project_merge_request_path(project, merge_request)
diff --git a/spec/features/merge_requests/create_new_mr_spec.rb b/spec/features/merge_requests/create_new_mr_spec.rb
index db5ce2d11a8..486555ed5cd 100644
--- a/spec/features/merge_requests/create_new_mr_spec.rb
+++ b/spec/features/merge_requests/create_new_mr_spec.rb
@@ -5,7 +5,7 @@ feature 'Create New Merge Request', :js do
let(:project) { create(:project, :public, :repository) }
before do
- project.team << [user, :master]
+ project.add_master(user)
sign_in user
end
diff --git a/spec/features/merge_requests/created_from_fork_spec.rb b/spec/features/merge_requests/created_from_fork_spec.rb
index ca2225318cd..53b62caf743 100644
--- a/spec/features/merge_requests/created_from_fork_spec.rb
+++ b/spec/features/merge_requests/created_from_fork_spec.rb
@@ -14,7 +14,7 @@ feature 'Merge request created from fork' do
end
background do
- forked_project.team << [user, :master]
+ forked_project.add_master(user)
sign_in user
end
diff --git a/spec/features/merge_requests/deleted_source_branch_spec.rb b/spec/features/merge_requests/deleted_source_branch_spec.rb
index 7f69e82af4c..56aa0b2ede2 100644
--- a/spec/features/merge_requests/deleted_source_branch_spec.rb
+++ b/spec/features/merge_requests/deleted_source_branch_spec.rb
@@ -9,7 +9,7 @@ describe 'Deleted source branch', :js do
before do
sign_in user
- merge_request.project.team << [user, :master]
+ merge_request.project.add_master(user)
merge_request.update!(source_branch: 'this-branch-does-not-exist')
visit project_merge_request_path(merge_request.project, merge_request)
end
diff --git a/spec/features/merge_requests/diff_notes_avatars_spec.rb b/spec/features/merge_requests/diff_notes_avatars_spec.rb
index 9e816cf041b..ef8f314cc03 100644
--- a/spec/features/merge_requests/diff_notes_avatars_spec.rb
+++ b/spec/features/merge_requests/diff_notes_avatars_spec.rb
@@ -19,7 +19,7 @@ feature 'Diff note avatars', :js do
let!(:note) { create(:diff_note_on_merge_request, project: project, noteable: merge_request, position: position) }
before do
- project.team << [user, :master]
+ project.add_master(user)
sign_in user
set_cookie('sidebar_collapsed', 'true')
diff --git a/spec/features/merge_requests/diff_notes_resolve_spec.rb b/spec/features/merge_requests/diff_notes_resolve_spec.rb
index 15d380b1bf4..9d4194d8ca0 100644
--- a/spec/features/merge_requests/diff_notes_resolve_spec.rb
+++ b/spec/features/merge_requests/diff_notes_resolve_spec.rb
@@ -18,7 +18,7 @@ feature 'Diff notes resolve', :js do
context 'no discussions' do
before do
- project.team << [user, :master]
+ project.add_master(user)
sign_in user
note.destroy
visit_merge_request
@@ -32,7 +32,7 @@ feature 'Diff notes resolve', :js do
context 'as authorized user' do
before do
- project.team << [user, :master]
+ project.add_master(user)
sign_in user
visit_merge_request
end
@@ -429,7 +429,7 @@ feature 'Diff notes resolve', :js do
let(:guest) { create(:user) }
before do
- project.team << [guest, :guest]
+ project.add_guest(guest)
sign_in guest
end
diff --git a/spec/features/merge_requests/edit_mr_spec.rb b/spec/features/merge_requests/edit_mr_spec.rb
index 4362f8b3fcc..79be2fbf945 100644
--- a/spec/features/merge_requests/edit_mr_spec.rb
+++ b/spec/features/merge_requests/edit_mr_spec.rb
@@ -6,7 +6,7 @@ feature 'Edit Merge Request' do
let(:merge_request) { create(:merge_request, :simple, source_project: project) }
before do
- project.team << [user, :master]
+ project.add_master(user)
sign_in user
diff --git a/spec/features/merge_requests/filter_by_milestone_spec.rb b/spec/features/merge_requests/filter_by_milestone_spec.rb
index 8b9ff9be943..8db94352f73 100644
--- a/spec/features/merge_requests/filter_by_milestone_spec.rb
+++ b/spec/features/merge_requests/filter_by_milestone_spec.rb
@@ -14,7 +14,7 @@ feature 'Merge Request filtering by Milestone' do
end
before do
- project.team << [user, :master]
+ project.add_master(user)
sign_in(user)
end
diff --git a/spec/features/merge_requests/form_spec.rb b/spec/features/merge_requests/form_spec.rb
index 1dcc1e139a0..1ebf762a006 100644
--- a/spec/features/merge_requests/form_spec.rb
+++ b/spec/features/merge_requests/form_spec.rb
@@ -12,8 +12,8 @@ describe 'New/edit merge request', :js do
let!(:label2) { create(:label, project: project) }
before do
- project.team << [user, :master]
- project.team << [user2, :master]
+ project.add_master(user)
+ project.add_master(user2)
end
context 'owned projects' do
@@ -172,7 +172,7 @@ describe 'New/edit merge request', :js do
context 'forked project' do
before do
- forked_project.team << [user, :master]
+ forked_project.add_master(user)
sign_in(user)
end
diff --git a/spec/features/merge_requests/image_diff_notes.rb b/spec/features/merge_requests/image_diff_notes_spec.rb
index 3c53b51e330..d0f8da4e6cd 100644
--- a/spec/features/merge_requests/image_diff_notes.rb
+++ b/spec/features/merge_requests/image_diff_notes_spec.rb
@@ -7,14 +7,13 @@ feature 'image diff notes', :js do
let(:project) { create(:project, :public, :repository) }
before do
- project.team << [user, :master]
+ project.add_master(user)
sign_in user
- page.driver.set_cookie('sidebar_collapsed', 'true')
-
# Stub helper to return any blob file as image from public app folder.
# This is necessary to run this specs since we don't display repo images in capybara.
- allow_any_instance_of(DiffHelper).to receive(:diff_file_blob_raw_path).and_return('/apple-touch-icon.png')
+ allow_any_instance_of(DiffHelper).to receive(:diff_file_blob_raw_url).and_return('/apple-touch-icon.png')
+ allow_any_instance_of(DiffHelper).to receive(:diff_file_old_blob_raw_url).and_return('/favicon.ico')
end
context 'create commit diff notes' do
@@ -141,13 +140,13 @@ feature 'image diff notes', :js do
end
it 'allows expanding/collapsing the discussion notes' do
- page.all('.js-diff-notes-toggle')[0].trigger('click')
- page.all('.js-diff-notes-toggle')[1].trigger('click')
+ page.all('.js-diff-notes-toggle')[0].click
+ page.all('.js-diff-notes-toggle')[1].click
expect(page).not_to have_content('image diff test comment')
- page.all('.js-diff-notes-toggle')[0].trigger('click')
- page.all('.js-diff-notes-toggle')[1].trigger('click')
+ page.all('.js-diff-notes-toggle')[0].click
+ page.all('.js-diff-notes-toggle')[1].click
expect(page).to have_content('image diff test comment')
end
@@ -185,12 +184,42 @@ feature 'image diff notes', :js do
expect(page).to have_content(diff_note.note)
end
end
-end
-def create_image_diff_note
- find('.js-add-image-diff-note-button', match: :first).click
- page.all('.js-add-image-diff-note-button')[0].trigger('click')
- find('.diff-content .note-textarea').native.send_keys('image diff test comment')
- click_button 'Comment'
- wait_for_requests
+ describe 'image view modes' do
+ before do
+ visit project_commit_path(project, '2f63565e7aac07bcdadb654e253078b727143ec4')
+ end
+
+ it 'resizes image in onion skin view mode' do
+ find('.view-modes-menu .onion-skin').click
+
+ expect(find('.onion-skin-frame')['style']).to match('width: 228px; height: 240px;')
+ end
+
+ it 'resets onion skin view mode opacity when toggling between view modes' do
+ find('.view-modes-menu .onion-skin').click
+
+ # Simulate dragging onion-skin slider
+ drag_and_drop_by(find('.dragger'), -30, 0)
+
+ expect(find('.onion-skin-frame .frame.added', visible: false)['style']).not_to match('opacity: 1;')
+
+ find('.view-modes-menu .swipe').click
+ find('.view-modes-menu .onion-skin').click
+
+ expect(find('.onion-skin-frame .frame.added', visible: false)['style']).to match('opacity: 1;')
+ end
+ end
+
+ def drag_and_drop_by(element, right_by, down_by)
+ page.driver.browser.action.drag_and_drop_by(element.native, right_by, down_by).perform
+ end
+
+ def create_image_diff_note
+ find('.js-add-image-diff-note-button', match: :first).click
+ page.all('.js-add-image-diff-note-button')[0].click
+ find('.diff-content .note-textarea').native.send_keys('image diff test comment')
+ click_button 'Comment'
+ wait_for_requests
+ end
end
diff --git a/spec/features/merge_requests/merge_commit_message_toggle_spec.rb b/spec/features/merge_requests/merge_commit_message_toggle_spec.rb
index 82b2b56ef80..ddd034e1376 100644
--- a/spec/features/merge_requests/merge_commit_message_toggle_spec.rb
+++ b/spec/features/merge_requests/merge_commit_message_toggle_spec.rb
@@ -32,7 +32,7 @@ feature 'Clicking toggle commit message link', :js do
end
before do
- project.team << [user, :master]
+ project.add_master(user)
sign_in user
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 0b5a595acce..e1317b33ad1 100644
--- a/spec/features/merge_requests/merge_immediately_with_pipeline_spec.rb
+++ b/spec/features/merge_requests/merge_immediately_with_pipeline_spec.rb
@@ -19,7 +19,7 @@ feature 'Merge immediately', :js do
end
before do
- project.team << [user, :master]
+ project.add_master(user)
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 93c5e945453..a7e7c0eeff6 100644
--- a/spec/features/merge_requests/mini_pipeline_graph_spec.rb
+++ b/spec/features/merge_requests/mini_pipeline_graph_spec.rb
@@ -15,8 +15,8 @@ feature 'Mini Pipeline Graph', :js do
visit_merge_request
end
- def visit_merge_request(format = :html)
- visit project_merge_request_path(project, merge_request, format: format)
+ def visit_merge_request(format: :html, serializer: nil)
+ visit project_merge_request_path(project, merge_request, format: format, serializer: serializer)
end
it 'should display a mini pipeline graph' do
@@ -33,12 +33,12 @@ feature 'Mini Pipeline Graph', :js do
end
it 'avoids repeated database queries' do
- before = ActiveRecord::QueryRecorder.new { visit_merge_request(:json) }
+ before = ActiveRecord::QueryRecorder.new { visit_merge_request(format: :json, serializer: 'widget') }
create(:ci_build, pipeline: pipeline, legacy_artifacts_file: artifacts_file2)
create(:ci_build, pipeline: pipeline, when: 'manual')
- after = ActiveRecord::QueryRecorder.new { visit_merge_request(:json) }
+ after = ActiveRecord::QueryRecorder.new { visit_merge_request(format: :json, serializer: 'widget') }
expect(before.count).to eq(after.count)
expect(before.cached_count).to eq(after.cached_count)
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 91f207bd339..7d9282b932b 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
@@ -7,7 +7,7 @@ feature 'Only allow merge requests to be merged if the pipeline succeeds', :js d
before do
sign_in merge_request.author
- project.team << [merge_request.author, :master]
+ project.add_master(merge_request.author)
end
context 'project does not have CI enabled', :js do
diff --git a/spec/features/merge_requests/pipelines_spec.rb b/spec/features/merge_requests/pipelines_spec.rb
index 307c860eac4..04e3f4bdcf1 100644
--- a/spec/features/merge_requests/pipelines_spec.rb
+++ b/spec/features/merge_requests/pipelines_spec.rb
@@ -7,7 +7,7 @@ feature 'Pipelines for Merge Requests', :js do
given(:project) { merge_request.target_project }
before do
- project.team << [user, :master]
+ project.add_master(user)
sign_in user
end
diff --git a/spec/features/merge_requests/reset_filters_spec.rb b/spec/features/merge_requests/reset_filters_spec.rb
index eed95816bdf..daca4422bf1 100644
--- a/spec/features/merge_requests/reset_filters_spec.rb
+++ b/spec/features/merge_requests/reset_filters_spec.rb
@@ -17,7 +17,7 @@ feature 'Merge requests filter clear button', :js do
before do
mr2.labels << bug
- project.team << [user, :developer]
+ project.add_developer(user)
end
context 'when a milestone filter has been applied' do
diff --git a/spec/features/merge_requests/target_branch_spec.rb b/spec/features/merge_requests/target_branch_spec.rb
index bce36e05e57..d9f7a056dea 100644
--- a/spec/features/merge_requests/target_branch_spec.rb
+++ b/spec/features/merge_requests/target_branch_spec.rb
@@ -11,7 +11,7 @@ describe 'Target branch', :js do
before do
sign_in user
- project.team << [user, :master]
+ project.add_master(user)
end
context 'when branch was deleted' do
diff --git a/spec/features/merge_requests/update_merge_requests_spec.rb b/spec/features/merge_requests/update_merge_requests_spec.rb
index c5498563b39..a96404b86ed 100644
--- a/spec/features/merge_requests/update_merge_requests_spec.rb
+++ b/spec/features/merge_requests/update_merge_requests_spec.rb
@@ -6,7 +6,7 @@ feature 'Multiple merge requests updating from merge_requests#index' do
let!(:merge_request) { create(:merge_request, source_project: project, target_project: project) }
before do
- project.team << [user, :master]
+ project.add_master(user)
sign_in(user)
end
diff --git a/spec/features/merge_requests/user_posts_notes_spec.rb b/spec/features/merge_requests/user_posts_notes_spec.rb
index f4c75a2f265..e17e9c2ccf5 100644
--- a/spec/features/merge_requests/user_posts_notes_spec.rb
+++ b/spec/features/merge_requests/user_posts_notes_spec.rb
@@ -66,6 +66,21 @@ describe 'Merge requests > User posts notes', :js do
end
end
+ describe 'when previewing a note' do
+ it 'shows the toolbar buttons when editing a note' do
+ page.within('.js-main-target-form') do
+ expect(page).to have_css('.md-header-toolbar.active')
+ end
+ end
+
+ it 'hides the toolbar buttons when previewing a note' do
+ find('.js-md-preview-button').click
+ page.within('.js-main-target-form') do
+ expect(page).not_to have_css('.md-header-toolbar.active')
+ end
+ end
+ end
+
describe 'when editing a note' do
it 'there should be a hidden edit form' do
is_expected.to have_css('.note-edit-form:not(.mr-note-edit-form)', visible: false, count: 1)
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 ee0766f1192..5874bf5e187 100644
--- a/spec/features/merge_requests/user_uses_slash_commands_spec.rb
+++ b/spec/features/merge_requests/user_uses_slash_commands_spec.rb
@@ -15,7 +15,7 @@ feature 'Merge Requests > User uses quick actions', :js do
let!(:milestone) { create(:milestone, project: project, title: 'ASAP') }
before do
- project.team << [user, :master]
+ project.add_master(user)
sign_in(user)
visit project_merge_request_path(project, merge_request)
end
@@ -58,7 +58,7 @@ feature 'Merge Requests > User uses quick actions', :js do
context 'when the current user cannot toggle the WIP prefix' do
let(:guest) { create(:user) }
before do
- project.team << [guest, :guest]
+ project.add_guest(guest)
sign_out(:user)
sign_in(guest)
visit project_merge_request_path(project, merge_request)
@@ -104,7 +104,7 @@ feature 'Merge Requests > User uses quick actions', :js do
context 'when the current user cannot merge the MR' do
let(:guest) { create(:user) }
before do
- project.team << [guest, :guest]
+ project.add_guest(guest)
sign_out(:user)
sign_in(guest)
visit project_merge_request_path(project, merge_request)
@@ -134,7 +134,7 @@ feature 'Merge Requests > User uses quick actions', :js do
before do
sign_out(:user)
- another_project.team << [user, :master]
+ another_project.add_master(user)
sign_in(user)
end
@@ -188,7 +188,7 @@ feature 'Merge Requests > User uses quick actions', :js do
context 'when current user can not change target branch' do
let(:guest) { create(:user) }
before do
- project.team << [guest, :guest]
+ project.add_guest(guest)
sign_out(:user)
sign_in(guest)
visit project_merge_request_path(project, merge_request)
diff --git a/spec/features/merge_requests/widget_deployments_spec.rb b/spec/features/merge_requests/widget_deployments_spec.rb
index 72a52c979b3..ec2da72ddff 100644
--- a/spec/features/merge_requests/widget_deployments_spec.rb
+++ b/spec/features/merge_requests/widget_deployments_spec.rb
@@ -13,7 +13,7 @@ feature 'Widget Deployments Header', :js do
background do
sign_in(user)
- project.team << [user, role]
+ project.add_role(user, role)
visit project_merge_request_path(project, merge_request)
end
diff --git a/spec/features/merge_requests/widget_spec.rb b/spec/features/merge_requests/widget_spec.rb
index 3ee094c216e..8970586a160 100644
--- a/spec/features/merge_requests/widget_spec.rb
+++ b/spec/features/merge_requests/widget_spec.rb
@@ -273,7 +273,7 @@ describe 'Merge request', :js do
let(:user2) { create(:user) }
before do
- project.team << [user2, :master]
+ project.add_master(user2)
sign_out(:user)
sign_in(user2)
merge_request.update(target_project: fork_project)
diff --git a/spec/features/merge_requests/wip_message_spec.rb b/spec/features/merge_requests/wip_message_spec.rb
index b422c76249d..2617e735c25 100644
--- a/spec/features/merge_requests/wip_message_spec.rb
+++ b/spec/features/merge_requests/wip_message_spec.rb
@@ -5,7 +5,7 @@ feature 'Work In Progress help message' do
let!(:user) { create(:user) }
before do
- project.team << [user, :master]
+ project.add_master(user)
sign_in(user)
end
diff --git a/spec/features/milestone_spec.rb b/spec/features/milestone_spec.rb
index 27efc32c95b..b02d2d4261c 100644
--- a/spec/features/milestone_spec.rb
+++ b/spec/features/milestone_spec.rb
@@ -7,7 +7,7 @@ feature 'Milestone' do
before do
create(:group_member, group: group, user: user)
- project.team << [user, :master]
+ project.add_master(user)
sign_in(user)
end
@@ -82,9 +82,9 @@ feature 'Milestone' do
milestone = create(:milestone, project: project, title: 8.7)
issue1 = create(:issue, project: project, milestone: milestone)
issue2 = create(:issue, project: project, milestone: milestone)
- issue1.spend_time(duration: 3600, user: user)
+ issue1.spend_time(duration: 3600, user_id: user.id)
issue1.save!
- issue2.spend_time(duration: 7200, user: user)
+ issue2.spend_time(duration: 7200, user_id: user.id)
issue2.save!
visit project_milestone_path(project, milestone)
diff --git a/spec/features/password_reset_spec.rb b/spec/features/password_reset_spec.rb
index b45972b7f6b..73a526c3d8a 100644
--- a/spec/features/password_reset_spec.rb
+++ b/spec/features/password_reset_spec.rb
@@ -33,6 +33,25 @@ feature 'Password reset' do
end
end
+ describe 'Changing password while logged in' do
+ it 'updates the password' do
+ user = create(:user)
+ token = user.send_reset_password_instructions
+
+ sign_in(user)
+
+ visit(edit_user_password_path(reset_password_token: token))
+
+ fill_in 'New password', with: 'hello1234'
+ fill_in 'Confirm new password', with: 'hello1234'
+
+ click_button 'Change your password'
+
+ expect(page).to have_content(I18n.t('devise.passwords.updated_not_active'))
+ expect(current_path).to eq new_user_session_path
+ end
+ end
+
def forgot_password(user)
visit root_path
click_on 'Forgot your password?'
diff --git a/spec/features/profile_spec.rb b/spec/features/profile_spec.rb
index c60883911f7..0848857ed1e 100644
--- a/spec/features/profile_spec.rb
+++ b/spec/features/profile_spec.rb
@@ -25,7 +25,7 @@ describe 'Profile account page', :js do
fill_in 'password', with: '12345678'
- page.within '.popup-dialog' do
+ page.within '.modal' do
click_button 'Delete account'
end
@@ -38,7 +38,7 @@ describe 'Profile account page', :js do
fill_in 'password', with: 'testing123'
- page.within '.popup-dialog' do
+ page.within '.modal' do
click_button 'Delete account'
end
diff --git a/spec/features/profiles/keys_spec.rb b/spec/features/profiles/keys_spec.rb
index 7d5ba3a7328..b04a5422fed 100644
--- a/spec/features/profiles/keys_spec.rb
+++ b/spec/features/profiles/keys_spec.rb
@@ -27,6 +27,7 @@ feature 'Profile > SSH Keys' do
expect(page).to have_content("Title: #{attrs[:title]}")
expect(page).to have_content(attrs[:key])
+ expect(find('.breadcrumbs-sub-title')).to have_link(attrs[:title])
end
context 'when only DSA and ECDSA keys are allowed' do
diff --git a/spec/features/profiles/oauth_applications_spec.rb b/spec/features/profiles/oauth_applications_spec.rb
index d1edeef8da4..7d204f89fba 100644
--- a/spec/features/profiles/oauth_applications_spec.rb
+++ b/spec/features/profiles/oauth_applications_spec.rb
@@ -2,12 +2,20 @@ require 'spec_helper'
describe 'Profile > Applications' do
let(:user) { create(:user) }
+ let(:application) { create(:oauth_application, owner: user) }
before do
sign_in(user)
end
describe 'User manages applications', :js do
+ it 'views an application' do
+ visit oauth_application_path(application)
+
+ expect(page).to have_content("Application: #{application.name}")
+ expect(find('.breadcrumbs-sub-title')).to have_link(application.name)
+ end
+
it 'deletes an application' do
create(:oauth_application, owner: user)
visit oauth_applications_path
diff --git a/spec/features/profiles/user_visits_notifications_tab_spec.rb b/spec/features/profiles/user_visits_notifications_tab_spec.rb
index df89918f17a..1952fdae798 100644
--- a/spec/features/profiles/user_visits_notifications_tab_spec.rb
+++ b/spec/features/profiles/user_visits_notifications_tab_spec.rb
@@ -5,7 +5,7 @@ feature 'User visits the notifications tab', :js do
let(:user) { create(:user) }
before do
- project.team << [user, :master]
+ project.add_master(user)
sign_in(user)
visit(profile_notifications_path)
end
diff --git a/spec/features/profiles/user_visits_profile_preferences_page_spec.rb b/spec/features/profiles/user_visits_profile_preferences_page_spec.rb
index 90d6841af0e..266af8f4e3d 100644
--- a/spec/features/profiles/user_visits_profile_preferences_page_spec.rb
+++ b/spec/features/profiles/user_visits_profile_preferences_page_spec.rb
@@ -32,6 +32,18 @@ describe 'User visits the profile preferences page' do
end
end
+ describe 'User changes their multi file editor preferences', :js do
+ it 'set the new_repo cookie when the option is ON' do
+ choose 'user_multi_file_on'
+ expect(get_cookie('new_repo')).not_to be_nil
+ end
+
+ it 'deletes the new_repo cookie when the option is OFF' do
+ choose 'user_multi_file_off'
+ expect(get_cookie('new_repo')).to be_nil
+ end
+ end
+
describe 'User changes their default dashboard', :js do
it 'creates a flash message' do
select 'Starred Projects', from: 'user_dashboard'
diff --git a/spec/features/projects/activity/rss_spec.rb b/spec/features/projects/activity/rss_spec.rb
index 84c2faa2015..2693e539268 100644
--- a/spec/features/projects/activity/rss_spec.rb
+++ b/spec/features/projects/activity/rss_spec.rb
@@ -11,7 +11,7 @@ feature 'Project Activity RSS' do
context 'when signed in' do
before do
- project.team << [user, :developer]
+ project.add_developer(user)
sign_in(user)
visit path
end
diff --git a/spec/features/projects/badges/coverage_spec.rb b/spec/features/projects/badges/coverage_spec.rb
index c68e10a2563..821ce88a402 100644
--- a/spec/features/projects/badges/coverage_spec.rb
+++ b/spec/features/projects/badges/coverage_spec.rb
@@ -6,7 +6,7 @@ feature 'test coverage badge' do
context 'when user has access to view badge' do
background do
- project.team << [user, :developer]
+ project.add_developer(user)
sign_in(user)
end
diff --git a/spec/features/projects/badges/list_spec.rb b/spec/features/projects/badges/list_spec.rb
index 68c4a647958..c705e479690 100644
--- a/spec/features/projects/badges/list_spec.rb
+++ b/spec/features/projects/badges/list_spec.rb
@@ -4,7 +4,7 @@ feature 'list of badges' do
background do
user = create(:user)
project = create(:project, :repository)
- project.team << [user, :master]
+ project.add_master(user)
sign_in(user)
visit project_pipelines_settings_path(project)
end
diff --git a/spec/features/projects/blobs/edit_spec.rb b/spec/features/projects/blobs/edit_spec.rb
index 965028a6f90..69e4c9f04a1 100644
--- a/spec/features/projects/blobs/edit_spec.rb
+++ b/spec/features/projects/blobs/edit_spec.rb
@@ -13,7 +13,7 @@ feature 'Editing file blob', :js do
let(:role) { :developer }
before do
- project.team << [user, role]
+ project.add_role(user, role)
sign_in(user)
end
@@ -55,7 +55,7 @@ feature 'Editing file blob', :js do
let(:user) { create(:user) }
before do
- project.team << [user, :developer]
+ project.add_developer(user)
visit project_edit_blob_path(project, tree_join(branch, file_path))
end
@@ -90,7 +90,7 @@ feature 'Editing file blob', :js do
let(:protected_branch) { 'protected-branch' }
before do
- project.team << [user, :developer]
+ project.add_developer(user)
project.repository.add_branch(user, protected_branch, 'master')
create(:protected_branch, project: project, name: protected_branch)
sign_in(user)
@@ -122,7 +122,7 @@ feature 'Editing file blob', :js do
let(:user) { create(:user) }
before do
- project.team << [user, :master]
+ project.add_master(user)
sign_in(user)
visit project_edit_blob_path(project, tree_join(branch, file_path))
end
diff --git a/spec/features/projects/branches/download_buttons_spec.rb b/spec/features/projects/branches/download_buttons_spec.rb
index 2f407b13c2f..39bcea013e7 100644
--- a/spec/features/projects/branches/download_buttons_spec.rb
+++ b/spec/features/projects/branches/download_buttons_spec.rb
@@ -23,7 +23,7 @@ feature 'Download buttons in branches page' do
background do
sign_in(user)
- project.team << [user, role]
+ project.add_role(user, role)
end
describe 'when checking branches' do
diff --git a/spec/features/projects/branches_spec.rb b/spec/features/projects/branches_spec.rb
index 7a77df83034..2fddd274078 100644
--- a/spec/features/projects/branches_spec.rb
+++ b/spec/features/projects/branches_spec.rb
@@ -8,7 +8,7 @@ describe 'Branches' do
context 'logged in as developer' do
before do
sign_in(user)
- project.team << [user, :developer]
+ project.add_developer(user)
end
describe 'Initial branches page' do
@@ -78,7 +78,7 @@ describe 'Branches' do
context 'logged in as master' do
before do
sign_in(user)
- project.team << [user, :master]
+ project.add_master(user)
end
describe 'Initial branches page' do
diff --git a/spec/features/projects/clusters/applications_spec.rb b/spec/features/projects/clusters/applications_spec.rb
index b34cd061ec6..9c4abec115f 100644
--- a/spec/features/projects/clusters/applications_spec.rb
+++ b/spec/features/projects/clusters/applications_spec.rb
@@ -73,7 +73,7 @@ feature 'Clusters Applications', :js do
before do
allow(ClusterInstallAppWorker).to receive(:perform_async).and_return(nil)
- create(:cluster_applications_helm, :installed, cluster: cluster)
+ create(:clusters_applications_helm, :installed, cluster: cluster)
page.within('.js-cluster-application-row-ingress') do
page.find(:css, '.js-cluster-application-install-button').click
diff --git a/spec/features/projects/clusters/gcp_spec.rb b/spec/features/projects/clusters/gcp_spec.rb
index 67b8901f8fb..523cc08496b 100644
--- a/spec/features/projects/clusters/gcp_spec.rb
+++ b/spec/features/projects/clusters/gcp_spec.rb
@@ -20,105 +20,126 @@ feature 'Gcp Cluster', :js do
.to receive(:expires_at_in_session).and_return(1.hour.since.to_i.to_s)
end
- context 'when user does not have a cluster and visits cluster index page' do
+ context 'when user has a GCP project with billing enabled' do
before do
- visit project_clusters_path(project)
-
- click_link 'Add cluster'
- click_link 'Create on GKE'
+ allow_any_instance_of(Projects::Clusters::GcpController).to receive(:authorize_google_project_billing)
+ stub_google_project_billing_status
end
- context 'when user filled form with valid parameters' do
+ context 'when user does not have a cluster and visits cluster index page' do
before do
- allow_any_instance_of(GoogleApi::CloudPlatform::Client)
- .to receive(:projects_zones_clusters_create) do
- OpenStruct.new(
- self_link: 'projects/gcp-project-12345/zones/us-central1-a/operations/ope-123',
- status: 'RUNNING'
- )
+ visit project_clusters_path(project)
+
+ click_link 'Add cluster'
+ click_link 'Create on GKE'
+ end
+
+ context 'when user filled form with valid parameters' do
+ before do
+ allow_any_instance_of(GoogleApi::CloudPlatform::Client)
+ .to receive(:projects_zones_clusters_create) do
+ OpenStruct.new(
+ self_link: 'projects/gcp-project-12345/zones/us-central1-a/operations/ope-123',
+ status: 'RUNNING'
+ )
+ end
+
+ allow(WaitForClusterCreationWorker).to receive(:perform_in).and_return(nil)
+
+ fill_in 'cluster_provider_gcp_attributes_gcp_project_id', with: 'gcp-project-123'
+ fill_in 'cluster_name', with: 'dev-cluster'
+ click_button 'Create cluster'
end
- allow(WaitForClusterCreationWorker).to receive(:perform_in).and_return(nil)
+ it 'user sees a cluster details page and creation status' do
+ expect(page).to have_content('Cluster is being created on Google Kubernetes Engine...')
- fill_in 'cluster_provider_gcp_attributes_gcp_project_id', with: 'gcp-project-123'
- fill_in 'cluster_name', with: 'dev-cluster'
- click_button 'Create cluster'
- end
+ Clusters::Cluster.last.provider.make_created!
- it 'user sees a cluster details page and creation status' do
- expect(page).to have_content('Cluster is being created on Google Kubernetes Engine...')
+ expect(page).to have_content('Cluster was successfully created on Google Kubernetes Engine')
+ end
- Clusters::Cluster.last.provider.make_created!
+ it 'user sees a error if something worng during creation' do
+ expect(page).to have_content('Cluster is being created on Google Kubernetes Engine...')
- expect(page).to have_content('Cluster was successfully created on Google Kubernetes Engine')
- end
+ Clusters::Cluster.last.provider.make_errored!('Something wrong!')
- it 'user sees a error if something worng during creation' do
- expect(page).to have_content('Cluster is being created on Google Kubernetes Engine...')
+ expect(page).to have_content('Something wrong!')
+ end
+ end
- Clusters::Cluster.last.provider.make_errored!('Something wrong!')
+ context 'when user filled form with invalid parameters' do
+ before do
+ click_button 'Create cluster'
+ end
- expect(page).to have_content('Something wrong!')
+ it 'user sees a validation error' do
+ expect(page).to have_css('#error_explanation')
+ end
end
end
- context 'when user filled form with invalid parameters' do
+ context 'when user does have a cluster and visits cluster page' do
+ let(:cluster) { create(:cluster, :provided_by_gcp, projects: [project]) }
+
before do
- click_button 'Create cluster'
+ visit project_cluster_path(project, cluster)
end
- it 'user sees a validation error' do
- expect(page).to have_css('#error_explanation')
+ it 'user sees a cluster details page' do
+ expect(page).to have_button('Save changes')
+ expect(page.find(:css, '.cluster-name').value).to eq(cluster.name)
end
- end
- end
- context 'when user does have a cluster and visits cluster page' do
- let(:cluster) { create(:cluster, :provided_by_gcp, projects: [project]) }
+ context 'when user disables the cluster' do
+ before do
+ page.find(:css, '.js-toggle-cluster').click
+ page.within('#cluster-integration') { click_button 'Save changes' }
+ end
- before do
- visit project_cluster_path(project, cluster)
- end
+ it 'user sees the successful message' do
+ expect(page).to have_content('Cluster was successfully updated.')
+ end
+ end
- it 'user sees a cluster details page' do
- expect(page).to have_button('Save')
- expect(page.find(:css, '.cluster-name').value).to eq(cluster.name)
- end
+ context 'when user changes cluster parameters' do
+ before do
+ fill_in 'cluster_platform_kubernetes_attributes_namespace', with: 'my-namespace'
+ page.within('#js-cluster-details') { click_button 'Save changes' }
+ end
- context 'when user disables the cluster' do
- before do
- page.find(:css, '.js-toggle-cluster').click
- click_button 'Save'
+ it 'user sees the successful message' do
+ expect(page).to have_content('Cluster was successfully updated.')
+ expect(cluster.reload.platform_kubernetes.namespace).to eq('my-namespace')
+ end
end
- it 'user sees the successful message' do
- expect(page).to have_content('Cluster was successfully updated.')
+ context 'when user destroy the cluster' do
+ before do
+ page.accept_confirm do
+ click_link 'Remove integration'
+ end
+ end
+
+ it 'user sees creation form with the successful message' do
+ expect(page).to have_content('Cluster integration was successfully removed.')
+ expect(page).to have_link('Add cluster')
+ end
end
end
+ end
- context 'when user changes cluster parameters' do
- before do
- fill_in 'cluster_platform_kubernetes_attributes_namespace', with: 'my-namespace'
- click_button 'Save changes'
- end
+ context 'when user does not have a GCP project with billing enabled' do
+ before do
+ visit project_clusters_path(project)
- it 'user sees the successful message' do
- expect(page).to have_content('Cluster was successfully updated.')
- expect(cluster.reload.platform_kubernetes.namespace).to eq('my-namespace')
- end
+ click_link 'Add cluster'
+ click_link 'Create on GKE'
end
- context 'when user destroy the cluster' do
- before do
- page.accept_confirm do
- click_link 'Remove integration'
- end
- end
-
- it 'user sees creation form with the successful message' do
- expect(page).to have_content('Cluster integration was successfully removed.')
- expect(page).to have_link('Add cluster')
- end
+ it 'user sees a check page' do
+ pending 'the frontend still has not been implemented'
+ expect(page).to have_link('Continue')
end
end
end
diff --git a/spec/features/projects/clusters/interchangeability_spec.rb b/spec/features/projects/clusters/interchangeability_spec.rb
index 01f9526608f..3ddb35c755c 100644
--- a/spec/features/projects/clusters/interchangeability_spec.rb
+++ b/spec/features/projects/clusters/interchangeability_spec.rb
@@ -1,7 +1,7 @@
require 'spec_helper'
feature 'Interchangeability between KubernetesService and Platform::Kubernetes' do
- EXCEPT_METHODS = %i[test title description help fields initialize_properties namespace namespace= api_url api_url=].freeze
+ EXCEPT_METHODS = %i[test title description help fields initialize_properties namespace namespace= api_url api_url= deprecated? deprecation_message].freeze
EXCEPT_METHODS_GREP_V = %w[_touched? _changed? _was].freeze
it 'Clusters::Platform::Kubernetes covers core interfaces in KubernetesService' do
diff --git a/spec/features/projects/clusters/user_spec.rb b/spec/features/projects/clusters/user_spec.rb
index 414f4acba86..a519b9f9c7e 100644
--- a/spec/features/projects/clusters/user_spec.rb
+++ b/spec/features/projects/clusters/user_spec.rb
@@ -29,7 +29,7 @@ feature 'User Cluster', :js do
end
it 'user sees a cluster details page' do
- expect(page).to have_content('Enable cluster integration')
+ expect(page).to have_content('Cluster integration')
expect(page.find_field('cluster[name]').value).to eq('dev-cluster')
expect(page.find_field('cluster[platform_kubernetes_attributes][api_url]').value)
.to have_content('http://example.com')
@@ -57,14 +57,14 @@ feature 'User Cluster', :js do
end
it 'user sees a cluster details page' do
- expect(page).to have_button('Save')
+ expect(page).to have_button('Save changes')
end
context 'when user disables the cluster' do
before do
page.find(:css, '.js-toggle-cluster').click
fill_in 'cluster_name', with: 'dev-cluster'
- click_button 'Save'
+ page.within('#cluster-integration') { click_button 'Save changes' }
end
it 'user sees the successful message' do
@@ -76,7 +76,7 @@ feature 'User Cluster', :js do
before do
fill_in 'cluster_name', with: 'my-dev-cluster'
fill_in 'cluster_platform_kubernetes_attributes_namespace', with: 'my-namespace'
- click_button 'Save changes'
+ page.within('#js-cluster-details') { click_button 'Save changes' }
end
it 'user sees the successful message' do
diff --git a/spec/features/projects/clusters_spec.rb b/spec/features/projects/clusters_spec.rb
index 008bdf2044b..eae2910a8f6 100644
--- a/spec/features/projects/clusters_spec.rb
+++ b/spec/features/projects/clusters_spec.rb
@@ -35,17 +35,6 @@ feature 'Clusters', :js do
expect(page).to have_selector('.gl-responsive-table-row', count: 2)
end
- it 'user sees navigation tabs' do
- expect(page.find('.js-active-tab').text).to include('Active')
- expect(page.find('.js-active-tab .badge').text).to include('1')
-
- expect(page.find('.js-inactive-tab').text).to include('Inactive')
- expect(page.find('.js-inactive-tab .badge').text).to include('0')
-
- expect(page.find('.js-all-tab').text).to include('All')
- expect(page.find('.js-all-tab .badge').text).to include('1')
- end
-
context 'inline update of cluster' do
it 'user can update cluster' do
expect(page).to have_selector('.js-toggle-cluster-list')
@@ -88,4 +77,18 @@ feature 'Clusters', :js do
end
end
end
+
+ context 'when user has not signed in Google' do
+ before do
+ visit project_clusters_path(project)
+
+ click_link 'Add cluster'
+ click_link 'Create on GKE'
+ end
+
+ it 'user sees a login page' do
+ expect(page).to have_css('.signin-with-google')
+ expect(page).to have_link('Google account')
+ end
+ end
end
diff --git a/spec/features/projects/commit/builds_spec.rb b/spec/features/projects/commit/builds_spec.rb
index 79e84a4f0a6..36a746ac83d 100644
--- a/spec/features/projects/commit/builds_spec.rb
+++ b/spec/features/projects/commit/builds_spec.rb
@@ -5,7 +5,7 @@ feature 'project commit pipelines', :js do
background do
user = create(:user)
- project.team << [user, :master]
+ project.add_master(user)
sign_in(user)
end
diff --git a/spec/features/projects/commit/cherry_pick_spec.rb b/spec/features/projects/commit/cherry_pick_spec.rb
index c11a95732b2..c4c399e3058 100644
--- a/spec/features/projects/commit/cherry_pick_spec.rb
+++ b/spec/features/projects/commit/cherry_pick_spec.rb
@@ -9,7 +9,7 @@ describe 'Cherry-pick Commits' do
before do
sign_in(user)
- project.team << [user, :master]
+ project.add_master(user)
visit project_commit_path(project, master_pickable_commit.id)
end
diff --git a/spec/features/projects/commits/rss_spec.rb b/spec/features/projects/commits/rss_spec.rb
index db958346f06..0d9c7355ddd 100644
--- a/spec/features/projects/commits/rss_spec.rb
+++ b/spec/features/projects/commits/rss_spec.rb
@@ -7,7 +7,7 @@ feature 'Project Commits RSS' do
context 'when signed in' do
before do
- project.team << [user, :developer]
+ project.add_developer(user)
sign_in(user)
visit path
end
diff --git a/spec/features/projects/compare_spec.rb b/spec/features/projects/compare_spec.rb
index 87ffc2a0b90..1fb22fd0e4c 100644
--- a/spec/features/projects/compare_spec.rb
+++ b/spec/features/projects/compare_spec.rb
@@ -5,7 +5,7 @@ describe "Compare", :js do
let(:project) { create(:project, :repository) }
before do
- project.team << [user, :master]
+ project.add_master(user)
sign_in user
visit project_compare_index_path(project, from: "master", to: "master")
end
diff --git a/spec/features/projects/deploy_keys_spec.rb b/spec/features/projects/deploy_keys_spec.rb
index e445758cb5e..886c56e7163 100644
--- a/spec/features/projects/deploy_keys_spec.rb
+++ b/spec/features/projects/deploy_keys_spec.rb
@@ -5,7 +5,7 @@ describe 'Project deploy keys', :js do
let(:project) { create(:project_empty_repo) }
before do
- project.team << [user, :master]
+ project.add_master(user)
sign_in(user)
end
diff --git a/spec/features/projects/developer_views_empty_project_instructions_spec.rb b/spec/features/projects/developer_views_empty_project_instructions_spec.rb
index 36809240f76..bf55917bf4c 100644
--- a/spec/features/projects/developer_views_empty_project_instructions_spec.rb
+++ b/spec/features/projects/developer_views_empty_project_instructions_spec.rb
@@ -5,7 +5,7 @@ feature 'Developer views empty project instructions' do
let(:developer) { create(:user) }
background do
- project.team << [developer, :developer]
+ project.add_developer(developer)
sign_in(developer)
end
diff --git a/spec/features/projects/edit_spec.rb b/spec/features/projects/edit_spec.rb
index 7a372757523..1d4b4d0fdca 100644
--- a/spec/features/projects/edit_spec.rb
+++ b/spec/features/projects/edit_spec.rb
@@ -7,7 +7,7 @@ feature 'Project edit', :js do
context 'feature visibility' do
before do
- project.team << [user, :master]
+ project.add_master(user)
sign_in(user)
visit edit_project_path(project)
diff --git a/spec/features/projects/environments/environment_spec.rb b/spec/features/projects/environments/environment_spec.rb
index dfcf97ad495..64e600144e0 100644
--- a/spec/features/projects/environments/environment_spec.rb
+++ b/spec/features/projects/environments/environment_spec.rb
@@ -7,7 +7,7 @@ feature 'Environment' do
background do
sign_in(user)
- project.team << [user, role]
+ project.add_role(user, role)
end
feature 'environment details page' do
diff --git a/spec/features/projects/environments/environments_spec.rb b/spec/features/projects/environments/environments_spec.rb
index 4a05313c14a..5248a783db4 100644
--- a/spec/features/projects/environments/environments_spec.rb
+++ b/spec/features/projects/environments/environments_spec.rb
@@ -6,7 +6,7 @@ feature 'Environments page', :js do
given(:role) { :developer }
background do
- project.team << [user, role]
+ project.add_role(user, role)
sign_in(user)
end
diff --git a/spec/features/projects/features_visibility_spec.rb b/spec/features/projects/features_visibility_spec.rb
index 033c45a60bf..b0eb7c5b42a 100644
--- a/spec/features/projects/features_visibility_spec.rb
+++ b/spec/features/projects/features_visibility_spec.rb
@@ -8,7 +8,7 @@ describe 'Edit Project Settings' do
describe 'project features visibility selectors', :js do
before do
- project.team << [member, :master]
+ project.add_master(member)
sign_in(member)
end
@@ -165,7 +165,7 @@ describe 'Edit Project Settings' do
describe 'repository visibility', :js do
before do
- project.team << [member, :master]
+ project.add_master(member)
sign_in(member)
visit edit_project_path(project)
end
@@ -261,7 +261,7 @@ describe 'Edit Project Settings' do
let!(:project) { create(:project, :private) }
before do
- project.team << [member, :guest]
+ project.add_guest(member)
sign_in(member)
visit project_path(project)
end
diff --git a/spec/features/projects/files/browse_files_spec.rb b/spec/features/projects/files/browse_files_spec.rb
index 84197e45dcb..2c38c380d9d 100644
--- a/spec/features/projects/files/browse_files_spec.rb
+++ b/spec/features/projects/files/browse_files_spec.rb
@@ -5,7 +5,7 @@ feature 'user browses project', :js do
let(:user) { create(:user) }
before do
- project.team << [user, :master]
+ project.add_master(user)
sign_in(user)
visit project_tree_path(project, project.default_branch)
end
diff --git a/spec/features/projects/files/creating_a_file_spec.rb b/spec/features/projects/files/creating_a_file_spec.rb
index e1852a6e544..8d982636525 100644
--- a/spec/features/projects/files/creating_a_file_spec.rb
+++ b/spec/features/projects/files/creating_a_file_spec.rb
@@ -5,7 +5,7 @@ feature 'User wants to create a file' do
let(:user) { create(:user) }
background do
- project.team << [user, :master]
+ project.add_master(user)
sign_in user
visit project_new_blob_path(project, project.default_branch)
end
diff --git a/spec/features/projects/files/dockerfile_dropdown_spec.rb b/spec/features/projects/files/dockerfile_dropdown_spec.rb
index 3c3a5326538..f4a39e331fd 100644
--- a/spec/features/projects/files/dockerfile_dropdown_spec.rb
+++ b/spec/features/projects/files/dockerfile_dropdown_spec.rb
@@ -5,7 +5,7 @@ feature 'User wants to add a Dockerfile file' do
before do
user = create(:user)
project = create(:project, :repository)
- project.team << [user, :master]
+ project.add_master(user)
sign_in user
diff --git a/spec/features/projects/files/download_buttons_spec.rb b/spec/features/projects/files/download_buttons_spec.rb
index d2382d55c0b..2101627f324 100644
--- a/spec/features/projects/files/download_buttons_spec.rb
+++ b/spec/features/projects/files/download_buttons_spec.rb
@@ -23,7 +23,7 @@ feature 'Download buttons in files tree' do
background do
sign_in(user)
- project.team << [user, role]
+ project.add_role(user, role)
end
describe 'when files tree' do
diff --git a/spec/features/projects/files/edit_file_soft_wrap_spec.rb b/spec/features/projects/files/edit_file_soft_wrap_spec.rb
index 3ab43b3c656..8d32ada5795 100644
--- a/spec/features/projects/files/edit_file_soft_wrap_spec.rb
+++ b/spec/features/projects/files/edit_file_soft_wrap_spec.rb
@@ -4,7 +4,7 @@ feature 'User uses soft wrap whilst editing file', :js do
before do
user = create(:user)
project = create(:project, :repository)
- project.team << [user, :master]
+ project.add_master(user)
sign_in user
visit project_new_blob_path(project, 'master', file_name: 'test_file-name')
page.within('.file-editor.code') do
diff --git a/spec/features/projects/files/editing_a_file_spec.rb b/spec/features/projects/files/editing_a_file_spec.rb
index 20be968e89f..d874cdbff8d 100644
--- a/spec/features/projects/files/editing_a_file_spec.rb
+++ b/spec/features/projects/files/editing_a_file_spec.rb
@@ -16,7 +16,7 @@ feature 'User wants to edit a file' do
end
background do
- project.team << [user, :master]
+ project.add_master(user)
sign_in user
visit project_edit_blob_path(project,
File.join(project.default_branch, '.gitignore'))
diff --git a/spec/features/projects/files/files_sort_submodules_with_folders_spec.rb b/spec/features/projects/files/files_sort_submodules_with_folders_spec.rb
index 702b99de733..ead9f7e9168 100644
--- a/spec/features/projects/files/files_sort_submodules_with_folders_spec.rb
+++ b/spec/features/projects/files/files_sort_submodules_with_folders_spec.rb
@@ -5,7 +5,7 @@ feature 'User views files page' do
let(:project) { create(:forked_project_with_submodules) }
before do
- project.team << [user, :master]
+ project.add_master(user)
sign_in user
visit project_tree_path(project, project.repository.root_ref)
end
diff --git a/spec/features/projects/files/find_file_keyboard_spec.rb b/spec/features/projects/files/find_file_keyboard_spec.rb
index 618725ee781..e9ff06c72d8 100644
--- a/spec/features/projects/files/find_file_keyboard_spec.rb
+++ b/spec/features/projects/files/find_file_keyboard_spec.rb
@@ -5,7 +5,7 @@ feature 'Find file keyboard shortcuts', :js do
let(:project) { create(:project, :repository) }
before do
- project.team << [user, :master]
+ project.add_master(user)
sign_in user
visit project_find_file_path(project, project.repository.root_ref)
diff --git a/spec/features/projects/files/gitignore_dropdown_spec.rb b/spec/features/projects/files/gitignore_dropdown_spec.rb
index 81d68c3d67c..79f3fd09b48 100644
--- a/spec/features/projects/files/gitignore_dropdown_spec.rb
+++ b/spec/features/projects/files/gitignore_dropdown_spec.rb
@@ -4,7 +4,7 @@ feature 'User wants to add a .gitignore file' do
before do
user = create(:user)
project = create(:project, :repository)
- project.team << [user, :master]
+ project.add_master(user)
sign_in user
visit project_new_blob_path(project, 'master', file_name: '.gitignore')
end
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 8e58fa7bd56..db6c67b802e 100644
--- a/spec/features/projects/files/gitlab_ci_yml_dropdown_spec.rb
+++ b/spec/features/projects/files/gitlab_ci_yml_dropdown_spec.rb
@@ -4,7 +4,7 @@ feature 'User wants to add a .gitlab-ci.yml file' do
before do
user = create(:user)
project = create(:project, :repository)
- project.team << [user, :master]
+ project.add_master(user)
sign_in user
visit project_new_blob_path(project, 'master', file_name: '.gitlab-ci.yml')
end
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 6c5b1086ec1..07599600876 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
@@ -6,7 +6,7 @@ feature 'project owner creates a license file', :js do
background do
project.repository.delete_file(project_master, 'LICENSE',
message: 'Remove LICENSE', branch_name: 'master')
- project.team << [project_master, :master]
+ project.add_master(project_master)
sign_in(project_master)
visit project_path(project)
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 6c616bf0456..8ac9821b879 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
@@ -2,15 +2,15 @@ require 'spec_helper'
feature 'project owner sees a link to create a license file in empty project', :js do
let(:project_master) { create(:user) }
- let(:project) { create(:project) }
+ let(:project) { create(:project_empty_repo) }
+
background do
- project.team << [project_master, :master]
+ project.add_master(project_master)
sign_in(project_master)
end
scenario 'project master creates a license file from a template' do
visit project_path(project)
- click_link 'Create empty bare repository'
click_on 'LICENSE'
expect(page).to have_content('New file')
@@ -26,8 +26,6 @@ feature 'project owner sees a link to create a license file in empty project', :
expect(file_content).to have_content("Copyright (c) #{Time.now.year} #{project.namespace.human_name}")
fill_in :commit_message, with: 'Add a LICENSE file', visible: true
- # Remove pre-receive hook so we can push without auth
- FileUtils.rm_f(File.join(project.repository.path, 'hooks', 'pre-receive'))
click_button 'Commit changes'
expect(current_path).to eq(
diff --git a/spec/features/projects/files/template_type_dropdown_spec.rb b/spec/features/projects/files/template_type_dropdown_spec.rb
index f95a60e5194..97408a9c41e 100644
--- a/spec/features/projects/files/template_type_dropdown_spec.rb
+++ b/spec/features/projects/files/template_type_dropdown_spec.rb
@@ -5,7 +5,7 @@ feature 'Template type dropdown selector', :js do
let(:user) { create(:user) }
before do
- project.team << [user, :master]
+ project.add_master(user)
sign_in user
end
diff --git a/spec/features/projects/files/undo_template_spec.rb b/spec/features/projects/files/undo_template_spec.rb
index 64fe350f3dc..fbf35fb4e1c 100644
--- a/spec/features/projects/files/undo_template_spec.rb
+++ b/spec/features/projects/files/undo_template_spec.rb
@@ -5,7 +5,7 @@ feature 'Template Undo Button', :js do
let(:user) { create(:user) }
before do
- project.team << [user, :master]
+ project.add_master(user)
sign_in user
end
diff --git a/spec/features/projects/guest_navigation_menu_spec.rb b/spec/features/projects/guest_navigation_menu_spec.rb
index 98c7ef57a51..199682b943c 100644
--- a/spec/features/projects/guest_navigation_menu_spec.rb
+++ b/spec/features/projects/guest_navigation_menu_spec.rb
@@ -5,7 +5,7 @@ describe 'Guest navigation menu' do
let(:guest) { create(:user) }
before do
- project.team << [guest, :guest]
+ project.add_guest(guest)
sign_in(guest)
end
diff --git a/spec/features/projects/import_export/export_file_spec.rb b/spec/features/projects/import_export/export_file_spec.rb
index 461aa39d0ad..6732cf61767 100644
--- a/spec/features/projects/import_export/export_file_spec.rb
+++ b/spec/features/projects/import_export/export_file_spec.rb
@@ -2,7 +2,7 @@ require 'spec_helper'
# Integration test that exports a file using the Import/Export feature
# It looks up for any sensitive word inside the JSON, so if a sensitive word is found
-# we''l have to either include it adding the model that includes it to the +safe_list+
+# we'll have to either include it adding the model that includes it to the +safe_list+
# or make sure the attribute is blacklisted in the +import_export.yml+ configuration
feature 'Import/Export - project export integration test', :js do
include Select2Helper
diff --git a/spec/features/projects/import_export/test_project_export.tar.gz b/spec/features/projects/import_export/test_project_export.tar.gz
index 0c354298433..0cc68aff494 100644
--- a/spec/features/projects/import_export/test_project_export.tar.gz
+++ b/spec/features/projects/import_export/test_project_export.tar.gz
Binary files differ
diff --git a/spec/features/projects/issuable_templates_spec.rb b/spec/features/projects/issuable_templates_spec.rb
index 0257cd157c9..e26caf1f456 100644
--- a/spec/features/projects/issuable_templates_spec.rb
+++ b/spec/features/projects/issuable_templates_spec.rb
@@ -8,7 +8,7 @@ feature 'issuable templates', :js do
let(:issue_form_location) { '#content-body .issuable-details .detail-page-description' }
before do
- project.team << [user, :master]
+ project.add_master(user)
sign_in user
end
@@ -32,9 +32,7 @@ feature 'issuable templates', :js do
message: 'added issue template',
branch_name: 'master')
visit project_issue_path project, issue
- page.within('.js-issuable-actions') do
- click_on 'Edit'
- end
+ page.find('.js-issuable-edit').click
fill_in :'issuable-title', with: 'test issue title'
end
@@ -77,9 +75,7 @@ feature 'issuable templates', :js do
message: 'added issue template',
branch_name: 'master')
visit project_issue_path project, issue
- page.within('.js-issuable-actions') do
- click_on 'Edit'
- end
+ page.find('.js-issuable-edit').click
fill_in :'issuable-title', with: 'test issue title'
fill_in :'issue-description', with: prior_description
end
@@ -124,7 +120,7 @@ feature 'issuable templates', :js do
background do
sign_out(:user)
- project.team << [fork_user, :developer]
+ project.add_developer(fork_user)
sign_in(fork_user)
diff --git a/spec/features/projects/issues/rss_spec.rb b/spec/features/projects/issues/rss_spec.rb
index 58eeef8c258..ff91aabc311 100644
--- a/spec/features/projects/issues/rss_spec.rb
+++ b/spec/features/projects/issues/rss_spec.rb
@@ -12,7 +12,7 @@ feature 'Project Issues RSS' do
let(:user) { create(:user) }
before do
- project.team << [user, :developer]
+ project.add_developer(user)
sign_in(user)
visit path
end
diff --git a/spec/features/projects/jobs/user_browses_job_spec.rb b/spec/features/projects/jobs/user_browses_job_spec.rb
index 5d9208ebadd..4c49cff30d4 100644
--- a/spec/features/projects/jobs/user_browses_job_spec.rb
+++ b/spec/features/projects/jobs/user_browses_job_spec.rb
@@ -1,7 +1,7 @@
require 'spec_helper'
describe 'User browses a job', :js do
- let!(:build) { create(:ci_build, :coverage, pipeline: pipeline) }
+ let!(:build) { create(:ci_build, :running, :coverage, pipeline: pipeline) }
let(:pipeline) { create(:ci_empty_pipeline, project: project, sha: project.commit.sha, ref: 'master') }
let(:project) { create(:project, :repository, namespace: user.namespace) }
let(:user) { create(:user) }
diff --git a/spec/features/projects/jobs_spec.rb b/spec/features/projects/jobs_spec.rb
index 0b0d5a2dce8..9a6b27c00f8 100644
--- a/spec/features/projects/jobs_spec.rb
+++ b/spec/features/projects/jobs_spec.rb
@@ -15,7 +15,7 @@ feature 'Jobs' do
end
before do
- project.team << [user, user_access_level]
+ project.add_role(user, user_access_level)
sign_in(user)
end
@@ -369,6 +369,34 @@ feature 'Jobs' do
end
end
end
+
+ context 'Playable manual action' do
+ let(:job) { create(:ci_build, :playable, pipeline: pipeline) }
+
+ before do
+ project.add_developer(user)
+ visit project_job_path(project, job)
+ end
+
+ it 'shows manual action empty state' do
+ expect(page).to have_content('This job requires a manual action')
+ expect(page).to have_content('This job depends on a user to trigger its process. Often they are used to deploy code to production environments.')
+ expect(page).to have_link('Trigger this manual action')
+ end
+ end
+
+ context 'Non triggered job' do
+ let(:job) { create(:ci_build, :created, pipeline: pipeline) }
+
+ before do
+ visit project_job_path(project, job)
+ end
+
+ it 'shows manual action empty state' do
+ expect(page).to have_content('This job has not been triggered yet')
+ expect(page).to have_content('This job depends on upstream jobs that need to succeed in order for this job to be triggered.')
+ end
+ end
end
describe "POST /:project/jobs/:id/cancel", :js do
diff --git a/spec/features/projects/labels/subscription_spec.rb b/spec/features/projects/labels/subscription_spec.rb
index e8c70dec854..70e8d436dcb 100644
--- a/spec/features/projects/labels/subscription_spec.rb
+++ b/spec/features/projects/labels/subscription_spec.rb
@@ -9,7 +9,7 @@ feature 'Labels subscription' do
context 'when signed in' do
before do
- project.team << [user, :developer]
+ project.add_developer(user)
sign_in user
end
diff --git a/spec/features/projects/labels/update_prioritization_spec.rb b/spec/features/projects/labels/update_prioritization_spec.rb
index d063f5c27b5..85bd776932b 100644
--- a/spec/features/projects/labels/update_prioritization_spec.rb
+++ b/spec/features/projects/labels/update_prioritization_spec.rb
@@ -12,7 +12,7 @@ feature 'Prioritize labels' do
context 'when user belongs to project team' do
before do
- project.team << [user, :developer]
+ project.add_developer(user)
sign_in user
end
diff --git a/spec/features/projects/labels/user_sees_links_to_issuables.rb b/spec/features/projects/labels/user_sees_links_to_issuables.rb
new file mode 100644
index 00000000000..aa56fd7f74e
--- /dev/null
+++ b/spec/features/projects/labels/user_sees_links_to_issuables.rb
@@ -0,0 +1,75 @@
+require 'spec_helper'
+
+feature 'Projects > Labels > User sees links to issuables' do
+ set(:user) { create(:user) }
+
+ before do
+ label # creates the label
+ project.add_developer(user)
+ sign_in user
+ visit project_labels_path(project)
+ end
+
+ context 'with a project label' do
+ let(:label) { create(:label, project: project, title: 'bug') }
+
+ context 'when merge requests and issues are enabled for the project' do
+ let(:project) { create(:project, :public) }
+
+ scenario 'shows links to MRs and issues' do
+ expect(page).to have_link('view merge requests')
+ expect(page).to have_link('view open issues')
+ end
+ end
+
+ context 'when issues are disabled for the project' do
+ let(:project) { create(:project, :public, issues_access_level: ProjectFeature::DISABLED) }
+
+ scenario 'shows links to MRs but not to issues' do
+ expect(page).to have_link('view merge requests')
+ expect(page).not_to have_link('view open issues')
+ end
+ end
+
+ context 'when merge requests are disabled for the project' do
+ let(:project) { create(:project, :public, merge_requests_access_level: ProjectFeature::DISABLED) }
+
+ scenario 'shows links to issues but not to MRs' do
+ expect(page).not_to have_link('view merge requests')
+ expect(page).to have_link('view open issues')
+ end
+ end
+ end
+
+ context 'with a group label' do
+ set(:group) { create(:group) }
+ let(:label) { create(:group_label, group: group, title: 'bug') }
+
+ context 'when merge requests and issues are enabled for the project' do
+ let(:project) { create(:project, :public, namespace: group) }
+
+ scenario 'shows links to MRs and issues' do
+ expect(page).to have_link('view merge requests')
+ expect(page).to have_link('view open issues')
+ end
+ end
+
+ context 'when issues are disabled for the project' do
+ let(:project) { create(:project, :public, namespace: group, issues_access_level: ProjectFeature::DISABLED) }
+
+ scenario 'shows links to MRs and issues' do
+ expect(page).to have_link('view merge requests')
+ expect(page).to have_link('view open issues')
+ end
+ end
+
+ context 'when merge requests are disabled for the project' do
+ let(:project) { create(:project, :public, namespace: group, merge_requests_access_level: ProjectFeature::DISABLED) }
+
+ scenario 'shows links to MRs and issues' do
+ expect(page).to have_link('view merge requests')
+ expect(page).to have_link('view open issues')
+ end
+ end
+ end
+end
diff --git a/spec/features/projects/main/download_buttons_spec.rb b/spec/features/projects/main/download_buttons_spec.rb
index 3f2579bb01a..81f08e44cf3 100644
--- a/spec/features/projects/main/download_buttons_spec.rb
+++ b/spec/features/projects/main/download_buttons_spec.rb
@@ -23,7 +23,7 @@ feature 'Download buttons in project main page' do
background do
sign_in(user)
- project.team << [user, role]
+ project.add_role(user, role)
end
describe 'when checking project main page' do
diff --git a/spec/features/projects/main/rss_spec.rb b/spec/features/projects/main/rss_spec.rb
index 7914180b951..3c98c11b490 100644
--- a/spec/features/projects/main/rss_spec.rb
+++ b/spec/features/projects/main/rss_spec.rb
@@ -7,7 +7,7 @@ feature 'Project RSS' do
context 'when signed in' do
before do
- project.team << [user, :developer]
+ project.add_developer(user)
sign_in(user)
visit path
end
diff --git a/spec/features/projects/members/anonymous_user_sees_members_spec.rb b/spec/features/projects/members/anonymous_user_sees_members_spec.rb
index bf0990d675d..e2a48bfd1d4 100644
--- a/spec/features/projects/members/anonymous_user_sees_members_spec.rb
+++ b/spec/features/projects/members/anonymous_user_sees_members_spec.rb
@@ -6,7 +6,7 @@ feature 'Projects > Members > Anonymous user sees members' do
let(:project) { create(:project, :public) }
background do
- project.team << [user, :master]
+ project.add_master(user)
create(:project_group_link, project: project, group: group)
end
diff --git a/spec/features/projects/members/group_members_spec.rb b/spec/features/projects/members/group_members_spec.rb
index c140fece41d..e22b6fa6c43 100644
--- a/spec/features/projects/members/group_members_spec.rb
+++ b/spec/features/projects/members/group_members_spec.rb
@@ -11,7 +11,7 @@ feature 'Projects members' do
let(:group_requester) { create(:user) }
background do
- project.team << [developer, :developer]
+ project.add_developer(developer)
group.add_owner(user)
sign_in(user)
end
diff --git a/spec/features/projects/members/groups_with_access_list_spec.rb b/spec/features/projects/members/groups_with_access_list_spec.rb
index 7f067aadec6..e6d0c6e00f8 100644
--- a/spec/features/projects/members/groups_with_access_list_spec.rb
+++ b/spec/features/projects/members/groups_with_access_list_spec.rb
@@ -6,7 +6,7 @@ feature 'Projects > Members > Groups with access list', :js do
let(:project) { create(:project, :public) }
background do
- project.team << [user, :master]
+ project.add_master(user)
@group_link = create(:project_group_link, project: project, group: group)
sign_in(user)
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 0f88f4cb1e8..8fe340d3bae 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
@@ -9,7 +9,7 @@ feature 'Projects > Members > Master adds member with expiration date', :js do
let!(:new_member) { create(:user) }
background do
- project.team << [master, :master]
+ project.add_master(master)
sign_in(master)
end
diff --git a/spec/features/projects/members/master_manages_access_requests_spec.rb b/spec/features/projects/members/master_manages_access_requests_spec.rb
index eb3c8034873..d575596937d 100644
--- a/spec/features/projects/members/master_manages_access_requests_spec.rb
+++ b/spec/features/projects/members/master_manages_access_requests_spec.rb
@@ -7,7 +7,7 @@ feature 'Projects > Members > Master manages access requests' do
background do
project.request_access(user)
- project.team << [master, :master]
+ project.add_master(master)
sign_in(master)
end
diff --git a/spec/features/projects/members/member_cannot_request_access_to_his_project_spec.rb b/spec/features/projects/members/member_cannot_request_access_to_his_project_spec.rb
index 04806f8fd9e..47911c32a72 100644
--- a/spec/features/projects/members/member_cannot_request_access_to_his_project_spec.rb
+++ b/spec/features/projects/members/member_cannot_request_access_to_his_project_spec.rb
@@ -5,7 +5,7 @@ feature 'Projects > Members > Member cannot request access to his project' do
let(:project) { create(:project) }
background do
- project.team << [member, :developer]
+ project.add_developer(member)
sign_in(member)
visit project_path(project)
end
diff --git a/spec/features/projects/members/member_leaves_project_spec.rb b/spec/features/projects/members/member_leaves_project_spec.rb
index 1bcf827d33c..e54c2c76975 100644
--- a/spec/features/projects/members/member_leaves_project_spec.rb
+++ b/spec/features/projects/members/member_leaves_project_spec.rb
@@ -5,7 +5,7 @@ feature 'Projects > Members > Member leaves project' do
let(:project) { create(:project, :repository) }
background do
- project.team << [user, :developer]
+ project.add_developer(user)
sign_in(user)
visit project_path(project)
end
diff --git a/spec/features/projects/merge_requests/list_spec.rb b/spec/features/projects/merge_requests/list_spec.rb
index a879efef4b5..b34b13db381 100644
--- a/spec/features/projects/merge_requests/list_spec.rb
+++ b/spec/features/projects/merge_requests/list_spec.rb
@@ -5,7 +5,7 @@ feature 'Merge Requests List' do
let(:project) { create(:project, :repository) }
background do
- project.team << [user, :developer]
+ project.add_developer(user)
sign_in(user)
end
diff --git a/spec/features/projects/pages_spec.rb b/spec/features/projects/pages_spec.rb
index 013ed6f2e58..2e334caa98f 100644
--- a/spec/features/projects/pages_spec.rb
+++ b/spec/features/projects/pages_spec.rb
@@ -8,7 +8,7 @@ feature 'Pages' do
background do
allow(Gitlab.config.pages).to receive(:enabled).and_return(true)
- project.team << [user, role]
+ project.add_role(user, role)
sign_in(user)
end
diff --git a/spec/features/projects/pipelines/pipeline_spec.rb b/spec/features/projects/pipelines/pipeline_spec.rb
index 888e290292b..266ef693d0b 100644
--- a/spec/features/projects/pipelines/pipeline_spec.rb
+++ b/spec/features/projects/pipelines/pipeline_spec.rb
@@ -6,7 +6,7 @@ describe 'Pipeline', :js do
before do
sign_in(user)
- project.team << [user, :developer]
+ project.add_developer(user)
end
shared_context 'pipeline builds' do
@@ -152,7 +152,7 @@ describe 'Pipeline', :js do
end
it 'shows counter in Jobs tab' do
- expect(page.find('.js-builds-counter').text).to eq(pipeline.statuses.count.to_s)
+ expect(page.find('.js-builds-counter').text).to eq(pipeline.total_size.to_s)
end
it 'shows Pipeline tab as active' do
@@ -248,7 +248,7 @@ describe 'Pipeline', :js do
end
it 'shows counter in Jobs tab' do
- expect(page.find('.js-builds-counter').text).to eq(pipeline.statuses.count.to_s)
+ expect(page.find('.js-builds-counter').text).to eq(pipeline.total_size.to_s)
end
it 'shows Jobs tab as active' do
diff --git a/spec/features/projects/pipelines/pipelines_spec.rb b/spec/features/projects/pipelines/pipelines_spec.rb
index b87b47d0e1a..592c99fc64a 100644
--- a/spec/features/projects/pipelines/pipelines_spec.rb
+++ b/spec/features/projects/pipelines/pipelines_spec.rb
@@ -8,7 +8,7 @@ describe 'Pipelines', :js do
before do
sign_in(user)
- project.team << [user, :developer]
+ project.add_developer(user)
end
describe 'GET /:project/pipelines' do
@@ -545,6 +545,40 @@ describe 'Pipelines', :js do
end
end
end
+
+ describe 'Reset runner caches' do
+ let(:project) { create(:project, :repository) }
+
+ before do
+ create(:ci_empty_pipeline, status: 'success', project: project, sha: project.commit.id, ref: 'master')
+ project.add_master(user)
+ visit project_pipelines_path(project)
+ end
+
+ it 'has a clear caches button' do
+ expect(page).to have_link 'Clear runner caches'
+ end
+
+ describe 'user clicks the button' do
+ context 'when project already has jobs_cache_index' do
+ before do
+ project.update_attributes(jobs_cache_index: 1)
+ end
+
+ it 'increments jobs_cache_index' do
+ click_link 'Clear runner caches'
+ expect(page.find('.flash-notice')).to have_content 'Project cache successfully reset.'
+ end
+ end
+
+ context 'when project does not have jobs_cache_index' do
+ it 'sets jobs_cache_index to 1' do
+ click_link 'Clear runner caches'
+ expect(page.find('.flash-notice')).to have_content 'Project cache successfully reset.'
+ end
+ end
+ end
+ end
end
context 'when user is not logged in' do
diff --git a/spec/features/projects/ref_switcher_spec.rb b/spec/features/projects/ref_switcher_spec.rb
deleted file mode 100644
index 33ccbc1a29f..00000000000
--- a/spec/features/projects/ref_switcher_spec.rb
+++ /dev/null
@@ -1,78 +0,0 @@
-require 'rails_helper'
-
-feature 'Ref switcher', :js do
- let(:user) { create(:user) }
- let(:project) { create(:project, :public, :repository) }
-
- before do
- project.team << [user, :master]
- set_cookie('new_repo', 'true')
- sign_in(user)
- visit project_tree_path(project, 'master')
- end
-
- it 'allow user to change ref by enter key' do
- click_button 'master'
- wait_for_requests
-
- page.within '.project-refs-form' do
- input = find('input[type="search"]')
- input.set 'binary'
- wait_for_requests
-
- expect(find('.dropdown-content ul')).to have_selector('li', count: 7)
-
- page.within '.dropdown-content ul' do
- input.native.send_keys :enter
- end
- end
-
- expect(page).to have_title 'add-pdf-text-binary'
- end
-
- it "user selects ref with special characters" do
- click_button 'master'
- wait_for_requests
-
- page.within '.project-refs-form' do
- page.fill_in 'Search branches and tags', with: "'test'"
- click_link "'test'"
- end
-
- expect(page).to have_title "'test'"
- end
-
- context "create branch" do
- let(:input) { find('.js-new-branch-name') }
-
- before do
- click_button 'master'
- wait_for_requests
-
- page.within '.project-refs-form' do
- find(".dropdown-footer-list a").click
- end
- end
-
- it "shows error message for the invalid branch name" do
- input.set 'foo bar'
- click_button('Create')
- wait_for_requests
- expect(page).to have_content 'Branch name is invalid'
- end
-
- it "should create new branch properly" do
- input.set 'new-branch-name'
- click_button('Create')
- wait_for_requests
- expect(find('.js-project-refs-dropdown')).to have_content 'new-branch-name'
- end
-
- it "should create new branch by Enter key" do
- input.set 'new-branch-name-2'
- input.native.send_keys :enter
- wait_for_requests
- expect(find('.js-project-refs-dropdown')).to have_content 'new-branch-name-2'
- end
- end
-end
diff --git a/spec/features/projects/services/user_activates_jira_spec.rb b/spec/features/projects/services/user_activates_jira_spec.rb
index ac78b1dfb1c..028669eeaf2 100644
--- a/spec/features/projects/services/user_activates_jira_spec.rb
+++ b/spec/features/projects/services/user_activates_jira_spec.rb
@@ -18,7 +18,7 @@ describe 'User activates Jira', :js do
end
before do
- project.team << [user, :master]
+ project.add_master(user)
sign_in(user)
visit project_settings_integrations_path(project)
diff --git a/spec/features/projects/services/user_activates_mattermost_slash_command_spec.rb b/spec/features/projects/services/user_activates_mattermost_slash_command_spec.rb
index 6f057137867..b2906e315f7 100644
--- a/spec/features/projects/services/user_activates_mattermost_slash_command_spec.rb
+++ b/spec/features/projects/services/user_activates_mattermost_slash_command_spec.rb
@@ -8,7 +8,7 @@ feature 'Setup Mattermost slash commands', :js do
before do
stub_mattermost_setting(enabled: mattermost_enabled)
- project.team << [user, :master]
+ project.add_master(user)
sign_in(user)
visit edit_project_service_path(project, service)
end
diff --git a/spec/features/projects/services/user_activates_slack_slash_command_spec.rb b/spec/features/projects/services/user_activates_slack_slash_command_spec.rb
index a8baf126269..4a88654210c 100644
--- a/spec/features/projects/services/user_activates_slack_slash_command_spec.rb
+++ b/spec/features/projects/services/user_activates_slack_slash_command_spec.rb
@@ -6,7 +6,7 @@ feature 'Slack slash commands' do
given(:service) { project.create_slack_slash_commands_service }
background do
- project.team << [user, :master]
+ project.add_master(user)
sign_in(user)
visit edit_project_service_path(project, service)
end
diff --git a/spec/features/projects/settings/integration_settings_spec.rb b/spec/features/projects/settings/integration_settings_spec.rb
index cbdb7973ac8..f6a1a46df11 100644
--- a/spec/features/projects/settings/integration_settings_spec.rb
+++ b/spec/features/projects/settings/integration_settings_spec.rb
@@ -8,7 +8,7 @@ feature 'Integration settings' do
background do
sign_in(user)
- project.team << [user, role]
+ project.add_role(user, role)
end
context 'for developer' do
diff --git a/spec/features/projects/settings/merge_requests_settings_spec.rb b/spec/features/projects/settings/merge_requests_settings_spec.rb
index ac76c30cc7c..015db603d33 100644
--- a/spec/features/projects/settings/merge_requests_settings_spec.rb
+++ b/spec/features/projects/settings/merge_requests_settings_spec.rb
@@ -5,7 +5,7 @@ feature 'Project settings > Merge Requests', :js do
let(:user) { create(:user) }
background do
- project.team << [user, :master]
+ project.add_master(user)
sign_in(user)
end
diff --git a/spec/features/projects/settings/pipelines_settings_spec.rb b/spec/features/projects/settings/pipelines_settings_spec.rb
index 561f08cba00..d0720855564 100644
--- a/spec/features/projects/settings/pipelines_settings_spec.rb
+++ b/spec/features/projects/settings/pipelines_settings_spec.rb
@@ -7,7 +7,7 @@ feature "Pipelines settings" do
background do
sign_in(user)
- project.team << [user, role]
+ project.add_role(user, role)
end
context 'for developer' do
diff --git a/spec/features/projects/settings/repository_settings_spec.rb b/spec/features/projects/settings/repository_settings_spec.rb
index e2a5619c22b..81b282502fc 100644
--- a/spec/features/projects/settings/repository_settings_spec.rb
+++ b/spec/features/projects/settings/repository_settings_spec.rb
@@ -6,7 +6,7 @@ feature 'Repository settings' do
let(:role) { :developer }
background do
- project.team << [user, role]
+ project.add_role(user, role)
sign_in(user)
end
@@ -66,7 +66,7 @@ feature 'Repository settings' do
scenario 'edit a deploy key from projects user has access to' do
project2 = create(:project_empty_repo)
- project2.team << [user, role]
+ project2.add_role(user, role)
project2.deploy_keys << private_deploy_key
visit project_settings_repository_path(project)
diff --git a/spec/features/projects/settings/visibility_settings_spec.rb b/spec/features/projects/settings/visibility_settings_spec.rb
index 1c3b84d0114..06f6702670b 100644
--- a/spec/features/projects/settings/visibility_settings_spec.rb
+++ b/spec/features/projects/settings/visibility_settings_spec.rb
@@ -31,7 +31,7 @@ feature 'Visibility settings', :js do
let(:master_user) { create(:user) }
before do
- project.team << [master_user, :master]
+ project.add_master(master_user)
sign_in(master_user)
visit edit_project_path(project)
end
diff --git a/spec/features/projects/snippets/create_snippet_spec.rb b/spec/features/projects/snippets/create_snippet_spec.rb
index e4215291f99..3466a3dfb77 100644
--- a/spec/features/projects/snippets/create_snippet_spec.rb
+++ b/spec/features/projects/snippets/create_snippet_spec.rb
@@ -16,7 +16,7 @@ feature 'Create Snippet', :js do
context 'when a user is authenticated' do
before do
- project.team << [user, :master]
+ project.add_master(user)
sign_in(user)
visit project_snippets_path(project)
diff --git a/spec/features/projects/snippets/show_spec.rb b/spec/features/projects/snippets/show_spec.rb
index 08dc7cf6c5b..216f2af7c88 100644
--- a/spec/features/projects/snippets/show_spec.rb
+++ b/spec/features/projects/snippets/show_spec.rb
@@ -6,7 +6,7 @@ feature 'Project snippet', :js do
let(:snippet) { create(:project_snippet, project: project, file_name: file_name, content: content) }
before do
- project.team << [user, :master]
+ project.add_master(user)
sign_in(user)
end
diff --git a/spec/features/projects/tags/download_buttons_spec.rb b/spec/features/projects/tags/download_buttons_spec.rb
index d38a5b1324b..b62498194c4 100644
--- a/spec/features/projects/tags/download_buttons_spec.rb
+++ b/spec/features/projects/tags/download_buttons_spec.rb
@@ -24,7 +24,7 @@ feature 'Download buttons in tags page' do
background do
sign_in(user)
- project.team << [user, role]
+ project.add_role(user, role)
end
describe 'when checking tags' do
diff --git a/spec/features/projects/tree/create_directory_spec.rb b/spec/features/projects/tree/create_directory_spec.rb
index 156293289dd..3f6d16c8acf 100644
--- a/spec/features/projects/tree/create_directory_spec.rb
+++ b/spec/features/projects/tree/create_directory_spec.rb
@@ -13,6 +13,14 @@ feature 'Multi-file editor new directory', :js do
visit project_tree_path(project, :master)
wait_for_requests
+
+ click_link('Multi Edit')
+
+ wait_for_requests
+ end
+
+ after do
+ set_cookie('new_repo', 'false')
end
it 'creates directory in current directory' do
@@ -20,18 +28,30 @@ feature 'Multi-file editor new directory', :js do
click_link('New directory')
- page.within('.popup-dialog') do
- find('.form-control').set('foldername')
+ page.within('.modal') do
+ find('.form-control').set('folder name')
click_button('Create directory')
end
+ find('.add-to-tree').click
+
+ click_link('New file')
+
+ page.within('.modal-dialog') do
+ find('.form-control').set('file name')
+
+ click_button('Create file')
+ end
+
+ wait_for_requests
+
find('.multi-file-commit-panel-collapse-btn').click
- fill_in('commit-message', with: 'commit message')
+ fill_in('commit-message', with: 'commit message ide')
click_button('Commit')
- expect(page).to have_selector('td', text: 'commit message')
+ expect(page).to have_content('folder name')
end
end
diff --git a/spec/features/projects/tree/create_file_spec.rb b/spec/features/projects/tree/create_file_spec.rb
index 8fb8476e631..ba71eef07f4 100644
--- a/spec/features/projects/tree/create_file_spec.rb
+++ b/spec/features/projects/tree/create_file_spec.rb
@@ -13,6 +13,14 @@ feature 'Multi-file editor new file', :js do
visit project_tree_path(project, :master)
wait_for_requests
+
+ click_link('Multi Edit')
+
+ wait_for_requests
+ end
+
+ after do
+ set_cookie('new_repo', 'false')
end
it 'creates file in current directory' do
@@ -20,18 +28,20 @@ feature 'Multi-file editor new file', :js do
click_link('New file')
- page.within('.popup-dialog') do
- find('.form-control').set('filename')
+ page.within('.modal') do
+ find('.form-control').set('file name')
click_button('Create file')
end
+ wait_for_requests
+
find('.multi-file-commit-panel-collapse-btn').click
- fill_in('commit-message', with: 'commit message')
+ fill_in('commit-message', with: 'commit message ide')
click_button('Commit')
- expect(page).to have_selector('td', text: 'commit message')
+ expect(page).to have_content('file name')
end
end
diff --git a/spec/features/projects/tree/rss_spec.rb b/spec/features/projects/tree/rss_spec.rb
index 4f2e0a76a65..6407370ac0d 100644
--- a/spec/features/projects/tree/rss_spec.rb
+++ b/spec/features/projects/tree/rss_spec.rb
@@ -7,7 +7,7 @@ feature 'Project Tree RSS' do
context 'when signed in' do
before do
- project.team << [user, :developer]
+ project.add_developer(user)
sign_in(user)
visit path
end
diff --git a/spec/features/projects/tree/tree_show_spec.rb b/spec/features/projects/tree/tree_show_spec.rb
new file mode 100644
index 00000000000..c8a17871508
--- /dev/null
+++ b/spec/features/projects/tree/tree_show_spec.rb
@@ -0,0 +1,28 @@
+require 'spec_helper'
+
+feature 'Projects tree' do
+ let(:user) { create(:user) }
+ let(:project) { create(:project, :repository) }
+
+ before do
+ project.add_master(user)
+ sign_in(user)
+
+ visit project_tree_path(project, 'master')
+ end
+
+ it 'renders tree table' do
+ expect(page).to have_selector('.tree-item')
+ expect(page).not_to have_selector('.label-lfs', text: 'LFS')
+ end
+
+ context 'LFS' do
+ before do
+ visit project_tree_path(project, File.join('master', 'files/lfs'))
+ end
+
+ it 'renders LFS badge on blob item' do
+ expect(page).to have_selector('.label-lfs', text: 'LFS')
+ end
+ end
+end
diff --git a/spec/features/projects/tree/upload_file_spec.rb b/spec/features/projects/tree/upload_file_spec.rb
index d4e57d1ecfa..9fbb1dbd0e8 100644
--- a/spec/features/projects/tree/upload_file_spec.rb
+++ b/spec/features/projects/tree/upload_file_spec.rb
@@ -15,6 +15,14 @@ feature 'Multi-file editor upload file', :js do
visit project_tree_path(project, :master)
wait_for_requests
+
+ click_link('Multi Edit')
+
+ wait_for_requests
+ end
+
+ after do
+ set_cookie('new_repo', 'false')
end
it 'uploads text file' do
@@ -41,6 +49,5 @@ feature 'Multi-file editor upload file', :js do
expect(page).to have_selector('.multi-file-tab', text: 'dk.png')
expect(page).not_to have_selector('.monaco-editor')
- expect(page).to have_content('The source could not be displayed for this temporary file.')
end
end
diff --git a/spec/features/projects/user_browses_files_spec.rb b/spec/features/projects/user_browses_files_spec.rb
index f5e4d7f5130..62e6419cc42 100644
--- a/spec/features/projects/user_browses_files_spec.rb
+++ b/spec/features/projects/user_browses_files_spec.rb
@@ -15,7 +15,7 @@ describe 'User browses files' do
let(:user) { create(:user) }
before do
- project.team << [user, :master]
+ project.add_master(user)
sign_in(user)
end
diff --git a/spec/features/projects/user_creates_directory_spec.rb b/spec/features/projects/user_creates_directory_spec.rb
index 052cb3188c5..00e48f6fabd 100644
--- a/spec/features/projects/user_creates_directory_spec.rb
+++ b/spec/features/projects/user_creates_directory_spec.rb
@@ -11,7 +11,7 @@ feature 'User creates a directory', :js do
let(:user) { create(:user) }
before do
- project.team << [user, :developer]
+ project.add_developer(user)
sign_in(user)
visit project_tree_path(project, 'master')
end
@@ -63,7 +63,7 @@ feature 'User creates a directory', :js do
context 'when an user does not have write access' do
before do
- project2.team << [user, :reporter]
+ project2.add_reporter(user)
visit(project2_tree_path_root_ref)
end
diff --git a/spec/features/projects/user_creates_files_spec.rb b/spec/features/projects/user_creates_files_spec.rb
index d84b91ddc32..7a935dd2477 100644
--- a/spec/features/projects/user_creates_files_spec.rb
+++ b/spec/features/projects/user_creates_files_spec.rb
@@ -12,7 +12,7 @@ describe 'User creates files' do
let(:user) { create(:user) }
before do
- project.team << [user, :master]
+ project.add_master(user)
sign_in(user)
end
@@ -33,7 +33,7 @@ describe 'User creates files' do
context 'when an user does not have write access' do
before do
- project2.team << [user, :reporter]
+ project2.add_reporter(user)
visit(project2_tree_path_root_ref)
end
@@ -131,7 +131,7 @@ describe 'User creates files' do
context 'when an user does not have write access' do
before do
- project2.team << [user, :reporter]
+ project2.add_reporter(user)
visit(project2_tree_path_root_ref)
end
diff --git a/spec/features/projects/user_deletes_files_spec.rb b/spec/features/projects/user_deletes_files_spec.rb
index 9e4e92ec076..9d55197e719 100644
--- a/spec/features/projects/user_deletes_files_spec.rb
+++ b/spec/features/projects/user_deletes_files_spec.rb
@@ -17,7 +17,7 @@ describe 'User deletes files' do
context 'when an user has write access' do
before do
- project.team << [user, :master]
+ project.add_master(user)
visit(project_tree_path_root_ref)
end
@@ -37,7 +37,7 @@ describe 'User deletes files' do
context 'when an user does not have write access' do
before do
- project2.team << [user, :reporter]
+ project2.add_reporter(user)
visit(project2_tree_path_root_ref)
end
diff --git a/spec/features/projects/user_edits_files_spec.rb b/spec/features/projects/user_edits_files_spec.rb
index d26ee653415..05c2be473da 100644
--- a/spec/features/projects/user_edits_files_spec.rb
+++ b/spec/features/projects/user_edits_files_spec.rb
@@ -14,7 +14,7 @@ describe 'User edits files' do
context 'when an user has write access' do
before do
- project.team << [user, :master]
+ project.add_master(user)
visit(project_tree_path_root_ref)
end
@@ -33,7 +33,9 @@ describe 'User edits files' do
binary_file = File.join(project.repository.root_ref, 'files/images/logo-black.png')
visit(project_blob_path(project, binary_file))
- expect(page).not_to have_link('edit')
+ page.within '.content' do
+ expect(page).not_to have_link('edit')
+ end
end
it 'commits an edited file', :js do
@@ -87,7 +89,7 @@ describe 'User edits files' do
context 'when an user does not have write access' do
before do
- project2.team << [user, :reporter]
+ project2.add_reporter(user)
visit(project2_tree_path_root_ref)
end
diff --git a/spec/features/projects/user_replaces_files_spec.rb b/spec/features/projects/user_replaces_files_spec.rb
index 245b6aa285b..74872403b35 100644
--- a/spec/features/projects/user_replaces_files_spec.rb
+++ b/spec/features/projects/user_replaces_files_spec.rb
@@ -19,7 +19,7 @@ describe 'User replaces files' do
context 'when an user has write access' do
before do
- project.team << [user, :master]
+ project.add_master(user)
visit(project_tree_path_root_ref)
end
@@ -45,7 +45,7 @@ describe 'User replaces files' do
context 'when an user does not have write access' do
before do
- project2.team << [user, :reporter]
+ project2.add_reporter(user)
visit(project2_tree_path_root_ref)
end
diff --git a/spec/features/projects/user_uploads_files_spec.rb b/spec/features/projects/user_uploads_files_spec.rb
index ae51901adc6..75898afcda9 100644
--- a/spec/features/projects/user_uploads_files_spec.rb
+++ b/spec/features/projects/user_uploads_files_spec.rb
@@ -14,7 +14,7 @@ describe 'User uploads files' do
let(:user) { create(:user) }
before do
- project.team << [user, :master]
+ project.add_master(user)
sign_in(user)
end
@@ -50,7 +50,7 @@ describe 'User uploads files' do
context 'when an user does not have write access' do
before do
- project2.team << [user, :reporter]
+ project2.add_reporter(user)
visit(project2_tree_path_root_ref)
end
diff --git a/spec/features/projects/wiki/markdown_preview_spec.rb b/spec/features/projects/wiki/markdown_preview_spec.rb
index 337baaf4dcd..006c15d60c5 100644
--- a/spec/features/projects/wiki/markdown_preview_spec.rb
+++ b/spec/features/projects/wiki/markdown_preview_spec.rb
@@ -13,7 +13,7 @@ feature 'Projects > Wiki > User previews markdown changes', :js do
end
background do
- project.team << [user, :master]
+ project.add_master(user)
sign_in(user)
diff --git a/spec/features/projects/wiki/user_views_wiki_in_project_page_spec.rb b/spec/features/projects/wiki/user_views_wiki_in_project_page_spec.rb
index ebb3bd044c1..2682b62fa04 100644
--- a/spec/features/projects/wiki/user_views_wiki_in_project_page_spec.rb
+++ b/spec/features/projects/wiki/user_views_wiki_in_project_page_spec.rb
@@ -4,7 +4,7 @@ describe 'Projects > Wiki > User views wiki in project page' do
let(:user) { create(:user) }
before do
- project.team << [user, :master]
+ project.add_master(user)
sign_in(user)
end
diff --git a/spec/features/projects_spec.rb b/spec/features/projects_spec.rb
index 63e6051b571..b66a7dea598 100644
--- a/spec/features/projects_spec.rb
+++ b/spec/features/projects_spec.rb
@@ -133,7 +133,7 @@ feature 'Project' do
before do
sign_in(user)
- project.team << [user, :master]
+ project.add_master(user)
visit edit_project_path(project)
end
@@ -151,7 +151,7 @@ feature 'Project' do
let(:project) { create(:forked_project_with_submodules) }
before do
- project.team << [user, :master]
+ project.add_master(user)
sign_in user
visit project_path(project)
end
@@ -180,7 +180,7 @@ feature 'Project' do
let(:project) { create(:project, :repository) }
before do
- project.team << [user, :master]
+ project.add_master(user)
sign_in user
visit project_path(project)
end
diff --git a/spec/features/runners_spec.rb b/spec/features/runners_spec.rb
index c7f0e342809..aec9de6c7ca 100644
--- a/spec/features/runners_spec.rb
+++ b/spec/features/runners_spec.rb
@@ -33,6 +33,26 @@ feature 'Runners' do
expect(page).to have_content(specific_runner.platform)
end
+ scenario 'user can pause and resume the specific runner' do
+ visit runners_path(project)
+
+ within '.activated-specific-runners' do
+ expect(page).to have_content('Pause')
+ end
+
+ click_on 'Pause'
+
+ within '.activated-specific-runners' do
+ expect(page).to have_content('Resume')
+ end
+
+ click_on 'Resume'
+
+ within '.activated-specific-runners' do
+ expect(page).to have_content('Pause')
+ end
+ end
+
scenario 'user removes an activated specific runner if this is last project for that runners' do
visit runners_path(project)
diff --git a/spec/features/signed_commits_spec.rb b/spec/features/signed_commits_spec.rb
index 8efa5b58141..6088b831c14 100644
--- a/spec/features/signed_commits_spec.rb
+++ b/spec/features/signed_commits_spec.rb
@@ -5,7 +5,7 @@ describe 'GPG signed commits', :js do
it 'changes from unverified to verified when the user changes his email to match the gpg key' do
user = create :user, email: 'unrelated.user@example.org'
- project.team << [user, :master]
+ project.add_master(user)
Sidekiq::Testing.inline! do
create :gpg_key, key: GpgHelpers::User1.public_key, user: user
@@ -36,7 +36,7 @@ describe 'GPG signed commits', :js do
it 'changes from unverified to verified when the user adds the missing gpg key' do
user = create :user, email: GpgHelpers::User1.emails.first
- project.team << [user, :master]
+ project.add_master(user)
sign_in(user)
@@ -86,7 +86,7 @@ describe 'GPG signed commits', :js do
before do
user = create :user
- project.team << [user, :master]
+ project.add_master(user)
sign_in(user)
end
diff --git a/spec/features/tags/master_creates_tag_spec.rb b/spec/features/tags/master_creates_tag_spec.rb
index 1f8bd8d681e..8a8f6933fa5 100644
--- a/spec/features/tags/master_creates_tag_spec.rb
+++ b/spec/features/tags/master_creates_tag_spec.rb
@@ -5,7 +5,7 @@ feature 'Master creates tag' do
let(:project) { create(:project, :repository, namespace: user.namespace) }
before do
- project.team << [user, :master]
+ project.add_master(user)
sign_in(user)
end
diff --git a/spec/features/tags/master_deletes_tag_spec.rb b/spec/features/tags/master_deletes_tag_spec.rb
index dfda664d673..c0b4fa52526 100644
--- a/spec/features/tags/master_deletes_tag_spec.rb
+++ b/spec/features/tags/master_deletes_tag_spec.rb
@@ -5,7 +5,7 @@ feature 'Master deletes tag' do
let(:project) { create(:project, :repository, namespace: user.namespace) }
before do
- project.team << [user, :master]
+ project.add_master(user)
sign_in(user)
visit project_tags_path(project)
end
diff --git a/spec/features/tags/master_updates_tag_spec.rb b/spec/features/tags/master_updates_tag_spec.rb
index b93ad44dfd3..1c370a99b13 100644
--- a/spec/features/tags/master_updates_tag_spec.rb
+++ b/spec/features/tags/master_updates_tag_spec.rb
@@ -5,7 +5,7 @@ feature 'Master updates tag' do
let(:project) { create(:project, :repository, namespace: user.namespace) }
before do
- project.team << [user, :master]
+ project.add_master(user)
sign_in(user)
visit project_tags_path(project)
end
diff --git a/spec/features/tags/master_views_tags_spec.rb b/spec/features/tags/master_views_tags_spec.rb
index 9edc7ced163..4662367d843 100644
--- a/spec/features/tags/master_views_tags_spec.rb
+++ b/spec/features/tags/master_views_tags_spec.rb
@@ -4,18 +4,17 @@ feature 'Master views tags' do
let(:user) { create(:user) }
before do
- project.team << [user, :master]
+ project.add_master(user)
sign_in(user)
end
context 'when project has no tags' do
let(:project) { create(:project_empty_repo) }
+
before do
visit project_path(project)
click_on 'README'
fill_in :commit_message, with: 'Add a README file', visible: true
- # Remove pre-receive hook so we can push without auth
- FileUtils.rm_f(File.join(project.repository.path, 'hooks', 'pre-receive'))
click_button 'Commit changes'
visit project_tags_path(project)
end
diff --git a/spec/features/triggers_spec.rb b/spec/features/triggers_spec.rb
index bc472e74997..19784120108 100644
--- a/spec/features/triggers_spec.rb
+++ b/spec/features/triggers_spec.rb
@@ -10,9 +10,9 @@ feature 'Triggers', :js do
sign_in(user)
@project = create(:project)
- @project.team << [user, :master]
- @project.team << [user2, :master]
- @project.team << [guest_user, :guest]
+ @project.add_master(user)
+ @project.add_master(user2)
+ @project.add_guest(guest_user)
visit project_settings_ci_cd_path(@project)
end
diff --git a/spec/features/u2f_spec.rb b/spec/features/u2f_spec.rb
index c9afef2a8de..50ee1656e10 100644
--- a/spec/features/u2f_spec.rb
+++ b/spec/features/u2f_spec.rb
@@ -264,7 +264,7 @@ feature 'Using U2F (Universal 2nd Factor) Devices for Authentication', :js do
end
it "deletes u2f registrations" do
- visit profile_account_path
+ visit profile_two_factor_auth_path
expect do
accept_confirm { click_on "Disable" }
end.to change { U2fRegistration.count }.by(-1)
diff --git a/spec/features/variables_spec.rb b/spec/features/variables_spec.rb
index c78f7d0d9be..79ca2b4bb4a 100644
--- a/spec/features/variables_spec.rb
+++ b/spec/features/variables_spec.rb
@@ -7,7 +7,7 @@ describe 'Project variables', :js do
before do
sign_in(user)
- project.team << [user, :master]
+ project.add_master(user)
project.variables << variable
visit project_settings_ci_cd_path(project)
@@ -65,14 +65,14 @@ describe 'Project variables', :js do
expect(page).to have_content('******')
end
- click_button('Reveal Values')
+ click_button('Reveal values')
page.within('.variables-table') do
expect(page).to have_content('key')
expect(page).to have_content('key value')
end
- click_button('Hide Values')
+ click_button('Hide values')
page.within('.variables-table') do
expect(page).to have_content('key')
diff --git a/spec/finders/access_requests_finder_spec.rb b/spec/finders/access_requests_finder_spec.rb
index 0789d3a9b44..650f7229647 100644
--- a/spec/finders/access_requests_finder_spec.rb
+++ b/spec/finders/access_requests_finder_spec.rb
@@ -51,7 +51,7 @@ describe AccessRequestsFinder do
context 'when current user can see access requests' do
before do
- project.team << [user, :master]
+ project.add_master(user)
group.add_owner(user)
end
@@ -78,7 +78,7 @@ describe AccessRequestsFinder do
context 'when current user can see access requests' do
before do
- project.team << [user, :master]
+ project.add_master(user)
group.add_owner(user)
end
diff --git a/spec/finders/group_descendants_finder_spec.rb b/spec/finders/group_descendants_finder_spec.rb
index 074914420a1..ae050f36b4a 100644
--- a/spec/finders/group_descendants_finder_spec.rb
+++ b/spec/finders/group_descendants_finder_spec.rb
@@ -73,6 +73,41 @@ describe GroupDescendantsFinder do
expect(finder.execute).to contain_exactly(matching_project)
end
end
+
+ context 'sorting by name' do
+ let!(:project1) { create(:project, namespace: group, name: 'a', path: 'project-a') }
+ let!(:project2) { create(:project, namespace: group, name: 'z', path: 'project-z') }
+ let(:params) do
+ {
+ sort: 'name_asc'
+ }
+ end
+
+ it 'sorts elements by name' do
+ expect(subject.execute).to eq(
+ [
+ project1,
+ project2
+ ]
+ )
+ end
+
+ context 'with nested groups', :nested_groups do
+ let!(:subgroup1) { create(:group, parent: group, name: 'a', path: 'sub-a') }
+ let!(:subgroup2) { create(:group, parent: group, name: 'z', path: 'sub-z') }
+
+ it 'sorts elements by name' do
+ expect(subject.execute).to eq(
+ [
+ subgroup1,
+ subgroup2,
+ project1,
+ project2
+ ]
+ )
+ end
+ end
+ end
end
context 'with nested groups', :nested_groups do
diff --git a/spec/finders/group_projects_finder_spec.rb b/spec/finders/group_projects_finder_spec.rb
index c6d257bc479..27a09d7c6f5 100644
--- a/spec/finders/group_projects_finder_spec.rb
+++ b/spec/finders/group_projects_finder_spec.rb
@@ -45,7 +45,7 @@ describe GroupProjectsFinder do
describe 'without group member current_user' do
before do
- shared_project_2.team << [current_user, Gitlab::Access::MASTER]
+ shared_project_2.add_master(current_user)
current_user.reload
end
@@ -70,7 +70,7 @@ describe GroupProjectsFinder do
context "without external user" do
before do
- private_project.team << [current_user, Gitlab::Access::MASTER]
+ private_project.add_master(current_user)
end
it { is_expected.to match_array([private_project, public_project]) }
diff --git a/spec/finders/issues_finder_spec.rb b/spec/finders/issues_finder_spec.rb
index 47b173dea0a..47fd98234f9 100644
--- a/spec/finders/issues_finder_spec.rb
+++ b/spec/finders/issues_finder_spec.rb
@@ -22,9 +22,9 @@ describe IssuesFinder do
let(:issues) { described_class.new(search_user, params.reverse_merge(scope: scope, state: 'opened')).execute }
before(:context) do
- project1.team << [user, :master]
- project2.team << [user, :developer]
- project2.team << [user2, :developer]
+ project1.add_master(user)
+ project2.add_developer(user)
+ project2.add_developer(user2)
issue1
issue2
diff --git a/spec/finders/labels_finder_spec.rb b/spec/finders/labels_finder_spec.rb
index afa2a40ed2a..06031aee217 100644
--- a/spec/finders/labels_finder_spec.rb
+++ b/spec/finders/labels_finder_spec.rb
@@ -27,7 +27,7 @@ describe LabelsFinder do
create(:label, project: project_3, title: 'Label 3')
create(:group_label, group: group_3, title: 'Group Label 4')
- project_1.team << [user, :developer]
+ project_1.add_developer(user)
end
context 'with no filter' do
@@ -56,6 +56,16 @@ describe LabelsFinder do
expect(finder.execute).to eq [group_label_2, group_label_1, project_label_5]
end
+
+ context 'when only_group_labels is true' do
+ it 'returns only group labels' do
+ group_1.add_developer(user)
+
+ finder = described_class.new(user, group_id: group_1.id, only_group_labels: true)
+
+ expect(finder.execute).to eq [group_label_2, group_label_1]
+ end
+ end
end
context 'filtering by project_id' do
@@ -73,7 +83,7 @@ describe LabelsFinder do
# project_3 has a label associated to it, which we don't want coming
# back when we ask for the isolated project's labels
- project_3.team << [admin, :reporter]
+ project_3.add_reporter(admin)
finder = described_class.new(admin, project_id: isolated_project.id)
expect(finder.execute).to be_empty
diff --git a/spec/finders/merge_requests_finder_spec.rb b/spec/finders/merge_requests_finder_spec.rb
index 883bdf3746a..687ffaec7cc 100644
--- a/spec/finders/merge_requests_finder_spec.rb
+++ b/spec/finders/merge_requests_finder_spec.rb
@@ -20,10 +20,10 @@ describe MergeRequestsFinder do
let!(:merge_request4) { create(:merge_request, :simple, author: user, source_project: project3, target_project: project3) }
before do
- project1.team << [user, :master]
- project2.team << [user, :developer]
- project3.team << [user, :developer]
- project2.team << [user2, :developer]
+ project1.add_master(user)
+ project2.add_developer(user)
+ project3.add_developer(user)
+ project2.add_developer(user2)
end
describe "#execute" do
diff --git a/spec/finders/move_to_project_finder_spec.rb b/spec/finders/move_to_project_finder_spec.rb
index e577083a2d0..74639d4147f 100644
--- a/spec/finders/move_to_project_finder_spec.rb
+++ b/spec/finders/move_to_project_finder_spec.rb
@@ -15,39 +15,39 @@ describe MoveToProjectFinder do
describe '#execute' do
context 'filter' do
it 'does not return projects under Gitlab::Access::REPORTER' do
- guest_project.team << [user, :guest]
+ guest_project.add_guest(user)
expect(subject.execute(project)).to be_empty
end
it 'returns projects equal or above Gitlab::Access::REPORTER ordered by id in descending order' do
- reporter_project.team << [user, :reporter]
- developer_project.team << [user, :developer]
- master_project.team << [user, :master]
+ reporter_project.add_reporter(user)
+ developer_project.add_developer(user)
+ master_project.add_master(user)
expect(subject.execute(project).to_a).to eq([master_project, developer_project, reporter_project])
end
it 'does not include the source project' do
- project.team << [user, :reporter]
+ project.add_reporter(user)
expect(subject.execute(project).to_a).to be_empty
end
it 'does not return archived projects' do
- reporter_project.team << [user, :reporter]
+ reporter_project.add_reporter(user)
reporter_project.archive!
other_reporter_project = create(:project)
- other_reporter_project.team << [user, :reporter]
+ other_reporter_project.add_reporter(user)
expect(subject.execute(project).to_a).to eq([other_reporter_project])
end
it 'does not return projects for which issues are disabled' do
- reporter_project.team << [user, :reporter]
+ reporter_project.add_reporter(user)
reporter_project.update_attributes(issues_enabled: false)
other_reporter_project = create(:project)
- other_reporter_project.team << [user, :reporter]
+ other_reporter_project.add_reporter(user)
expect(subject.execute(project).to_a).to eq([other_reporter_project])
end
@@ -55,9 +55,9 @@ describe MoveToProjectFinder do
it 'returns a page of projects ordered by id in descending order' do
stub_const 'MoveToProjectFinder::PAGE_SIZE', 2
- reporter_project.team << [user, :reporter]
- developer_project.team << [user, :developer]
- master_project.team << [user, :master]
+ reporter_project.add_reporter(user)
+ developer_project.add_developer(user)
+ master_project.add_master(user)
expect(subject.execute(project).to_a).to eq([master_project, developer_project])
end
@@ -65,9 +65,9 @@ describe MoveToProjectFinder do
it 'returns projects after the given offset id' do
stub_const 'MoveToProjectFinder::PAGE_SIZE', 2
- reporter_project.team << [user, :reporter]
- developer_project.team << [user, :developer]
- master_project.team << [user, :master]
+ reporter_project.add_reporter(user)
+ developer_project.add_developer(user)
+ master_project.add_master(user)
expect(subject.execute(project, search: nil, offset_id: master_project.id).to_a).to eq([developer_project, reporter_project])
expect(subject.execute(project, search: nil, offset_id: developer_project.id).to_a).to eq([reporter_project])
@@ -84,10 +84,10 @@ describe MoveToProjectFinder do
it 'returns projects matching a search query' do
foo_project = create(:project)
- foo_project.team << [user, :master]
+ foo_project.add_master(user)
wadus_project = create(:project, name: 'wadus')
- wadus_project.team << [user, :master]
+ wadus_project.add_master(user)
expect(subject.execute(project).to_a).to eq([wadus_project, foo_project])
expect(subject.execute(project, search: 'wadus').to_a).to eq([wadus_project])
diff --git a/spec/finders/notes_finder_spec.rb b/spec/finders/notes_finder_spec.rb
index 900fa2b12d1..7b43494eea2 100644
--- a/spec/finders/notes_finder_spec.rb
+++ b/spec/finders/notes_finder_spec.rb
@@ -5,7 +5,7 @@ describe NotesFinder do
let(:project) { create(:project) }
before do
- project.team << [user, :master]
+ project.add_master(user)
end
describe '#execute' do
@@ -147,7 +147,7 @@ describe NotesFinder do
it 'raises an error for project members with guest role' do
user = create(:user)
- project.team << [user, :guest]
+ project.add_guest(user)
expect { described_class.new(project, user, params).execute }.to raise_error(ActiveRecord::RecordNotFound)
end
@@ -189,7 +189,7 @@ describe NotesFinder do
it "does not return notes with matching content for project members with guest role" do
user = create(:user)
- project.team << [user, :guest]
+ project.add_guest(user)
expect(described_class.new(confidential_note.project, user, search: confidential_note.note).execute).to be_empty
end
diff --git a/spec/finders/personal_projects_finder_spec.rb b/spec/finders/personal_projects_finder_spec.rb
index d0113ba87df..5e52898e9c0 100644
--- a/spec/finders/personal_projects_finder_spec.rb
+++ b/spec/finders/personal_projects_finder_spec.rb
@@ -15,7 +15,7 @@ describe PersonalProjectsFinder do
end
before do
- private_project.team << [current_user, Gitlab::Access::DEVELOPER]
+ private_project.add_developer(current_user)
end
describe 'without a current user' do
diff --git a/spec/finders/snippets_finder_spec.rb b/spec/finders/snippets_finder_spec.rb
index 7ae7b7d2140..0a018d2b417 100644
--- a/spec/finders/snippets_finder_spec.rb
+++ b/spec/finders/snippets_finder_spec.rb
@@ -188,7 +188,7 @@ describe SnippetsFinder do
end
it "returns all snippets for project members" do
- project1.team << [user, :developer]
+ project1.add_developer(user)
snippets = described_class.new(user, project: project1).execute
@@ -196,7 +196,7 @@ describe SnippetsFinder do
end
it "returns private snippets for project members" do
- project1.team << [user, :developer]
+ project1.add_developer(user)
snippets = described_class.new(user, project: project1, visibility: Snippet::PRIVATE).execute
diff --git a/spec/finders/todos_finder_spec.rb b/spec/finders/todos_finder_spec.rb
index 884ce22091e..90eb0fe21e4 100644
--- a/spec/finders/todos_finder_spec.rb
+++ b/spec/finders/todos_finder_spec.rb
@@ -7,7 +7,7 @@ describe TodosFinder do
let(:finder) { described_class }
before do
- project.team << [user, :developer]
+ project.add_developer(user)
end
describe '#sort' do
diff --git a/spec/fixtures/api/schemas/contributor.json b/spec/fixtures/api/schemas/contributor.json
new file mode 100644
index 00000000000..e88470a2363
--- /dev/null
+++ b/spec/fixtures/api/schemas/contributor.json
@@ -0,0 +1,18 @@
+{
+ "type": "object",
+ "required" : [
+ "name",
+ "email",
+ "commits",
+ "additions",
+ "deletions"
+ ],
+ "properties" : {
+ "name": { "type": "string" },
+ "email": { "type": "string" },
+ "commits": { "type": "integer" },
+ "additions": { "type": "integer" },
+ "deletions": { "type": "integer" }
+ },
+ "additionalProperties": false
+}
diff --git a/spec/fixtures/api/schemas/contributors.json b/spec/fixtures/api/schemas/contributors.json
new file mode 100644
index 00000000000..a9f1d1ea64f
--- /dev/null
+++ b/spec/fixtures/api/schemas/contributors.json
@@ -0,0 +1,4 @@
+{
+ "type": "array",
+ "items": { "$ref": "contributor.json" }
+}
diff --git a/spec/fixtures/api/schemas/entities/merge_request_basic.json b/spec/fixtures/api/schemas/entities/merge_request_basic.json
index 995f13381ad..f1199468d53 100644
--- a/spec/fixtures/api/schemas/entities/merge_request_basic.json
+++ b/spec/fixtures/api/schemas/entities/merge_request_basic.json
@@ -9,6 +9,7 @@
"human_time_estimate": { "type": ["string", "null"] },
"human_total_time_spent": { "type": ["string", "null"] },
"merge_error": { "type": ["string", "null"] },
+ "rebase_in_progress": { "type": "boolean" },
"assignee_id": { "type": ["integer", "null"] },
"subscribed": { "type": ["boolean", "null"] },
"participants": { "type": "array" }
diff --git a/spec/fixtures/api/schemas/entities/merge_request_metrics.json b/spec/fixtures/api/schemas/entities/merge_request_metrics.json
new file mode 100644
index 00000000000..3fa767f85df
--- /dev/null
+++ b/spec/fixtures/api/schemas/entities/merge_request_metrics.json
@@ -0,0 +1,21 @@
+{
+ "type": "object",
+ "required": ["closed_at", "merged_at", "closed_by", "merged_by"],
+ "properties" : {
+ "closed_at": { "type": ["datetime", "null"] },
+ "merged_at": { "type": ["datetime", "null"] },
+ "closed_by": {
+ "oneOf": [
+ { "type": "null" },
+ { "$ref": "user.json" }
+ ]
+ },
+ "merged_by": {
+ "oneOf": [
+ { "type": "null" },
+ { "$ref": "user.json" }
+ ]
+ }
+ },
+ "additionalProperties": false
+}
diff --git a/spec/fixtures/api/schemas/entities/merge_request.json b/spec/fixtures/api/schemas/entities/merge_request_widget.json
index ba094ba1657..7f662098216 100644
--- a/spec/fixtures/api/schemas/entities/merge_request.json
+++ b/spec/fixtures/api/schemas/entities/merge_request_widget.json
@@ -31,8 +31,12 @@
"source_project_id": { "type": "integer" },
"target_branch": { "type": "string" },
"target_project_id": { "type": "integer" },
- "merge_event": { "type": ["object", "null"] },
- "closed_event": { "type": ["object", "null"] },
+ "metrics": {
+ "oneOf": [
+ { "type": "null" },
+ { "$ref": "merge_request_metrics.json" }
+ ]
+ },
"author": { "type": ["object", "null"] },
"merge_user": { "type": ["object", "null"] },
"diff_head_sha": { "type": ["string", "null"] },
@@ -80,15 +84,15 @@
"target_branch_tree_path": { "type": "string" },
"source_branch_path": { "type": "string" },
"conflict_resolution_path": { "type": ["string", "null"] },
- "cancel_merge_when_pipeline_succeeds_path": { "type": "string" },
- "create_issue_to_resolve_discussions_path": { "type": "string" },
- "merge_path": { "type": "string" },
+ "cancel_merge_when_pipeline_succeeds_path": { "type": ["string", "null"] },
+ "create_issue_to_resolve_discussions_path": { "type": ["string", "null"] },
+ "merge_path": { "type": ["string", "null"] },
"cherry_pick_in_fork_path": { "type": ["string", "null"] },
"revert_in_fork_path": { "type": ["string", "null"] },
"email_patches_path": { "type": "string" },
"plain_diff_path": { "type": "string" },
"status_path": { "type": "string" },
- "new_blob_path": { "type": "string" },
+ "new_blob_path": { "type": ["string", "null"] },
"merge_check_path": { "type": "string" },
"ci_environments_status_path": { "type": "string" },
"merge_commit_message_with_description": { "type": "string" },
@@ -99,7 +103,11 @@
"remove_source_branch": { "type": ["boolean", "null"] },
"merge_ongoing": { "type": "boolean" },
"ff_only_enabled": { "type": ["boolean", false] },
- "should_be_rebased": { "type": "boolean" }
+ "should_be_rebased": { "type": "boolean" },
+ "rebase_commit_sha": { "type": ["string", "null"] },
+ "rebase_in_progress": { "type": "boolean" },
+ "can_push_to_source_branch": { "type": "boolean" },
+ "rebase_path": { "type": ["string", "null"] }
},
"additionalProperties": false
}
diff --git a/spec/fixtures/api/schemas/entities/user.json b/spec/fixtures/api/schemas/entities/user.json
new file mode 100644
index 00000000000..6482e0eedd2
--- /dev/null
+++ b/spec/fixtures/api/schemas/entities/user.json
@@ -0,0 +1,17 @@
+{
+ "type": "object",
+ "required": [
+ "id",
+ "state",
+ "avatar_url",
+ "web_url",
+ "path"
+ ],
+ "properties": {
+ "id": { "type": "integer" },
+ "state": { "type": "string" },
+ "avatar_url": { "type": "string" },
+ "web_url": { "type": "string" },
+ "path": { "type": "string" }
+ }
+}
diff --git a/spec/fixtures/api/schemas/public_api/v4/board.json b/spec/fixtures/api/schemas/public_api/v4/board.json
new file mode 100644
index 00000000000..d667f1d631c
--- /dev/null
+++ b/spec/fixtures/api/schemas/public_api/v4/board.json
@@ -0,0 +1,86 @@
+{
+ "type": "object",
+ "required" : [
+ "id",
+ "project",
+ "lists"
+ ],
+ "properties" : {
+ "id": { "type": "integer" },
+ "project": {
+ "type": ["object", "null"],
+ "required": [
+ "id",
+ "avatar_url",
+ "description",
+ "default_branch",
+ "tag_list",
+ "ssh_url_to_repo",
+ "http_url_to_repo",
+ "web_url",
+ "name",
+ "name_with_namespace",
+ "path",
+ "path_with_namespace",
+ "star_count",
+ "forks_count",
+ "created_at",
+ "last_activity_at"
+ ],
+ "properties": {
+ "id": { "type": "integer" },
+ "avatar_url": { "type": ["string", "null"] },
+ "description": { "type": ["string", "null"] },
+ "default_branch": { "type": ["string", "null"] },
+ "tag_list": { "type": "array" },
+ "ssh_url_to_repo": { "type": "string" },
+ "http_url_to_repo": { "type": "string" },
+ "web_url": { "type": "string" },
+ "name": { "type": "string" },
+ "name_with_namespace": { "type": "string" },
+ "path": { "type": "string" },
+ "path_with_namespace": { "type": "string" },
+ "star_count": { "type": "integer" },
+ "forks_count": { "type": "integer" },
+ "created_at": { "type": "date" },
+ "last_activity_at": { "type": "date" }
+ },
+ "additionalProperties": false
+ },
+ "lists": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "required" : [
+ "id",
+ "label",
+ "position"
+ ],
+ "properties" : {
+ "id": { "type": "integer" },
+ "label": {
+ "type": ["object", "null"],
+ "required": [
+ "id",
+ "color",
+ "description",
+ "name"
+ ],
+ "properties": {
+ "id": { "type": "integer" },
+ "color": {
+ "type": "string",
+ "pattern": "^#[0-9A-Fa-f]{3}{1,2}+$"
+ },
+ "description": { "type": ["string", "null"] },
+ "name": { "type": "string" }
+ }
+ },
+ "position": { "type": ["integer", "null"] }
+ },
+ "additionalProperties": false
+ }
+ }
+ },
+ "additionalProperties": true
+}
diff --git a/spec/fixtures/api/schemas/public_api/v4/boards.json b/spec/fixtures/api/schemas/public_api/v4/boards.json
new file mode 100644
index 00000000000..117564ef77a
--- /dev/null
+++ b/spec/fixtures/api/schemas/public_api/v4/boards.json
@@ -0,0 +1,4 @@
+{
+ "type": "array",
+ "items": { "$ref": "board.json" }
+}
diff --git a/spec/fixtures/api/schemas/public_api/v4/pages_domain/basic.json b/spec/fixtures/api/schemas/public_api/v4/pages_domain/basic.json
index 4ba6422406c..e8c17298b43 100644
--- a/spec/fixtures/api/schemas/public_api/v4/pages_domain/basic.json
+++ b/spec/fixtures/api/schemas/public_api/v4/pages_domain/basic.json
@@ -3,6 +3,7 @@
"properties": {
"domain": { "type": "string" },
"url": { "type": "uri" },
+ "project_id": { "type": "integer" },
"certificate_expiration": {
"type": "object",
"properties": {
@@ -13,6 +14,6 @@
"additionalProperties": false
}
},
- "required": ["domain", "url"],
+ "required": ["domain", "url", "project_id"],
"additionalProperties": false
}
diff --git a/spec/fixtures/api/schemas/public_api/v4/user/basic.json b/spec/fixtures/api/schemas/public_api/v4/user/basic.json
index 9f69d31971c..bf330d8278c 100644
--- a/spec/fixtures/api/schemas/public_api/v4/user/basic.json
+++ b/spec/fixtures/api/schemas/public_api/v4/user/basic.json
@@ -1,5 +1,5 @@
{
- "type": "object",
+ "type": ["object", "null"],
"required": [
"id",
"state",
diff --git a/spec/fixtures/emails/valid_new_merge_request.eml b/spec/fixtures/emails/valid_new_merge_request.eml
index 480675a6d7e..729df674604 100644
--- a/spec/fixtures/emails/valid_new_merge_request.eml
+++ b/spec/fixtures/emails/valid_new_merge_request.eml
@@ -16,3 +16,5 @@ X-Sieve: CMU Sieve 2.2
X-Received: by 10.0.0.1 with SMTP id n7mr11234144ipb.85.1371157428600; Thu,
13 Jun 2013 14:03:48 -0700 (PDT)
X-Scanned-By: MIMEDefang 2.69 on IPv6:2001:470:1d:165::1
+
+Merge request description
diff --git a/spec/fixtures/emails/valid_new_merge_request_no_description.eml b/spec/fixtures/emails/valid_new_merge_request_no_description.eml
new file mode 100644
index 00000000000..480675a6d7e
--- /dev/null
+++ b/spec/fixtures/emails/valid_new_merge_request_no_description.eml
@@ -0,0 +1,18 @@
+Return-Path: <jake@adventuretime.ooo>
+Received: from iceking.adventuretime.ooo ([unix socket]) by iceking (Cyrus v2.2.13-Debian-2.2.13-19+squeeze3) with LMTPA; Thu, 13 Jun 2013 17:03:50 -0400
+Received: from mail-ie0-x234.google.com (mail-ie0-x234.google.com [IPv6:2607:f8b0:4001:c03::234]) by iceking.adventuretime.ooo (8.14.3/8.14.3/Debian-9.4) with ESMTP id r5DL3nFJ016967 (version=TLSv1/SSLv3 cipher=RC4-SHA bits=128 verify=NOT) for <incoming+gitlabhq/gitlabhq@appmail.adventuretime.ooo>; Thu, 13 Jun 2013 17:03:50 -0400
+Received: by mail-ie0-f180.google.com with SMTP id f4so21977375iea.25 for <incoming+gitlabhq/gitlabhq@appmail.adventuretime.ooo>; Thu, 13 Jun 2013 14:03:48 -0700
+Received: by 10.0.0.1 with HTTP; Thu, 13 Jun 2013 14:03:48 -0700
+Date: Thu, 13 Jun 2013 17:03:48 -0400
+From: Jake the Dog <jake@adventuretime.ooo>
+To: incoming+gitlabhq/gitlabhq+merge-request+auth_token@appmail.adventuretime.ooo
+Message-ID: <CADkmRc+rNGAGGbV2iE5p918UVy4UyJqVcXRO2=otppgzduJSg@mail.gmail.com>
+Subject: feature
+Mime-Version: 1.0
+Content-Type: text/plain;
+ charset=ISO-8859-1
+Content-Transfer-Encoding: 7bit
+X-Sieve: CMU Sieve 2.2
+X-Received: by 10.0.0.1 with SMTP id n7mr11234144ipb.85.1371157428600; Thu,
+ 13 Jun 2013 14:03:48 -0700 (PDT)
+X-Scanned-By: MIMEDefang 2.69 on IPv6:2001:470:1d:165::1
diff --git a/spec/fixtures/markdown.md.erb b/spec/fixtures/markdown.md.erb
index 638cd8b07c8..71abb6da607 100644
--- a/spec/fixtures/markdown.md.erb
+++ b/spec/fixtures/markdown.md.erb
@@ -258,12 +258,23 @@ With inline diffs tags you can display {+ additions +} or [- deletions -].
The wrapping tags can be either curly braces or square brackets [+ additions +] or {- deletions -}.
-However the wrapping tags can not be mixed as such -
+Examples:
+```
+- {+ additions +}
+- [+ additions +]
+- {- deletions -}
+- [- deletions -]
+```
+
+However the wrapping tags cannot be mixed as such:
+
+```
- {+ additions +]
- [+ additions +}
-- {- delletions -]
-- [- delletions -}
+- {- deletions -]
+- [- deletions -}
+```
### Videos
diff --git a/spec/helpers/labels_helper_spec.rb b/spec/helpers/labels_helper_spec.rb
index 4ac4302adfd..0286d36952c 100644
--- a/spec/helpers/labels_helper_spec.rb
+++ b/spec/helpers/labels_helper_spec.rb
@@ -1,6 +1,69 @@
require 'spec_helper'
describe LabelsHelper do
+ describe '#show_label_issuables_link?' do
+ shared_examples 'a valid response to show_label_issuables_link?' do |issuables_type, when_enabled = true, when_disabled = false|
+ let(:context_project) { project }
+
+ context "when asking for a #{issuables_type} link" do
+ subject { show_label_issuables_link?(label, issuables_type, project: context_project) }
+
+ context "when #{issuables_type} are enabled for the project" do
+ let(:project) { create(:project, "#{issuables_type}_access_level": ProjectFeature::ENABLED) }
+
+ it { is_expected.to be(when_enabled) }
+ end
+
+ context "when #{issuables_type} are disabled for the project" do
+ let(:project) { create(:project, :public, "#{issuables_type}_access_level": ProjectFeature::DISABLED) }
+
+ it { is_expected.to be(when_disabled) }
+ end
+ end
+ end
+
+ context 'with a project label' do
+ let(:label) { create(:label, project: project, title: 'bug') }
+
+ context 'when asking for an issue link' do
+ it_behaves_like 'a valid response to show_label_issuables_link?', :issues
+ end
+
+ context 'when asking for a merge requests link' do
+ it_behaves_like 'a valid response to show_label_issuables_link?', :merge_requests
+ end
+ end
+
+ context 'with a group label' do
+ set(:group) { create(:group) }
+ let(:label) { create(:group_label, group: group, title: 'bug') }
+
+ context 'when asking for an issue link' do
+ context 'in the context of a project' do
+ it_behaves_like 'a valid response to show_label_issuables_link?', :issues, true, true
+ end
+
+ context 'in the context of a group' do
+ let(:context_project) { nil }
+
+ it_behaves_like 'a valid response to show_label_issuables_link?', :issues, true, true
+ end
+ end
+
+ context 'when asking for a merge requests link' do
+ context 'in the context of a project' do
+ it_behaves_like 'a valid response to show_label_issuables_link?', :merge_requests, true, true
+ end
+
+ context 'in the context of a group' do
+ let(:context_project) { nil }
+
+ it_behaves_like 'a valid response to show_label_issuables_link?', :merge_requests, true, true
+ end
+ end
+ end
+ end
+
describe 'link_to_label' do
let(:project) { create(:project) }
let(:label) { create(:label, project: project) }
diff --git a/spec/helpers/markup_helper_spec.rb b/spec/helpers/markup_helper_spec.rb
index ba0039f3a11..c0dc9293397 100644
--- a/spec/helpers/markup_helper_spec.rb
+++ b/spec/helpers/markup_helper_spec.rb
@@ -11,7 +11,7 @@ describe MarkupHelper do
before do
# Ensure the generated reference links aren't redacted
- project.team << [user, :master]
+ project.add_master(user)
# Helper expects a @project instance variable
helper.instance_variable_set(:@project, project)
diff --git a/spec/helpers/members_helper_spec.rb b/spec/helpers/members_helper_spec.rb
index 33186cf50d5..45ffbeb27a4 100644
--- a/spec/helpers/members_helper_spec.rb
+++ b/spec/helpers/members_helper_spec.rb
@@ -1,14 +1,6 @@
require 'spec_helper'
describe MembersHelper do
- describe '#action_member_permission' do
- let(:project_member) { build(:project_member) }
- let(:group_member) { build(:group_member) }
-
- it { expect(action_member_permission(:admin, project_member)).to eq :admin_project_member }
- it { expect(action_member_permission(:admin, group_member)).to eq :admin_group_member }
- end
-
describe '#remove_member_message' do
let(:requester) { create(:user) }
let(:project) { create(:project, :public, :access_requestable) }
diff --git a/spec/helpers/notes_helper_spec.rb b/spec/helpers/notes_helper_spec.rb
index cd15e27b497..b992bdb4a5e 100644
--- a/spec/helpers/notes_helper_spec.rb
+++ b/spec/helpers/notes_helper_spec.rb
@@ -17,9 +17,9 @@ describe NotesHelper do
before do
group.add_owner(owner)
- project.team << [master, :master]
- project.team << [reporter, :reporter]
- project.team << [guest, :guest]
+ project.add_master(master)
+ project.add_reporter(reporter)
+ project.add_guest(guest)
end
describe "#notes_max_access_for_users" do
@@ -31,7 +31,7 @@ describe NotesHelper do
it 'handles access in different projects' do
second_project = create(:project)
- second_project.team << [master, :reporter]
+ second_project.add_reporter(master)
other_note = create(:note, author: master, project: second_project)
expect(helper.note_max_access_for_user(master_note)).to eq(Gitlab::Access::MASTER)
@@ -41,6 +41,7 @@ describe NotesHelper do
describe '#discussion_path' do
let(:project) { create(:project, :repository) }
+ let(:anchor) { discussion.line_code }
context 'for a merge request discusion' do
let(:merge_request) { create(:merge_request, source_project: project, target_project: project, importing: true) }
@@ -151,6 +152,15 @@ describe NotesHelper do
expect(helper.discussion_path(discussion)).to be_nil
end
end
+
+ context 'for a contextual commit discussion' do
+ let(:commit) { merge_request.commits.last }
+ let(:discussion) { create(:diff_note_on_merge_request, noteable: merge_request, project: project, commit_id: commit.id).to_discussion }
+
+ it 'returns the merge request diff discussion scoped in the commit' do
+ expect(helper.discussion_path(discussion)).to eq(diffs_project_merge_request_path(project, merge_request, commit_id: commit.id, anchor: anchor))
+ end
+ end
end
context 'for a commit discussion' do
@@ -160,7 +170,7 @@ describe NotesHelper do
let(:discussion) { create(:diff_note_on_commit, project: project).to_discussion }
it 'returns the commit path with the line code' do
- expect(helper.discussion_path(discussion)).to eq(project_commit_path(project, commit, anchor: discussion.line_code))
+ expect(helper.discussion_path(discussion)).to eq(project_commit_path(project, commit, anchor: anchor))
end
end
@@ -168,7 +178,7 @@ describe NotesHelper do
let(:discussion) { create(:legacy_diff_note_on_commit, project: project).to_discussion }
it 'returns the commit path with the line code' do
- expect(helper.discussion_path(discussion)).to eq(project_commit_path(project, commit, anchor: discussion.line_code))
+ expect(helper.discussion_path(discussion)).to eq(project_commit_path(project, commit, anchor: anchor))
end
end
diff --git a/spec/helpers/preferences_helper_spec.rb b/spec/helpers/preferences_helper_spec.rb
index 8b8080563d3..749aa25e632 100644
--- a/spec/helpers/preferences_helper_spec.rb
+++ b/spec/helpers/preferences_helper_spec.rb
@@ -77,15 +77,6 @@ describe PreferencesHelper do
end
end
- def stub_user(messages = {})
- if messages.empty?
- allow(helper).to receive(:current_user).and_return(nil)
- else
- allow(helper).to receive(:current_user)
- .and_return(double('user', messages))
- end
- end
-
describe '#default_project_view' do
context 'user not signed in' do
before do
@@ -125,5 +116,70 @@ describe PreferencesHelper do
end
end
end
+
+ context 'user signed in' do
+ let(:user) { create(:user, :readme) }
+ let(:project) { create(:project, :public, :repository) }
+
+ before do
+ helper.instance_variable_set(:@project, project)
+ allow(helper).to receive(:current_user).and_return(user)
+ end
+
+ context 'when the user is allowed to see the code' do
+ it 'returns the project view' do
+ allow(helper).to receive(:can?).with(user, :download_code, project).and_return(true)
+
+ expect(helper.default_project_view).to eq('readme')
+ end
+ end
+
+ context 'with wikis enabled and the right policy for the user' do
+ before do
+ project.project_feature.update_attribute(:issues_access_level, 0)
+ allow(helper).to receive(:can?).with(user, :download_code, project).and_return(false)
+ end
+
+ it 'returns wiki if the user has the right policy' do
+ allow(helper).to receive(:can?).with(user, :read_wiki, project).and_return(true)
+
+ expect(helper.default_project_view).to eq('wiki')
+ end
+
+ it 'returns customize_workflow if the user does not have the right policy' do
+ allow(helper).to receive(:can?).with(user, :read_wiki, project).and_return(false)
+
+ expect(helper.default_project_view).to eq('customize_workflow')
+ end
+ end
+
+ context 'with issues as a feature available' do
+ it 'return issues' do
+ allow(helper).to receive(:can?).with(user, :download_code, project).and_return(false)
+ allow(helper).to receive(:can?).with(user, :read_wiki, project).and_return(false)
+
+ expect(helper.default_project_view).to eq('projects/issues/issues')
+ end
+ end
+
+ context 'with no activity, no wikies and no issues' do
+ it 'returns customize_workflow as default' do
+ project.project_feature.update_attribute(:issues_access_level, 0)
+ allow(helper).to receive(:can?).with(user, :download_code, project).and_return(false)
+ allow(helper).to receive(:can?).with(user, :read_wiki, project).and_return(false)
+
+ expect(helper.default_project_view).to eq('customize_workflow')
+ end
+ end
+ end
+ end
+
+ def stub_user(messages = {})
+ if messages.empty?
+ allow(helper).to receive(:current_user).and_return(nil)
+ else
+ allow(helper).to receive(:current_user)
+ .and_return(double('user', messages))
+ end
end
end
diff --git a/spec/helpers/runners_helper_spec.rb b/spec/helpers/runners_helper_spec.rb
index 35f91b7decf..a4a483e68a8 100644
--- a/spec/helpers/runners_helper_spec.rb
+++ b/spec/helpers/runners_helper_spec.rb
@@ -2,17 +2,17 @@ require 'spec_helper'
describe RunnersHelper do
it "returns - not contacted yet" do
- runner = FactoryGirl.build :ci_runner
+ runner = FactoryBot.build :ci_runner
expect(runner_status_icon(runner)).to include("not connected yet")
end
it "returns offline text" do
- runner = FactoryGirl.build(:ci_runner, contacted_at: 1.day.ago, active: true)
+ runner = FactoryBot.build(:ci_runner, contacted_at: 1.day.ago, active: true)
expect(runner_status_icon(runner)).to include("Runner is offline")
end
it "returns online text" do
- runner = FactoryGirl.build(:ci_runner, contacted_at: 1.second.ago, active: true)
+ runner = FactoryBot.build(:ci_runner, contacted_at: 1.second.ago, active: true)
expect(runner_status_icon(runner)).to include("Runner is online")
end
end
diff --git a/spec/helpers/tree_helper_spec.rb b/spec/helpers/tree_helper_spec.rb
index c358ccae9c3..d3b1be599dd 100644
--- a/spec/helpers/tree_helper_spec.rb
+++ b/spec/helpers/tree_helper_spec.rb
@@ -9,6 +9,7 @@ describe TreeHelper do
before do
@id = sha
@project = project
+ @lfs_blob_ids = []
end
it 'displays all entries without a warning' do
diff --git a/spec/javascripts/activities_spec.js b/spec/javascripts/activities_spec.js
index e8c5f721423..7a9c539e9d0 100644
--- a/spec/javascripts/activities_spec.js
+++ b/spec/javascripts/activities_spec.js
@@ -1,8 +1,7 @@
/* eslint-disable no-unused-expressions, no-prototype-builtins, no-new, no-shadow, max-len */
import 'vendor/jquery.endless-scroll';
-import '~/pager';
-import '~/activities';
+import Activities from '~/activities';
(() => {
window.gon || (window.gon = {});
@@ -35,7 +34,7 @@ import '~/activities';
describe('Activities', () => {
beforeEach(() => {
loadFixtures(fixtureTemplate);
- new gl.Activities();
+ new Activities();
});
for (let i = 0; i < filters.length; i += 1) {
diff --git a/spec/javascripts/behaviors/secret_values_spec.js b/spec/javascripts/behaviors/secret_values_spec.js
new file mode 100644
index 00000000000..9eeae474e7d
--- /dev/null
+++ b/spec/javascripts/behaviors/secret_values_spec.js
@@ -0,0 +1,146 @@
+import SecretValues from '~/behaviors/secret_values';
+
+function generateFixtureMarkup(secrets, isRevealed) {
+ return `
+ <div class="js-secret-container">
+ ${secrets.map(secret => `
+ <div class="js-secret-value-placeholder">
+ ***
+ </div>
+ <div class="hide js-secret-value">
+ ${secret}
+ </div>
+ `).join('')}
+ <button
+ class="js-secret-value-reveal-button"
+ data-secret-reveal-status="${isRevealed}"
+ >
+ ...
+ </button>
+ </div>
+ `;
+}
+
+function setupSecretFixture(secrets, isRevealed) {
+ const wrapper = document.createElement('div');
+ wrapper.innerHTML = generateFixtureMarkup(secrets, isRevealed);
+
+ const secretValues = new SecretValues(wrapper.querySelector('.js-secret-container'));
+ secretValues.init();
+
+ return wrapper;
+}
+
+describe('setupSecretValues', () => {
+ describe('with a single secret', () => {
+ const secrets = ['mysecret123'];
+
+ it('should have correct "Reveal" label when values are hidden', () => {
+ const wrapper = setupSecretFixture(secrets, false);
+ const revealButton = wrapper.querySelector('.js-secret-value-reveal-button');
+
+ expect(revealButton.textContent).toEqual('Reveal value');
+ });
+
+ it('should have correct "Hide" label when values are shown', () => {
+ const wrapper = setupSecretFixture(secrets, true);
+ const revealButton = wrapper.querySelector('.js-secret-value-reveal-button');
+
+ expect(revealButton.textContent).toEqual('Hide value');
+ });
+
+ it('should value hidden initially', () => {
+ const wrapper = setupSecretFixture(secrets, false);
+ const values = wrapper.querySelectorAll('.js-secret-value');
+ const placeholders = wrapper.querySelectorAll('.js-secret-value-placeholder');
+
+ expect(values.length).toEqual(1);
+ expect(values[0].classList.contains('hide')).toEqual(true);
+ expect(placeholders.length).toEqual(1);
+ expect(placeholders[0].classList.contains('hide')).toEqual(false);
+ });
+
+ it('should toggle value and placeholder', () => {
+ const wrapper = setupSecretFixture(secrets, false);
+ const revealButton = wrapper.querySelector('.js-secret-value-reveal-button');
+ const values = wrapper.querySelectorAll('.js-secret-value');
+ const placeholders = wrapper.querySelectorAll('.js-secret-value-placeholder');
+
+ revealButton.click();
+
+ expect(values.length).toEqual(1);
+ expect(values[0].classList.contains('hide')).toEqual(false);
+ expect(placeholders.length).toEqual(1);
+ expect(placeholders[0].classList.contains('hide')).toEqual(true);
+
+ revealButton.click();
+
+ expect(values.length).toEqual(1);
+ expect(values[0].classList.contains('hide')).toEqual(true);
+ expect(placeholders.length).toEqual(1);
+ expect(placeholders[0].classList.contains('hide')).toEqual(false);
+ });
+ });
+
+ describe('with a multiple secrets', () => {
+ const secrets = ['mysecret123', 'happygoat456', 'tanuki789'];
+
+ it('should have correct "Reveal" label when values are hidden', () => {
+ const wrapper = setupSecretFixture(secrets, false);
+ const revealButton = wrapper.querySelector('.js-secret-value-reveal-button');
+
+ expect(revealButton.textContent).toEqual('Reveal values');
+ });
+
+ it('should have correct "Hide" label when values are shown', () => {
+ const wrapper = setupSecretFixture(secrets, true);
+ const revealButton = wrapper.querySelector('.js-secret-value-reveal-button');
+
+ expect(revealButton.textContent).toEqual('Hide values');
+ });
+
+ it('should have all values hidden initially', () => {
+ const wrapper = setupSecretFixture(secrets, false);
+ const values = wrapper.querySelectorAll('.js-secret-value');
+ const placeholders = wrapper.querySelectorAll('.js-secret-value-placeholder');
+
+ expect(values.length).toEqual(3);
+ values.forEach((value) => {
+ expect(value.classList.contains('hide')).toEqual(true);
+ });
+ expect(placeholders.length).toEqual(3);
+ placeholders.forEach((placeholder) => {
+ expect(placeholder.classList.contains('hide')).toEqual(false);
+ });
+ });
+
+ it('should toggle values and placeholders', () => {
+ const wrapper = setupSecretFixture(secrets, false);
+ const revealButton = wrapper.querySelector('.js-secret-value-reveal-button');
+ const values = wrapper.querySelectorAll('.js-secret-value');
+ const placeholders = wrapper.querySelectorAll('.js-secret-value-placeholder');
+
+ revealButton.click();
+
+ expect(values.length).toEqual(3);
+ values.forEach((value) => {
+ expect(value.classList.contains('hide')).toEqual(false);
+ });
+ expect(placeholders.length).toEqual(3);
+ placeholders.forEach((placeholder) => {
+ expect(placeholder.classList.contains('hide')).toEqual(true);
+ });
+
+ revealButton.click();
+
+ expect(values.length).toEqual(3);
+ values.forEach((value) => {
+ expect(value.classList.contains('hide')).toEqual(true);
+ });
+ expect(placeholders.length).toEqual(3);
+ placeholders.forEach((placeholder) => {
+ expect(placeholder.classList.contains('hide')).toEqual(false);
+ });
+ });
+ });
+});
diff --git a/spec/javascripts/blob/notebook/index_spec.js b/spec/javascripts/blob/notebook/index_spec.js
index c3e67550f05..df1b2c9960b 100644
--- a/spec/javascripts/blob/notebook/index_spec.js
+++ b/spec/javascripts/blob/notebook/index_spec.js
@@ -1,4 +1,5 @@
-import Vue from 'vue';
+import MockAdapter from 'axios-mock-adapter';
+import axios from '~/lib/utils/axios_utils';
import renderNotebook from '~/blob/notebook';
describe('iPython notebook renderer', () => {
@@ -17,8 +18,11 @@ describe('iPython notebook renderer', () => {
});
describe('successful response', () => {
- const response = (request, next) => {
- next(request.respondWith(JSON.stringify({
+ let mock;
+
+ beforeEach((done) => {
+ mock = new MockAdapter(axios);
+ mock.onGet('/test').reply(200, {
cells: [{
cell_type: 'markdown',
source: ['# test'],
@@ -31,13 +35,7 @@ describe('iPython notebook renderer', () => {
],
outputs: [],
}],
- }), {
- status: 200,
- }));
- };
-
- beforeEach((done) => {
- Vue.http.interceptors.push(response);
+ });
renderNotebook();
@@ -47,9 +45,7 @@ describe('iPython notebook renderer', () => {
});
afterEach(() => {
- Vue.http.interceptors = _.without(
- Vue.http.interceptors, response,
- );
+ mock.reset();
});
it('does not show loading icon', () => {
@@ -86,14 +82,11 @@ describe('iPython notebook renderer', () => {
});
describe('error in JSON response', () => {
- const response = (request, next) => {
- next(request.respondWith('{ "cells": [{"cell_type": "markdown"} }', {
- status: 200,
- }));
- };
+ let mock;
beforeEach((done) => {
- Vue.http.interceptors.push(response);
+ mock = new MockAdapter(axios);
+ mock.onGet('/test').reply(() => Promise.reject({ status: 200, data: '{ "cells": [{"cell_type": "markdown"} }' }));
renderNotebook();
@@ -103,9 +96,7 @@ describe('iPython notebook renderer', () => {
});
afterEach(() => {
- Vue.http.interceptors = _.without(
- Vue.http.interceptors, response,
- );
+ mock.reset();
});
it('does not show loading icon', () => {
@@ -122,14 +113,11 @@ describe('iPython notebook renderer', () => {
});
describe('error getting file', () => {
- const response = (request, next) => {
- next(request.respondWith('', {
- status: 500,
- }));
- };
+ let mock;
beforeEach((done) => {
- Vue.http.interceptors.push(response);
+ mock = new MockAdapter(axios);
+ mock.onGet('/test').reply(500, '');
renderNotebook();
@@ -139,9 +127,7 @@ describe('iPython notebook renderer', () => {
});
afterEach(() => {
- Vue.http.interceptors = _.without(
- Vue.http.interceptors, response,
- );
+ mock.reset();
});
it('does not show loading icon', () => {
diff --git a/spec/javascripts/boards/board_blank_state_spec.js b/spec/javascripts/boards/board_blank_state_spec.js
index 2ee3792dd65..f757dadfada 100644
--- a/spec/javascripts/boards/board_blank_state_spec.js
+++ b/spec/javascripts/boards/board_blank_state_spec.js
@@ -1,9 +1,8 @@
/* global BoardService */
-/* global mockBoardService */
import Vue from 'vue';
import '~/boards/stores/boards_store';
import boardBlankState from '~/boards/components/board_blank_state';
-import './mock_data';
+import { mockBoardService } from './mock_data';
describe('Boards blank state', () => {
let vm;
@@ -20,17 +19,15 @@ describe('Boards blank state', () => {
reject();
} else {
resolve({
- json() {
- return [{
- id: 1,
- title: 'To Do',
- label: { id: 1 },
- }, {
- id: 2,
- title: 'Doing',
- label: { id: 2 },
- }];
- },
+ data: [{
+ id: 1,
+ title: 'To Do',
+ label: { id: 1 },
+ }, {
+ id: 2,
+ title: 'Doing',
+ label: { id: 2 },
+ }],
});
}
}));
diff --git a/spec/javascripts/boards/board_card_spec.js b/spec/javascripts/boards/board_card_spec.js
index 8f607899b20..4e73fa1fe87 100644
--- a/spec/javascripts/boards/board_card_spec.js
+++ b/spec/javascripts/boards/board_card_spec.js
@@ -1,12 +1,11 @@
/* global List */
/* global ListAssignee */
/* global ListLabel */
-/* global listObj */
-/* global boardsMockInterceptor */
/* global BoardService */
-/* global mockBoardService */
import Vue from 'vue';
+import MockAdapter from 'axios-mock-adapter';
+import axios from '~/lib/utils/axios_utils';
import '~/boards/models/assignee';
import eventHub from '~/boards/eventhub';
@@ -14,13 +13,15 @@ import '~/boards/models/list';
import '~/boards/models/label';
import '~/boards/stores/boards_store';
import boardCard from '~/boards/components/board_card.vue';
-import './mock_data';
+import { listObj, boardsMockInterceptor, mockBoardService } from './mock_data';
describe('Board card', () => {
let vm;
+ let mock;
beforeEach((done) => {
- Vue.http.interceptors.push(boardsMockInterceptor);
+ mock = new MockAdapter(axios);
+ mock.onAny().reply(boardsMockInterceptor);
gl.boardService = mockBoardService();
gl.issueBoards.BoardsStore.create();
@@ -54,7 +55,7 @@ describe('Board card', () => {
});
afterEach(() => {
- Vue.http.interceptors = _.without(Vue.http.interceptors, boardsMockInterceptor);
+ mock.reset();
});
it('returns false when detailIssue is empty', () => {
diff --git a/spec/javascripts/boards/board_list_spec.js b/spec/javascripts/boards/board_list_spec.js
index 6bd00943a8f..7c5888b6d82 100644
--- a/spec/javascripts/boards/board_list_spec.js
+++ b/spec/javascripts/boards/board_list_spec.js
@@ -1,11 +1,9 @@
/* global BoardService */
-/* global boardsMockInterceptor */
/* global List */
-/* global listObj */
/* global ListIssue */
-/* global mockBoardService */
import Vue from 'vue';
-import _ from 'underscore';
+import MockAdapter from 'axios-mock-adapter';
+import axios from '~/lib/utils/axios_utils';
import Sortable from 'vendor/Sortable';
import BoardList from '~/boards/components/board_list';
import eventHub from '~/boards/eventhub';
@@ -13,18 +11,20 @@ import '~/boards/mixins/sortable_default_options';
import '~/boards/models/issue';
import '~/boards/models/list';
import '~/boards/stores/boards_store';
-import './mock_data';
+import { listObj, boardsMockInterceptor, mockBoardService } from './mock_data';
window.Sortable = Sortable;
describe('Board list component', () => {
+ let mock;
let component;
beforeEach((done) => {
const el = document.createElement('div');
document.body.appendChild(el);
- Vue.http.interceptors.push(boardsMockInterceptor);
+ mock = new MockAdapter(axios);
+ mock.onAny().reply(boardsMockInterceptor);
gl.boardService = mockBoardService();
gl.issueBoards.BoardsStore.create();
gl.IssueBoardsApp = new Vue();
@@ -60,7 +60,7 @@ describe('Board list component', () => {
});
afterEach(() => {
- Vue.http.interceptors = _.without(Vue.http.interceptors, boardsMockInterceptor);
+ mock.reset();
});
it('renders component', () => {
diff --git a/spec/javascripts/boards/board_new_issue_spec.js b/spec/javascripts/boards/board_new_issue_spec.js
index 02e6692dda8..c62c537841c 100644
--- a/spec/javascripts/boards/board_new_issue_spec.js
+++ b/spec/javascripts/boards/board_new_issue_spec.js
@@ -1,24 +1,22 @@
-/* global boardsMockInterceptor */
/* global BoardService */
/* global List */
-/* global listObj */
-/* global mockBoardService */
import Vue from 'vue';
+import MockAdapter from 'axios-mock-adapter';
+import axios from '~/lib/utils/axios_utils';
import boardNewIssue from '~/boards/components/board_new_issue';
import '~/boards/models/list';
-import './mock_data';
+import { listObj, boardsMockInterceptor, mockBoardService } from './mock_data';
describe('Issue boards new issue form', () => {
let vm;
let list;
+ let mock;
let newIssueMock;
const promiseReturn = {
- json() {
- return {
- iid: 100,
- };
+ data: {
+ iid: 100,
},
};
@@ -35,7 +33,9 @@ describe('Issue boards new issue form', () => {
const BoardNewIssueComp = Vue.extend(boardNewIssue);
- Vue.http.interceptors.push(boardsMockInterceptor);
+ mock = new MockAdapter(axios);
+ mock.onAny().reply(boardsMockInterceptor);
+
gl.boardService = mockBoardService();
gl.issueBoards.BoardsStore.create();
gl.IssueBoardsApp = new Vue();
@@ -56,7 +56,10 @@ describe('Issue boards new issue form', () => {
.catch(done.fail);
});
- afterEach(() => vm.$destroy());
+ afterEach(() => {
+ vm.$destroy();
+ mock.reset();
+ });
it('calls submit if submit button is clicked', (done) => {
spyOn(vm, 'submit').and.callFake(e => e.preventDefault());
diff --git a/spec/javascripts/boards/boards_store_spec.js b/spec/javascripts/boards/boards_store_spec.js
index 9e5b0bd3efe..49fb20f4c84 100644
--- a/spec/javascripts/boards/boards_store_spec.js
+++ b/spec/javascripts/boards/boards_store_spec.js
@@ -1,26 +1,26 @@
/* eslint-disable comma-dangle, one-var, no-unused-vars */
/* global BoardService */
-/* global boardsMockInterceptor */
-/* global listObj */
-/* global listObjDuplicate */
/* global ListIssue */
-/* global mockBoardService */
import Vue from 'vue';
+import MockAdapter from 'axios-mock-adapter';
+import axios from '~/lib/utils/axios_utils';
import Cookies from 'js-cookie';
-import '~/lib/utils/url_utility';
import '~/boards/models/issue';
import '~/boards/models/label';
import '~/boards/models/list';
import '~/boards/models/assignee';
import '~/boards/services/board_service';
import '~/boards/stores/boards_store';
-import './mock_data';
+import { listObj, listObjDuplicate, boardsMockInterceptor, mockBoardService } from './mock_data';
describe('Store', () => {
+ let mock;
+
beforeEach(() => {
- Vue.http.interceptors.push(boardsMockInterceptor);
+ mock = new MockAdapter(axios);
+ mock.onAny().reply(boardsMockInterceptor);
gl.boardService = mockBoardService();
gl.issueBoards.BoardsStore.create();
@@ -35,7 +35,7 @@ describe('Store', () => {
});
afterEach(() => {
- Vue.http.interceptors = _.without(Vue.http.interceptors, boardsMockInterceptor);
+ mock.reset();
});
it('starts with a blank state', () => {
diff --git a/spec/javascripts/boards/components/board_spec.js b/spec/javascripts/boards/components/board_spec.js
index 8dacac20cad..19346e305cf 100644
--- a/spec/javascripts/boards/components/board_spec.js
+++ b/spec/javascripts/boards/components/board_spec.js
@@ -1,9 +1,8 @@
-/* global mockBoardService */
import Vue from 'vue';
import '~/boards/services/board_service';
import '~/boards/components/board';
import '~/boards/models/list';
-import '../mock_data';
+import { mockBoardService } from '../mock_data';
describe('Board component', () => {
let vm;
diff --git a/spec/javascripts/boards/issue_card_spec.js b/spec/javascripts/boards/issue_card_spec.js
index 7d430ec35e2..8ef221257be 100644
--- a/spec/javascripts/boards/issue_card_spec.js
+++ b/spec/javascripts/boards/issue_card_spec.js
@@ -1,6 +1,5 @@
/* global ListAssignee */
/* global ListLabel */
-/* global listObj */
/* global ListIssue */
import Vue from 'vue';
@@ -11,7 +10,7 @@ import '~/boards/models/list';
import '~/boards/models/assignee';
import '~/boards/stores/boards_store';
import '~/boards/components/issue_card_inner';
-import './mock_data';
+import { listObj } from './mock_data';
describe('Issue card component', () => {
const user = new ListAssignee({
diff --git a/spec/javascripts/boards/issue_spec.js b/spec/javascripts/boards/issue_spec.js
index 10b88878c2a..dbbe14fe3e0 100644
--- a/spec/javascripts/boards/issue_spec.js
+++ b/spec/javascripts/boards/issue_spec.js
@@ -1,17 +1,15 @@
/* eslint-disable comma-dangle */
/* global BoardService */
/* global ListIssue */
-/* global mockBoardService */
import Vue from 'vue';
-import '~/lib/utils/url_utility';
import '~/boards/models/issue';
import '~/boards/models/label';
import '~/boards/models/list';
import '~/boards/models/assignee';
import '~/boards/services/board_service';
import '~/boards/stores/boards_store';
-import './mock_data';
+import { mockBoardService } from './mock_data';
describe('Issue model', () => {
let issue;
diff --git a/spec/javascripts/boards/list_spec.js b/spec/javascripts/boards/list_spec.js
index d4627223a12..645ce831b53 100644
--- a/spec/javascripts/boards/list_spec.js
+++ b/spec/javascripts/boards/list_spec.js
@@ -1,28 +1,26 @@
/* eslint-disable comma-dangle */
-/* global boardsMockInterceptor */
/* global BoardService */
-/* global mockBoardService */
/* global List */
/* global ListIssue */
-/* global listObj */
-/* global listObjDuplicate */
-import Vue from 'vue';
+import MockAdapter from 'axios-mock-adapter';
+import axios from '~/lib/utils/axios_utils';
-import '~/lib/utils/url_utility';
import '~/boards/models/issue';
import '~/boards/models/label';
import '~/boards/models/list';
import '~/boards/models/assignee';
import '~/boards/services/board_service';
import '~/boards/stores/boards_store';
-import './mock_data';
+import { listObj, listObjDuplicate, boardsMockInterceptor, mockBoardService } from './mock_data';
describe('List model', () => {
let list;
+ let mock;
beforeEach(() => {
- Vue.http.interceptors.push(boardsMockInterceptor);
+ mock = new MockAdapter(axios);
+ mock.onAny().reply(boardsMockInterceptor);
gl.boardService = mockBoardService({
bulkUpdatePath: '/test/issue-boards/board/1/lists',
});
@@ -32,7 +30,7 @@ describe('List model', () => {
});
afterEach(() => {
- Vue.http.interceptors = _.without(Vue.http.interceptors, boardsMockInterceptor);
+ mock.reset();
});
it('gets issues when created', (done) => {
@@ -159,10 +157,8 @@ describe('List model', () => {
describe('newIssue', () => {
beforeEach(() => {
spyOn(gl.boardService, 'newIssue').and.returnValue(Promise.resolve({
- json() {
- return {
- id: 42,
- };
+ data: {
+ id: 42,
},
}));
});
diff --git a/spec/javascripts/boards/mock_data.js b/spec/javascripts/boards/mock_data.js
index 0a93086985e..9ae2d535398 100644
--- a/spec/javascripts/boards/mock_data.js
+++ b/spec/javascripts/boards/mock_data.js
@@ -1,20 +1,20 @@
/* global BoardService */
/* eslint-disable comma-dangle, no-unused-vars, quote-props */
-const listObj = {
- id: _.random(10000),
+export const listObj = {
+ id: 300,
position: 0,
title: 'Test',
list_type: 'label',
label: {
- id: _.random(10000),
+ id: 5000,
title: 'Testing',
color: 'red',
description: 'testing;'
}
};
-const listObjDuplicate = {
+export const listObjDuplicate = {
id: listObj.id,
position: 1,
title: 'Test',
@@ -27,9 +27,9 @@ const listObjDuplicate = {
}
};
-const BoardsMockData = {
+export const BoardsMockData = {
'GET': {
- '/test/boards/1{/id}/issues': {
+ '/test/-/boards/1/lists/300/issues?id=300&page=1&=': {
issues: [{
title: 'Testing',
id: 1,
@@ -41,7 +41,7 @@ const BoardsMockData = {
}
},
'POST': {
- '/test/boards/1{/id}': listObj
+ '/test/-/boards/1/lists': listObj
},
'PUT': {
'/test/issue-boards/board/1/lists{/id}': {}
@@ -51,17 +51,14 @@ const BoardsMockData = {
}
};
-const boardsMockInterceptor = (request, next) => {
- const body = BoardsMockData[request.method][request.url];
-
- next(request.respondWith(JSON.stringify(body), {
- status: 200
- }));
+export const boardsMockInterceptor = (config) => {
+ const body = BoardsMockData[config.method.toUpperCase()][config.url];
+ return [200, body];
};
-const mockBoardService = (opts = {}) => {
- const boardsEndpoint = opts.boardsEndpoint || '/test/issue-boards/board';
- const listsEndpoint = opts.listsEndpoint || '/test/boards/1';
+export const mockBoardService = (opts = {}) => {
+ const boardsEndpoint = opts.boardsEndpoint || '/test/issue-boards/boards.json';
+ const listsEndpoint = opts.listsEndpoint || '/test/-/boards/1/lists';
const bulkUpdatePath = opts.bulkUpdatePath || '';
const boardId = opts.boardId || '1';
@@ -72,9 +69,3 @@ const mockBoardService = (opts = {}) => {
boardId,
});
};
-
-window.listObj = listObj;
-window.listObjDuplicate = listObjDuplicate;
-window.BoardsMockData = BoardsMockData;
-window.boardsMockInterceptor = boardsMockInterceptor;
-window.mockBoardService = mockBoardService;
diff --git a/spec/javascripts/clusters/components/applications_spec.js b/spec/javascripts/clusters/components/applications_spec.js
index 7460da031c4..1a8affad4e3 100644
--- a/spec/javascripts/clusters/components/applications_spec.js
+++ b/spec/javascripts/clusters/components/applications_spec.js
@@ -21,6 +21,7 @@ describe('Applications', () => {
helm: { title: 'Helm Tiller' },
ingress: { title: 'Ingress' },
runner: { title: 'GitLab Runner' },
+ prometheus: { title: 'Prometheus' },
},
});
});
@@ -33,6 +34,10 @@ describe('Applications', () => {
expect(vm.$el.querySelector('.js-cluster-application-row-ingress')).toBeDefined();
});
+ it('renders a row for Prometheus', () => {
+ expect(vm.$el.querySelector('.js-cluster-application-row-prometheus')).toBeDefined();
+ });
+
/* * /
it('renders a row for GitLab Runner', () => {
expect(vm.$el.querySelector('.js-cluster-application-row-runner')).toBeDefined();
diff --git a/spec/javascripts/clusters/services/mock_data.js b/spec/javascripts/clusters/services/mock_data.js
index af6b6a73819..253b3c45243 100644
--- a/spec/javascripts/clusters/services/mock_data.js
+++ b/spec/javascripts/clusters/services/mock_data.js
@@ -22,6 +22,11 @@ const CLUSTERS_MOCK_DATA = {
name: 'runner',
status: APPLICATION_INSTALLING,
status_reason: null,
+ },
+ {
+ name: 'prometheus',
+ status: APPLICATION_ERROR,
+ status_reason: 'Cannot connect',
}],
},
},
@@ -30,6 +35,7 @@ const CLUSTERS_MOCK_DATA = {
'/gitlab-org/gitlab-shell/clusters/1/applications/helm': { },
'/gitlab-org/gitlab-shell/clusters/1/applications/ingress': { },
'/gitlab-org/gitlab-shell/clusters/1/applications/runner': { },
+ '/gitlab-org/gitlab-shell/clusters/1/applications/prometheus': { },
},
};
diff --git a/spec/javascripts/clusters/stores/clusters_store_spec.js b/spec/javascripts/clusters/stores/clusters_store_spec.js
index cb8b3d38e2e..ec2889355e6 100644
--- a/spec/javascripts/clusters/stores/clusters_store_spec.js
+++ b/spec/javascripts/clusters/stores/clusters_store_spec.js
@@ -82,6 +82,13 @@ describe('Clusters Store', () => {
requestStatus: null,
requestReason: null,
},
+ prometheus: {
+ title: 'Prometheus',
+ status: mockResponseData.applications[3].status,
+ statusReason: mockResponseData.applications[3].status_reason,
+ requestStatus: null,
+ requestReason: null,
+ },
},
});
});
diff --git a/spec/javascripts/collapsed_sidebar_todo_spec.js b/spec/javascripts/collapsed_sidebar_todo_spec.js
index 974815fe939..5026eaafaca 100644
--- a/spec/javascripts/collapsed_sidebar_todo_spec.js
+++ b/spec/javascripts/collapsed_sidebar_todo_spec.js
@@ -1,7 +1,6 @@
-/* global Sidebar */
/* eslint-disable no-new */
import _ from 'underscore';
-import '~/right_sidebar';
+import Sidebar from '~/right_sidebar';
describe('Issuable right sidebar collapsed todo toggle', () => {
const fixtureName = 'issues/open-issue.html.raw';
diff --git a/spec/javascripts/commits_spec.js b/spec/javascripts/commits_spec.js
index e5a5e3293b9..d0176520440 100644
--- a/spec/javascripts/commits_spec.js
+++ b/spec/javascripts/commits_spec.js
@@ -1,5 +1,4 @@
import 'vendor/jquery.endless-scroll';
-import '~/pager';
import CommitsList from '~/commits';
describe('Commits List', () => {
diff --git a/spec/javascripts/datetime_utility_spec.js b/spec/javascripts/datetime_utility_spec.js
index 0f7bf9ec712..2e5b65f5610 100644
--- a/spec/javascripts/datetime_utility_spec.js
+++ b/spec/javascripts/datetime_utility_spec.js
@@ -1,110 +1,108 @@
import * as datetimeUtility from '~/lib/utils/datetime_utility';
-(() => {
- describe('Date time utils', () => {
- describe('timeFor', () => {
- it('returns `past due` when in past', () => {
- const date = new Date();
- date.setFullYear(date.getFullYear() - 1);
-
- expect(
- gl.utils.timeFor(date),
- ).toBe('Past due');
- });
-
- it('returns remaining time when in the future', () => {
- const date = new Date();
- date.setFullYear(date.getFullYear() + 1);
-
- // Add a day to prevent a transient error. If date is even 1 second
- // short of a full year, timeFor will return '11 months remaining'
- date.setDate(date.getDate() + 1);
-
- expect(
- gl.utils.timeFor(date),
- ).toBe('1 year remaining');
- });
+describe('Date time utils', () => {
+ describe('timeFor', () => {
+ it('returns `past due` when in past', () => {
+ const date = new Date();
+ date.setFullYear(date.getFullYear() - 1);
+
+ expect(
+ datetimeUtility.timeFor(date),
+ ).toBe('Past due');
});
- describe('get day name', () => {
- it('should return Sunday', () => {
- const day = gl.utils.getDayName(new Date('07/17/2016'));
- expect(day).toBe('Sunday');
- });
-
- it('should return Monday', () => {
- const day = gl.utils.getDayName(new Date('07/18/2016'));
- expect(day).toBe('Monday');
- });
-
- it('should return Tuesday', () => {
- const day = gl.utils.getDayName(new Date('07/19/2016'));
- expect(day).toBe('Tuesday');
- });
-
- it('should return Wednesday', () => {
- const day = gl.utils.getDayName(new Date('07/20/2016'));
- expect(day).toBe('Wednesday');
- });
-
- it('should return Thursday', () => {
- const day = gl.utils.getDayName(new Date('07/21/2016'));
- expect(day).toBe('Thursday');
- });
-
- it('should return Friday', () => {
- const day = gl.utils.getDayName(new Date('07/22/2016'));
- expect(day).toBe('Friday');
- });
-
- it('should return Saturday', () => {
- const day = gl.utils.getDayName(new Date('07/23/2016'));
- expect(day).toBe('Saturday');
- });
- });
+ it('returns remaining time when in the future', () => {
+ const date = new Date();
+ date.setFullYear(date.getFullYear() + 1);
+
+ // Add a day to prevent a transient error. If date is even 1 second
+ // short of a full year, timeFor will return '11 months remaining'
+ date.setDate(date.getDate() + 1);
- describe('get day difference', () => {
- it('should return 7', () => {
- const firstDay = new Date('07/01/2016');
- const secondDay = new Date('07/08/2016');
- const difference = gl.utils.getDayDifference(firstDay, secondDay);
- expect(difference).toBe(7);
- });
-
- it('should return 31', () => {
- const firstDay = new Date('07/01/2016');
- const secondDay = new Date('08/01/2016');
- const difference = gl.utils.getDayDifference(firstDay, secondDay);
- expect(difference).toBe(31);
- });
-
- it('should return 365', () => {
- const firstDay = new Date('07/02/2015');
- const secondDay = new Date('07/01/2016');
- const difference = gl.utils.getDayDifference(firstDay, secondDay);
- expect(difference).toBe(365);
- });
+ expect(
+ datetimeUtility.timeFor(date),
+ ).toBe('1 year remaining');
});
});
- describe('timeIntervalInWords', () => {
- it('should return string with number of minutes and seconds', () => {
- expect(datetimeUtility.timeIntervalInWords(9.54)).toEqual('9 seconds');
- expect(datetimeUtility.timeIntervalInWords(1)).toEqual('1 second');
- expect(datetimeUtility.timeIntervalInWords(200)).toEqual('3 minutes 20 seconds');
- expect(datetimeUtility.timeIntervalInWords(6008)).toEqual('100 minutes 8 seconds');
+ describe('get day name', () => {
+ it('should return Sunday', () => {
+ const day = datetimeUtility.getDayName(new Date('07/17/2016'));
+ expect(day).toBe('Sunday');
+ });
+
+ it('should return Monday', () => {
+ const day = datetimeUtility.getDayName(new Date('07/18/2016'));
+ expect(day).toBe('Monday');
+ });
+
+ it('should return Tuesday', () => {
+ const day = datetimeUtility.getDayName(new Date('07/19/2016'));
+ expect(day).toBe('Tuesday');
+ });
+
+ it('should return Wednesday', () => {
+ const day = datetimeUtility.getDayName(new Date('07/20/2016'));
+ expect(day).toBe('Wednesday');
+ });
+
+ it('should return Thursday', () => {
+ const day = datetimeUtility.getDayName(new Date('07/21/2016'));
+ expect(day).toBe('Thursday');
+ });
+
+ it('should return Friday', () => {
+ const day = datetimeUtility.getDayName(new Date('07/22/2016'));
+ expect(day).toBe('Friday');
+ });
+
+ it('should return Saturday', () => {
+ const day = datetimeUtility.getDayName(new Date('07/23/2016'));
+ expect(day).toBe('Saturday');
});
});
- describe('dateInWords', () => {
- const date = new Date('07/01/2016');
+ describe('get day difference', () => {
+ it('should return 7', () => {
+ const firstDay = new Date('07/01/2016');
+ const secondDay = new Date('07/08/2016');
+ const difference = datetimeUtility.getDayDifference(firstDay, secondDay);
+ expect(difference).toBe(7);
+ });
- it('should return date in words', () => {
- expect(datetimeUtility.dateInWords(date)).toEqual('July 1, 2016');
+ it('should return 31', () => {
+ const firstDay = new Date('07/01/2016');
+ const secondDay = new Date('08/01/2016');
+ const difference = datetimeUtility.getDayDifference(firstDay, secondDay);
+ expect(difference).toBe(31);
});
- it('should return abbreviated month name', () => {
- expect(datetimeUtility.dateInWords(date, true)).toEqual('Jul 1, 2016');
+ it('should return 365', () => {
+ const firstDay = new Date('07/02/2015');
+ const secondDay = new Date('07/01/2016');
+ const difference = datetimeUtility.getDayDifference(firstDay, secondDay);
+ expect(difference).toBe(365);
});
});
-})();
+});
+
+describe('timeIntervalInWords', () => {
+ it('should return string with number of minutes and seconds', () => {
+ expect(datetimeUtility.timeIntervalInWords(9.54)).toEqual('9 seconds');
+ expect(datetimeUtility.timeIntervalInWords(1)).toEqual('1 second');
+ expect(datetimeUtility.timeIntervalInWords(200)).toEqual('3 minutes 20 seconds');
+ expect(datetimeUtility.timeIntervalInWords(6008)).toEqual('100 minutes 8 seconds');
+ });
+});
+
+describe('dateInWords', () => {
+ const date = new Date('07/01/2016');
+
+ it('should return date in words', () => {
+ expect(datetimeUtility.dateInWords(date)).toEqual('July 1, 2016');
+ });
+
+ it('should return abbreviated month name', () => {
+ expect(datetimeUtility.dateInWords(date, true)).toEqual('Jul 1, 2016');
+ });
+});
diff --git a/spec/javascripts/deploy_keys/components/key_spec.js b/spec/javascripts/deploy_keys/components/key_spec.js
index 5b64cbb2dfc..2f28c5bbf01 100644
--- a/spec/javascripts/deploy_keys/components/key_spec.js
+++ b/spec/javascripts/deploy_keys/components/key_spec.js
@@ -1,6 +1,7 @@
import Vue from 'vue';
import DeployKeysStore from '~/deploy_keys/store';
import key from '~/deploy_keys/components/key.vue';
+import { getTimeago } from '~/lib/utils/datetime_utility';
describe('Deploy keys key', () => {
let vm;
@@ -37,7 +38,7 @@ describe('Deploy keys key', () => {
it('renders human friendly formatted created date', () => {
expect(
vm.$el.querySelector('.key-created-at').textContent.trim(),
- ).toBe(`created ${gl.utils.getTimeago().format(deployKey.created_at)}`);
+ ).toBe(`created ${getTimeago().format(deployKey.created_at)}`);
});
it('shows edit button', () => {
diff --git a/spec/javascripts/filtered_search/filtered_search_manager_spec.js b/spec/javascripts/filtered_search/filtered_search_manager_spec.js
index 230c15e5de6..b8890e4cda1 100644
--- a/spec/javascripts/filtered_search/filtered_search_manager_spec.js
+++ b/spec/javascripts/filtered_search/filtered_search_manager_spec.js
@@ -1,8 +1,8 @@
+import * as urlUtils from '~/lib/utils/url_utility';
import * as recentSearchesStoreSrc from '~/filtered_search/stores/recent_searches_store';
import RecentSearchesService from '~/filtered_search/services/recent_searches_service';
import RecentSearchesServiceError from '~/filtered_search/services/recent_searches_service_error';
import RecentSearchesRoot from '~/filtered_search/recent_searches_root';
-import '~/lib/utils/url_utility';
import '~/lib/utils/common_utils';
import '~/filtered_search/filtered_search_token_keys';
import '~/filtered_search/filtered_search_tokenizer';
@@ -162,7 +162,7 @@ describe('Filtered Search Manager', () => {
it('should search with a single word', (done) => {
input.value = 'searchTerm';
- spyOn(gl.utils, 'visitUrl').and.callFake((url) => {
+ spyOn(urlUtils, 'visitUrl').and.callFake((url) => {
expect(url).toEqual(`${defaultParams}&search=searchTerm`);
done();
});
@@ -173,7 +173,7 @@ describe('Filtered Search Manager', () => {
it('should search with multiple words', (done) => {
input.value = 'awesome search terms';
- spyOn(gl.utils, 'visitUrl').and.callFake((url) => {
+ spyOn(urlUtils, 'visitUrl').and.callFake((url) => {
expect(url).toEqual(`${defaultParams}&search=awesome+search+terms`);
done();
});
@@ -184,7 +184,7 @@ describe('Filtered Search Manager', () => {
it('should search with special characters', (done) => {
input.value = '~!@#$%^&*()_+{}:<>,.?/';
- spyOn(gl.utils, 'visitUrl').and.callFake((url) => {
+ spyOn(urlUtils, 'visitUrl').and.callFake((url) => {
expect(url).toEqual(`${defaultParams}&search=~!%40%23%24%25%5E%26*()_%2B%7B%7D%3A%3C%3E%2C.%3F%2F`);
done();
});
@@ -198,7 +198,7 @@ describe('Filtered Search Manager', () => {
${FilteredSearchSpecHelper.createFilterVisualTokenHTML('label', '~bug')}
`);
- spyOn(gl.utils, 'visitUrl').and.callFake((url) => {
+ spyOn(urlUtils, 'visitUrl').and.callFake((url) => {
expect(url).toEqual(`${defaultParams}&label_name[]=bug`);
done();
});
@@ -252,6 +252,7 @@ describe('Filtered Search Manager', () => {
it('removes last token', () => {
spyOn(gl.FilteredSearchVisualTokens, 'removeLastTokenPartial').and.callThrough();
dispatchBackspaceEvent(input, 'keyup');
+ dispatchBackspaceEvent(input, 'keyup');
expect(gl.FilteredSearchVisualTokens.removeLastTokenPartial).toHaveBeenCalled();
});
@@ -259,6 +260,7 @@ describe('Filtered Search Manager', () => {
it('sets the input', () => {
spyOn(gl.FilteredSearchVisualTokens, 'getLastTokenPartial').and.callThrough();
dispatchDeleteEvent(input, 'keyup');
+ dispatchDeleteEvent(input, 'keyup');
expect(gl.FilteredSearchVisualTokens.getLastTokenPartial).toHaveBeenCalled();
expect(input.value).toEqual('~bug');
@@ -276,6 +278,18 @@ describe('Filtered Search Manager', () => {
expect(gl.FilteredSearchVisualTokens.getLastTokenPartial).not.toHaveBeenCalled();
expect(input.value).toEqual('text');
});
+
+ it('does not remove previous token on single backspace press', () => {
+ spyOn(gl.FilteredSearchVisualTokens, 'removeLastTokenPartial').and.callThrough();
+ spyOn(gl.FilteredSearchVisualTokens, 'getLastTokenPartial').and.callThrough();
+
+ input.value = 't';
+ dispatchDeleteEvent(input, 'keyup');
+
+ expect(gl.FilteredSearchVisualTokens.removeLastTokenPartial).not.toHaveBeenCalled();
+ expect(gl.FilteredSearchVisualTokens.getLastTokenPartial).not.toHaveBeenCalled();
+ expect(input.value).toEqual('t');
+ });
});
describe('removeToken', () => {
diff --git a/spec/javascripts/fixtures/pipelines.html.haml b/spec/javascripts/fixtures/pipelines.html.haml
index 85ee61f0b54..0161c0550d1 100644
--- a/spec/javascripts/fixtures/pipelines.html.haml
+++ b/spec/javascripts/fixtures/pipelines.html.haml
@@ -7,4 +7,6 @@
"new-pipeline-path" => 'foo',
"can-create-pipeline" => 'true',
"has-ci" => 'foo',
- "ci-lint-path" => 'foo' } }
+ "ci-lint-path" => 'foo',
+ "reset-cache-path" => 'foo' } }
+
diff --git a/spec/javascripts/fly_out_nav_spec.js b/spec/javascripts/fly_out_nav_spec.js
index 4f20e31f511..a3fa07d5bc2 100644
--- a/spec/javascripts/fly_out_nav_spec.js
+++ b/spec/javascripts/fly_out_nav_spec.js
@@ -253,7 +253,7 @@ describe('Fly out sidebar navigation', () => {
it('shows collapsed only sub-items if icon only sidebar', () => {
const subItems = el.querySelector('.sidebar-sub-level-items');
const sidebar = document.createElement('div');
- sidebar.classList.add('sidebar-icons-only');
+ sidebar.classList.add('sidebar-collapsed-desktop');
subItems.classList.add('is-fly-out-only');
setSidebar(sidebar);
@@ -343,7 +343,7 @@ describe('Fly out sidebar navigation', () => {
it('returns true when active & collapsed sidebar', () => {
const sidebar = document.createElement('div');
- sidebar.classList.add('sidebar-icons-only');
+ sidebar.classList.add('sidebar-collapsed-desktop');
el.classList.add('active');
setSidebar(sidebar);
diff --git a/spec/javascripts/gl_dropdown_spec.js b/spec/javascripts/gl_dropdown_spec.js
index ca048123bf7..b13d1bf8dff 100644
--- a/spec/javascripts/gl_dropdown_spec.js
+++ b/spec/javascripts/gl_dropdown_spec.js
@@ -2,7 +2,7 @@
import '~/gl_dropdown';
import '~/lib/utils/common_utils';
-import '~/lib/utils/url_utility';
+import * as urlUtils from '~/lib/utils/url_utility';
describe('glDropdown', function describeDropdown() {
preloadFixtures('static/gl_dropdown.html.raw');
@@ -137,13 +137,13 @@ describe('glDropdown', function describeDropdown() {
expect(this.dropdownContainerElement).toHaveClass('open');
const randomIndex = Math.floor(Math.random() * (this.projectsData.length - 1)) + 0;
navigateWithKeys('down', randomIndex, () => {
- spyOn(gl.utils, 'visitUrl').and.stub();
+ spyOn(urlUtils, 'visitUrl').and.stub();
navigateWithKeys('enter', null, () => {
expect(this.dropdownContainerElement).not.toHaveClass('open');
const link = $(`${ITEM_SELECTOR}:eq(${randomIndex}) a`, this.$dropdownMenuElement);
expect(link).toHaveClass('is-active');
const linkedLocation = link.attr('href');
- if (linkedLocation && linkedLocation !== '#') expect(gl.utils.visitUrl).toHaveBeenCalledWith(linkedLocation);
+ if (linkedLocation && linkedLocation !== '#') expect(urlUtils.visitUrl).toHaveBeenCalledWith(linkedLocation);
});
});
});
diff --git a/spec/javascripts/graphs/stat_graph_contributors_graph_spec.js b/spec/javascripts/graphs/stat_graph_contributors_graph_spec.js
index 861f26e162f..6599839a526 100644
--- a/spec/javascripts/graphs/stat_graph_contributors_graph_spec.js
+++ b/spec/javascripts/graphs/stat_graph_contributors_graph_spec.js
@@ -1,8 +1,10 @@
/* eslint-disable quotes, jasmine/no-suite-dupes, vars-on-top, no-var */
-
-import d3 from 'd3';
+import { scaleLinear, scaleTime } from 'd3-scale';
+import { timeParse } from 'd3-time-format';
import { ContributorsGraph, ContributorsMasterGraph } from '~/graphs/stat_graph_contributors_graph';
+const d3 = { scaleLinear, scaleTime, timeParse };
+
describe("ContributorsGraph", function () {
describe("#set_x_domain", function () {
it("set the x_domain", function () {
@@ -53,7 +55,7 @@ describe("ContributorsGraph", function () {
it("sets the instance's x domain using the prototype's x_domain", function () {
ContributorsGraph.prototype.x_domain = 20;
var instance = new ContributorsGraph();
- instance.x = d3.time.scale().range([0, 100]).clamp(true);
+ instance.x = d3.scaleTime().range([0, 100]).clamp(true);
spyOn(instance.x, 'domain');
instance.set_x_domain();
expect(instance.x.domain).toHaveBeenCalledWith(20);
@@ -64,7 +66,7 @@ describe("ContributorsGraph", function () {
it("sets the instance's y domain using the prototype's y_domain", function () {
ContributorsGraph.prototype.y_domain = 30;
var instance = new ContributorsGraph();
- instance.y = d3.scale.linear().range([100, 0]).nice();
+ instance.y = d3.scaleLinear().range([100, 0]).nice();
spyOn(instance.y, 'domain');
instance.set_y_domain();
expect(instance.y.domain).toHaveBeenCalledWith(30);
@@ -118,7 +120,7 @@ describe("ContributorsMasterGraph", function () {
describe("#parse_dates", function () {
it("parses the dates", function () {
var graph = new ContributorsMasterGraph();
- var parseDate = d3.time.format("%Y-%m-%d").parse;
+ var parseDate = d3.timeParse("%Y-%m-%d");
var data = [{ date: "2013-01-01" }, { date: "2012-12-15" }];
var correct = [{ date: parseDate(data[0].date) }, { date: parseDate(data[1].date) }];
graph.parse_dates(data);
diff --git a/spec/javascripts/graphs/stat_graph_contributors_spec.js b/spec/javascripts/graphs/stat_graph_contributors_spec.js
new file mode 100644
index 00000000000..962423462e7
--- /dev/null
+++ b/spec/javascripts/graphs/stat_graph_contributors_spec.js
@@ -0,0 +1,26 @@
+import ContributorsStatGraph from '~/graphs/stat_graph_contributors';
+import { ContributorsGraph } from '~/graphs/stat_graph_contributors_graph';
+
+import { setLanguage } from '../helpers/locale_helper';
+
+describe('ContributorsStatGraph', () => {
+ describe('change_date_header', () => {
+ beforeAll(() => {
+ setLanguage('de');
+ });
+
+ afterAll(() => {
+ setLanguage(null);
+ });
+
+ it('uses the locale to display date ranges', () => {
+ ContributorsGraph.init_x_domain([{ date: '2013-01-31' }, { date: '2012-01-31' }]);
+ setFixtures('<div id="date_header"></div>');
+ const graph = new ContributorsStatGraph();
+
+ graph.change_date_header();
+
+ expect(document.getElementById('date_header').innerText).toBe('31. Januar 2012 – 31. Januar 2013');
+ });
+ });
+});
diff --git a/spec/javascripts/groups/components/app_spec.js b/spec/javascripts/groups/components/app_spec.js
index 59d4f7c45c6..97e39f6411b 100644
--- a/spec/javascripts/groups/components/app_spec.js
+++ b/spec/javascripts/groups/components/app_spec.js
@@ -1,9 +1,9 @@
import Vue from 'vue';
+import * as utils from '~/lib/utils/url_utility';
import appComponent from '~/groups/components/app.vue';
import groupFolderComponent from '~/groups/components/group_folder.vue';
import groupItemComponent from '~/groups/components/group_item.vue';
-
import eventHub from '~/groups/event_hub';
import GroupsStore from '~/groups/store/groups_store';
import GroupsService from '~/groups/service/groups_service';
@@ -176,7 +176,7 @@ describe('AppComponent', () => {
it('should fetch groups for provided page details and update window state', (done) => {
spyOn(vm, 'fetchGroups').and.returnValue(returnServicePromise(mockGroups));
spyOn(vm, 'updateGroups').and.callThrough();
- spyOn(gl.utils, 'mergeUrlParams').and.callThrough();
+ spyOn(utils, 'mergeUrlParams').and.callThrough();
spyOn(window.history, 'replaceState');
spyOn($, 'scrollTo');
@@ -192,7 +192,7 @@ describe('AppComponent', () => {
setTimeout(() => {
expect(vm.isLoading).toBeFalsy();
expect($.scrollTo).toHaveBeenCalledWith(0);
- expect(gl.utils.mergeUrlParams).toHaveBeenCalledWith({ page: 2 }, jasmine.any(String));
+ expect(utils.mergeUrlParams).toHaveBeenCalledWith({ page: 2 }, jasmine.any(String));
expect(window.history.replaceState).toHaveBeenCalledWith({
page: jasmine.any(String),
}, jasmine.any(String), jasmine.any(String));
diff --git a/spec/javascripts/groups/components/group_item_spec.js b/spec/javascripts/groups/components/group_item_spec.js
index 0f4fbdae445..618d0022e4f 100644
--- a/spec/javascripts/groups/components/group_item_spec.js
+++ b/spec/javascripts/groups/components/group_item_spec.js
@@ -1,5 +1,5 @@
import Vue from 'vue';
-
+import * as urlUtils from '~/lib/utils/url_utility';
import groupItemComponent from '~/groups/components/group_item.vue';
import groupFolderComponent from '~/groups/components/group_folder.vue';
import eventHub from '~/groups/event_hub';
@@ -136,13 +136,13 @@ describe('GroupItemComponent', () => {
const group = Object.assign({}, mockParentGroupItem);
group.childrenCount = 0;
const newVm = createComponent(group);
- spyOn(gl.utils, 'visitUrl').and.stub();
+ spyOn(urlUtils, 'visitUrl').and.stub();
spyOn(eventHub, '$emit');
newVm.onClickRowGroup(event);
setTimeout(() => {
expect(eventHub.$emit).not.toHaveBeenCalled();
- expect(gl.utils.visitUrl).toHaveBeenCalledWith(newVm.group.relativePath);
+ expect(urlUtils.visitUrl).toHaveBeenCalledWith(newVm.group.relativePath);
done();
}, 0);
});
diff --git a/spec/javascripts/groups/components/item_actions_spec.js b/spec/javascripts/groups/components/item_actions_spec.js
index 2ce1a749a96..6d6fb410859 100644
--- a/spec/javascripts/groups/components/item_actions_spec.js
+++ b/spec/javascripts/groups/components/item_actions_spec.js
@@ -36,28 +36,22 @@ describe('ItemActionsComponent', () => {
describe('methods', () => {
describe('onLeaveGroup', () => {
- it('should change `dialogStatus` prop to `true` which shows confirmation dialog', () => {
- expect(vm.dialogStatus).toBeFalsy();
+ it('should change `modalStatus` prop to `true` which shows confirmation dialog', () => {
+ expect(vm.modalStatus).toBeFalsy();
vm.onLeaveGroup();
- expect(vm.dialogStatus).toBeTruthy();
+ expect(vm.modalStatus).toBeTruthy();
});
});
describe('leaveGroup', () => {
- it('should change `dialogStatus` prop to `false` and emit `leaveGroup` event with required params when called with `leaveConfirmed` as `true`', () => {
+ it('should change `modalStatus` prop to `false` and emit `leaveGroup` event with required params when called with `leaveConfirmed` as `true`', () => {
spyOn(eventHub, '$emit');
- vm.dialogStatus = true;
- vm.leaveGroup(true);
- expect(vm.dialogStatus).toBeFalsy();
- expect(eventHub.$emit).toHaveBeenCalledWith('leaveGroup', vm.group, vm.parentGroup);
- });
+ vm.modalStatus = true;
- it('should change `dialogStatus` prop to `false` and should NOT emit `leaveGroup` event when called with `leaveConfirmed` as `false`', () => {
- spyOn(eventHub, '$emit');
- vm.dialogStatus = true;
- vm.leaveGroup(false);
- expect(vm.dialogStatus).toBeFalsy();
- expect(eventHub.$emit).not.toHaveBeenCalled();
+ vm.leaveGroup();
+
+ expect(vm.modalStatus).toBeFalsy();
+ expect(eventHub.$emit).toHaveBeenCalledWith('leaveGroup', vm.group, vm.parentGroup);
});
});
});
@@ -99,9 +93,9 @@ describe('ItemActionsComponent', () => {
newVm.$destroy();
});
- it('should show modal dialog when `dialogStatus` is set to `true`', () => {
- vm.dialogStatus = true;
- const modalDialogEl = vm.$el.querySelector('.modal.popup-dialog');
+ it('should show modal dialog when `modalStatus` is set to `true`', () => {
+ vm.modalStatus = true;
+ const modalDialogEl = vm.$el.querySelector('.modal');
expect(modalDialogEl).toBeDefined();
expect(modalDialogEl.querySelector('.modal-title').innerText.trim()).toBe('Are you sure?');
expect(modalDialogEl.querySelector('.btn.btn-warning').innerText.trim()).toBe('Leave');
diff --git a/spec/javascripts/groups/components/item_caret_spec.js b/spec/javascripts/groups/components/item_caret_spec.js
index 4310a07e6e6..8faad455825 100644
--- a/spec/javascripts/groups/components/item_caret_spec.js
+++ b/spec/javascripts/groups/components/item_caret_spec.js
@@ -16,24 +16,20 @@ describe('ItemCaretComponent', () => {
describe('template', () => {
it('should render component template correctly', () => {
const vm = createComponent();
- vm.$mount();
expect(vm.$el.classList.contains('folder-caret')).toBeTruthy();
+ expect(vm.$el.querySelectorAll('svg').length).toBe(1);
vm.$destroy();
});
it('should render caret down icon if `isGroupOpen` prop is `true`', () => {
const vm = createComponent(true);
- vm.$mount();
- expect(vm.$el.querySelectorAll('i.fa.fa-caret-down').length).toBe(1);
- expect(vm.$el.querySelectorAll('i.fa.fa-caret-right').length).toBe(0);
+ expect(vm.$el.querySelector('svg use').getAttribute('xlink:href')).toContain('angle-down');
vm.$destroy();
});
it('should render caret right icon if `isGroupOpen` prop is `false`', () => {
const vm = createComponent();
- vm.$mount();
- expect(vm.$el.querySelectorAll('i.fa.fa-caret-down').length).toBe(0);
- expect(vm.$el.querySelectorAll('i.fa.fa-caret-right').length).toBe(1);
+ expect(vm.$el.querySelector('svg use').getAttribute('xlink:href')).toContain('angle-right');
vm.$destroy();
});
});
diff --git a/spec/javascripts/groups/components/item_stats_spec.js b/spec/javascripts/groups/components/item_stats_spec.js
index e200f9f08bd..55a7a713ca6 100644
--- a/spec/javascripts/groups/components/item_stats_spec.js
+++ b/spec/javascripts/groups/components/item_stats_spec.js
@@ -26,7 +26,6 @@ describe('ItemStatsComponent', () => {
Object.keys(VISIBILITY_TYPE_ICON).forEach((visibility) => {
const item = Object.assign({}, mockParentGroupItem, { visibility });
const vm = createComponent(item);
- vm.$mount();
expect(vm.visibilityIcon).toBe(VISIBILITY_TYPE_ICON[visibility]);
vm.$destroy();
});
@@ -41,7 +40,6 @@ describe('ItemStatsComponent', () => {
type: ITEM_TYPE.GROUP,
});
const vm = createComponent(item);
- vm.$mount();
expect(vm.visibilityTooltip).toBe(GROUP_VISIBILITY_TYPE[visibility]);
vm.$destroy();
});
@@ -54,7 +52,6 @@ describe('ItemStatsComponent', () => {
type: ITEM_TYPE.PROJECT,
});
const vm = createComponent(item);
- vm.$mount();
expect(vm.visibilityTooltip).toBe(PROJECT_VISIBILITY_TYPE[visibility]);
vm.$destroy();
});
@@ -68,13 +65,11 @@ describe('ItemStatsComponent', () => {
item = Object.assign({}, mockParentGroupItem, { type: ITEM_TYPE.PROJECT });
vm = createComponent(item);
- vm.$mount();
expect(vm.isProject).toBeTruthy();
vm.$destroy();
item = Object.assign({}, mockParentGroupItem, { type: ITEM_TYPE.GROUP });
vm = createComponent(item);
- vm.$mount();
expect(vm.isProject).toBeFalsy();
vm.$destroy();
});
@@ -87,13 +82,11 @@ describe('ItemStatsComponent', () => {
item = Object.assign({}, mockParentGroupItem, { type: ITEM_TYPE.GROUP });
vm = createComponent(item);
- vm.$mount();
expect(vm.isGroup).toBeTruthy();
vm.$destroy();
item = Object.assign({}, mockParentGroupItem, { type: ITEM_TYPE.PROJECT });
vm = createComponent(item);
- vm.$mount();
expect(vm.isGroup).toBeFalsy();
vm.$destroy();
});
@@ -101,57 +94,37 @@ describe('ItemStatsComponent', () => {
});
describe('template', () => {
- it('should render component template correctly', () => {
+ it('renders component container element correctly', () => {
const vm = createComponent();
- vm.$mount();
- const visibilityIconEl = vm.$el.querySelector('.item-visibility');
- expect(vm.$el.classList.contains('.stats')).toBeDefined();
- expect(visibilityIconEl).toBeDefined();
- expect(visibilityIconEl.dataset.originalTitle).toBe(vm.visibilityTooltip);
- expect(visibilityIconEl.querySelector('i.fa')).toBeDefined();
+ expect(vm.$el.classList.contains('stats')).toBeTruthy();
vm.$destroy();
});
- it('should render stat icons if `item.type` is Group', () => {
- const item = Object.assign({}, mockParentGroupItem, { type: ITEM_TYPE.GROUP });
- const vm = createComponent(item);
- vm.$mount();
-
- const subgroupIconEl = vm.$el.querySelector('span.number-subgroups');
- expect(subgroupIconEl).toBeDefined();
- expect(subgroupIconEl.dataset.originalTitle).toBe('Subgroups');
- expect(subgroupIconEl.querySelector('i.fa.fa-folder')).toBeDefined();
- expect(subgroupIconEl.innerText.trim()).toBe(`${vm.item.subgroupCount}`);
-
- const projectsIconEl = vm.$el.querySelector('span.number-projects');
- expect(projectsIconEl).toBeDefined();
- expect(projectsIconEl.dataset.originalTitle).toBe('Projects');
- expect(projectsIconEl.querySelector('i.fa.fa-bookmark')).toBeDefined();
- expect(projectsIconEl.innerText.trim()).toBe(`${vm.item.projectCount}`);
-
- const membersIconEl = vm.$el.querySelector('span.number-users');
- expect(membersIconEl).toBeDefined();
- expect(membersIconEl.dataset.originalTitle).toBe('Members');
- expect(membersIconEl.querySelector('i.fa.fa-users')).toBeDefined();
- expect(membersIconEl.innerText.trim()).toBe(`${vm.item.memberCount}`);
+ it('renders item visibility icon and tooltip correctly', () => {
+ const vm = createComponent();
+
+ const visibilityIconEl = vm.$el.querySelector('.item-visibility');
+ expect(visibilityIconEl).not.toBe(null);
+ expect(visibilityIconEl.dataset.originalTitle).toBe(vm.visibilityTooltip);
+ expect(visibilityIconEl.querySelectorAll('svg').length > 0).toBeTruthy();
vm.$destroy();
});
- it('should render stat icons if `item.type` is Project', () => {
+ it('renders start count and last updated information for project item correctly', () => {
const item = Object.assign({}, mockParentGroupItem, {
type: ITEM_TYPE.PROJECT,
starCount: 4,
});
const vm = createComponent(item);
- vm.$mount();
const projectStarIconEl = vm.$el.querySelector('.project-stars');
- expect(projectStarIconEl).toBeDefined();
- expect(projectStarIconEl.querySelector('i.fa.fa-star')).toBeDefined();
- expect(projectStarIconEl.innerText.trim()).toBe(`${vm.item.starCount}`);
+ expect(projectStarIconEl).not.toBe(null);
+ expect(projectStarIconEl.querySelectorAll('svg').length > 0).toBeTruthy();
+ expect(projectStarIconEl.querySelectorAll('.stat-value').length > 0).toBeTruthy();
+ expect(vm.$el.querySelectorAll('.last-updated').length > 0).toBeTruthy();
vm.$destroy();
});
diff --git a/spec/javascripts/groups/components/item_stats_value_spec.js b/spec/javascripts/groups/components/item_stats_value_spec.js
new file mode 100644
index 00000000000..e990870aaa6
--- /dev/null
+++ b/spec/javascripts/groups/components/item_stats_value_spec.js
@@ -0,0 +1,81 @@
+import Vue from 'vue';
+
+import itemStatsValueComponent from '~/groups/components/item_stats_value.vue';
+
+import mountComponent from '../../helpers/vue_mount_component_helper';
+
+const createComponent = ({ title, cssClass, iconName, tooltipPlacement, value }) => {
+ const Component = Vue.extend(itemStatsValueComponent);
+
+ return mountComponent(Component, {
+ title,
+ cssClass,
+ iconName,
+ tooltipPlacement,
+ value,
+ });
+};
+
+describe('ItemStatsValueComponent', () => {
+ describe('computed', () => {
+ let vm;
+ const itemConfig = {
+ title: 'Subgroups',
+ cssClass: 'number-subgroups',
+ iconName: 'folder',
+ tooltipPlacement: 'left',
+ };
+
+ describe('isValuePresent', () => {
+ it('returns true if non-empty `value` is present', () => {
+ vm = createComponent(Object.assign({}, itemConfig, { value: 10 }));
+ expect(vm.isValuePresent).toBeTruthy();
+ });
+
+ it('returns false if empty `value` is present', () => {
+ vm = createComponent(itemConfig);
+ expect(vm.isValuePresent).toBeFalsy();
+ });
+
+ afterEach(() => {
+ vm.$destroy();
+ });
+ });
+ });
+
+ describe('template', () => {
+ let vm;
+ beforeEach(() => {
+ vm = createComponent({
+ title: 'Subgroups',
+ cssClass: 'number-subgroups',
+ iconName: 'folder',
+ tooltipPlacement: 'left',
+ value: 10,
+ });
+ });
+
+ it('renders component element correctly', () => {
+ expect(vm.$el.classList.contains('number-subgroups')).toBeTruthy();
+ expect(vm.$el.querySelectorAll('svg').length > 0).toBeTruthy();
+ expect(vm.$el.querySelectorAll('.stat-value').length > 0).toBeTruthy();
+ });
+
+ it('renders element tooltip correctly', () => {
+ expect(vm.$el.dataset.originalTitle).toBe('Subgroups');
+ expect(vm.$el.dataset.placement).toBe('left');
+ });
+
+ it('renders element icon correctly', () => {
+ expect(vm.$el.querySelector('svg use').getAttribute('xlink:href')).toContain('folder');
+ });
+
+ it('renders value count correctly', () => {
+ expect(vm.$el.querySelector('.stat-value').innerText.trim()).toContain('10');
+ });
+
+ afterEach(() => {
+ vm.$destroy();
+ });
+ });
+});
diff --git a/spec/javascripts/groups/components/item_type_icon_spec.js b/spec/javascripts/groups/components/item_type_icon_spec.js
index 528e6ed1b4c..495cc97b475 100644
--- a/spec/javascripts/groups/components/item_type_icon_spec.js
+++ b/spec/javascripts/groups/components/item_type_icon_spec.js
@@ -28,12 +28,12 @@ describe('ItemTypeIconComponent', () => {
vm = createComponent(ITEM_TYPE.GROUP, true);
vm.$mount();
- expect(vm.$el.querySelector('i.fa.fa-folder-open')).toBeDefined();
+ expect(vm.$el.querySelector('use').getAttribute('xlink:href')).toContain('folder-open');
vm.$destroy();
vm = createComponent(ITEM_TYPE.GROUP);
vm.$mount();
- expect(vm.$el.querySelector('i.fa.fa-folder')).toBeDefined();
+ expect(vm.$el.querySelector('use').getAttribute('xlink:href')).toContain('folder');
vm.$destroy();
});
@@ -42,12 +42,12 @@ describe('ItemTypeIconComponent', () => {
vm = createComponent(ITEM_TYPE.PROJECT);
vm.$mount();
- expect(vm.$el.querySelectorAll('i.fa.fa-bookmark').length).toBe(1);
+ expect(vm.$el.querySelector('use').getAttribute('xlink:href')).toContain('bookmark');
vm.$destroy();
vm = createComponent(ITEM_TYPE.GROUP);
vm.$mount();
- expect(vm.$el.querySelectorAll('i.fa.fa-bookmark').length).toBe(0);
+ expect(vm.$el.querySelector('use').getAttribute('xlink:href')).not.toContain('bookmark');
vm.$destroy();
});
});
diff --git a/spec/javascripts/groups/mock_data.js b/spec/javascripts/groups/mock_data.js
index 6184d671790..8bf6417487d 100644
--- a/spec/javascripts/groups/mock_data.js
+++ b/spec/javascripts/groups/mock_data.js
@@ -18,9 +18,9 @@ export const PROJECT_VISIBILITY_TYPE = {
};
export const VISIBILITY_TYPE_ICON = {
- public: 'fa-globe',
- internal: 'fa-shield',
- private: 'fa-lock',
+ public: 'earth',
+ internal: 'shield',
+ private: 'lock',
};
export const mockParentGroupItem = {
@@ -46,6 +46,7 @@ export const mockParentGroupItem = {
isOpen: true,
isChildrenLoading: false,
isBeingRemoved: false,
+ updatedAt: '2017-04-09T18:40:39.101Z',
};
export const mockRawChildren = [
@@ -69,6 +70,7 @@ export const mockRawChildren = [
subgroup_count: 2,
can_leave: false,
children: [],
+ updated_at: '2017-04-09T18:40:39.101Z',
},
];
@@ -96,6 +98,7 @@ export const mockChildren = [
isOpen: true,
isChildrenLoading: false,
isBeingRemoved: false,
+ updatedAt: '2017-04-09T18:40:39.101Z',
},
];
@@ -119,6 +122,7 @@ export const mockGroups = [
project_count: 2,
subgroup_count: 0,
can_leave: false,
+ updated_at: '2017-04-09T18:40:39.101Z',
},
{
id: 67,
@@ -139,6 +143,7 @@ export const mockGroups = [
project_count: 0,
subgroup_count: 0,
can_leave: false,
+ updated_at: '2017-04-09T18:40:39.101Z',
},
{
id: 54,
@@ -159,6 +164,7 @@ export const mockGroups = [
project_count: 0,
subgroup_count: 1,
can_leave: false,
+ updated_at: '2017-04-09T18:40:39.101Z',
},
{
id: 5,
@@ -179,6 +185,7 @@ export const mockGroups = [
project_count: 1,
subgroup_count: 0,
can_leave: false,
+ updated_at: '2017-04-09T18:40:39.101Z',
},
{
id: 4,
@@ -199,6 +206,7 @@ export const mockGroups = [
project_count: 2,
subgroup_count: 0,
can_leave: false,
+ updated_at: '2017-04-09T18:40:39.101Z',
},
{
id: 3,
@@ -219,6 +227,7 @@ export const mockGroups = [
project_count: 1,
subgroup_count: 0,
can_leave: false,
+ updated_at: '2017-04-09T18:40:39.101Z',
},
{
id: 2,
@@ -239,6 +248,7 @@ export const mockGroups = [
project_count: 4,
subgroup_count: 0,
can_leave: false,
+ updated_at: '2017-04-09T18:40:39.101Z',
},
];
@@ -262,6 +272,7 @@ export const mockSearchedGroups = [
project_count: 1,
subgroup_count: 2,
can_leave: false,
+ updated_at: '2017-04-09T18:40:39.101Z',
children: [
{
id: 57,
@@ -282,6 +293,7 @@ export const mockSearchedGroups = [
project_count: 4,
subgroup_count: 2,
can_leave: false,
+ updated_at: '2017-04-09T18:40:39.101Z',
children: [
{
id: 60,
@@ -302,6 +314,7 @@ export const mockSearchedGroups = [
project_count: 0,
subgroup_count: 1,
can_leave: false,
+ updated_at: '2017-04-09T18:40:39.101Z',
children: [
{
id: 61,
@@ -322,6 +335,7 @@ export const mockSearchedGroups = [
project_count: 2,
subgroup_count: 0,
can_leave: false,
+ updated_at: '2017-04-09T18:40:39.101Z',
children: [
{
id: 17,
@@ -336,6 +350,7 @@ export const mockSearchedGroups = [
permission: null,
edit_path: '/platform/hardware/bsp/kernel/common/v4.4/edit',
star_count: 0,
+ updated_at: '2017-09-12T06:37:04.925Z',
},
{
id: 16,
@@ -350,6 +365,7 @@ export const mockSearchedGroups = [
permission: null,
edit_path: '/platform/hardware/bsp/kernel/common/v4.1/edit',
star_count: 0,
+ updated_at: '2017-04-09T18:41:03.112Z',
},
],
},
diff --git a/spec/javascripts/helpers/locale_helper.js b/spec/javascripts/helpers/locale_helper.js
new file mode 100644
index 00000000000..99e6ce61234
--- /dev/null
+++ b/spec/javascripts/helpers/locale_helper.js
@@ -0,0 +1,11 @@
+/* eslint-disable import/prefer-default-export */
+
+export const setLanguage = (languageCode) => {
+ const htmlElement = document.querySelector('html');
+
+ if (languageCode) {
+ htmlElement.setAttribute('lang', languageCode);
+ } else {
+ htmlElement.removeAttribute('lang');
+ }
+};
diff --git a/spec/javascripts/image_diff/helpers/badge_helper_spec.js b/spec/javascripts/image_diff/helpers/badge_helper_spec.js
index fb9c7e59031..ce3add1fd90 100644
--- a/spec/javascripts/image_diff/helpers/badge_helper_spec.js
+++ b/spec/javascripts/image_diff/helpers/badge_helper_spec.js
@@ -89,15 +89,8 @@ describe('badge helper', () => {
});
it('should create icon comment button', () => {
- const iconEl = buttonEl.querySelector('i');
+ const iconEl = buttonEl.querySelector('svg');
expect(iconEl).toBeDefined();
- expect(iconEl.classList.contains('fa')).toEqual(true);
- expect(iconEl.classList.contains('fa-comment-o')).toEqual(true);
- });
-
- it('should have .image-comment-badge.inverted in button class', () => {
- expect(buttonEl.classList.contains('image-comment-badge')).toEqual(true);
- expect(buttonEl.classList.contains('inverted')).toEqual(true);
});
});
diff --git a/spec/javascripts/issue_show/components/app_spec.js b/spec/javascripts/issue_show/components/app_spec.js
index b47a8bf705f..1454ca52018 100644
--- a/spec/javascripts/issue_show/components/app_spec.js
+++ b/spec/javascripts/issue_show/components/app_spec.js
@@ -1,34 +1,41 @@
import Vue from 'vue';
+import MockAdapter from 'axios-mock-adapter';
+import axios from '~/lib/utils/axios_utils';
import '~/render_math';
import '~/render_gfm';
+import * as urlUtils from '~/lib/utils/url_utility';
import issuableApp from '~/issue_show/components/app.vue';
import eventHub from '~/issue_show/event_hub';
import issueShowData from '../mock_data';
+import setTimeoutPromise from '../../helpers/set_timeout_promise_helper';
function formatText(text) {
return text.trim().replace(/\s\s+/g, ' ');
}
+const REALTIME_REQUEST_STACK = [
+ issueShowData.initialRequest,
+ issueShowData.secondRequest,
+];
+
describe('Issuable output', () => {
- let requestData = issueShowData.initialRequest;
+ let mock;
+ let realtimeRequestCount = 0;
+ let vm;
document.body.innerHTML = '<span id="task_status"></span>';
- const interceptor = (request, next) => {
- next(request.respondWith(JSON.stringify(requestData), {
- status: 200,
- }));
- };
-
- let vm;
-
beforeEach((done) => {
spyOn(eventHub, '$emit');
const IssuableDescriptionComponent = Vue.extend(issuableApp);
- requestData = issueShowData.initialRequest;
- Vue.http.interceptors.push(interceptor);
+ mock = new MockAdapter(axios);
+ mock.onGet('/gitlab-org/gitlab-shell/issues/9/realtime_changes/realtime_changes').reply(() => {
+ const res = Promise.resolve([200, REALTIME_REQUEST_STACK[realtimeRequestCount]]);
+ realtimeRequestCount += 1;
+ return res;
+ });
vm = new IssuableDescriptionComponent({
propsData: {
@@ -52,9 +59,11 @@ describe('Issuable output', () => {
});
afterEach(() => {
- Vue.http.interceptors = _.without(Vue.http.interceptors, interceptor);
+ mock.reset();
+ realtimeRequestCount = 0;
vm.poll.stop();
+ vm.$destroy();
});
it('should render a title/description/edited and update title/description/edited on update', (done) => {
@@ -73,7 +82,6 @@ describe('Issuable output', () => {
expect(editedText.querySelector('time')).toBeTruthy();
})
.then(() => {
- requestData = issueShowData.secondRequest;
vm.poll.makeRequest();
})
.then(() => new Promise(resolve => setTimeout(resolve)))
@@ -137,24 +145,19 @@ describe('Issuable output', () => {
spyOn(vm.service, 'getData').and.callThrough();
spyOn(vm.service, 'updateIssuable').and.callFake(() => new Promise((resolve) => {
resolve({
- json() {
- return {
- confidential: false,
- web_url: location.pathname,
- };
+ data: {
+ confidential: false,
+ web_url: location.pathname,
},
});
}));
- vm.updateIssuable();
-
- setTimeout(() => {
- expect(
- vm.service.getData,
- ).toHaveBeenCalled();
-
- done();
- });
+ vm.updateIssuable()
+ .then(() => {
+ expect(vm.service.getData).toHaveBeenCalled();
+ })
+ .then(done)
+ .catch(done.fail);
});
it('correctly updates issuable data', (done) => {
@@ -162,29 +165,22 @@ describe('Issuable output', () => {
resolve();
}));
- vm.updateIssuable();
-
- setTimeout(() => {
- expect(
- vm.service.updateIssuable,
- ).toHaveBeenCalledWith(vm.formState);
- expect(
- eventHub.$emit,
- ).toHaveBeenCalledWith('close.form');
-
- done();
- });
+ vm.updateIssuable()
+ .then(() => {
+ expect(vm.service.updateIssuable).toHaveBeenCalledWith(vm.formState);
+ expect(eventHub.$emit).toHaveBeenCalledWith('close.form');
+ })
+ .then(done)
+ .catch(done.fail);
});
it('does not redirect if issue has not moved', (done) => {
- spyOn(gl.utils, 'visitUrl');
+ spyOn(urlUtils, 'visitUrl');
spyOn(vm.service, 'updateIssuable').and.callFake(() => new Promise((resolve) => {
resolve({
- json() {
- return {
- web_url: location.pathname,
- confidential: vm.isConfidential,
- };
+ data: {
+ web_url: location.pathname,
+ confidential: vm.isConfidential,
},
});
}));
@@ -193,7 +189,7 @@ describe('Issuable output', () => {
setTimeout(() => {
expect(
- gl.utils.visitUrl,
+ urlUtils.visitUrl,
).not.toHaveBeenCalled();
done();
@@ -201,14 +197,12 @@ describe('Issuable output', () => {
});
it('redirects if returned web_url has changed', (done) => {
- spyOn(gl.utils, 'visitUrl');
+ spyOn(urlUtils, 'visitUrl');
spyOn(vm.service, 'updateIssuable').and.callFake(() => new Promise((resolve) => {
resolve({
- json() {
- return {
- web_url: '/testing-issue-move',
- confidential: vm.isConfidential,
- };
+ data: {
+ web_url: '/testing-issue-move',
+ confidential: vm.isConfidential,
},
});
}));
@@ -217,7 +211,7 @@ describe('Issuable output', () => {
setTimeout(() => {
expect(
- gl.utils.visitUrl,
+ urlUtils.visitUrl,
).toHaveBeenCalledWith('/testing-issue-move');
done();
@@ -268,13 +262,57 @@ describe('Issuable output', () => {
});
});
+ it('opens recaptcha modal if update rejected as spam', (done) => {
+ function mockScriptSrc() {
+ const recaptchaChild = vm.$children
+ .find(child => child.$options._componentTag === 'recaptcha-modal'); // eslint-disable-line no-underscore-dangle
+
+ recaptchaChild.scriptSrc = '//scriptsrc';
+ }
+
+ let modal;
+ const promise = new Promise((resolve) => {
+ resolve({
+ data: {
+ recaptcha_html: '<div class="g-recaptcha">recaptcha_html</div>',
+ },
+ });
+ });
+
+ spyOn(vm.service, 'updateIssuable').and.returnValue(promise);
+
+ vm.canUpdate = true;
+ vm.showForm = true;
+
+ vm.$nextTick()
+ .then(() => mockScriptSrc())
+ .then(() => vm.updateIssuable())
+ .then(promise)
+ .then(() => setTimeoutPromise())
+ .then(() => {
+ modal = vm.$el.querySelector('.js-recaptcha-modal');
+
+ expect(modal.style.display).not.toEqual('none');
+ expect(modal.querySelector('.g-recaptcha').textContent).toEqual('recaptcha_html');
+ expect(document.body.querySelector('.js-recaptcha-script').src).toMatch('//scriptsrc');
+ })
+ .then(() => modal.querySelector('.close').click())
+ .then(() => vm.$nextTick())
+ .then(() => {
+ expect(modal.style.display).toEqual('none');
+ expect(document.body.querySelector('.js-recaptcha-script')).toBeNull();
+ })
+ .then(done)
+ .catch(done.fail);
+ });
+
describe('deleteIssuable', () => {
it('changes URL when deleted', (done) => {
- spyOn(gl.utils, 'visitUrl');
+ spyOn(urlUtils, 'visitUrl');
spyOn(vm.service, 'deleteIssuable').and.callFake(() => new Promise((resolve) => {
resolve({
- json() {
- return { web_url: '/test' };
+ data: {
+ web_url: '/test',
},
});
}));
@@ -283,7 +321,7 @@ describe('Issuable output', () => {
setTimeout(() => {
expect(
- gl.utils.visitUrl,
+ urlUtils.visitUrl,
).toHaveBeenCalledWith('/test');
done();
@@ -291,12 +329,12 @@ describe('Issuable output', () => {
});
it('stops polling when deleting', (done) => {
- spyOn(gl.utils, 'visitUrl');
+ spyOn(urlUtils, 'visitUrl');
spyOn(vm.poll, 'stop').and.callThrough();
spyOn(vm.service, 'deleteIssuable').and.callFake(() => new Promise((resolve) => {
resolve({
- json() {
- return { web_url: '/test' };
+ data: {
+ web_url: '/test',
},
});
}));
@@ -335,22 +373,21 @@ describe('Issuable output', () => {
describe('open form', () => {
it('shows locked warning if form is open & data is different', (done) => {
- Vue.nextTick()
+ vm.$nextTick()
.then(() => {
vm.openForm();
- requestData = issueShowData.secondRequest;
vm.poll.makeRequest();
})
- .then(() => new Promise(resolve => setTimeout(resolve)))
+ // Wait for the request
+ .then(vm.$nextTick)
+ // Wait for the successCallback to update the store state
+ .then(vm.$nextTick)
+ // Wait for the new state to flow to the Vue components
+ .then(vm.$nextTick)
.then(() => {
- expect(
- vm.formState.lockedWarningVisible,
- ).toBeTruthy();
-
- expect(
- vm.$el.querySelector('.alert'),
- ).not.toBeNull();
+ expect(vm.formState.lockedWarningVisible).toEqual(true);
+ expect(vm.$el.querySelector('.alert')).not.toBeNull();
})
.then(done)
.catch(done.fail);
diff --git a/spec/javascripts/issue_show/components/description_spec.js b/spec/javascripts/issue_show/components/description_spec.js
index 163e5cdd062..0da25bdca9c 100644
--- a/spec/javascripts/issue_show/components/description_spec.js
+++ b/spec/javascripts/issue_show/components/description_spec.js
@@ -51,6 +51,35 @@ describe('Description component', () => {
});
});
+ it('opens recaptcha dialog if update rejected as spam', (done) => {
+ let modal;
+ const recaptchaChild = vm.$children
+ .find(child => child.$options._componentTag === 'recaptcha-modal'); // eslint-disable-line no-underscore-dangle
+
+ recaptchaChild.scriptSrc = '//scriptsrc';
+
+ vm.taskListUpdateSuccess({
+ recaptcha_html: '<div class="g-recaptcha">recaptcha_html</div>',
+ });
+
+ vm.$nextTick()
+ .then(() => {
+ modal = vm.$el.querySelector('.js-recaptcha-modal');
+
+ expect(modal.style.display).not.toEqual('none');
+ expect(modal.querySelector('.g-recaptcha').textContent).toEqual('recaptcha_html');
+ expect(document.body.querySelector('.js-recaptcha-script').src).toMatch('//scriptsrc');
+ })
+ .then(() => modal.querySelector('.close').click())
+ .then(() => vm.$nextTick())
+ .then(() => {
+ expect(modal.style.display).toEqual('none');
+ expect(document.body.querySelector('.js-recaptcha-script')).toBeNull();
+ })
+ .then(done)
+ .catch(done.fail);
+ });
+
describe('TaskList', () => {
beforeEach(() => {
vm = mountComponent(DescriptionComponent, Object.assign({}, props, {
@@ -86,6 +115,7 @@ describe('Description component', () => {
dataType: 'issuableType',
fieldName: 'description',
selector: '.detail-page-description',
+ onSuccess: jasmine.any(Function),
});
done();
});
diff --git a/spec/javascripts/issue_show/mock_data.js b/spec/javascripts/issue_show/mock_data.js
index eb3111412a7..74b3efb014b 100644
--- a/spec/javascripts/issue_show/mock_data.js
+++ b/spec/javascripts/issue_show/mock_data.js
@@ -19,14 +19,4 @@ export default {
updated_by_name: 'Other User',
updated_by_path: '/other_user',
},
- issueSpecRequest: {
- title: '<p>this is a title</p>',
- title_text: 'this is a title',
- description: '<li class="task-list-item enabled"><input type="checkbox" class="task-list-item-checkbox">Task List Item</li>',
- description_text: '- [ ] Task List Item',
- task_status: '0 of 1 completed',
- updated_at: '2017-05-15T12:31:04.428Z',
- updated_by_name: 'Last User',
- updated_by_path: '/last_user',
- },
};
diff --git a/spec/javascripts/issue_spec.js b/spec/javascripts/issue_spec.js
index 3636aac79a0..2cd2e63b15d 100644
--- a/spec/javascripts/issue_spec.js
+++ b/spec/javascripts/issue_spec.js
@@ -55,7 +55,7 @@ describe('Issue', function() {
}
function findElements(isIssueInitiallyOpen) {
- $boxClosed = $('div.status-box-closed');
+ $boxClosed = $('div.status-box-issue-closed');
expect($boxClosed).toExist();
expect($boxClosed).toHaveText('Closed');
diff --git a/spec/javascripts/job_spec.js b/spec/javascripts/job_spec.js
index 20c4caa865d..b740c9ed893 100644
--- a/spec/javascripts/job_spec.js
+++ b/spec/javascripts/job_spec.js
@@ -1,6 +1,6 @@
-import { bytesToKiB } from '~/lib/utils/number_utils';
+import { numberToHumanSize } from '~/lib/utils/number_utils';
+import * as urlUtils from '~/lib/utils/url_utility';
import '~/lib/utils/datetime_utility';
-import '~/lib/utils/url_utility';
import Job from '~/job';
import '~/breakpoints';
@@ -65,7 +65,7 @@ describe('Job', () => {
const deferred2 = $.Deferred();
const deferred3 = $.Deferred();
spyOn($, 'ajax').and.returnValues(deferred1.promise(), deferred2.promise(), deferred3.promise());
- spyOn(gl.utils, 'visitUrl');
+ spyOn(urlUtils, 'visitUrl');
deferred1.resolve({
html: '<span>Update<span>',
@@ -103,7 +103,7 @@ describe('Job', () => {
spyOn($, 'ajax').and.returnValues(deferred1.promise(), deferred2.promise(), deferred3.promise());
- spyOn(gl.utils, 'visitUrl');
+ spyOn(urlUtils, 'visitUrl');
deferred1.resolve({
html: '<span>Update<span>',
@@ -134,7 +134,7 @@ describe('Job', () => {
describe('truncated information', () => {
describe('when size is less than total', () => {
it('shows information about truncated log', () => {
- spyOn(gl.utils, 'visitUrl');
+ spyOn(urlUtils, 'visitUrl');
const deferred = $.Deferred();
spyOn($, 'ajax').and.returnValue(deferred.promise());
@@ -153,7 +153,7 @@ describe('Job', () => {
it('shows the size in KiB', () => {
const size = 50;
- spyOn(gl.utils, 'visitUrl');
+ spyOn(urlUtils, 'visitUrl');
const deferred = $.Deferred();
spyOn($, 'ajax').and.returnValue(deferred.promise());
@@ -169,7 +169,7 @@ describe('Job', () => {
expect(
document.querySelector('.js-truncated-info-size').textContent.trim(),
- ).toEqual(`${bytesToKiB(size)}`);
+ ).toEqual(`${numberToHumanSize(size)}`);
});
it('shows incremented size', () => {
@@ -179,7 +179,7 @@ describe('Job', () => {
spyOn($, 'ajax').and.returnValues(deferred1.promise(), deferred2.promise(), deferred3.promise());
- spyOn(gl.utils, 'visitUrl');
+ spyOn(urlUtils, 'visitUrl');
deferred1.resolve({
html: '<span>Update</span>',
@@ -195,7 +195,7 @@ describe('Job', () => {
expect(
document.querySelector('.js-truncated-info-size').textContent.trim(),
- ).toEqual(`${bytesToKiB(50)}`);
+ ).toEqual(`${numberToHumanSize(50)}`);
jasmine.clock().tick(4001);
@@ -209,12 +209,12 @@ describe('Job', () => {
expect(
document.querySelector('.js-truncated-info-size').textContent.trim(),
- ).toEqual(`${bytesToKiB(60)}`);
+ ).toEqual(`${numberToHumanSize(60)}`);
});
it('renders the raw link', () => {
const deferred = $.Deferred();
- spyOn(gl.utils, 'visitUrl');
+ spyOn(urlUtils, 'visitUrl');
spyOn($, 'ajax').and.returnValue(deferred.promise());
deferred.resolve({
@@ -236,7 +236,7 @@ describe('Job', () => {
describe('when size is equal than total', () => {
it('does not show the trunctated information', () => {
const deferred = $.Deferred();
- spyOn(gl.utils, 'visitUrl');
+ spyOn(urlUtils, 'visitUrl');
spyOn($, 'ajax').and.returnValue(deferred.promise());
deferred.resolve({
@@ -257,7 +257,7 @@ describe('Job', () => {
describe('output trace', () => {
beforeEach(() => {
const deferred = $.Deferred();
- spyOn(gl.utils, 'visitUrl');
+ spyOn(urlUtils, 'visitUrl');
spyOn($, 'ajax').and.returnValue(deferred.promise());
deferred.resolve({
diff --git a/spec/javascripts/jobs/header_spec.js b/spec/javascripts/jobs/header_spec.js
index 4a210faa017..83395ea451e 100644
--- a/spec/javascripts/jobs/header_spec.js
+++ b/spec/javascripts/jobs/header_spec.js
@@ -1,5 +1,6 @@
import Vue from 'vue';
import headerComponent from '~/jobs/components/header.vue';
+import mountComponent from '../helpers/vue_mount_component_helper';
describe('Job details header', () => {
let HeaderComponent;
@@ -35,7 +36,7 @@ describe('Job details header', () => {
isLoading: false,
};
- vm = new HeaderComponent({ propsData: props }).$mount();
+ vm = mountComponent(HeaderComponent, props);
});
afterEach(() => {
diff --git a/spec/javascripts/lib/utils/text_utility_spec.js b/spec/javascripts/lib/utils/text_utility_spec.js
index 1f46c225071..6f8dad6b835 100644
--- a/spec/javascripts/lib/utils/text_utility_spec.js
+++ b/spec/javascripts/lib/utils/text_utility_spec.js
@@ -62,4 +62,14 @@ describe('text_utility', () => {
expect(textUtils.slugify('João')).toEqual('joão');
});
});
+
+ describe('stripeHtml', () => {
+ it('replaces html tag with the default replacement', () => {
+ expect(textUtils.stripeHtml('This is a text with <p>html</p>.')).toEqual('This is a text with html.');
+ });
+
+ it('replaces html tags with the provided replacement', () => {
+ expect(textUtils.stripeHtml('This is a text with <p>html</p>.', ' ')).toEqual('This is a text with html .');
+ });
+ });
});
diff --git a/spec/javascripts/lib/utils/tick_formats_spec.js b/spec/javascripts/lib/utils/tick_formats_spec.js
new file mode 100644
index 00000000000..283989b4fc8
--- /dev/null
+++ b/spec/javascripts/lib/utils/tick_formats_spec.js
@@ -0,0 +1,40 @@
+import { dateTickFormat, initDateFormats } from '~/lib/utils/tick_formats';
+
+import { setLanguage } from '../../helpers/locale_helper';
+
+describe('tick formats', () => {
+ describe('dateTickFormat', () => {
+ beforeAll(() => {
+ setLanguage('de');
+ initDateFormats();
+ });
+
+ afterAll(() => {
+ setLanguage(null);
+ });
+
+ it('returns year for first of January', () => {
+ const tick = dateTickFormat(new Date('2001-01-01'));
+
+ expect(tick).toBe('2001');
+ });
+
+ it('returns month for first of February', () => {
+ const tick = dateTickFormat(new Date('2001-02-01'));
+
+ expect(tick).toBe('Februar');
+ });
+
+ it('returns day and month for second of February', () => {
+ const tick = dateTickFormat(new Date('2001-02-02'));
+
+ expect(tick).toBe('2. Feb.');
+ });
+
+ it('ignores time', () => {
+ const tick = dateTickFormat(new Date('2001-02-02 12:34:56'));
+
+ expect(tick).toBe('2. Feb.');
+ });
+ });
+});
diff --git a/spec/javascripts/line_highlighter_spec.js b/spec/javascripts/line_highlighter_spec.js
index 645664a5219..89f4b85541d 100644
--- a/spec/javascripts/line_highlighter_spec.js
+++ b/spec/javascripts/line_highlighter_spec.js
@@ -1,7 +1,6 @@
/* eslint-disable space-before-function-paren, no-var, no-param-reassign, quotes, prefer-template, no-else-return, new-cap, dot-notation, no-return-assign, comma-dangle, no-new, one-var, one-var-declaration-per-line, jasmine/no-spec-dupes, no-underscore-dangle, max-len */
-/* global LineHighlighter */
-import '~/line_highlighter';
+import LineHighlighter from '~/line_highlighter';
(function() {
describe('LineHighlighter', function() {
diff --git a/spec/javascripts/locale/index_spec.js b/spec/javascripts/locale/index_spec.js
new file mode 100644
index 00000000000..29b0b21eed7
--- /dev/null
+++ b/spec/javascripts/locale/index_spec.js
@@ -0,0 +1,35 @@
+import { createDateTimeFormat, languageCode } from '~/locale';
+
+import { setLanguage } from '../helpers/locale_helper';
+
+describe('locale', () => {
+ afterEach(() => {
+ setLanguage(null);
+ });
+
+ describe('languageCode', () => {
+ it('parses the lang attribute', () => {
+ setLanguage('ja');
+
+ expect(languageCode()).toBe('ja');
+ });
+
+ it('falls back to English', () => {
+ setLanguage(null);
+
+ expect(languageCode()).toBe('en');
+ });
+ });
+
+ describe('createDateTimeFormat', () => {
+ beforeEach(() => {
+ setLanguage('de');
+ });
+
+ it('creates an instance of Intl.DateTimeFormat', () => {
+ const dateFormat = createDateTimeFormat({ year: 'numeric', month: 'long', day: 'numeric' });
+
+ expect(dateFormat.format(new Date(2015, 6, 3))).toBe('3. Juli 2015');
+ });
+ });
+});
diff --git a/spec/javascripts/merge_request_notes_spec.js b/spec/javascripts/merge_request_notes_spec.js
index 6054b75d0b8..e983e4de3fc 100644
--- a/spec/javascripts/merge_request_notes_spec.js
+++ b/spec/javascripts/merge_request_notes_spec.js
@@ -1,11 +1,9 @@
-/* global Notes */
-
import 'autosize';
import '~/gl_form';
import '~/lib/utils/text_utility';
import '~/render_gfm';
import '~/render_math';
-import '~/notes';
+import Notes from '~/notes';
const upArrowKeyCode = 38;
diff --git a/spec/javascripts/merge_request_spec.js b/spec/javascripts/merge_request_spec.js
index 70ae63ba036..2f02c11482f 100644
--- a/spec/javascripts/merge_request_spec.js
+++ b/spec/javascripts/merge_request_spec.js
@@ -1,7 +1,6 @@
/* eslint-disable space-before-function-paren, no-return-assign */
-/* global MergeRequest */
-import '~/merge_request';
+import MergeRequest from '~/merge_request';
import CloseReopenReportToggle from '~/close_reopen_report_toggle';
import IssuablesHelper from '~/helpers/issuables_helper';
diff --git a/spec/javascripts/merge_request_tabs_spec.js b/spec/javascripts/merge_request_tabs_spec.js
index e441d1153ed..a6be474805b 100644
--- a/spec/javascripts/merge_request_tabs_spec.js
+++ b/spec/javascripts/merge_request_tabs_spec.js
@@ -1,12 +1,12 @@
/* eslint-disable no-var, comma-dangle, object-shorthand */
-/* global Notes */
-import '~/merge_request_tabs';
+import * as urlUtils from '~/lib/utils/url_utility';
+import MergeRequestTabs from '~/merge_request_tabs';
import '~/commit/pipelines/pipelines_bundle';
import '~/breakpoints';
import '~/lib/utils/common_utils';
import Diff from '~/diff';
-import '~/notes';
+import Notes from '~/notes';
import 'vendor/jquery.scrollTo';
(function () {
@@ -31,7 +31,7 @@ import 'vendor/jquery.scrollTo';
);
beforeEach(function () {
- this.class = new gl.MergeRequestTabs({ stubLocation: stubLocation });
+ this.class = new MergeRequestTabs({ stubLocation: stubLocation });
setLocation();
this.spies = {
@@ -278,8 +278,8 @@ import 'vendor/jquery.scrollTo';
loadFixtures('merge_requests/diff_comment.html.raw');
$('body').attr('data-page', 'projects:merge_requests:show');
window.gl.ImageFile = () => {};
- window.notes = new Notes('', []);
- spyOn(window.notes, 'toggleDiffNote').and.callThrough();
+ Notes.initialize('', []);
+ spyOn(Notes.instance, 'toggleDiffNote').and.callThrough();
});
afterEach(() => {
@@ -290,15 +290,18 @@ import 'vendor/jquery.scrollTo';
$('body').removeAttr('data-page');
});
- it('requires an absolute pathname', function () {
- spyOn($, 'ajax').and.callFake(function (options) {
- expect(options.url).toEqual('/foo/bar/merge_requests/1/diffs.json');
+ it('triggers Ajax request to JSON endpoint', function (done) {
+ const url = '/foo/bar/merge_requests/1/diffs';
+ spyOn(this.class, 'ajaxGet').and.callFake((options) => {
+ expect(options.url).toEqual(`${url}.json`);
+ done();
});
- this.class.loadDiff('/foo/bar/merge_requests/1/diffs');
+ this.class.loadDiff(url);
});
- it('triggers scroll event when diff already loaded', function () {
+ it('triggers scroll event when diff already loaded', function (done) {
+ spyOn(this.class, 'ajaxGet').and.callFake(() => done.fail());
spyOn(document, 'dispatchEvent');
this.class.diffsLoaded = true;
@@ -307,6 +310,7 @@ import 'vendor/jquery.scrollTo';
expect(
document.dispatchEvent,
).toHaveBeenCalledWith(new CustomEvent('scroll'));
+ done();
});
describe('with inline diff', () => {
@@ -333,11 +337,11 @@ import 'vendor/jquery.scrollTo';
describe('with note fragment hash', () => {
it('should expand and scroll to linked fragment hash #note_xxx', function () {
- spyOn(window.gl.utils, 'getLocationHash').and.returnValue(noteId);
+ spyOn(urlUtils, 'getLocationHash').and.returnValue(noteId);
this.class.loadDiff('/foo/bar/merge_requests/1/diffs');
expect(noteId.length).toBeGreaterThan(0);
- expect(window.notes.toggleDiffNote).toHaveBeenCalledWith({
+ expect(Notes.instance.toggleDiffNote).toHaveBeenCalledWith({
target: jasmine.any(Object),
lineType: 'old',
forceShow: true,
@@ -345,20 +349,20 @@ import 'vendor/jquery.scrollTo';
});
it('should gracefully ignore non-existant fragment hash', function () {
- spyOn(window.gl.utils, 'getLocationHash').and.returnValue('note_something-that-does-not-exist');
+ spyOn(urlUtils, 'getLocationHash').and.returnValue('note_something-that-does-not-exist');
this.class.loadDiff('/foo/bar/merge_requests/1/diffs');
- expect(window.notes.toggleDiffNote).not.toHaveBeenCalled();
+ expect(Notes.instance.toggleDiffNote).not.toHaveBeenCalled();
});
});
describe('with line number fragment hash', () => {
it('should gracefully ignore line number fragment hash', function () {
- spyOn(window.gl.utils, 'getLocationHash').and.returnValue(noteLineNumId);
+ spyOn(urlUtils, 'getLocationHash').and.returnValue(noteLineNumId);
this.class.loadDiff('/foo/bar/merge_requests/1/diffs');
expect(noteLineNumId.length).toBeGreaterThan(0);
- expect(window.notes.toggleDiffNote).not.toHaveBeenCalled();
+ expect(Notes.instance.toggleDiffNote).not.toHaveBeenCalled();
});
});
});
@@ -387,12 +391,12 @@ import 'vendor/jquery.scrollTo';
describe('with note fragment hash', () => {
it('should expand and scroll to linked fragment hash #note_xxx', function () {
- spyOn(window.gl.utils, 'getLocationHash').and.returnValue(noteId);
+ spyOn(urlUtils, 'getLocationHash').and.returnValue(noteId);
this.class.loadDiff('/foo/bar/merge_requests/1/diffs');
expect(noteId.length).toBeGreaterThan(0);
- expect(window.notes.toggleDiffNote).toHaveBeenCalledWith({
+ expect(Notes.instance.toggleDiffNote).toHaveBeenCalledWith({
target: jasmine.any(Object),
lineType: 'new',
forceShow: true,
@@ -400,20 +404,20 @@ import 'vendor/jquery.scrollTo';
});
it('should gracefully ignore non-existant fragment hash', function () {
- spyOn(window.gl.utils, 'getLocationHash').and.returnValue('note_something-that-does-not-exist');
+ spyOn(urlUtils, 'getLocationHash').and.returnValue('note_something-that-does-not-exist');
this.class.loadDiff('/foo/bar/merge_requests/1/diffs');
- expect(window.notes.toggleDiffNote).not.toHaveBeenCalled();
+ expect(Notes.instance.toggleDiffNote).not.toHaveBeenCalled();
});
});
describe('with line number fragment hash', () => {
it('should gracefully ignore line number fragment hash', function () {
- spyOn(window.gl.utils, 'getLocationHash').and.returnValue(noteLineNumId);
+ spyOn(urlUtils, 'getLocationHash').and.returnValue(noteLineNumId);
this.class.loadDiff('/foo/bar/merge_requests/1/diffs');
expect(noteLineNumId.length).toBeGreaterThan(0);
- expect(window.notes.toggleDiffNote).not.toHaveBeenCalled();
+ expect(Notes.instance.toggleDiffNote).not.toHaveBeenCalled();
});
});
});
diff --git a/spec/javascripts/monitoring/graph/deployment_spec.js b/spec/javascripts/monitoring/graph/deployment_spec.js
index dea42d755d4..d07db871d69 100644
--- a/spec/javascripts/monitoring/graph/deployment_spec.js
+++ b/spec/javascripts/monitoring/graph/deployment_spec.js
@@ -11,145 +11,38 @@ const createComponent = (propsData) => {
};
describe('MonitoringDeployment', () => {
- const reducedDeploymentData = [deploymentData[0]];
- reducedDeploymentData[0].ref = reducedDeploymentData[0].ref.name;
- reducedDeploymentData[0].xPos = 10;
- reducedDeploymentData[0].time = new Date(reducedDeploymentData[0].created_at);
describe('Methods', () => {
- it('refText shows the ref when a tag is available', () => {
- reducedDeploymentData[0].tag = '1.0';
- const component = createComponent({
- showDeployInfo: false,
- deploymentData: reducedDeploymentData,
- graphWidth: 440,
- graphHeight: 300,
- graphHeightOffset: 120,
- });
-
- expect(
- component.refText(reducedDeploymentData[0]),
- ).toEqual(reducedDeploymentData[0].ref);
- });
-
- it('refText shows the sha when no tag is available', () => {
- reducedDeploymentData[0].tag = null;
- const component = createComponent({
- showDeployInfo: false,
- deploymentData: reducedDeploymentData,
- graphHeight: 300,
- graphWidth: 440,
- graphHeightOffset: 120,
- });
-
- expect(
- component.refText(reducedDeploymentData[0]),
- ).toContain('f5bcd1');
- });
-
- it('nameDeploymentClass creates a class with the prefix deploy-info-', () => {
+ it('should contain a hidden gradient', () => {
const component = createComponent({
- showDeployInfo: false,
- deploymentData: reducedDeploymentData,
+ showDeployInfo: true,
+ deploymentData,
graphHeight: 300,
graphWidth: 440,
graphHeightOffset: 120,
});
- expect(
- component.nameDeploymentClass(reducedDeploymentData[0]),
- ).toContain('deploy-info');
+ expect(component.$el.querySelector('#shadow-gradient')).not.toBeNull();
});
it('transformDeploymentGroup translates an available deployment', () => {
const component = createComponent({
showDeployInfo: false,
- deploymentData: reducedDeploymentData,
+ deploymentData,
graphHeight: 300,
graphWidth: 440,
graphHeightOffset: 120,
});
expect(
- component.transformDeploymentGroup(reducedDeploymentData[0]),
+ component.transformDeploymentGroup({ xPos: 16 }),
).toContain('translate(11, 20)');
});
- it('hides the deployment flag', () => {
- reducedDeploymentData[0].showDeploymentFlag = false;
- const component = createComponent({
- showDeployInfo: true,
- deploymentData: reducedDeploymentData,
- graphWidth: 440,
- graphHeight: 300,
- graphHeightOffset: 120,
- });
-
- expect(component.$el.querySelector('.js-deploy-info-box')).toBeNull();
- });
-
- it('positions the flag to the left when the xPos is too far right', () => {
- reducedDeploymentData[0].showDeploymentFlag = false;
- reducedDeploymentData[0].xPos = 250;
- const component = createComponent({
- showDeployInfo: true,
- deploymentData: reducedDeploymentData,
- graphWidth: 440,
- graphHeight: 300,
- graphHeightOffset: 120,
- });
-
- expect(
- component.positionFlag(reducedDeploymentData[0]),
- ).toBeLessThan(0);
- });
-
- it('shows the deployment flag', () => {
- reducedDeploymentData[0].showDeploymentFlag = true;
- const component = createComponent({
- showDeployInfo: true,
- deploymentData: reducedDeploymentData,
- graphHeight: 300,
- graphWidth: 440,
- graphHeightOffset: 120,
- });
-
- expect(
- component.$el.querySelector('.js-deploy-info-box').style.display,
- ).not.toEqual('display: none;');
- });
-
- it('shows the refText inside a text element with the deploy-info-text class', () => {
- reducedDeploymentData[0].showDeploymentFlag = true;
- const component = createComponent({
- showDeployInfo: true,
- deploymentData: reducedDeploymentData,
- graphHeight: 300,
- graphWidth: 440,
- graphHeightOffset: 120,
- });
-
- expect(
- component.$el.querySelector('.deploy-info-text').firstChild.nodeValue.trim(),
- ).toEqual(component.refText(reducedDeploymentData[0]));
- });
-
- it('should contain a hidden gradient', () => {
- const component = createComponent({
- showDeployInfo: true,
- deploymentData: reducedDeploymentData,
- graphHeight: 300,
- graphWidth: 440,
- graphHeightOffset: 120,
- });
-
- expect(component.$el.querySelector('#shadow-gradient')).not.toBeNull();
- });
-
describe('Computed props', () => {
it('calculatedHeight', () => {
const component = createComponent({
showDeployInfo: true,
- deploymentData: reducedDeploymentData,
+ deploymentData,
graphHeight: 300,
graphWidth: 440,
graphHeightOffset: 120,
diff --git a/spec/javascripts/monitoring/graph/flag_spec.js b/spec/javascripts/monitoring/graph/flag_spec.js
index 8ee1171419d..2d474e9092f 100644
--- a/spec/javascripts/monitoring/graph/flag_spec.js
+++ b/spec/javascripts/monitoring/graph/flag_spec.js
@@ -1,5 +1,6 @@
import Vue from 'vue';
import GraphFlag from '~/monitoring/components/graph/flag.vue';
+import { deploymentData } from '../mock_data';
const createComponent = (propsData) => {
const Component = Vue.extend(GraphFlag);
@@ -9,11 +10,6 @@ const createComponent = (propsData) => {
}).$mount();
};
-function getCoordinate(component, selector, coordinate) {
- const coordinateVal = component.$el.querySelector(selector).getAttribute(coordinate);
- return parseInt(coordinateVal, 10);
-}
-
const defaultValuesComponent = {
currentXCoordinate: 200,
currentYCoordinate: 100,
@@ -25,31 +21,111 @@ const defaultValuesComponent = {
graphHeight: 300,
graphHeightOffset: 120,
showFlagContent: true,
+ realPixelRatio: 1,
+ timeSeries: [{
+ values: [{
+ time: new Date('2017-06-04T18:17:33.501Z'),
+ value: '1.49609375',
+ }],
+ }],
+ unitOfDisplay: 'ms',
+ currentDataIndex: 0,
+ legendTitle: 'Average',
+};
+
+const deploymentFlagData = {
+ ...deploymentData[0],
+ ref: deploymentData[0].ref.name,
+ xPos: 10,
+ time: new Date(deploymentData[0].created_at),
};
describe('GraphFlag', () => {
- it('has a line and a circle located at the currentXCoordinate and currentYCoordinate', () => {
- const component = createComponent(defaultValuesComponent);
+ let component;
- expect(getCoordinate(component, '.selected-metric-line', 'x1'))
- .toEqual(component.currentXCoordinate);
- expect(getCoordinate(component, '.selected-metric-line', 'x2'))
- .toEqual(component.currentXCoordinate);
+ it('has a line at the currentXCoordinate', () => {
+ component = createComponent(defaultValuesComponent);
+
+ expect(component.$el.style.left)
+ .toEqual(`${70 + component.currentXCoordinate}px`);
});
- it('has a SVG with the class rect-text-metric at the currentFlagPosition', () => {
- const component = createComponent(defaultValuesComponent);
+ describe('Deployment flag', () => {
+ it('shows a deployment flag when deployment data provided', () => {
+ const deploymentFlagComponent = createComponent({
+ ...defaultValuesComponent,
+ deploymentFlagData,
+ });
+
+ expect(
+ deploymentFlagComponent.$el.querySelector('.popover-title'),
+ ).toContainText('Deployed');
+ });
+
+ it('contains the ref when a tag is available', () => {
+ const deploymentFlagComponent = createComponent({
+ ...defaultValuesComponent,
+ deploymentFlagData: {
+ ...deploymentFlagData,
+ sha: 'f5bcd1d9dac6fa4137e2510b9ccd134ef2e84187',
+ tag: true,
+ ref: '1.0',
+ },
+ });
+
+ expect(
+ deploymentFlagComponent.$el.querySelector('.deploy-meta-content'),
+ ).toContainText('f5bcd1d9');
+
+ expect(
+ deploymentFlagComponent.$el.querySelector('.deploy-meta-content'),
+ ).toContainText('1.0');
+ });
+
+ it('does not contain the ref when a tag is unavailable', () => {
+ const deploymentFlagComponent = createComponent({
+ ...defaultValuesComponent,
+ deploymentFlagData: {
+ ...deploymentFlagData,
+ sha: 'f5bcd1d9dac6fa4137e2510b9ccd134ef2e84187',
+ tag: false,
+ ref: '1.0',
+ },
+ });
+
+ expect(
+ deploymentFlagComponent.$el.querySelector('.deploy-meta-content'),
+ ).toContainText('f5bcd1d9');
- const svg = component.$el.querySelector('.rect-text-metric');
- expect(svg.tagName).toEqual('svg');
- expect(parseInt(svg.getAttribute('x'), 10)).toEqual(component.currentFlagPosition);
+ expect(
+ deploymentFlagComponent.$el.querySelector('.deploy-meta-content'),
+ ).not.toContainText('1.0');
+ });
});
describe('Computed props', () => {
- it('calculatedHeight', () => {
- const component = createComponent(defaultValuesComponent);
+ beforeEach(() => {
+ component = createComponent(defaultValuesComponent);
+ });
+
+ it('formatTime', () => {
+ expect(component.formatTime).toMatch(/\d:17PM/);
+ });
+
+ it('formatDate', () => {
+ expect(component.formatDate).toEqual('Sun, Jun 4');
+ });
+
+ it('cursorStyle', () => {
+ expect(component.cursorStyle).toEqual({
+ top: '20px',
+ left: '270px',
+ height: '180px',
+ });
+ });
- expect(component.calculatedHeight).toEqual(180);
+ it('flagOrientation', () => {
+ expect(component.flagOrientation).toEqual('left');
});
});
});
diff --git a/spec/javascripts/monitoring/graph_spec.js b/spec/javascripts/monitoring/graph_spec.js
index fd79abe241a..b1d69752bad 100644
--- a/spec/javascripts/monitoring/graph_spec.js
+++ b/spec/javascripts/monitoring/graph_spec.js
@@ -4,6 +4,8 @@ import MonitoringMixins from '~/monitoring/mixins/monitoring_mixins';
import eventHub from '~/monitoring/event_hub';
import { deploymentData, convertDatesMultipleSeries, singleRowMetricsMultipleSeries } from './mock_data';
+const tagsPath = 'http://test.host/frontend-fixtures/environments-project/tags';
+const projectPath = 'http://test.host/frontend-fixtures/environments-project';
const createComponent = (propsData) => {
const Component = Vue.extend(Graph);
@@ -25,6 +27,8 @@ describe('Graph', () => {
classType: 'col-md-6',
updateAspectRatio: false,
deploymentData,
+ tagsPath,
+ projectPath,
});
expect(component.$el.querySelector('.text-center').innerText.trim()).toBe(component.graphData.title);
@@ -37,6 +41,8 @@ describe('Graph', () => {
classType: 'col-md-6',
updateAspectRatio: false,
deploymentData,
+ tagsPath,
+ projectPath,
});
const transformedHeight = `${component.graphHeight - 100}`;
@@ -50,6 +56,8 @@ describe('Graph', () => {
classType: 'col-md-6',
updateAspectRatio: false,
deploymentData,
+ tagsPath,
+ projectPath,
});
const viewBoxArray = component.outerViewBox.split(' ');
@@ -65,6 +73,8 @@ describe('Graph', () => {
classType: 'col-md-6',
updateAspectRatio: false,
deploymentData,
+ tagsPath,
+ projectPath,
});
spyOn(eventHub, '$emit');
@@ -81,6 +91,8 @@ describe('Graph', () => {
classType: 'col-md-6',
updateAspectRatio: false,
deploymentData,
+ tagsPath,
+ projectPath,
});
expect(component.yAxisLabel).toEqual(component.graphData.y_label);
@@ -98,6 +110,8 @@ describe('Graph', () => {
hoveredDate: new Date('Sun Aug 27 2017 06:11:51 GMT-0500 (CDT)'),
currentDeployXPos: null,
},
+ tagsPath,
+ projectPath,
});
component.positionFlag();
diff --git a/spec/javascripts/monitoring/mock_data.js b/spec/javascripts/monitoring/mock_data.js
index 6b34855b8b2..2bbe963e393 100644
--- a/spec/javascripts/monitoring/mock_data.js
+++ b/spec/javascripts/monitoring/mock_data.js
@@ -1,6 +1,8 @@
/* eslint-disable quote-props, indent, comma-dangle */
-const metricsGroupsAPIResponse = {
+export const mockApiEndpoint = `${gl.TEST_HOST}/monitoring/mock`;
+
+export const metricsGroupsAPIResponse = {
'success': true,
'data': [
{
@@ -2430,33 +2432,39 @@ export const deploymentData = [
id: 111,
iid: 3,
sha: 'f5bcd1d9dac6fa4137e2510b9ccd134ef2e84187',
+ commitUrl: 'http://test.host/frontend-fixtures/environments-project/commit/f5bcd1d9dac6fa4137e2510b9ccd134ef2e84187',
ref: {
name: 'master'
},
created_at: '2017-05-31T21:23:37.881Z',
tag: false,
+ tagUrl: 'http://test.host/frontend-fixtures/environments-project/tags/false',
'last?': true
},
{
id: 110,
iid: 2,
sha: 'f5bcd1d9dac6fa4137e2510b9ccd134ef2e84187',
+ commitUrl: 'http://test.host/frontend-fixtures/environments-project/commit/f5bcd1d9dac6fa4137e2510b9ccd134ef2e84187',
ref: {
name: 'master'
},
created_at: '2017-05-30T20:08:04.629Z',
tag: false,
+ tagUrl: 'http://test.host/frontend-fixtures/environments-project/tags/false',
'last?': false
},
{
id: 109,
iid: 1,
sha: '6511e58faafaa7ad2228990ec57f19d66f7db7c2',
+ commitUrl: 'http://test.host/frontend-fixtures/environments-project/commit/6511e58faafaa7ad2228990ec57f19d66f7db7c2',
ref: {
name: 'update2-readme'
},
created_at: '2017-05-30T17:42:38.409Z',
tag: false,
+ tagUrl: 'http://test.host/frontend-fixtures/environments-project/tags/false',
'last?': false
}
];
diff --git a/spec/javascripts/notes/components/issue_comment_form_spec.js b/spec/javascripts/notes/components/comment_form_spec.js
index 04a7f8e32f1..20e352dd8bd 100644
--- a/spec/javascripts/notes/components/issue_comment_form_spec.js
+++ b/spec/javascripts/notes/components/comment_form_spec.js
@@ -1,7 +1,7 @@
import Vue from 'vue';
import Autosize from 'autosize';
import store from '~/notes/stores';
-import issueCommentForm from '~/notes/components/issue_comment_form.vue';
+import issueCommentForm from '~/notes/components/comment_form.vue';
import { loggedOutnoteableData, notesDataMock, userDataMock, noteableDataMock } from '../mock_data';
import { keyboardDownEvent } from '../../issue_show/helpers';
diff --git a/spec/javascripts/notes/components/issue_note_app_spec.js b/spec/javascripts/notes/components/note_app_spec.js
index 8e43037f356..7c8d6685ee1 100644
--- a/spec/javascripts/notes/components/issue_note_app_spec.js
+++ b/spec/javascripts/notes/components/note_app_spec.js
@@ -1,26 +1,15 @@
import Vue from 'vue';
-import issueNotesApp from '~/notes/components/issue_notes_app.vue';
+import notesApp from '~/notes/components/notes_app.vue';
import service from '~/notes/services/notes_service';
import * as mockData from '../mock_data';
+import getSetTimeoutPromise from '../../helpers/set_timeout_promise_helper';
-describe('issue_note_app', () => {
+describe('note_app', () => {
let mountComponent;
let vm;
- const individualNoteInterceptor = (request, next) => {
- next(request.respondWith(JSON.stringify(mockData.individualNoteServerResponse), {
- status: 200,
- }));
- };
-
- const discussionNoteInterceptor = (request, next) => {
- next(request.respondWith(JSON.stringify(mockData.discussionNoteServerResponse), {
- status: 200,
- }));
- };
-
beforeEach(() => {
- const IssueNotesApp = Vue.extend(issueNotesApp);
+ const IssueNotesApp = Vue.extend(notesApp);
mountComponent = (data) => {
const props = data || {
@@ -74,16 +63,16 @@ describe('issue_note_app', () => {
describe('render', () => {
beforeEach(() => {
- Vue.http.interceptors.push(individualNoteInterceptor);
+ Vue.http.interceptors.push(mockData.individualNoteInterceptor);
vm = mountComponent();
});
afterEach(() => {
- Vue.http.interceptors = _.without(Vue.http.interceptors, individualNoteInterceptor);
+ Vue.http.interceptors = _.without(Vue.http.interceptors, mockData.individualNoteInterceptor);
});
it('should render list of notes', (done) => {
- const note = mockData.individualNoteServerResponse[0].notes[0];
+ const note = mockData.INDIVIDUAL_NOTE_RESPONSE_MAP.GET['/gitlab-org/gitlab-ce/issues/26/discussions.json'][0].notes[0];
setTimeout(() => {
expect(
@@ -129,13 +118,16 @@ describe('issue_note_app', () => {
describe('update note', () => {
describe('individual note', () => {
beforeEach(() => {
- Vue.http.interceptors.push(individualNoteInterceptor);
- spyOn(service, 'updateNote').and.callFake(() => Promise.resolve());
+ Vue.http.interceptors.push(mockData.individualNoteInterceptor);
+ spyOn(service, 'updateNote').and.callThrough();
vm = mountComponent();
});
afterEach(() => {
- Vue.http.interceptors = _.without(Vue.http.interceptors, individualNoteInterceptor);
+ Vue.http.interceptors = _.without(
+ Vue.http.interceptors,
+ mockData.individualNoteInterceptor,
+ );
});
it('renders edit form', (done) => {
@@ -149,28 +141,36 @@ describe('issue_note_app', () => {
});
it('calls the service to update the note', (done) => {
- setTimeout(() => {
- vm.$el.querySelector('.js-note-edit').click();
- Vue.nextTick(() => {
+ getSetTimeoutPromise()
+ .then(() => {
+ vm.$el.querySelector('.js-note-edit').click();
+ })
+ .then(Vue.nextTick)
+ .then(() => {
vm.$el.querySelector('.js-vue-issue-note-form').value = 'this is a note';
vm.$el.querySelector('.js-vue-issue-save').click();
expect(service.updateNote).toHaveBeenCalled();
- done();
- });
- }, 0);
+ })
+ // Wait for the requests to finish before destroying
+ .then(Vue.nextTick)
+ .then(done)
+ .catch(done.fail);
});
});
describe('dicussion note', () => {
beforeEach(() => {
- Vue.http.interceptors.push(discussionNoteInterceptor);
- spyOn(service, 'updateNote').and.callFake(() => Promise.resolve());
+ Vue.http.interceptors.push(mockData.discussionNoteInterceptor);
+ spyOn(service, 'updateNote').and.callThrough();
vm = mountComponent();
});
afterEach(() => {
- Vue.http.interceptors = _.without(Vue.http.interceptors, discussionNoteInterceptor);
+ Vue.http.interceptors = _.without(
+ Vue.http.interceptors,
+ mockData.discussionNoteInterceptor,
+ );
});
it('renders edit form', (done) => {
@@ -184,16 +184,21 @@ describe('issue_note_app', () => {
});
it('updates the note and resets the edit form', (done) => {
- setTimeout(() => {
- vm.$el.querySelector('.js-note-edit').click();
- Vue.nextTick(() => {
+ getSetTimeoutPromise()
+ .then(() => {
+ vm.$el.querySelector('.js-note-edit').click();
+ })
+ .then(Vue.nextTick)
+ .then(() => {
vm.$el.querySelector('.js-vue-issue-note-form').value = 'this is a note';
vm.$el.querySelector('.js-vue-issue-save').click();
expect(service.updateNote).toHaveBeenCalled();
- done();
- });
- }, 0);
+ })
+ // Wait for the requests to finish before destroying
+ .then(Vue.nextTick)
+ .then(done)
+ .catch(done.fail);
});
});
});
@@ -216,12 +221,12 @@ describe('issue_note_app', () => {
describe('edit form', () => {
beforeEach(() => {
- Vue.http.interceptors.push(individualNoteInterceptor);
+ Vue.http.interceptors.push(mockData.individualNoteInterceptor);
vm = mountComponent();
});
afterEach(() => {
- Vue.http.interceptors = _.without(Vue.http.interceptors, individualNoteInterceptor);
+ Vue.http.interceptors = _.without(Vue.http.interceptors, mockData.individualNoteInterceptor);
});
it('should render markdown docs url', (done) => {
diff --git a/spec/javascripts/notes/components/issue_note_body_spec.js b/spec/javascripts/notes/components/note_body_spec.js
index 37aad50737b..b42e7943b98 100644
--- a/spec/javascripts/notes/components/issue_note_body_spec.js
+++ b/spec/javascripts/notes/components/note_body_spec.js
@@ -1,7 +1,7 @@
import Vue from 'vue';
import store from '~/notes/stores';
-import noteBody from '~/notes/components/issue_note_body.vue';
+import noteBody from '~/notes/components/note_body.vue';
import { noteableDataMock, notesDataMock, note } from '../mock_data';
describe('issue_note_body component', () => {
diff --git a/spec/javascripts/notes/components/issue_note_form_spec.js b/spec/javascripts/notes/components/note_form_spec.js
index d42ef239711..86e9e2a32a9 100644
--- a/spec/javascripts/notes/components/issue_note_form_spec.js
+++ b/spec/javascripts/notes/components/note_form_spec.js
@@ -1,6 +1,6 @@
import Vue from 'vue';
import store from '~/notes/stores';
-import issueNoteForm from '~/notes/components/issue_note_form.vue';
+import issueNoteForm from '~/notes/components/note_form.vue';
import { noteableDataMock, notesDataMock } from '../mock_data';
import { keyboardDownEvent } from '../../issue_show/helpers';
diff --git a/spec/javascripts/notes/components/issue_discussion_spec.js b/spec/javascripts/notes/components/noteable_discussion_spec.js
index b6ae55d44f5..19504e4f7c8 100644
--- a/spec/javascripts/notes/components/issue_discussion_spec.js
+++ b/spec/javascripts/notes/components/noteable_discussion_spec.js
@@ -1,6 +1,6 @@
import Vue from 'vue';
import store from '~/notes/stores';
-import issueDiscussion from '~/notes/components/issue_discussion.vue';
+import issueDiscussion from '~/notes/components/noteable_discussion.vue';
import { noteableDataMock, discussionMock, notesDataMock } from '../mock_data';
describe('issue_discussion component', () => {
@@ -30,7 +30,7 @@ describe('issue_discussion component', () => {
it('should render discussion header', () => {
expect(vm.$el.querySelector('.discussion-header')).toBeDefined();
- expect(vm.$el.querySelectorAll('.notes li').length).toEqual(discussionMock.notes.length);
+ expect(vm.$el.querySelector('.notes').children.length).toEqual(discussionMock.notes.length);
});
describe('actions', () => {
diff --git a/spec/javascripts/notes/components/issue_note_spec.js b/spec/javascripts/notes/components/noteable_note_spec.js
index 73fd188dbe5..c8a6cb7e612 100644
--- a/spec/javascripts/notes/components/issue_note_spec.js
+++ b/spec/javascripts/notes/components/noteable_note_spec.js
@@ -1,7 +1,7 @@
import Vue from 'vue';
import store from '~/notes/stores';
-import issueNote from '~/notes/components/issue_note.vue';
+import issueNote from '~/notes/components/noteable_note.vue';
import { noteableDataMock, notesDataMock, note } from '../mock_data';
describe('issue_note', () => {
@@ -41,4 +41,19 @@ describe('issue_note', () => {
it('should render issue body', () => {
expect(vm.$el.querySelector('.note-text').innerHTML).toEqual(note.note_html);
});
+
+ it('prevents note preview xss', (done) => {
+ const imgSrc = 'data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7';
+ const noteBody = `<img src="${imgSrc}" onload="alert(1)" />`;
+ const alertSpy = spyOn(window, 'alert');
+ vm.updateNote = () => new Promise($.noop);
+
+ vm.formUpdateHandler(noteBody, null, $.noop);
+
+ setTimeout(() => {
+ expect(alertSpy).not.toHaveBeenCalled();
+ expect(vm.note.note_html).toEqual(_.escape(noteBody));
+ done();
+ }, 0);
+ });
});
diff --git a/spec/javascripts/notes/mock_data.js b/spec/javascripts/notes/mock_data.js
index 42497de3c55..6b608adff15 100644
--- a/spec/javascripts/notes/mock_data.js
+++ b/spec/javascripts/notes/mock_data.js
@@ -312,138 +312,212 @@ export const loggedOutnoteableData = {
"preview_note_path": "/gitlab-org/gitlab-ce/preview_markdown?quick_actions_target_id=98&quick_actions_target_type=Issue"
}
-export const individualNoteServerResponse = [{
- "id": "0fb4e0e3f9276e55ff32eb4195add694aece4edd",
- "reply_id": "0fb4e0e3f9276e55ff32eb4195add694aece4edd",
- "expanded": true,
- "notes": [{
- "id": 1390,
- "attachment": {
- "url": null,
- "filename": null,
- "image": false
- },
- "author": {
- "id": 1,
- "name": "Root",
- "username": "root",
- "state": "active",
- "avatar_url": null,
- "path": "/root"
- },
- "created_at": "2017-08-01T17:09:33.762Z",
- "updated_at": "2017-08-01T17:09:33.762Z",
- "system": false,
- "noteable_id": 98,
- "noteable_type": "Issue",
- "type": null,
- "human_access": "Owner",
- "note": "sdfdsaf",
- "note_html": "\u003cp dir=\"auto\"\u003esdfdsaf\u003c/p\u003e",
- "current_user": {
- "can_edit": true
+export const INDIVIDUAL_NOTE_RESPONSE_MAP = {
+ 'GET': {
+ '/gitlab-org/gitlab-ce/issues/26/discussions.json': [{
+ "id": "0fb4e0e3f9276e55ff32eb4195add694aece4edd",
+ "reply_id": "0fb4e0e3f9276e55ff32eb4195add694aece4edd",
+ "expanded": true,
+ "notes": [{
+ "id": 1390,
+ "attachment": {
+ "url": null,
+ "filename": null,
+ "image": false
+ },
+ "author": {
+ "id": 1,
+ "name": "Root",
+ "username": "root",
+ "state": "active",
+ "avatar_url": null,
+ "path": "/root"
+ },
+ "created_at": "2017-08-01T17:09:33.762Z",
+ "updated_at": "2017-08-01T17:09:33.762Z",
+ "system": false,
+ "noteable_id": 98,
+ "noteable_type": "Issue",
+ "type": null,
+ "human_access": "Owner",
+ "note": "sdfdsaf",
+ "note_html": "\u003cp dir=\"auto\"\u003esdfdsaf\u003c/p\u003e",
+ "current_user": {
+ "can_edit": true
+ },
+ "discussion_id": "0fb4e0e3f9276e55ff32eb4195add694aece4edd",
+ "emoji_awardable": true,
+ "award_emoji": [{
+ "name": "baseball",
+ "user": {
+ "id": 1,
+ "name": "Root",
+ "username": "root"
+ }
+ }, {
+ "name": "art",
+ "user": {
+ "id": 1,
+ "name": "Root",
+ "username": "root"
+ }
+ }],
+ "toggle_award_path": "/gitlab-org/gitlab-ce/notes/1390/toggle_award_emoji",
+ "report_abuse_path": "/abuse_reports/new?ref_url=http%3A%2F%2Flocalhost%3A3000%2Fgitlab-org%2Fgitlab-ce%2Fissues%2F26%23note_1390\u0026user_id=1",
+ "path": "/gitlab-org/gitlab-ce/notes/1390"
+ }],
+ "individual_note": true
+ }, {
+ "id": "70d5c92a4039a36c70100c6691c18c27e4b0a790",
+ "reply_id": "70d5c92a4039a36c70100c6691c18c27e4b0a790",
+ "expanded": true,
+ "notes": [{
+ "id": 1391,
+ "attachment": {
+ "url": null,
+ "filename": null,
+ "image": false
+ },
+ "author": {
+ "id": 1,
+ "name": "Root",
+ "username": "root",
+ "state": "active",
+ "avatar_url": null,
+ "path": "/root"
+ },
+ "created_at": "2017-08-02T10:51:38.685Z",
+ "updated_at": "2017-08-02T10:51:38.685Z",
+ "system": false,
+ "noteable_id": 98,
+ "noteable_type": "Issue",
+ "type": null,
+ "human_access": "Owner",
+ "note": "New note!",
+ "note_html": "\u003cp dir=\"auto\"\u003eNew note!\u003c/p\u003e",
+ "current_user": {
+ "can_edit": true
+ },
+ "discussion_id": "70d5c92a4039a36c70100c6691c18c27e4b0a790",
+ "emoji_awardable": true,
+ "award_emoji": [],
+ "toggle_award_path": "/gitlab-org/gitlab-ce/notes/1391/toggle_award_emoji",
+ "report_abuse_path": "/abuse_reports/new?ref_url=http%3A%2F%2Flocalhost%3A3000%2Fgitlab-org%2Fgitlab-ce%2Fissues%2F26%23note_1391\u0026user_id=1",
+ "path": "/gitlab-org/gitlab-ce/notes/1391"
+ }],
+ "individual_note": true
+ }],
+ '/gitlab-org/gitlab-ce/noteable/issue/98/notes': {
+ last_fetched_at: 1512900838,
+ notes: [],
},
- "discussion_id": "0fb4e0e3f9276e55ff32eb4195add694aece4edd",
- "emoji_awardable": true,
- "award_emoji": [{
- "name": "baseball",
- "user": {
+ },
+ 'PUT': {
+ '/gitlab-org/gitlab-ce/notes/1471': {
+ "commands_changes": null,
+ "valid": true,
+ "id": 1471,
+ "attachment": null,
+ "author": {
"id": 1,
"name": "Root",
- "username": "root"
- }
- }, {
- "name": "art",
- "user": {
+ "username": "root",
+ "state": "active",
+ "avatar_url": null,
+ "path": "/root"
+ },
+ "created_at": "2017-08-08T16:53:00.666Z",
+ "updated_at": "2017-12-10T11:03:21.876Z",
+ "system": false,
+ "noteable_id": 124,
+ "noteable_type": "Issue",
+ "noteable_iid": 29,
+ "type": "DiscussionNote",
+ "human_access": "Owner",
+ "note": "Adding a comment",
+ "note_html": "\u003cp dir=\"auto\"\u003eAdding a comment\u003c/p\u003e",
+ "last_edited_at": "2017-12-10T11:03:21.876Z",
+ "last_edited_by": {
"id": 1,
- "name": "Root",
- "username": "root"
- }
- }],
- "toggle_award_path": "/gitlab-org/gitlab-ce/notes/1390/toggle_award_emoji",
- "report_abuse_path": "/abuse_reports/new?ref_url=http%3A%2F%2Flocalhost%3A3000%2Fgitlab-org%2Fgitlab-ce%2Fissues%2F26%23note_1390\u0026user_id=1",
- "path": "/gitlab-org/gitlab-ce/notes/1390"
- }],
- "individual_note": true
- }, {
- "id": "70d5c92a4039a36c70100c6691c18c27e4b0a790",
- "reply_id": "70d5c92a4039a36c70100c6691c18c27e4b0a790",
- "expanded": true,
- "notes": [{
- "id": 1391,
- "attachment": {
- "url": null,
- "filename": null,
- "image": false
- },
- "author": {
- "id": 1,
- "name": "Root",
- "username": "root",
- "state": "active",
- "avatar_url": null,
- "path": "/root"
- },
- "created_at": "2017-08-02T10:51:38.685Z",
- "updated_at": "2017-08-02T10:51:38.685Z",
- "system": false,
- "noteable_id": 98,
- "noteable_type": "Issue",
- "type": null,
- "human_access": "Owner",
- "note": "New note!",
- "note_html": "\u003cp dir=\"auto\"\u003eNew note!\u003c/p\u003e",
- "current_user": {
- "can_edit": true
+ "name": 'Root',
+ "username": 'root',
+ "state": 'active',
+ "avatar_url": null,
+ "path": '/root',
+ },
+ "current_user": {
+ "can_edit": true
+ },
+ "discussion_id": "a3ed36e29b1957efb3b68c53e2d7a2b24b1df052",
+ "emoji_awardable": true,
+ "award_emoji": [],
+ "toggle_award_path": "/gitlab-org/gitlab-ce/notes/1471/toggle_award_emoji",
+ "report_abuse_path": "/abuse_reports/new?ref_url=http%3A%2F%2Flocalhost%3A3000%2Fgitlab-org%2Fgitlab-ce%2Fissues%2F29%23note_1471\u0026user_id=1",
+ "path": "/gitlab-org/gitlab-ce/notes/1471"
},
- "discussion_id": "70d5c92a4039a36c70100c6691c18c27e4b0a790",
- "emoji_awardable": true,
- "award_emoji": [],
- "toggle_award_path": "/gitlab-org/gitlab-ce/notes/1391/toggle_award_emoji",
- "report_abuse_path": "/abuse_reports/new?ref_url=http%3A%2F%2Flocalhost%3A3000%2Fgitlab-org%2Fgitlab-ce%2Fissues%2F26%23note_1391\u0026user_id=1",
- "path": "/gitlab-org/gitlab-ce/notes/1391"
- }],
- "individual_note": true
-}];
+ }
+};
-export const discussionNoteServerResponse = [{
- "id": "a3ed36e29b1957efb3b68c53e2d7a2b24b1df052",
- "reply_id": "a3ed36e29b1957efb3b68c53e2d7a2b24b1df052",
- "expanded": true,
- "notes": [{
- "id": 1471,
- "attachment": {
- "url": null,
- "filename": null,
- "image": false
- },
- "author": {
- "id": 1,
- "name": "Root",
- "username": "root",
- "state": "active",
- "avatar_url": null,
- "path": "/root"
- },
- "created_at": "2017-08-08T16:53:00.666Z",
- "updated_at": "2017-08-08T16:53:00.666Z",
- "system": false,
- "noteable_id": 124,
- "noteable_type": "Issue",
- "noteable_iid": 29,
- "type": "DiscussionNote",
- "human_access": "Owner",
- "note": "Adding a comment",
- "note_html": "\u003cp dir=\"auto\"\u003eAdding a comment\u003c/p\u003e",
- "current_user": {
- "can_edit": true
- },
- "discussion_id": "a3ed36e29b1957efb3b68c53e2d7a2b24b1df052",
- "emoji_awardable": true,
- "award_emoji": [],
- "toggle_award_path": "/gitlab-org/gitlab-ce/notes/1471/toggle_award_emoji",
- "report_abuse_path": "/abuse_reports/new?ref_url=http%3A%2F%2Flocalhost%3A3000%2Fgitlab-org%2Fgitlab-ce%2Fissues%2F29%23note_1471\u0026user_id=1",
- "path": "/gitlab-org/gitlab-ce/notes/1471"
- }],
- "individual_note": false
-}];
+export const DISCUSSION_NOTE_RESPONSE_MAP = {
+ ...INDIVIDUAL_NOTE_RESPONSE_MAP,
+ 'GET': {
+ ...INDIVIDUAL_NOTE_RESPONSE_MAP.GET,
+ '/gitlab-org/gitlab-ce/issues/26/discussions.json': [{
+ "id": "a3ed36e29b1957efb3b68c53e2d7a2b24b1df052",
+ "reply_id": "a3ed36e29b1957efb3b68c53e2d7a2b24b1df052",
+ "expanded": true,
+ "notes": [{
+ "id": 1471,
+ "attachment": {
+ "url": null,
+ "filename": null,
+ "image": false
+ },
+ "author": {
+ "id": 1,
+ "name": "Root",
+ "username": "root",
+ "state": "active",
+ "avatar_url": null,
+ "path": "/root"
+ },
+ "created_at": "2017-08-08T16:53:00.666Z",
+ "updated_at": "2017-08-08T16:53:00.666Z",
+ "system": false,
+ "noteable_id": 124,
+ "noteable_type": "Issue",
+ "noteable_iid": 29,
+ "type": "DiscussionNote",
+ "human_access": "Owner",
+ "note": "Adding a comment",
+ "note_html": "\u003cp dir=\"auto\"\u003eAdding a comment\u003c/p\u003e",
+ "current_user": {
+ "can_edit": true
+ },
+ "discussion_id": "a3ed36e29b1957efb3b68c53e2d7a2b24b1df052",
+ "emoji_awardable": true,
+ "award_emoji": [],
+ "toggle_award_path": "/gitlab-org/gitlab-ce/notes/1471/toggle_award_emoji",
+ "report_abuse_path": "/abuse_reports/new?ref_url=http%3A%2F%2Flocalhost%3A3000%2Fgitlab-org%2Fgitlab-ce%2Fissues%2F29%23note_1471\u0026user_id=1",
+ "path": "/gitlab-org/gitlab-ce/notes/1471"
+ }],
+ "individual_note": false
+ }],
+ },
+};
+
+export function individualNoteInterceptor(request, next) {
+ const body = INDIVIDUAL_NOTE_RESPONSE_MAP[request.method.toUpperCase()][request.url];
+
+ next(request.respondWith(JSON.stringify(body), {
+ status: 200,
+ }));
+}
+
+export function discussionNoteInterceptor(request, next) {
+ const body = DISCUSSION_NOTE_RESPONSE_MAP[request.method.toUpperCase()][request.url];
+
+ next(request.respondWith(JSON.stringify(body), {
+ status: 200,
+ }));
+}
diff --git a/spec/javascripts/notes_spec.js b/spec/javascripts/notes_spec.js
index 677a389b88f..167f074fb9b 100644
--- a/spec/javascripts/notes_spec.js
+++ b/spec/javascripts/notes_spec.js
@@ -1,11 +1,10 @@
/* eslint-disable space-before-function-paren, no-unused-expressions, no-var, object-shorthand, comma-dangle, max-len */
-/* global Notes */
-
+import * as urlUtils from '~/lib/utils/url_utility';
import 'autosize';
import '~/gl_form';
import '~/lib/utils/text_utility';
import '~/render_gfm';
-import '~/notes';
+import Notes from '~/notes';
(function() {
window.gon || (window.gon = {});
@@ -168,8 +167,7 @@ import '~/notes';
});
it('sets target when hash matches', () => {
- spyOn(gl.utils, 'getLocationHash');
- gl.utils.getLocationHash.and.returnValue(hash);
+ spyOn(urlUtils, 'getLocationHash').and.returnValue(hash);
Notes.updateNoteTargetSelector($note);
@@ -178,8 +176,7 @@ import '~/notes';
});
it('unsets target when hash does not match', () => {
- spyOn(gl.utils, 'getLocationHash');
- gl.utils.getLocationHash.and.returnValue('note_doesnotexist');
+ spyOn(urlUtils, 'getLocationHash').and.returnValue('note_doesnotexist');
Notes.updateNoteTargetSelector($note);
@@ -187,8 +184,7 @@ import '~/notes';
});
it('unsets target when there is not a hash fragment anymore', () => {
- spyOn(gl.utils, 'getLocationHash');
- gl.utils.getLocationHash.and.returnValue(null);
+ spyOn(urlUtils, 'getLocationHash').and.returnValue(null);
Notes.updateNoteTargetSelector($note);
@@ -224,7 +220,6 @@ import '~/notes';
notes.note_ids = [];
notes.updatedNotesTrackingMap = {};
- spyOn(gl.utils, 'localTimeAgo');
spyOn(Notes, 'isNewNote').and.callThrough();
spyOn(Notes, 'isUpdatedNote').and.callThrough();
spyOn(Notes, 'animateAppendNote').and.callThrough();
@@ -351,7 +346,6 @@ import '~/notes';
]);
notes.note_ids = [];
- spyOn(gl.utils, 'localTimeAgo');
spyOn(Notes, 'isNewNote');
spyOn(Notes, 'animateAppendNote');
Notes.isNewNote.and.returnValue(true);
diff --git a/spec/javascripts/pager_spec.js b/spec/javascripts/pager_spec.js
index 1d3e1263371..2fd87754238 100644
--- a/spec/javascripts/pager_spec.js
+++ b/spec/javascripts/pager_spec.js
@@ -1,14 +1,9 @@
/* global fixture */
-import '~/pager';
+import * as utils from '~/lib/utils/url_utility';
+import Pager from '~/pager';
describe('pager', () => {
- const Pager = window.Pager;
-
- it('is defined on window', () => {
- expect(window.Pager).toBeDefined();
- });
-
describe('init', () => {
const originalHref = window.location.href;
@@ -30,7 +25,7 @@ describe('pager', () => {
it('should use current url if data-href attribute not provided', () => {
const href = `${gl.TEST_HOST}/some_list`;
- spyOn(gl.utils, 'removeParams').and.returnValue(href);
+ spyOn(utils, 'removeParams').and.returnValue(href);
Pager.init();
expect(Pager.url).toBe(href);
});
@@ -44,9 +39,9 @@ describe('pager', () => {
it('keeps extra query parameters from url', () => {
window.history.replaceState({}, null, '?filter=test&offset=100');
const href = `${gl.TEST_HOST}/some_list?filter=test`;
- spyOn(gl.utils, 'removeParams').and.returnValue(href);
+ spyOn(utils, 'removeParams').and.returnValue(href);
Pager.init();
- expect(gl.utils.removeParams).toHaveBeenCalledWith(['limit', 'offset']);
+ expect(utils.removeParams).toHaveBeenCalledWith(['limit', 'offset']);
expect(Pager.url).toEqual(href);
});
});
diff --git a/spec/javascripts/pipelines/graph/job_component_spec.js b/spec/javascripts/pipelines/graph/job_component_spec.js
index 23c87610d83..35e36e9c353 100644
--- a/spec/javascripts/pipelines/graph/job_component_spec.js
+++ b/spec/javascripts/pipelines/graph/job_component_spec.js
@@ -113,4 +113,35 @@ describe('pipeline graph job component', () => {
component.$el.querySelector('a').classList.contains('css-class-job-name'),
).toBe(true);
});
+
+ describe('status label', () => {
+ it('should not render status label when it is not provided', () => {
+ component = mountComponent(JobComponent, {
+ job: {
+ id: 4256,
+ name: 'test',
+ status: {
+ icon: 'icon_status_success',
+ },
+ },
+ });
+
+ expect(component.$el.querySelector('.js-job-component-tooltip').getAttribute('data-original-title')).toEqual('test');
+ });
+
+ it('should not render status label when it is provided', () => {
+ component = mountComponent(JobComponent, {
+ job: {
+ id: 4256,
+ name: 'test',
+ status: {
+ icon: 'icon_status_success',
+ label: 'success',
+ },
+ },
+ });
+
+ expect(component.$el.querySelector('.js-job-component-tooltip').getAttribute('data-original-title')).toEqual('test - success');
+ });
+ });
});
diff --git a/spec/javascripts/pipelines/nav_controls_spec.js b/spec/javascripts/pipelines/nav_controls_spec.js
index f1697840fcd..09a0c14d96c 100644
--- a/spec/javascripts/pipelines/nav_controls_spec.js
+++ b/spec/javascripts/pipelines/nav_controls_spec.js
@@ -14,6 +14,7 @@ describe('Pipelines Nav Controls', () => {
hasCiEnabled: true,
helpPagePath: 'foo',
ciLintPath: 'foo',
+ resetCachePath: 'foo',
canCreatePipeline: true,
};
@@ -31,6 +32,7 @@ describe('Pipelines Nav Controls', () => {
hasCiEnabled: true,
helpPagePath: 'foo',
ciLintPath: 'foo',
+ resetCachePath: 'foo',
canCreatePipeline: false,
};
@@ -41,12 +43,31 @@ describe('Pipelines Nav Controls', () => {
expect(component.$el.querySelector('.btn-create')).toEqual(null);
});
+ it('should render link for resetting runner caches', () => {
+ const mockData = {
+ newPipelinePath: 'foo',
+ hasCiEnabled: true,
+ helpPagePath: 'foo',
+ ciLintPath: 'foo',
+ resetCachePath: 'foo',
+ canCreatePipeline: false,
+ };
+
+ const component = new NavControlsComponent({
+ propsData: mockData,
+ }).$mount();
+
+ expect(component.$el.querySelectorAll('.btn-default')[0].textContent).toContain('Clear runner caches');
+ expect(component.$el.querySelectorAll('.btn-default')[0].getAttribute('href')).toEqual(mockData.resetCachePath);
+ });
+
it('should render link for CI lint', () => {
const mockData = {
newPipelinePath: 'foo',
hasCiEnabled: true,
helpPagePath: 'foo',
ciLintPath: 'foo',
+ resetCachePath: 'foo',
canCreatePipeline: true,
};
@@ -54,8 +75,8 @@ describe('Pipelines Nav Controls', () => {
propsData: mockData,
}).$mount();
- expect(component.$el.querySelector('.btn-default').textContent).toContain('CI Lint');
- expect(component.$el.querySelector('.btn-default').getAttribute('href')).toEqual(mockData.ciLintPath);
+ expect(component.$el.querySelectorAll('.btn-default')[1].textContent).toContain('CI Lint');
+ expect(component.$el.querySelectorAll('.btn-default')[1].getAttribute('href')).toEqual(mockData.ciLintPath);
});
it('should render link to help page when CI is not enabled', () => {
@@ -64,6 +85,7 @@ describe('Pipelines Nav Controls', () => {
hasCiEnabled: false,
helpPagePath: 'foo',
ciLintPath: 'foo',
+ resetCachePath: 'foo',
canCreatePipeline: true,
};
@@ -81,6 +103,7 @@ describe('Pipelines Nav Controls', () => {
hasCiEnabled: true,
helpPagePath: 'foo',
ciLintPath: 'foo',
+ resetCachePath: 'foo',
canCreatePipeline: true,
};
diff --git a/spec/javascripts/profile/account/components/delete_account_modal_spec.js b/spec/javascripts/profile/account/components/delete_account_modal_spec.js
index 2e94948cfb2..588b61196a5 100644
--- a/spec/javascripts/profile/account/components/delete_account_modal_spec.js
+++ b/spec/javascripts/profile/account/components/delete_account_modal_spec.js
@@ -51,7 +51,7 @@ describe('DeleteAccountModal component', () => {
Vue.nextTick()
.then(() => {
expect(vm.enteredPassword).toBe(input.value);
- expect(submitButton).toHaveClass('disabled');
+ expect(submitButton).toHaveAttr('disabled', 'disabled');
submitButton.click();
expect(form.submit).not.toHaveBeenCalled();
})
@@ -68,7 +68,7 @@ describe('DeleteAccountModal component', () => {
Vue.nextTick()
.then(() => {
expect(vm.enteredPassword).toBe(input.value);
- expect(submitButton).not.toHaveClass('disabled');
+ expect(submitButton).not.toHaveAttr('disabled', 'disabled');
submitButton.click();
expect(form.submit).toHaveBeenCalled();
})
@@ -101,7 +101,7 @@ describe('DeleteAccountModal component', () => {
Vue.nextTick()
.then(() => {
expect(vm.enteredUsername).toBe(input.value);
- expect(submitButton).toHaveClass('disabled');
+ expect(submitButton).toHaveAttr('disabled', 'disabled');
submitButton.click();
expect(form.submit).not.toHaveBeenCalled();
})
@@ -118,7 +118,7 @@ describe('DeleteAccountModal component', () => {
Vue.nextTick()
.then(() => {
expect(vm.enteredUsername).toBe(input.value);
- expect(submitButton).not.toHaveClass('disabled');
+ expect(submitButton).not.toHaveAttr('disabled', 'disabled');
submitButton.click();
expect(form.submit).toHaveBeenCalled();
})
diff --git a/spec/javascripts/projects/project_new_spec.js b/spec/javascripts/projects/project_new_spec.js
index 850768f0e4f..c314ca8ab72 100644
--- a/spec/javascripts/projects/project_new_spec.js
+++ b/spec/javascripts/projects/project_new_spec.js
@@ -6,8 +6,12 @@ describe('New Project', () => {
beforeEach(() => {
setFixtures(`
- <input id="project_import_url" />
- <input id="project_path" />
+ <div class='toggle-import-form'>
+ <div class='import-url-data'>
+ <input id="project_import_url" />
+ <input id="project_path" />
+ </div>
+ </div>
`);
$projectImportUrl = $('#project_import_url');
@@ -25,7 +29,7 @@ describe('New Project', () => {
it('does not change project path for disabled $projectImportUrl', () => {
$projectImportUrl.attr('disabled', true);
- projectNew.deriveProjectPathFromUrl($projectImportUrl, $projectPath);
+ projectNew.deriveProjectPathFromUrl($projectImportUrl);
expect($projectPath.val()).toEqual(dummyImportUrl);
});
@@ -38,7 +42,7 @@ describe('New Project', () => {
it('does not change project path if it is set by user', () => {
$projectPath.keyup();
- projectNew.deriveProjectPathFromUrl($projectImportUrl, $projectPath);
+ projectNew.deriveProjectPathFromUrl($projectImportUrl);
expect($projectPath.val()).toEqual(dummyImportUrl);
});
@@ -46,7 +50,7 @@ describe('New Project', () => {
it('does not change project path for empty $projectImportUrl', () => {
$projectImportUrl.val('');
- projectNew.deriveProjectPathFromUrl($projectImportUrl, $projectPath);
+ projectNew.deriveProjectPathFromUrl($projectImportUrl);
expect($projectPath.val()).toEqual(dummyImportUrl);
});
@@ -54,7 +58,7 @@ describe('New Project', () => {
it('does not change project path for whitespace $projectImportUrl', () => {
$projectImportUrl.val(' ');
- projectNew.deriveProjectPathFromUrl($projectImportUrl, $projectPath);
+ projectNew.deriveProjectPathFromUrl($projectImportUrl);
expect($projectPath.val()).toEqual(dummyImportUrl);
});
@@ -62,7 +66,7 @@ describe('New Project', () => {
it('does not change project path for $projectImportUrl without slashes', () => {
$projectImportUrl.val('has-no-slash');
- projectNew.deriveProjectPathFromUrl($projectImportUrl, $projectPath);
+ projectNew.deriveProjectPathFromUrl($projectImportUrl);
expect($projectPath.val()).toEqual(dummyImportUrl);
});
@@ -70,7 +74,7 @@ describe('New Project', () => {
it('changes project path to last $projectImportUrl component', () => {
$projectImportUrl.val('/this/is/last');
- projectNew.deriveProjectPathFromUrl($projectImportUrl, $projectPath);
+ projectNew.deriveProjectPathFromUrl($projectImportUrl);
expect($projectPath.val()).toEqual('last');
});
@@ -78,7 +82,7 @@ describe('New Project', () => {
it('ignores trailing slashes in $projectImportUrl', () => {
$projectImportUrl.val('/has/trailing/slash/');
- projectNew.deriveProjectPathFromUrl($projectImportUrl, $projectPath);
+ projectNew.deriveProjectPathFromUrl($projectImportUrl);
expect($projectPath.val()).toEqual('slash');
});
@@ -86,7 +90,7 @@ describe('New Project', () => {
it('ignores fragment identifier in $projectImportUrl', () => {
$projectImportUrl.val('/this/has/a#fragment-identifier/');
- projectNew.deriveProjectPathFromUrl($projectImportUrl, $projectPath);
+ projectNew.deriveProjectPathFromUrl($projectImportUrl);
expect($projectPath.val()).toEqual('a');
});
@@ -94,7 +98,7 @@ describe('New Project', () => {
it('ignores query string in $projectImportUrl', () => {
$projectImportUrl.val('/url/with?query=string');
- projectNew.deriveProjectPathFromUrl($projectImportUrl, $projectPath);
+ projectNew.deriveProjectPathFromUrl($projectImportUrl);
expect($projectPath.val()).toEqual('with');
});
@@ -102,7 +106,7 @@ describe('New Project', () => {
it('ignores trailing .git in $projectImportUrl', () => {
$projectImportUrl.val('/repository.git');
- projectNew.deriveProjectPathFromUrl($projectImportUrl, $projectPath);
+ projectNew.deriveProjectPathFromUrl($projectImportUrl);
expect($projectPath.val()).toEqual('repository');
});
@@ -110,7 +114,7 @@ describe('New Project', () => {
it('changes project path for HTTPS URL in $projectImportUrl', () => {
$projectImportUrl.val('https://username:password@gitlab.company.com/group/project.git');
- projectNew.deriveProjectPathFromUrl($projectImportUrl, $projectPath);
+ projectNew.deriveProjectPathFromUrl($projectImportUrl);
expect($projectPath.val()).toEqual('project');
});
@@ -118,7 +122,7 @@ describe('New Project', () => {
it('changes project path for SSH URL in $projectImportUrl', () => {
$projectImportUrl.val('git@gitlab.com:gitlab-org/gitlab-ce.git');
- projectNew.deriveProjectPathFromUrl($projectImportUrl, $projectPath);
+ projectNew.deriveProjectPathFromUrl($projectImportUrl);
expect($projectPath.val()).toEqual('gitlab-ce');
});
diff --git a/spec/javascripts/repo/components/commit_sidebar/list_collapsed_spec.js b/spec/javascripts/repo/components/commit_sidebar/list_collapsed_spec.js
index f750061a6a1..c4d3866c922 100644
--- a/spec/javascripts/repo/components/commit_sidebar/list_collapsed_spec.js
+++ b/spec/javascripts/repo/components/commit_sidebar/list_collapsed_spec.js
@@ -1,6 +1,6 @@
import Vue from 'vue';
-import store from '~/repo/stores';
-import listCollapsed from '~/repo/components/commit_sidebar/list_collapsed.vue';
+import store from '~/ide/stores';
+import listCollapsed from '~/ide/components/commit_sidebar/list_collapsed.vue';
import { createComponentWithStore } from '../../../helpers/vue_mount_component_helper';
import { file } from '../../helpers';
diff --git a/spec/javascripts/repo/components/commit_sidebar/list_item_spec.js b/spec/javascripts/repo/components/commit_sidebar/list_item_spec.js
index 18c9b46fcd9..fc7c9ae9dd7 100644
--- a/spec/javascripts/repo/components/commit_sidebar/list_item_spec.js
+++ b/spec/javascripts/repo/components/commit_sidebar/list_item_spec.js
@@ -1,5 +1,5 @@
import Vue from 'vue';
-import listItem from '~/repo/components/commit_sidebar/list_item.vue';
+import listItem from '~/ide/components/commit_sidebar/list_item.vue';
import mountComponent from '../../../helpers/vue_mount_component_helper';
import { file } from '../../helpers';
diff --git a/spec/javascripts/repo/components/commit_sidebar/list_spec.js b/spec/javascripts/repo/components/commit_sidebar/list_spec.js
index df7e3c5de21..cb5240ad118 100644
--- a/spec/javascripts/repo/components/commit_sidebar/list_spec.js
+++ b/spec/javascripts/repo/components/commit_sidebar/list_spec.js
@@ -1,6 +1,6 @@
import Vue from 'vue';
-import store from '~/repo/stores';
-import commitSidebarList from '~/repo/components/commit_sidebar/list.vue';
+import store from '~/ide/stores';
+import commitSidebarList from '~/ide/components/commit_sidebar/list.vue';
import { createComponentWithStore } from '../../../helpers/vue_mount_component_helper';
import { file } from '../../helpers';
@@ -13,8 +13,11 @@ describe('Multi-file editor commit sidebar list', () => {
vm = createComponentWithStore(Component, store, {
title: 'Staged',
fileList: [],
- collapsed: false,
- }).$mount();
+ });
+
+ vm.$store.state.rightPanelCollapsed = false;
+
+ vm.$mount();
});
afterEach(() => {
@@ -43,30 +46,14 @@ describe('Multi-file editor commit sidebar list', () => {
describe('collapsed', () => {
beforeEach((done) => {
- vm.collapsed = true;
+ vm.$store.state.rightPanelCollapsed = true;
Vue.nextTick(done);
});
- it('adds collapsed class', () => {
- expect(vm.$el.querySelector('.is-collapsed')).not.toBeNull();
- });
-
it('hides list', () => {
expect(vm.$el.querySelector('.list-unstyled')).toBeNull();
expect(vm.$el.querySelector('.help-block')).toBeNull();
});
-
- it('hides collapse button', () => {
- expect(vm.$el.querySelector('.multi-file-commit-panel-collapse-btn')).toBeNull();
- });
- });
-
- it('clicking toggle collapse button emits toggle event', () => {
- spyOn(vm, '$emit');
-
- vm.$el.querySelector('.multi-file-commit-panel-collapse-btn').click();
-
- expect(vm.$emit).toHaveBeenCalledWith('toggleCollapsed');
});
});
diff --git a/spec/javascripts/repo/components/ide_context_bar_spec.js b/spec/javascripts/repo/components/ide_context_bar_spec.js
new file mode 100644
index 00000000000..3f8f37d2343
--- /dev/null
+++ b/spec/javascripts/repo/components/ide_context_bar_spec.js
@@ -0,0 +1,49 @@
+import Vue from 'vue';
+import store from '~/ide/stores';
+import ideContextBar from '~/ide/components/ide_context_bar.vue';
+import { createComponentWithStore } from '../../helpers/vue_mount_component_helper';
+
+describe('Multi-file editor right context bar', () => {
+ let vm;
+
+ beforeEach(() => {
+ const Component = Vue.extend(ideContextBar);
+
+ vm = createComponentWithStore(Component, store);
+
+ vm.$store.state.rightPanelCollapsed = false;
+
+ vm.$mount();
+ });
+
+ afterEach(() => {
+ vm.$destroy();
+ });
+
+ describe('collapsed', () => {
+ beforeEach((done) => {
+ vm.$store.state.rightPanelCollapsed = true;
+
+ Vue.nextTick(done);
+ });
+
+ it('adds collapsed class', () => {
+ expect(vm.$el.querySelector('.is-collapsed')).not.toBeNull();
+ });
+
+ it('shows correct icon', () => {
+ expect(vm.currentIcon).toBe('angle-double-left');
+ });
+ });
+
+ it('clicking toggle collapse button collapses the bar', () => {
+ spyOn(vm, 'setPanelCollapsedStatus').and.returnValue(Promise.resolve());
+
+ vm.$el.querySelector('.multi-file-commit-panel-collapse-btn').click();
+
+ expect(vm.setPanelCollapsedStatus).toHaveBeenCalledWith({
+ side: 'right',
+ collapsed: true,
+ });
+ });
+});
diff --git a/spec/javascripts/repo/components/repo_sidebar_spec.js b/spec/javascripts/repo/components/ide_repo_tree_spec.js
index df7cf8aabbb..e3bbda514da 100644
--- a/spec/javascripts/repo/components/repo_sidebar_spec.js
+++ b/spec/javascripts/repo/components/ide_repo_tree_spec.js
@@ -1,20 +1,26 @@
import Vue from 'vue';
-import store from '~/repo/stores';
-import repoSidebar from '~/repo/components/repo_sidebar.vue';
+import store from '~/ide/stores';
+import ideRepoTree from '~/ide/components/ide_repo_tree.vue';
import { file, resetStore } from '../helpers';
-describe('RepoSidebar', () => {
+describe('IdeRepoTree', () => {
let vm;
beforeEach(() => {
- const RepoSidebar = Vue.extend(repoSidebar);
+ const IdeRepoTree = Vue.extend(ideRepoTree);
- vm = new RepoSidebar({
+ vm = new IdeRepoTree({
store,
+ propsData: {
+ treeId: 'abcproject/mybranch',
+ },
});
+ vm.$store.state.currentBranch = 'master';
vm.$store.state.isRoot = true;
- vm.$store.state.tree.push(file());
+ vm.$store.state.trees['abcproject/mybranch'] = {
+ tree: [file()],
+ };
vm.$mount();
});
@@ -26,25 +32,20 @@ describe('RepoSidebar', () => {
});
it('renders a sidebar', () => {
- const thead = vm.$el.querySelector('thead');
const tbody = vm.$el.querySelector('tbody');
expect(vm.$el.classList.contains('sidebar-mini')).toBeFalsy();
- expect(thead.querySelector('.name').textContent.trim()).toEqual('Name');
- expect(thead.querySelector('.last-commit').textContent.trim()).toEqual('Last commit');
- expect(thead.querySelector('.last-update').textContent.trim()).toEqual('Last update');
expect(tbody.querySelector('.repo-file-options')).toBeFalsy();
expect(tbody.querySelector('.prev-directory')).toBeFalsy();
expect(tbody.querySelector('.loading-file')).toBeFalsy();
expect(tbody.querySelector('.file')).toBeTruthy();
});
- it('renders 5 loading files if tree is loading', (done) => {
- vm.$store.state.tree = [];
- vm.$store.state.loading = true;
+ it('renders 3 loading files if tree is loading', (done) => {
+ vm.treeId = '123';
Vue.nextTick(() => {
- expect(vm.$el.querySelectorAll('tbody .loading-file').length).toEqual(5);
+ expect(vm.$el.querySelectorAll('.multi-file-loading-container').length).toEqual(3);
done();
});
diff --git a/spec/javascripts/repo/components/ide_side_bar_spec.js b/spec/javascripts/repo/components/ide_side_bar_spec.js
new file mode 100644
index 00000000000..30e45169205
--- /dev/null
+++ b/spec/javascripts/repo/components/ide_side_bar_spec.js
@@ -0,0 +1,43 @@
+import Vue from 'vue';
+import store from '~/ide/stores';
+import ideSidebar from '~/ide/components/ide_side_bar.vue';
+import { resetStore } from '../helpers';
+import { createComponentWithStore } from '../../helpers/vue_mount_component_helper';
+
+describe('IdeSidebar', () => {
+ let vm;
+
+ beforeEach(() => {
+ const Component = Vue.extend(ideSidebar);
+
+ vm = createComponentWithStore(Component, store).$mount();
+
+ vm.$store.state.leftPanelCollapsed = false;
+ });
+
+ afterEach(() => {
+ vm.$destroy();
+
+ resetStore(vm.$store);
+ });
+
+ it('renders a sidebar', () => {
+ expect(vm.$el.querySelector('.multi-file-commit-panel-inner')).not.toBeNull();
+ });
+
+ describe('collapsed', () => {
+ beforeEach((done) => {
+ vm.$store.state.leftPanelCollapsed = true;
+
+ Vue.nextTick(done);
+ });
+
+ it('adds collapsed class', () => {
+ expect(vm.$el.classList).toContain('is-collapsed');
+ });
+
+ it('shows correct icon', () => {
+ expect(vm.currentIcon).toBe('angle-double-right');
+ });
+ });
+});
diff --git a/spec/javascripts/repo/components/repo_spec.js b/spec/javascripts/repo/components/ide_spec.js
index b32d2c13af8..acfd63eb8de 100644
--- a/spec/javascripts/repo/components/repo_spec.js
+++ b/spec/javascripts/repo/components/ide_spec.js
@@ -1,16 +1,18 @@
import Vue from 'vue';
-import store from '~/repo/stores';
-import repo from '~/repo/components/repo.vue';
+import store from '~/ide/stores';
+import ide from '~/ide/components/ide.vue';
import { createComponentWithStore } from '../../helpers/vue_mount_component_helper';
import { file, resetStore } from '../helpers';
-describe('repo component', () => {
+describe('ide component', () => {
let vm;
beforeEach(() => {
- const Component = Vue.extend(repo);
+ const Component = Vue.extend(ide);
- vm = createComponentWithStore(Component, store).$mount();
+ vm = createComponentWithStore(Component, store, {
+ emptyStateSvgPath: 'svg',
+ }).$mount();
});
afterEach(() => {
@@ -24,7 +26,9 @@ describe('repo component', () => {
});
it('renders panel right when files are open', (done) => {
- vm.$store.state.tree.push(file());
+ vm.$store.state.trees['abcproject/mybranch'] = {
+ tree: [file()],
+ };
Vue.nextTick(() => {
expect(vm.$el.querySelector('.panel-right')).toBeNull();
diff --git a/spec/javascripts/repo/components/new_branch_form_spec.js b/spec/javascripts/repo/components/new_branch_form_spec.js
index 9a705a1f0ed..cd1d073ec18 100644
--- a/spec/javascripts/repo/components/new_branch_form_spec.js
+++ b/spec/javascripts/repo/components/new_branch_form_spec.js
@@ -1,6 +1,6 @@
import Vue from 'vue';
-import store from '~/repo/stores';
-import newBranchForm from '~/repo/components/new_branch_form.vue';
+import store from '~/ide/stores';
+import newBranchForm from '~/ide/components/new_branch_form.vue';
import { createComponentWithStore } from '../../helpers/vue_mount_component_helper';
import { resetStore } from '../helpers';
diff --git a/spec/javascripts/repo/components/new_dropdown/index_spec.js b/spec/javascripts/repo/components/new_dropdown/index_spec.js
index 93b10fc1fee..6efbbf6d75e 100644
--- a/spec/javascripts/repo/components/new_dropdown/index_spec.js
+++ b/spec/javascripts/repo/components/new_dropdown/index_spec.js
@@ -1,6 +1,6 @@
import Vue from 'vue';
-import store from '~/repo/stores';
-import newDropdown from '~/repo/components/new_dropdown/index.vue';
+import store from '~/ide/stores';
+import newDropdown from '~/ide/components/new_dropdown/index.vue';
import { createComponentWithStore } from '../../../helpers/vue_mount_component_helper';
import { resetStore } from '../../helpers';
@@ -10,8 +10,12 @@ describe('new dropdown component', () => {
beforeEach(() => {
const component = Vue.extend(newDropdown);
- vm = createComponentWithStore(component, store);
+ vm = createComponentWithStore(component, store, {
+ branch: 'master',
+ path: '',
+ });
+ vm.$store.state.currentProjectId = 'abcproject';
vm.$store.state.path = '';
vm.$mount();
@@ -23,9 +27,10 @@ describe('new dropdown component', () => {
resetStore(vm.$store);
});
- it('renders new file and new directory links', () => {
+ it('renders new file, upload and new directory links', () => {
expect(vm.$el.querySelectorAll('a')[0].textContent.trim()).toBe('New file');
- expect(vm.$el.querySelectorAll('a')[1].textContent.trim()).toBe('New directory');
+ expect(vm.$el.querySelectorAll('a')[1].textContent.trim()).toBe('Upload file');
+ expect(vm.$el.querySelectorAll('a')[2].textContent.trim()).toBe('New directory');
});
describe('createNewItem', () => {
@@ -36,7 +41,7 @@ describe('new dropdown component', () => {
});
it('sets modalType to tree when new directory is clicked', () => {
- vm.$el.querySelectorAll('a')[1].click();
+ vm.$el.querySelectorAll('a')[2].click();
expect(vm.modalType).toBe('tree');
});
@@ -52,16 +57,17 @@ describe('new dropdown component', () => {
});
});
- describe('toggleModalOpen', () => {
+ describe('hideModal', () => {
+ beforeAll((done) => {
+ vm.openModal = true;
+ Vue.nextTick(done);
+ });
+
it('closes modal after toggling', (done) => {
- vm.toggleModalOpen();
+ vm.hideModal();
Vue.nextTick()
.then(() => {
- expect(vm.$el.querySelector('.modal')).not.toBeNull();
- })
- .then(vm.toggleModalOpen)
- .then(() => {
expect(vm.$el.querySelector('.modal')).toBeNull();
})
.then(done)
diff --git a/spec/javascripts/repo/components/new_dropdown/modal_spec.js b/spec/javascripts/repo/components/new_dropdown/modal_spec.js
index 1ff7590ec79..233cca06ed0 100644
--- a/spec/javascripts/repo/components/new_dropdown/modal_spec.js
+++ b/spec/javascripts/repo/components/new_dropdown/modal_spec.js
@@ -1,12 +1,42 @@
import Vue from 'vue';
-import store from '~/repo/stores';
-import modal from '~/repo/components/new_dropdown/modal.vue';
+import store from '~/ide/stores';
+import service from '~/ide/services';
+import modal from '~/ide/components/new_dropdown/modal.vue';
import { createComponentWithStore } from '../../../helpers/vue_mount_component_helper';
import { file, resetStore } from '../../helpers';
describe('new file modal component', () => {
const Component = Vue.extend(modal);
let vm;
+ let projectTree;
+
+ beforeEach(() => {
+ spyOn(service, 'getProjectData').and.returnValue(Promise.resolve({
+ data: {
+ id: '123',
+ },
+ }));
+
+ spyOn(service, 'getBranchData').and.returnValue(Promise.resolve({
+ commit: {
+ id: '123branch',
+ },
+ }));
+
+ spyOn(service, 'getTreeData').and.returnValue(Promise.resolve({
+ headers: {
+ 'page-title': 'test',
+ },
+ json: () => Promise.resolve({
+ last_commit_path: 'last_commit_path',
+ parent_tree_url: 'parent_tree_url',
+ path: '/',
+ trees: [{ name: 'tree' }],
+ blobs: [{ name: 'blob' }],
+ submodules: [{ name: 'submodule' }],
+ }),
+ }));
+ });
afterEach(() => {
vm.$destroy();
@@ -17,12 +47,26 @@ describe('new file modal component', () => {
['tree', 'blob'].forEach((type) => {
describe(type, () => {
beforeEach(() => {
+ store.state.projects.abcproject = {
+ web_url: '',
+ };
+ store.state.trees = [];
+ store.state.trees['abcproject/mybranch'] = {
+ tree: [],
+ };
+ projectTree = store.state.trees['abcproject/mybranch'];
+ store.state.currentProjectId = 'abcproject';
+
vm = createComponentWithStore(Component, store, {
type,
+ branchId: 'master',
path: '',
- }).$mount();
+ parent: projectTree,
+ });
vm.entryName = 'testing';
+
+ vm.$mount();
});
it(`sets modal title as ${type}`, () => {
@@ -50,6 +94,9 @@ describe('new file modal component', () => {
vm.createEntryInStore();
expect(vm.createTempEntry).toHaveBeenCalledWith({
+ projectId: 'abcproject',
+ branchId: 'master',
+ parent: projectTree,
name: 'testing',
type,
});
@@ -76,31 +123,18 @@ describe('new file modal component', () => {
});
it('opens newly created file', (done) => {
- vm.createEntryInStore();
-
- setTimeout(() => {
- expect(vm.$store.state.openFiles.length).toBe(1);
- expect(vm.$store.state.openFiles[0].name).toBe(type === 'blob' ? 'testing' : '.gitkeep');
-
- done();
- });
- });
-
- it(`creates ${type} in the current stores path`, (done) => {
- vm.$store.state.path = 'app';
-
- vm.createEntryInStore();
-
- setTimeout(() => {
- expect(vm.$store.state.tree[0].path).toBe('app/testing');
- expect(vm.$store.state.tree[0].name).toBe('testing');
+ if (type === 'blob') {
+ vm.createEntryInStore();
- if (type === 'tree') {
- expect(vm.$store.state.tree[0].tree.length).toBe(1);
- }
+ setTimeout(() => {
+ expect(vm.$store.state.openFiles.length).toBe(1);
+ expect(vm.$store.state.openFiles[0].name).toBe(type === 'blob' ? 'testing' : '.gitkeep');
+ done();
+ });
+ } else {
done();
- });
+ }
});
if (type === 'blob') {
@@ -108,25 +142,27 @@ describe('new file modal component', () => {
vm.createEntryInStore();
setTimeout(() => {
- expect(vm.$store.state.tree.length).toBe(1);
- expect(vm.$store.state.tree[0].name).toBe('testing');
- expect(vm.$store.state.tree[0].type).toBe('blob');
- expect(vm.$store.state.tree[0].tempFile).toBeTruthy();
+ const baseTree = vm.$store.state.trees['abcproject/mybranch'].tree;
+ expect(baseTree.length).toBe(1);
+ expect(baseTree[0].name).toBe('testing');
+ expect(baseTree[0].type).toBe('blob');
+ expect(baseTree[0].tempFile).toBeTruthy();
done();
});
});
it('does not create temp file when file already exists', (done) => {
- vm.$store.state.tree.push(file('testing', '1', type));
+ const baseTree = vm.$store.state.trees['abcproject/mybranch'].tree;
+ baseTree.push(file('testing', '1', type));
vm.createEntryInStore();
setTimeout(() => {
- expect(vm.$store.state.tree.length).toBe(1);
- expect(vm.$store.state.tree[0].name).toBe('testing');
- expect(vm.$store.state.tree[0].type).toBe('blob');
- expect(vm.$store.state.tree[0].tempFile).toBeFalsy();
+ expect(baseTree.length).toBe(1);
+ expect(baseTree[0].name).toBe('testing');
+ expect(baseTree[0].type).toBe('blob');
+ expect(baseTree[0].tempFile).toBeFalsy();
done();
});
@@ -135,48 +171,47 @@ describe('new file modal component', () => {
it('creates new tree', () => {
vm.createEntryInStore();
- expect(vm.$store.state.tree.length).toBe(1);
- expect(vm.$store.state.tree[0].name).toBe('testing');
- expect(vm.$store.state.tree[0].type).toBe('tree');
- expect(vm.$store.state.tree[0].tempFile).toBeTruthy();
- expect(vm.$store.state.tree[0].tree.length).toBe(1);
- expect(vm.$store.state.tree[0].tree[0].name).toBe('.gitkeep');
+ const baseTree = vm.$store.state.trees['abcproject/mybranch'].tree;
+ expect(baseTree.length).toBe(1);
+ expect(baseTree[0].name).toBe('testing');
+ expect(baseTree[0].type).toBe('tree');
+ expect(baseTree[0].tempFile).toBeTruthy();
});
it('creates multiple trees when entryName has slashes', () => {
vm.entryName = 'app/test';
vm.createEntryInStore();
- expect(vm.$store.state.tree.length).toBe(1);
- expect(vm.$store.state.tree[0].name).toBe('app');
- expect(vm.$store.state.tree[0].tree[0].name).toBe('test');
- expect(vm.$store.state.tree[0].tree[0].tree[0].name).toBe('.gitkeep');
+ const baseTree = vm.$store.state.trees['abcproject/mybranch'].tree;
+ expect(baseTree.length).toBe(1);
+ expect(baseTree[0].name).toBe('app');
});
it('creates tree in existing tree', () => {
- vm.$store.state.tree.push(file('app', '1', 'tree'));
+ const baseTree = vm.$store.state.trees['abcproject/mybranch'].tree;
+ baseTree.push(file('app', '1', 'tree'));
vm.entryName = 'app/test';
vm.createEntryInStore();
- expect(vm.$store.state.tree.length).toBe(1);
- expect(vm.$store.state.tree[0].name).toBe('app');
- expect(vm.$store.state.tree[0].tempFile).toBeFalsy();
- expect(vm.$store.state.tree[0].tree[0].tempFile).toBeTruthy();
- expect(vm.$store.state.tree[0].tree[0].name).toBe('test');
- expect(vm.$store.state.tree[0].tree[0].tree[0].name).toBe('.gitkeep');
+ expect(baseTree.length).toBe(1);
+ expect(baseTree[0].name).toBe('app');
+ expect(baseTree[0].tempFile).toBeFalsy();
+ expect(baseTree[0].tree[0].tempFile).toBeTruthy();
+ expect(baseTree[0].tree[0].name).toBe('test');
});
it('does not create new tree when already exists', () => {
- vm.$store.state.tree.push(file('app', '1', 'tree'));
+ const baseTree = vm.$store.state.trees['abcproject/mybranch'].tree;
+ baseTree.push(file('app', '1', 'tree'));
vm.entryName = 'app';
vm.createEntryInStore();
- expect(vm.$store.state.tree.length).toBe(1);
- expect(vm.$store.state.tree[0].name).toBe('app');
- expect(vm.$store.state.tree[0].tempFile).toBeFalsy();
- expect(vm.$store.state.tree[0].tree.length).toBe(0);
+ expect(baseTree.length).toBe(1);
+ expect(baseTree[0].name).toBe('app');
+ expect(baseTree[0].tempFile).toBeFalsy();
+ expect(baseTree[0].tree.length).toBe(0);
});
}
});
@@ -188,6 +223,8 @@ describe('new file modal component', () => {
vm = createComponentWithStore(Component, store, {
type: 'tree',
+ projectId: 'abcproject',
+ branchId: 'master',
path: '',
}).$mount('.js-test');
diff --git a/spec/javascripts/repo/components/new_dropdown/upload_spec.js b/spec/javascripts/repo/components/new_dropdown/upload_spec.js
index bf7893029b1..788c08e5279 100644
--- a/spec/javascripts/repo/components/new_dropdown/upload_spec.js
+++ b/spec/javascripts/repo/components/new_dropdown/upload_spec.js
@@ -1,19 +1,61 @@
import Vue from 'vue';
-import upload from '~/repo/components/new_dropdown/upload.vue';
-import store from '~/repo/stores';
+import upload from '~/ide/components/new_dropdown/upload.vue';
+import store from '~/ide/stores';
+import service from '~/ide/services';
import { createComponentWithStore } from '../../../helpers/vue_mount_component_helper';
import { resetStore } from '../../helpers';
describe('new dropdown upload', () => {
let vm;
+ let projectTree;
beforeEach(() => {
+ spyOn(service, 'getProjectData').and.returnValue(Promise.resolve({
+ data: {
+ id: '123',
+ },
+ }));
+
+ spyOn(service, 'getBranchData').and.returnValue(Promise.resolve({
+ commit: {
+ id: '123branch',
+ },
+ }));
+
+ spyOn(service, 'getTreeData').and.returnValue(Promise.resolve({
+ headers: {
+ 'page-title': 'test',
+ },
+ json: () => Promise.resolve({
+ last_commit_path: 'last_commit_path',
+ parent_tree_url: 'parent_tree_url',
+ path: '/',
+ trees: [{ name: 'tree' }],
+ blobs: [{ name: 'blob' }],
+ submodules: [{ name: 'submodule' }],
+ }),
+ }));
+
const Component = Vue.extend(upload);
+ store.state.projects.abcproject = {
+ web_url: '',
+ };
+ store.state.currentProjectId = 'abcproject';
+ store.state.trees = [];
+ store.state.trees['abcproject/mybranch'] = {
+ tree: [],
+ };
+ projectTree = store.state.trees['abcproject/mybranch'];
+
vm = createComponentWithStore(Component, store, {
+ branchId: 'master',
path: '',
+ parent: projectTree,
});
+ vm.entryName = 'testing';
+
vm.$mount();
});
@@ -65,23 +107,33 @@ describe('new dropdown upload', () => {
vm.createFile(target, file, true);
vm.$nextTick(() => {
- expect(vm.$store.state.tree.length).toBe(1);
- expect(vm.$store.state.tree[0].name).toBe(file.name);
- expect(vm.$store.state.tree[0].content).toBe(target.result);
+ const baseTree = vm.$store.state.trees['abcproject/mybranch'].tree;
+ expect(baseTree.length).toBe(1);
+ expect(baseTree[0].name).toBe(file.name);
+ expect(baseTree[0].content).toBe(target.result);
done();
});
});
it('creates new file in path', (done) => {
- vm.$store.state.path = 'testing';
+ const baseTree = vm.$store.state.trees['abcproject/mybranch'].tree;
+ const tree = {
+ type: 'tree',
+ name: 'testing',
+ path: 'testing',
+ tree: [],
+ };
+ baseTree.push(tree);
+
+ vm.parent = tree;
vm.createFile(target, file, true);
vm.$nextTick(() => {
- expect(vm.$store.state.tree.length).toBe(1);
- expect(vm.$store.state.tree[0].name).toBe(file.name);
- expect(vm.$store.state.tree[0].content).toBe(target.result);
- expect(vm.$store.state.tree[0].path).toBe(`testing/${file.name}`);
+ expect(baseTree.length).toBe(1);
+ expect(baseTree[0].tree[0].name).toBe(file.name);
+ expect(baseTree[0].tree[0].content).toBe(target.result);
+ expect(baseTree[0].tree[0].path).toBe(`testing/${file.name}`);
done();
});
@@ -91,10 +143,11 @@ describe('new dropdown upload', () => {
vm.createFile(binaryTarget, file, false);
vm.$nextTick(() => {
- expect(vm.$store.state.tree.length).toBe(1);
- expect(vm.$store.state.tree[0].name).toBe(file.name);
- expect(vm.$store.state.tree[0].content).toBe(binaryTarget.result.split('base64,')[1]);
- expect(vm.$store.state.tree[0].base64).toBe(true);
+ const baseTree = vm.$store.state.trees['abcproject/mybranch'].tree;
+ expect(baseTree.length).toBe(1);
+ expect(baseTree[0].name).toBe(file.name);
+ expect(baseTree[0].content).toBe(binaryTarget.result.split('base64,')[1]);
+ expect(baseTree[0].base64).toBe(true);
done();
});
diff --git a/spec/javascripts/repo/components/repo_commit_section_spec.js b/spec/javascripts/repo/components/repo_commit_section_spec.js
index 1c794123095..cd93fb3ccbf 100644
--- a/spec/javascripts/repo/components/repo_commit_section_spec.js
+++ b/spec/javascripts/repo/components/repo_commit_section_spec.js
@@ -1,7 +1,8 @@
import Vue from 'vue';
-import store from '~/repo/stores';
-import service from '~/repo/services';
-import repoCommitSection from '~/repo/components/repo_commit_section.vue';
+import * as urlUtils from '~/lib/utils/url_utility';
+import store from '~/ide/stores';
+import service from '~/ide/services';
+import repoCommitSection from '~/ide/components/repo_commit_section.vue';
import getSetTimeoutPromise from '../../helpers/set_timeout_promise_helper';
import { file, resetStore } from '../helpers';
@@ -15,6 +16,18 @@ describe('RepoCommitSection', () => {
store,
}).$mount();
+ comp.$store.state.currentProjectId = 'abcproject';
+ comp.$store.state.currentBranchId = 'master';
+ comp.$store.state.projects.abcproject = {
+ web_url: '',
+ branches: {
+ master: {
+ workingReference: '1',
+ },
+ },
+ };
+
+ comp.$store.state.rightPanelCollapsed = false;
comp.$store.state.currentBranch = 'master';
comp.$store.state.openFiles = [file(), file()];
comp.$store.state.openFiles.forEach(f => Object.assign(f, {
@@ -28,7 +41,19 @@ describe('RepoCommitSection', () => {
beforeEach((done) => {
vm = createComponent();
- vm.collapsed = false;
+ spyOn(service, 'getTreeData').and.returnValue(Promise.resolve({
+ headers: {
+ 'page-title': 'test',
+ },
+ json: () => Promise.resolve({
+ last_commit_path: 'last_commit_path',
+ parent_tree_url: 'parent_tree_url',
+ path: '/',
+ trees: [{ name: 'tree' }],
+ blobs: [{ name: 'blob' }],
+ submodules: [{ name: 'submodule' }],
+ }),
+ }));
Vue.nextTick(done);
});
@@ -44,7 +69,6 @@ describe('RepoCommitSection', () => {
const submitCommit = vm.$el.querySelector('form .btn');
expect(vm.$el.querySelector('.multi-file-commit-form')).not.toBeNull();
- expect(vm.$el.querySelector('.multi-file-commit-panel-section header').textContent.trim()).toEqual('Staged');
expect(changedFileElements.length).toEqual(2);
changedFileElements.forEach((changedFile, i) => {
@@ -97,7 +121,7 @@ describe('RepoCommitSection', () => {
});
it('redirects to MR creation page if start new MR checkbox checked', (done) => {
- spyOn(gl.utils, 'visitUrl');
+ spyOn(urlUtils, 'visitUrl');
vm.startNewMR = true;
vm.makeCommit();
@@ -105,7 +129,7 @@ describe('RepoCommitSection', () => {
getSetTimeoutPromise()
.then(() => Vue.nextTick())
.then(() => {
- expect(gl.utils.visitUrl).toHaveBeenCalled();
+ expect(urlUtils.visitUrl).toHaveBeenCalled();
})
.then(done)
.catch(done.fail);
diff --git a/spec/javascripts/repo/components/repo_edit_button_spec.js b/spec/javascripts/repo/components/repo_edit_button_spec.js
index 44018464b35..2895b794506 100644
--- a/spec/javascripts/repo/components/repo_edit_button_spec.js
+++ b/spec/javascripts/repo/components/repo_edit_button_spec.js
@@ -1,6 +1,6 @@
import Vue from 'vue';
-import store from '~/repo/stores';
-import repoEditButton from '~/repo/components/repo_edit_button.vue';
+import store from '~/ide/stores';
+import repoEditButton from '~/ide/components/repo_edit_button.vue';
import { file, resetStore } from '../helpers';
describe('RepoEditButton', () => {
@@ -32,7 +32,7 @@ describe('RepoEditButton', () => {
vm.$mount();
expect(vm.$el.querySelector('.btn')).not.toBeNull();
- expect(vm.$el.querySelector('.btn').textContent.trim()).toBe('Edit');
+ expect(vm.$el.querySelector('.btn').textContent.trim()).toBe('Cancel edit');
});
it('renders edit button with cancel text', () => {
@@ -50,7 +50,7 @@ describe('RepoEditButton', () => {
vm.$el.querySelector('.btn').click();
vm.$nextTick(() => {
- expect(vm.$el.querySelector('.btn').textContent.trim()).toBe('Cancel edit');
+ expect(vm.$el.querySelector('.btn').textContent.trim()).toBe('Edit');
done();
});
diff --git a/spec/javascripts/repo/components/repo_editor_spec.js b/spec/javascripts/repo/components/repo_editor_spec.js
index 81158cad639..e7b2ed08acd 100644
--- a/spec/javascripts/repo/components/repo_editor_spec.js
+++ b/spec/javascripts/repo/components/repo_editor_spec.js
@@ -1,7 +1,7 @@
import Vue from 'vue';
-import store from '~/repo/stores';
-import repoEditor from '~/repo/components/repo_editor.vue';
-import monacoLoader from '~/repo/monaco_loader';
+import store from '~/ide/stores';
+import repoEditor from '~/ide/components/repo_editor.vue';
+import monacoLoader from '~/ide/monaco_loader';
import { file, resetStore } from '../helpers';
describe('RepoEditor', () => {
diff --git a/spec/javascripts/repo/components/repo_file_buttons_spec.js b/spec/javascripts/repo/components/repo_file_buttons_spec.js
index d6e255e4810..115569a9117 100644
--- a/spec/javascripts/repo/components/repo_file_buttons_spec.js
+++ b/spec/javascripts/repo/components/repo_file_buttons_spec.js
@@ -1,6 +1,6 @@
import Vue from 'vue';
-import store from '~/repo/stores';
-import repoFileButtons from '~/repo/components/repo_file_buttons.vue';
+import store from '~/ide/stores';
+import repoFileButtons from '~/ide/components/repo_file_buttons.vue';
import { file, resetStore } from '../helpers';
describe('RepoFileButtons', () => {
diff --git a/spec/javascripts/repo/components/repo_file_spec.js b/spec/javascripts/repo/components/repo_file_spec.js
index bf9181fb09c..0810da87e80 100644
--- a/spec/javascripts/repo/components/repo_file_spec.js
+++ b/spec/javascripts/repo/components/repo_file_spec.js
@@ -1,6 +1,6 @@
import Vue from 'vue';
-import store from '~/repo/stores';
-import repoFile from '~/repo/components/repo_file.vue';
+import store from '~/ide/stores';
+import repoFile from '~/ide/components/repo_file.vue';
import { file, resetStore } from '../helpers';
describe('RepoFile', () => {
@@ -32,14 +32,9 @@ describe('RepoFile', () => {
vm.$mount();
const name = vm.$el.querySelector('.repo-file-name');
- const fileIcon = vm.$el.querySelector('.file-icon');
- expect(vm.$el.querySelector(`.${vm.file.icon}`).style.marginLeft).toEqual('0px');
- expect(name.href).toMatch(`/${vm.file.url}`);
+ expect(name.href).toMatch('');
expect(name.textContent.trim()).toEqual(vm.file.name);
- expect(fileIcon.classList.contains(vm.file.icon)).toBeTruthy();
- expect(fileIcon.style.marginLeft).toEqual(`${vm.file.level * 10}px`);
- expect(vm.$el.querySelectorAll('.animation-container').length).toBe(2);
});
it('does render if hasFiles is true and is loading tree', () => {
@@ -50,17 +45,6 @@ describe('RepoFile', () => {
expect(vm.$el.querySelector('.fa-spin.fa-spinner')).toBeFalsy();
});
- it('renders a spinner if the file is loading', () => {
- const f = file();
- f.loading = true;
- vm = createComponent({
- file: f,
- });
-
- expect(vm.$el.querySelector('.fa-spin.fa-spinner')).not.toBeNull();
- expect(vm.$el.querySelector('.fa-spin.fa-spinner').style.marginLeft).toEqual(`${vm.file.level * 16}px`);
- });
-
it('does not render commit message and datetime if mini', (done) => {
vm = createComponent({
file: file(),
@@ -75,16 +59,16 @@ describe('RepoFile', () => {
});
});
- it('fires clickedTreeRow when the link is clicked', () => {
+ it('fires clickFile when the link is clicked', () => {
vm = createComponent({
file: file(),
});
- spyOn(vm, 'clickedTreeRow');
+ spyOn(vm, 'clickFile');
vm.$el.click();
- expect(vm.clickedTreeRow).toHaveBeenCalledWith(vm.file);
+ expect(vm.clickFile).toHaveBeenCalledWith(vm.file);
});
describe('submodule', () => {
diff --git a/spec/javascripts/repo/components/repo_loading_file_spec.js b/spec/javascripts/repo/components/repo_loading_file_spec.js
index 031f2a9c0b2..18366fb89bc 100644
--- a/spec/javascripts/repo/components/repo_loading_file_spec.js
+++ b/spec/javascripts/repo/components/repo_loading_file_spec.js
@@ -1,6 +1,6 @@
import Vue from 'vue';
-import store from '~/repo/stores';
-import repoLoadingFile from '~/repo/components/repo_loading_file.vue';
+import store from '~/ide/stores';
+import repoLoadingFile from '~/ide/components/repo_loading_file.vue';
import { resetStore } from '../helpers';
describe('RepoLoadingFile', () => {
@@ -48,6 +48,7 @@ describe('RepoLoadingFile', () => {
it('renders 1 column of animated LoC if isMini', (done) => {
vm = createComponent();
+ vm.$store.state.leftPanelCollapsed = true;
vm.$store.state.openFiles.push('test');
vm.$nextTick(() => {
diff --git a/spec/javascripts/repo/components/repo_prev_directory_spec.js b/spec/javascripts/repo/components/repo_prev_directory_spec.js
index 7f82ae36a64..ff26cab2262 100644
--- a/spec/javascripts/repo/components/repo_prev_directory_spec.js
+++ b/spec/javascripts/repo/components/repo_prev_directory_spec.js
@@ -1,6 +1,6 @@
import Vue from 'vue';
-import store from '~/repo/stores';
-import repoPrevDirectory from '~/repo/components/repo_prev_directory.vue';
+import store from '~/ide/stores';
+import repoPrevDirectory from '~/ide/components/repo_prev_directory.vue';
import { resetStore } from '../helpers';
describe('RepoPrevDirectory', () => {
diff --git a/spec/javascripts/repo/components/repo_preview_spec.js b/spec/javascripts/repo/components/repo_preview_spec.js
index 8d1a87494cf..e90837e4cb2 100644
--- a/spec/javascripts/repo/components/repo_preview_spec.js
+++ b/spec/javascripts/repo/components/repo_preview_spec.js
@@ -1,6 +1,6 @@
import Vue from 'vue';
-import store from '~/repo/stores';
-import repoPreview from '~/repo/components/repo_preview.vue';
+import store from '~/ide/stores';
+import repoPreview from '~/ide/components/repo_preview.vue';
import { file, resetStore } from '../helpers';
describe('RepoPreview', () => {
diff --git a/spec/javascripts/repo/components/repo_tab_spec.js b/spec/javascripts/repo/components/repo_tab_spec.js
index 7d2174196c9..507bca983df 100644
--- a/spec/javascripts/repo/components/repo_tab_spec.js
+++ b/spec/javascripts/repo/components/repo_tab_spec.js
@@ -1,6 +1,6 @@
import Vue from 'vue';
-import store from '~/repo/stores';
-import repoTab from '~/repo/components/repo_tab.vue';
+import store from '~/ide/stores';
+import repoTab from '~/ide/components/repo_tab.vue';
import { file, resetStore } from '../helpers';
describe('RepoTab', () => {
@@ -31,16 +31,16 @@ describe('RepoTab', () => {
expect(name.textContent.trim()).toEqual(vm.tab.name);
});
- it('calls setFileActive when clicking tab', () => {
+ it('fires clickFile when the link is clicked', () => {
vm = createComponent({
tab: file(),
});
- spyOn(vm, 'setFileActive');
+ spyOn(vm, 'clickFile');
vm.$el.click();
- expect(vm.setFileActive).toHaveBeenCalledWith(vm.tab);
+ expect(vm.clickFile).toHaveBeenCalledWith(vm.tab);
});
it('calls closeFile when clicking close button', () => {
diff --git a/spec/javascripts/repo/components/repo_tabs_spec.js b/spec/javascripts/repo/components/repo_tabs_spec.js
index 1fb2242c051..0beaf643793 100644
--- a/spec/javascripts/repo/components/repo_tabs_spec.js
+++ b/spec/javascripts/repo/components/repo_tabs_spec.js
@@ -1,6 +1,6 @@
import Vue from 'vue';
-import store from '~/repo/stores';
-import repoTabs from '~/repo/components/repo_tabs.vue';
+import store from '~/ide/stores';
+import repoTabs from '~/ide/components/repo_tabs.vue';
import { file, resetStore } from '../helpers';
describe('RepoTabs', () => {
diff --git a/spec/javascripts/repo/helpers.js b/spec/javascripts/repo/helpers.js
index 820a44992b4..ac43d221198 100644
--- a/spec/javascripts/repo/helpers.js
+++ b/spec/javascripts/repo/helpers.js
@@ -1,5 +1,5 @@
-import { decorateData } from '~/repo/stores/utils';
-import state from '~/repo/stores/state';
+import { decorateData } from '~/ide/stores/utils';
+import state from '~/ide/stores/state';
export const resetStore = (store) => {
store.replaceState(state());
@@ -12,4 +12,5 @@ export const file = (name = 'name', id = name, type = '') => decorateData({
url: 'url',
name,
path: name,
+ lastCommit: {},
});
diff --git a/spec/javascripts/repo/lib/common/disposable_spec.js b/spec/javascripts/repo/lib/common/disposable_spec.js
index 62c3913bf4d..af12ca15369 100644
--- a/spec/javascripts/repo/lib/common/disposable_spec.js
+++ b/spec/javascripts/repo/lib/common/disposable_spec.js
@@ -1,4 +1,4 @@
-import Disposable from '~/repo/lib/common/disposable';
+import Disposable from '~/ide/lib/common/disposable';
describe('Multi-file editor library disposable class', () => {
let instance;
diff --git a/spec/javascripts/repo/lib/common/model_manager_spec.js b/spec/javascripts/repo/lib/common/model_manager_spec.js
index 8c134f178c0..563c2e33834 100644
--- a/spec/javascripts/repo/lib/common/model_manager_spec.js
+++ b/spec/javascripts/repo/lib/common/model_manager_spec.js
@@ -1,6 +1,6 @@
/* global monaco */
-import monacoLoader from '~/repo/monaco_loader';
-import ModelManager from '~/repo/lib/common/model_manager';
+import monacoLoader from '~/ide/monaco_loader';
+import ModelManager from '~/ide/lib/common/model_manager';
import { file } from '../../helpers';
describe('Multi-file editor library model manager', () => {
diff --git a/spec/javascripts/repo/lib/common/model_spec.js b/spec/javascripts/repo/lib/common/model_spec.js
index d41ade237ca..878a4a3f3fe 100644
--- a/spec/javascripts/repo/lib/common/model_spec.js
+++ b/spec/javascripts/repo/lib/common/model_spec.js
@@ -1,6 +1,6 @@
/* global monaco */
-import monacoLoader from '~/repo/monaco_loader';
-import Model from '~/repo/lib/common/model';
+import monacoLoader from '~/ide/monaco_loader';
+import Model from '~/ide/lib/common/model';
import { file } from '../../helpers';
describe('Multi-file editor library model', () => {
diff --git a/spec/javascripts/repo/lib/decorations/controller_spec.js b/spec/javascripts/repo/lib/decorations/controller_spec.js
index 2e32e8fa0bd..fea12d74dca 100644
--- a/spec/javascripts/repo/lib/decorations/controller_spec.js
+++ b/spec/javascripts/repo/lib/decorations/controller_spec.js
@@ -1,8 +1,8 @@
/* global monaco */
-import monacoLoader from '~/repo/monaco_loader';
-import editor from '~/repo/lib/editor';
-import DecorationsController from '~/repo/lib/decorations/controller';
-import Model from '~/repo/lib/common/model';
+import monacoLoader from '~/ide/monaco_loader';
+import editor from '~/ide/lib/editor';
+import DecorationsController from '~/ide/lib/decorations/controller';
+import Model from '~/ide/lib/common/model';
import { file } from '../../helpers';
describe('Multi-file editor library decorations controller', () => {
diff --git a/spec/javascripts/repo/lib/diff/controller_spec.js b/spec/javascripts/repo/lib/diff/controller_spec.js
index ed62e28d3a3..1d55c165260 100644
--- a/spec/javascripts/repo/lib/diff/controller_spec.js
+++ b/spec/javascripts/repo/lib/diff/controller_spec.js
@@ -1,10 +1,10 @@
/* global monaco */
-import monacoLoader from '~/repo/monaco_loader';
-import editor from '~/repo/lib/editor';
-import ModelManager from '~/repo/lib/common/model_manager';
-import DecorationsController from '~/repo/lib/decorations/controller';
-import DirtyDiffController, { getDiffChangeType, getDecorator } from '~/repo/lib/diff/controller';
-import { computeDiff } from '~/repo/lib/diff/diff';
+import monacoLoader from '~/ide/monaco_loader';
+import editor from '~/ide/lib/editor';
+import ModelManager from '~/ide/lib/common/model_manager';
+import DecorationsController from '~/ide/lib/decorations/controller';
+import DirtyDiffController, { getDiffChangeType, getDecorator } from '~/ide/lib/diff/controller';
+import { computeDiff } from '~/ide/lib/diff/diff';
import { file } from '../../helpers';
describe('Multi-file editor library dirty diff controller', () => {
diff --git a/spec/javascripts/repo/lib/diff/diff_spec.js b/spec/javascripts/repo/lib/diff/diff_spec.js
index 3269ec5d2c9..57f3ac3d365 100644
--- a/spec/javascripts/repo/lib/diff/diff_spec.js
+++ b/spec/javascripts/repo/lib/diff/diff_spec.js
@@ -1,4 +1,4 @@
-import { computeDiff } from '~/repo/lib/diff/diff';
+import { computeDiff } from '~/ide/lib/diff/diff';
describe('Multi-file editor library diff calculator', () => {
describe('computeDiff', () => {
diff --git a/spec/javascripts/repo/lib/editor_options_spec.js b/spec/javascripts/repo/lib/editor_options_spec.js
index b4887d063ed..edbf5450dce 100644
--- a/spec/javascripts/repo/lib/editor_options_spec.js
+++ b/spec/javascripts/repo/lib/editor_options_spec.js
@@ -1,4 +1,4 @@
-import editorOptions from '~/repo/lib/editor_options';
+import editorOptions from '~/ide/lib/editor_options';
describe('Multi-file editor library editor options', () => {
it('returns an array', () => {
diff --git a/spec/javascripts/repo/lib/editor_spec.js b/spec/javascripts/repo/lib/editor_spec.js
index cd32832a232..8d51d48a782 100644
--- a/spec/javascripts/repo/lib/editor_spec.js
+++ b/spec/javascripts/repo/lib/editor_spec.js
@@ -1,6 +1,6 @@
/* global monaco */
-import monacoLoader from '~/repo/monaco_loader';
-import editor from '~/repo/lib/editor';
+import monacoLoader from '~/ide/monaco_loader';
+import editor from '~/ide/lib/editor';
import { file } from '../helpers';
describe('Multi-file editor library', () => {
diff --git a/spec/javascripts/repo/monaco_loader_spec.js b/spec/javascripts/repo/monaco_loader_spec.js
index 887a80160fc..b8ac36972aa 100644
--- a/spec/javascripts/repo/monaco_loader_spec.js
+++ b/spec/javascripts/repo/monaco_loader_spec.js
@@ -1,5 +1,5 @@
import monacoContext from 'monaco-editor/dev/vs/loader';
-import monacoLoader from '~/repo/monaco_loader';
+import monacoLoader from '~/ide/monaco_loader';
describe('MonacoLoader', () => {
it('calls require.config and exports require', () => {
diff --git a/spec/javascripts/repo/stores/actions/branch_spec.js b/spec/javascripts/repo/stores/actions/branch_spec.js
index af9d6835a67..00d16fd790d 100644
--- a/spec/javascripts/repo/stores/actions/branch_spec.js
+++ b/spec/javascripts/repo/stores/actions/branch_spec.js
@@ -1,5 +1,5 @@
-import store from '~/repo/stores';
-import service from '~/repo/services';
+import store from '~/ide/stores';
+import service from '~/ide/services';
import { resetStore } from '../../helpers';
describe('Multi-file store branch actions', () => {
@@ -16,19 +16,25 @@ describe('Multi-file store branch actions', () => {
}));
spyOn(history, 'pushState');
- store.state.project.id = 2;
- store.state.currentBranch = 'testing';
+ store.state.currentProjectId = 'abcproject';
+ store.state.currentBranchId = 'testing';
+ store.state.projects.abcproject = {
+ branches: {
+ master: {
+ workingReference: '1',
+ },
+ },
+ };
});
it('creates new branch', (done) => {
store.dispatch('createNewBranch', 'master')
.then(() => {
- expect(store.state.currentBranch).toBe('testing');
- expect(service.createBranch).toHaveBeenCalledWith(2, {
+ expect(store.state.currentBranchId).toBe('testing');
+ expect(service.createBranch).toHaveBeenCalledWith('abcproject', {
branch: 'master',
ref: 'testing',
});
- expect(history.pushState).toHaveBeenCalled();
done();
})
diff --git a/spec/javascripts/repo/stores/actions/file_spec.js b/spec/javascripts/repo/stores/actions/file_spec.js
index 099c0556e71..8ce01d3bf12 100644
--- a/spec/javascripts/repo/stores/actions/file_spec.js
+++ b/spec/javascripts/repo/stores/actions/file_spec.js
@@ -1,6 +1,6 @@
import Vue from 'vue';
-import store from '~/repo/stores';
-import service from '~/repo/services';
+import store from '~/ide/stores';
+import service from '~/ide/services';
import { file, resetStore } from '../../helpers';
describe('Multi-file store file actions', () => {
@@ -24,8 +24,6 @@ describe('Multi-file store file actions', () => {
localFile.parentTreeUrl = 'parentTreeUrl';
store.state.openFiles.push(localFile);
-
- spyOn(history, 'pushState');
});
afterEach(() => {
@@ -82,15 +80,6 @@ describe('Multi-file store file actions', () => {
}).catch(done.fail);
});
- it('calls pushState when no open files are left', (done) => {
- store.dispatch('closeFile', { file: localFile })
- .then(() => {
- expect(history.pushState).toHaveBeenCalledWith(jasmine.anything(), '', 'parentTreeUrl');
-
- done();
- }).catch(done.fail);
- });
-
it('sets next file as active', (done) => {
const f = file();
store.state.openFiles.push(f);
@@ -322,8 +311,26 @@ describe('Multi-file store file actions', () => {
});
describe('createTempFile', () => {
+ let projectTree;
+
beforeEach(() => {
document.body.innerHTML += '<div class="flash-container"></div>';
+
+ store.state.currentProjectId = 'abcproject';
+ store.state.currentBranchId = 'master';
+ store.state.projects.abcproject = {
+ branches: {
+ master: {
+ workingReference: '1',
+ },
+ },
+ };
+
+ store.state.trees['abcproject/mybranch'] = {
+ tree: [],
+ };
+
+ projectTree = store.state.trees['abcproject/mybranch'];
});
afterEach(() => {
@@ -332,11 +339,13 @@ describe('Multi-file store file actions', () => {
it('creates temp file', (done) => {
store.dispatch('createTempFile', {
- tree: store.state,
name: 'test',
+ projectId: 'abcproject',
+ branchId: 'mybranch',
+ parent: projectTree,
}).then((f) => {
expect(f.tempFile).toBeTruthy();
- expect(store.state.tree.length).toBe(1);
+ expect(store.state.trees['abcproject/mybranch'].tree.length).toBe(1);
done();
}).catch(done.fail);
@@ -344,8 +353,10 @@ describe('Multi-file store file actions', () => {
it('adds tmp file to open files', (done) => {
store.dispatch('createTempFile', {
- tree: store.state,
name: 'test',
+ projectId: 'abcproject',
+ branchId: 'mybranch',
+ parent: projectTree,
}).then((f) => {
expect(store.state.openFiles.length).toBe(1);
expect(store.state.openFiles[0].name).toBe(f.name);
@@ -356,8 +367,10 @@ describe('Multi-file store file actions', () => {
it('sets tmp file as active', (done) => {
store.dispatch('createTempFile', {
- tree: store.state,
name: 'test',
+ projectId: 'abcproject',
+ branchId: 'mybranch',
+ parent: projectTree,
}).then((f) => {
expect(f.active).toBeTruthy();
@@ -367,8 +380,10 @@ describe('Multi-file store file actions', () => {
it('enters edit mode if file is not base64', (done) => {
store.dispatch('createTempFile', {
- tree: store.state,
name: 'test',
+ projectId: 'abcproject',
+ branchId: 'mybranch',
+ parent: projectTree,
}).then(() => {
expect(store.state.editMode).toBeTruthy();
@@ -376,24 +391,14 @@ describe('Multi-file store file actions', () => {
}).catch(done.fail);
});
- it('does not enter edit mode if file is base64', (done) => {
- store.dispatch('createTempFile', {
- tree: store.state,
- name: 'test',
- base64: true,
- }).then(() => {
- expect(store.state.editMode).toBeFalsy();
-
- done();
- }).catch(done.fail);
- });
-
it('creates flash message is file already exists', (done) => {
- store.state.tree.push(file('test', '1', 'blob'));
+ store.state.trees['abcproject/mybranch'].tree.push(file('test', '1', 'blob'));
store.dispatch('createTempFile', {
- tree: store.state,
name: 'test',
+ projectId: 'abcproject',
+ branchId: 'mybranch',
+ parent: projectTree,
}).then(() => {
expect(document.querySelector('.flash-alert')).not.toBeNull();
@@ -402,11 +407,13 @@ describe('Multi-file store file actions', () => {
});
it('increases level of file', (done) => {
- store.state.level = 1;
+ store.state.trees['abcproject/mybranch'].level = 1;
store.dispatch('createTempFile', {
- tree: store.state,
name: 'test',
+ projectId: 'abcproject',
+ branchId: 'mybranch',
+ parent: projectTree,
}).then((f) => {
expect(f.level).toBe(2);
diff --git a/spec/javascripts/repo/stores/actions/tree_spec.js b/spec/javascripts/repo/stores/actions/tree_spec.js
index 393a797c6a3..65351dbb7d9 100644
--- a/spec/javascripts/repo/stores/actions/tree_spec.js
+++ b/spec/javascripts/repo/stores/actions/tree_spec.js
@@ -1,9 +1,30 @@
import Vue from 'vue';
-import store from '~/repo/stores';
-import service from '~/repo/services';
+import store from '~/ide/stores';
+import service from '~/ide/services';
import { file, resetStore } from '../../helpers';
describe('Multi-file store tree actions', () => {
+ let projectTree;
+
+ const basicCallParameters = {
+ endpoint: 'rootEndpoint',
+ projectId: 'abcproject',
+ branch: 'master',
+ };
+
+ beforeEach(() => {
+ store.state.currentProjectId = 'abcproject';
+ store.state.currentBranchId = 'master';
+ store.state.projects.abcproject = {
+ web_url: '',
+ branches: {
+ master: {
+ workingReference: '1',
+ },
+ },
+ };
+ });
+
afterEach(() => {
resetStore(store);
});
@@ -23,38 +44,32 @@ describe('Multi-file store tree actions', () => {
submodules: [{ name: 'submodule' }],
}),
}));
- spyOn(history, 'pushState');
-
- Object.assign(store.state.endpoints, {
- rootEndpoint: 'rootEndpoint',
- });
});
it('calls service getTreeData', (done) => {
- store.dispatch('getTreeData')
- .then(() => {
- expect(service.getTreeData).toHaveBeenCalledWith('rootEndpoint');
+ store.dispatch('getTreeData', basicCallParameters)
+ .then(() => {
+ expect(service.getTreeData).toHaveBeenCalledWith('rootEndpoint');
- done();
- }).catch(done.fail);
+ done();
+ }).catch(done.fail);
});
it('adds data into tree', (done) => {
- store.dispatch('getTreeData')
- .then(Vue.nextTick)
+ store.dispatch('getTreeData', basicCallParameters)
.then(() => {
- expect(store.state.tree.length).toBe(3);
- expect(store.state.tree[0].type).toBe('tree');
- expect(store.state.tree[1].type).toBe('submodule');
- expect(store.state.tree[2].type).toBe('blob');
+ projectTree = store.state.trees['abcproject/master'];
+ expect(projectTree.tree.length).toBe(3);
+ expect(projectTree.tree[0].type).toBe('tree');
+ expect(projectTree.tree[1].type).toBe('submodule');
+ expect(projectTree.tree[2].type).toBe('blob');
done();
}).catch(done.fail);
});
it('sets parent tree URL', (done) => {
- store.dispatch('getTreeData')
- .then(Vue.nextTick)
+ store.dispatch('getTreeData', basicCallParameters)
.then(() => {
expect(store.state.parentTreeUrl).toBe('parent_tree_url');
@@ -63,10 +78,9 @@ describe('Multi-file store tree actions', () => {
});
it('sets last commit path', (done) => {
- store.dispatch('getTreeData')
- .then(Vue.nextTick)
+ store.dispatch('getTreeData', basicCallParameters)
.then(() => {
- expect(store.state.lastCommitPath).toBe('last_commit_path');
+ expect(store.state.trees['abcproject/master'].lastCommitPath).toBe('last_commit_path');
done();
}).catch(done.fail);
@@ -75,8 +89,7 @@ describe('Multi-file store tree actions', () => {
it('sets root if not currently at root', (done) => {
store.state.isInitialRoot = false;
- store.dispatch('getTreeData')
- .then(Vue.nextTick)
+ store.dispatch('getTreeData', basicCallParameters)
.then(() => {
expect(store.state.isInitialRoot).toBeTruthy();
expect(store.state.isRoot).toBeTruthy();
@@ -86,7 +99,7 @@ describe('Multi-file store tree actions', () => {
});
it('sets page title', (done) => {
- store.dispatch('getTreeData')
+ store.dispatch('getTreeData', basicCallParameters)
.then(() => {
expect(document.title).toBe('test');
@@ -94,40 +107,15 @@ describe('Multi-file store tree actions', () => {
}).catch(done.fail);
});
- it('toggles loading', (done) => {
- store.dispatch('getTreeData')
- .then(() => {
- expect(store.state.loading).toBeTruthy();
-
- return Vue.nextTick();
- })
- .then(() => {
- expect(store.state.loading).toBeFalsy();
-
- done();
- }).catch(done.fail);
- });
-
- it('calls pushState with endpoint', (done) => {
- store.dispatch('getTreeData')
- .then(Vue.nextTick)
- .then(() => {
- expect(history.pushState).toHaveBeenCalledWith(jasmine.anything(), '', 'rootEndpoint');
-
- done();
- }).catch(done.fail);
- });
-
it('calls getLastCommitData if prevLastCommitPath is not null', (done) => {
const getLastCommitDataSpy = jasmine.createSpy('getLastCommitData');
const oldGetLastCommitData = store._actions.getLastCommitData; // eslint-disable-line
store._actions.getLastCommitData = [getLastCommitDataSpy]; // eslint-disable-line
store.state.prevLastCommitPath = 'test';
- store.dispatch('getTreeData')
- .then(Vue.nextTick)
+ store.dispatch('getTreeData', basicCallParameters)
.then(() => {
- expect(getLastCommitDataSpy).toHaveBeenCalledWith(store.state);
+ expect(getLastCommitDataSpy).toHaveBeenCalledWith(projectTree);
store._actions.getLastCommitData = oldGetLastCommitData; // eslint-disable-line
@@ -148,6 +136,8 @@ describe('Multi-file store tree actions', () => {
store._actions.getTreeData = [getTreeDataSpy]; // eslint-disable-line
tree = {
+ projectId: 'abcproject',
+ branchId: 'master',
opened: false,
tree: [],
};
@@ -174,10 +164,11 @@ describe('Multi-file store tree actions', () => {
tree,
}).then(() => {
expect(getTreeDataSpy).toHaveBeenCalledWith({
+ projectId: 'abcproject',
+ branch: 'master',
endpoint: 'test',
tree,
});
- expect(store.state.previousUrl).toBe('test');
done();
}).catch(done.fail);
@@ -198,155 +189,29 @@ describe('Multi-file store tree actions', () => {
done();
}).catch(done.fail);
});
-
- it('pushes new state', (done) => {
- spyOn(history, 'pushState');
- Object.assign(tree, {
- opened: true,
- parentTreeUrl: 'testing',
- });
-
- store.dispatch('toggleTreeOpen', {
- endpoint: 'test',
- tree,
- }).then(() => {
- expect(history.pushState).toHaveBeenCalledWith(jasmine.anything(), '', 'testing');
-
- done();
- }).catch(done.fail);
- });
- });
-
- describe('clickedTreeRow', () => {
- describe('tree', () => {
- let toggleTreeOpenSpy;
- let oldToggleTreeOpen;
-
- beforeEach(() => {
- toggleTreeOpenSpy = jasmine.createSpy('toggleTreeOpen');
-
- oldToggleTreeOpen = store._actions.toggleTreeOpen; // eslint-disable-line
- store._actions.toggleTreeOpen = [toggleTreeOpenSpy]; // eslint-disable-line
- });
-
- afterEach(() => {
- store._actions.toggleTreeOpen = oldToggleTreeOpen; // eslint-disable-line
- });
-
- it('opens tree', (done) => {
- const tree = {
- url: 'a',
- type: 'tree',
- };
-
- store.dispatch('clickedTreeRow', tree)
- .then(() => {
- expect(toggleTreeOpenSpy).toHaveBeenCalledWith({
- endpoint: tree.url,
- tree,
- });
-
- done();
- }).catch(done.fail);
- });
- });
-
- describe('submodule', () => {
- let row;
-
- beforeEach(() => {
- spyOn(gl.utils, 'visitUrl');
-
- row = {
- url: 'submoduleurl',
- type: 'submodule',
- loading: false,
- };
- });
-
- it('toggles loading for row', (done) => {
- store.dispatch('clickedTreeRow', row)
- .then(() => {
- expect(row.loading).toBeTruthy();
-
- done();
- }).catch(done.fail);
- });
-
- it('opens submodule URL', (done) => {
- store.dispatch('clickedTreeRow', row)
- .then(() => {
- expect(gl.utils.visitUrl).toHaveBeenCalledWith('submoduleurl');
-
- done();
- }).catch(done.fail);
- });
- });
-
- describe('blob', () => {
- let row;
-
- beforeEach(() => {
- row = {
- type: 'blob',
- opened: false,
- };
- });
-
- it('calls getFileData', (done) => {
- const getFileDataSpy = jasmine.createSpy('getFileData');
- const oldGetFileData = store._actions.getFileData; // eslint-disable-line
- store._actions.getFileData = [getFileDataSpy]; // eslint-disable-line
-
- store.dispatch('clickedTreeRow', row)
- .then(() => {
- expect(getFileDataSpy).toHaveBeenCalledWith(row);
-
- store._actions.getFileData = oldGetFileData; // eslint-disable-line
-
- done();
- }).catch(done.fail);
- });
-
- it('calls setFileActive when file is opened', (done) => {
- const setFileActiveSpy = jasmine.createSpy('setFileActive');
- const oldSetFileActive = store._actions.setFileActive; // eslint-disable-line
- store._actions.setFileActive = [setFileActiveSpy]; // eslint-disable-line
-
- row.opened = true;
-
- store.dispatch('clickedTreeRow', row)
- .then(() => {
- expect(setFileActiveSpy).toHaveBeenCalledWith(row);
-
- store._actions.setFileActive = oldSetFileActive; // eslint-disable-line
-
- done();
- }).catch(done.fail);
- });
- });
});
describe('createTempTree', () => {
- it('creates temp tree', (done) => {
- store.dispatch('createTempTree', 'test')
- .then(() => {
- expect(store.state.tree[0].tempFile).toBeTruthy();
- expect(store.state.tree[0].name).toBe('test');
- expect(store.state.tree[0].type).toBe('tree');
-
- done();
- }).catch(done.fail);
+ beforeEach(() => {
+ store.state.trees['abcproject/mybranch'] = {
+ tree: [],
+ };
+ projectTree = store.state.trees['abcproject/mybranch'];
});
- it('creates .gitkeep file in temp tree', (done) => {
- store.dispatch('createTempTree', 'test')
- .then(() => {
- expect(store.state.tree[0].tree[0].tempFile).toBeTruthy();
- expect(store.state.tree[0].tree[0].name).toBe('.gitkeep');
+ it('creates temp tree', (done) => {
+ store.dispatch('createTempTree', {
+ projectId: store.state.currentProjectId,
+ branchId: store.state.currentBranchId,
+ name: 'test',
+ parent: projectTree,
+ })
+ .then(() => {
+ expect(projectTree.tree[0].name).toBe('test');
+ expect(projectTree.tree[0].type).toBe('tree');
- done();
- }).catch(done.fail);
+ done();
+ }).catch(done.fail);
});
it('creates new folder inside another tree', (done) => {
@@ -356,35 +221,46 @@ describe('Multi-file store tree actions', () => {
tree: [],
};
- store.state.tree.push(tree);
+ projectTree.tree.push(tree);
- store.dispatch('createTempTree', 'testing/test')
- .then(() => {
- expect(store.state.tree[0].name).toBe('testing');
- expect(store.state.tree[0].tree[0].tempFile).toBeTruthy();
- expect(store.state.tree[0].tree[0].name).toBe('test');
- expect(store.state.tree[0].tree[0].type).toBe('tree');
+ store.dispatch('createTempTree', {
+ projectId: store.state.currentProjectId,
+ branchId: store.state.currentBranchId,
+ name: 'testing/test',
+ parent: projectTree,
+ })
+ .then(() => {
+ expect(projectTree.tree[0].name).toBe('testing');
+ expect(projectTree.tree[0].tree[0].tempFile).toBeTruthy();
+ expect(projectTree.tree[0].tree[0].name).toBe('test');
+ expect(projectTree.tree[0].tree[0].type).toBe('tree');
- done();
- }).catch(done.fail);
+ done();
+ }).catch(done.fail);
});
it('does not create new tree if already exists', (done) => {
const tree = {
type: 'tree',
name: 'testing',
+ endpoint: 'test',
tree: [],
};
- store.state.tree.push(tree);
+ projectTree.tree.push(tree);
- store.dispatch('createTempTree', 'testing/test')
- .then(() => {
- expect(store.state.tree[0].name).toBe('testing');
- expect(store.state.tree[0].tempFile).toBeUndefined();
+ store.dispatch('createTempTree', {
+ projectId: store.state.currentProjectId,
+ branchId: store.state.currentBranchId,
+ name: 'testing/test',
+ parent: projectTree,
+ })
+ .then(() => {
+ expect(projectTree.tree[0].name).toBe('testing');
+ expect(projectTree.tree[0].tempFile).toBeUndefined();
- done();
- }).catch(done.fail);
+ done();
+ }).catch(done.fail);
});
});
@@ -404,12 +280,17 @@ describe('Multi-file store tree actions', () => {
}]),
}));
- store.state.tree.push(file('testing', '1', 'tree'));
- store.state.lastCommitPath = 'lastcommitpath';
+ store.state.trees['abcproject/mybranch'] = {
+ tree: [],
+ };
+
+ projectTree = store.state.trees['abcproject/mybranch'];
+ projectTree.tree.push(file('testing', '1', 'tree'));
+ projectTree.lastCommitPath = 'lastcommitpath';
});
it('calls service with lastCommitPath', (done) => {
- store.dispatch('getLastCommitData')
+ store.dispatch('getLastCommitData', projectTree)
.then(() => {
expect(service.getTreeLastCommit).toHaveBeenCalledWith('lastcommitpath');
@@ -418,22 +299,22 @@ describe('Multi-file store tree actions', () => {
});
it('updates trees last commit data', (done) => {
- store.dispatch('getLastCommitData')
- .then(Vue.nextTick)
+ store.dispatch('getLastCommitData', projectTree)
+ .then(Vue.nextTick)
.then(() => {
- expect(store.state.tree[0].lastCommit.message).toBe('commit message');
+ expect(projectTree.tree[0].lastCommit.message).toBe('commit message');
done();
}).catch(done.fail);
});
it('does not update entry if not found', (done) => {
- store.state.tree[0].name = 'a';
+ projectTree.tree[0].name = 'a';
- store.dispatch('getLastCommitData')
+ store.dispatch('getLastCommitData', projectTree)
.then(Vue.nextTick)
.then(() => {
- expect(store.state.tree[0].lastCommit.message).not.toBe('commit message');
+ expect(projectTree.tree[0].lastCommit.message).not.toBe('commit message');
done();
}).catch(done.fail);
diff --git a/spec/javascripts/repo/stores/actions_spec.js b/spec/javascripts/repo/stores/actions_spec.js
index f2a7a698912..0b0d34f072a 100644
--- a/spec/javascripts/repo/stores/actions_spec.js
+++ b/spec/javascripts/repo/stores/actions_spec.js
@@ -1,6 +1,7 @@
import Vue from 'vue';
-import store from '~/repo/stores';
-import service from '~/repo/services';
+import * as urlUtils from '~/lib/utils/url_utility';
+import store from '~/ide/stores';
+import service from '~/ide/services';
import { resetStore, file } from '../helpers';
describe('Multi-file store actions', () => {
@@ -10,11 +11,11 @@ describe('Multi-file store actions', () => {
describe('redirectToUrl', () => {
it('calls visitUrl', (done) => {
- spyOn(gl.utils, 'visitUrl');
+ spyOn(urlUtils, 'visitUrl');
store.dispatch('redirectToUrl', 'test')
.then(() => {
- expect(gl.utils.visitUrl).toHaveBeenCalledWith('test');
+ expect(urlUtils.visitUrl).toHaveBeenCalledWith('test');
done();
})
@@ -109,6 +110,7 @@ describe('Multi-file store actions', () => {
it('can force closed if there are changed files', (done) => {
store.state.editMode = true;
+
store.state.openFiles.push(file());
store.state.openFiles[0].changed = true;
@@ -124,7 +126,6 @@ describe('Multi-file store actions', () => {
it('discards file changes', (done) => {
const f = file();
store.state.editMode = true;
- store.state.tree.push(f);
store.state.openFiles.push(f);
f.changed = true;
@@ -140,8 +141,6 @@ describe('Multi-file store actions', () => {
describe('toggleBlobView', () => {
it('sets edit mode view if in edit mode', (done) => {
- store.state.editMode = true;
-
store.dispatch('toggleBlobView')
.then(() => {
expect(store.state.currentBlobView).toBe('repo-editor');
@@ -152,6 +151,8 @@ describe('Multi-file store actions', () => {
});
it('sets preview mode view if not in edit mode', (done) => {
+ store.state.editMode = false;
+
store.dispatch('toggleBlobView')
.then(() => {
expect(store.state.currentBlobView).toBe('repo-preview');
@@ -164,9 +165,15 @@ describe('Multi-file store actions', () => {
describe('checkCommitStatus', () => {
beforeEach(() => {
- store.state.project.id = 2;
- store.state.currentBranch = 'master';
- store.state.currentRef = '1';
+ store.state.currentProjectId = 'abcproject';
+ store.state.currentBranchId = 'master';
+ store.state.projects.abcproject = {
+ branches: {
+ master: {
+ workingReference: '1',
+ },
+ },
+ };
});
it('calls service', (done) => {
@@ -176,7 +183,7 @@ describe('Multi-file store actions', () => {
store.dispatch('checkCommitStatus')
.then(() => {
- expect(service.getBranchData).toHaveBeenCalledWith(2, 'master');
+ expect(service.getBranchData).toHaveBeenCalledWith('abcproject', 'master');
done();
})
@@ -220,7 +227,17 @@ describe('Multi-file store actions', () => {
document.body.innerHTML += '<div class="flash-container"></div>';
- store.state.project.id = 123;
+ store.state.currentProjectId = 'abcproject';
+ store.state.currentBranchId = 'master';
+ store.state.projects.abcproject = {
+ web_url: 'webUrl',
+ branches: {
+ master: {
+ workingReference: '1',
+ },
+ },
+ };
+
payload = {
branch: 'master',
};
@@ -247,7 +264,7 @@ describe('Multi-file store actions', () => {
it('calls service', (done) => {
store.dispatch('commitChanges', { payload, newMr: false })
.then(() => {
- expect(service.commit).toHaveBeenCalledWith(123, payload);
+ expect(service.commit).toHaveBeenCalledWith('abcproject', payload);
done();
}).catch(done.fail);
@@ -283,17 +300,6 @@ describe('Multi-file store actions', () => {
}).catch(done.fail);
});
- it('toggles edit mode', (done) => {
- store.state.editMode = true;
-
- store.dispatch('commitChanges', { payload, newMr: false })
- .then(() => {
- expect(store.state.editMode).toBeFalsy();
-
- done();
- }).catch(done.fail);
- });
-
it('closes all files', (done) => {
store.state.openFiles.push(file());
store.state.openFiles[0].opened = true;
@@ -316,23 +322,12 @@ describe('Multi-file store actions', () => {
}).catch(done.fail);
});
- it('updates commit ref', (done) => {
- store.dispatch('commitChanges', { payload, newMr: false })
- .then(() => {
- expect(store.state.currentRef).toBe('123456');
-
- done();
- }).catch(done.fail);
- });
-
it('redirects to new merge request page', (done) => {
- spyOn(gl.utils, 'visitUrl');
-
- store.state.endpoints.newMergeRequestUrl = 'newMergeRequestUrl?branch=';
+ spyOn(urlUtils, 'visitUrl');
store.dispatch('commitChanges', { payload, newMr: true })
.then(() => {
- expect(gl.utils.visitUrl).toHaveBeenCalledWith('newMergeRequestUrl?branch=master');
+ expect(urlUtils.visitUrl).toHaveBeenCalledWith('webUrl/merge_requests/new?merge_request%5Bsource_branch%5D=master');
done();
}).catch(done.fail);
@@ -362,15 +357,30 @@ describe('Multi-file store actions', () => {
});
describe('createTempEntry', () => {
+ beforeEach(() => {
+ store.state.trees['abcproject/mybranch'] = {
+ tree: [],
+ };
+ store.state.projects.abcproject = {
+ web_url: '',
+ };
+ });
+
it('creates a temp tree', (done) => {
+ const projectTree = store.state.trees['abcproject/mybranch'];
+
store.dispatch('createTempEntry', {
+ projectId: 'abcproject',
+ branchId: 'mybranch',
+ parent: projectTree,
name: 'test',
type: 'tree',
})
.then(() => {
- expect(store.state.tree.length).toBe(1);
- expect(store.state.tree[0].tempFile).toBeTruthy();
- expect(store.state.tree[0].type).toBe('tree');
+ const baseTree = projectTree.tree;
+ expect(baseTree.length).toBe(1);
+ expect(baseTree[0].tempFile).toBeTruthy();
+ expect(baseTree[0].type).toBe('tree');
done();
})
@@ -378,14 +388,20 @@ describe('Multi-file store actions', () => {
});
it('creates temp file', (done) => {
+ const projectTree = store.state.trees['abcproject/mybranch'];
+
store.dispatch('createTempEntry', {
+ projectId: 'abcproject',
+ branchId: 'mybranch',
+ parent: projectTree,
name: 'test',
type: 'blob',
})
.then(() => {
- expect(store.state.tree.length).toBe(1);
- expect(store.state.tree[0].tempFile).toBeTruthy();
- expect(store.state.tree[0].type).toBe('blob');
+ const baseTree = projectTree.tree;
+ expect(baseTree.length).toBe(1);
+ expect(baseTree[0].tempFile).toBeTruthy();
+ expect(baseTree[0].type).toBe('blob');
done();
})
diff --git a/spec/javascripts/repo/stores/getters_spec.js b/spec/javascripts/repo/stores/getters_spec.js
index 952b8ec3a59..d0d5934f29a 100644
--- a/spec/javascripts/repo/stores/getters_spec.js
+++ b/spec/javascripts/repo/stores/getters_spec.js
@@ -1,5 +1,5 @@
-import * as getters from '~/repo/stores/getters';
-import state from '~/repo/stores/state';
+import * as getters from '~/ide/stores/getters';
+import state from '~/ide/stores/state';
import { file } from '../helpers';
describe('Multi-file store getters', () => {
@@ -9,20 +9,6 @@ describe('Multi-file store getters', () => {
localState = state();
});
- describe('treeList', () => {
- it('returns flat tree list', () => {
- localState.tree.push(file('1'));
- localState.tree[0].tree.push(file('2'));
- localState.tree[0].tree[0].tree.push(file('3'));
-
- const treeList = getters.treeList(localState);
-
- expect(treeList.length).toBe(3);
- expect(treeList[1].name).toBe(localState.tree[0].tree[0].name);
- expect(treeList[2].name).toBe(localState.tree[0].tree[0].tree[0].name);
- });
- });
-
describe('changedFiles', () => {
it('returns a list of changed opened files', () => {
localState.openFiles.push(file());
@@ -49,7 +35,7 @@ describe('Multi-file store getters', () => {
localState.openFiles.push(file());
localState.openFiles.push(file('active'));
- expect(getters.activeFile(localState)).toBeUndefined();
+ expect(getters.activeFile(localState)).toBeNull();
});
});
@@ -67,18 +53,6 @@ describe('Multi-file store getters', () => {
});
});
- describe('isCollapsed', () => {
- it('returns true if state has open files', () => {
- localState.openFiles.push(file());
-
- expect(getters.isCollapsed(localState)).toBeTruthy();
- });
-
- it('returns false if state has no open files', () => {
- expect(getters.isCollapsed(localState)).toBeFalsy();
- });
- });
-
describe('canEditFile', () => {
beforeEach(() => {
localState.onTopOfBranch = true;
@@ -109,12 +83,6 @@ describe('Multi-file store getters', () => {
expect(getters.canEditFile(localState)).toBeFalsy();
});
-
- it('returns false if user can commit but on a branch', () => {
- localState.onTopOfBranch = false;
-
- expect(getters.canEditFile(localState)).toBeFalsy();
- });
});
describe('modifiedFiles', () => {
diff --git a/spec/javascripts/repo/stores/mutations/branch_spec.js b/spec/javascripts/repo/stores/mutations/branch_spec.js
index 3c06794d5e3..a7167537ef2 100644
--- a/spec/javascripts/repo/stores/mutations/branch_spec.js
+++ b/spec/javascripts/repo/stores/mutations/branch_spec.js
@@ -1,5 +1,5 @@
-import mutations from '~/repo/stores/mutations/branch';
-import state from '~/repo/stores/state';
+import mutations from '~/ide/stores/mutations/branch';
+import state from '~/ide/stores/state';
describe('Multi-file store branch mutations', () => {
let localState;
@@ -12,7 +12,7 @@ describe('Multi-file store branch mutations', () => {
it('sets currentBranch', () => {
mutations.SET_CURRENT_BRANCH(localState, 'master');
- expect(localState.currentBranch).toBe('master');
+ expect(localState.currentBranchId).toBe('master');
});
});
});
diff --git a/spec/javascripts/repo/stores/mutations/file_spec.js b/spec/javascripts/repo/stores/mutations/file_spec.js
index 2f2835dde1f..947a60587df 100644
--- a/spec/javascripts/repo/stores/mutations/file_spec.js
+++ b/spec/javascripts/repo/stores/mutations/file_spec.js
@@ -1,5 +1,5 @@
-import mutations from '~/repo/stores/mutations/file';
-import state from '~/repo/stores/state';
+import mutations from '~/ide/stores/mutations/file';
+import state from '~/ide/stores/state';
import { file } from '../../helpers';
describe('Multi-file store file mutations', () => {
diff --git a/spec/javascripts/repo/stores/mutations/tree_spec.js b/spec/javascripts/repo/stores/mutations/tree_spec.js
index 1c76cfed9c8..cf1248ba28b 100644
--- a/spec/javascripts/repo/stores/mutations/tree_spec.js
+++ b/spec/javascripts/repo/stores/mutations/tree_spec.js
@@ -1,5 +1,5 @@
-import mutations from '~/repo/stores/mutations/tree';
-import state from '~/repo/stores/state';
+import mutations from '~/ide/stores/mutations/tree';
+import state from '~/ide/stores/state';
import { file } from '../../helpers';
describe('Multi-file store tree mutations', () => {
diff --git a/spec/javascripts/repo/stores/mutations_spec.js b/spec/javascripts/repo/stores/mutations_spec.js
index d1c9885e01d..5fd8ad94972 100644
--- a/spec/javascripts/repo/stores/mutations_spec.js
+++ b/spec/javascripts/repo/stores/mutations_spec.js
@@ -1,5 +1,5 @@
-import mutations from '~/repo/stores/mutations';
-import state from '~/repo/stores/state';
+import mutations from '~/ide/stores/mutations';
+import state from '~/ide/stores/state';
import { file } from '../helpers';
describe('Multi-file store mutations', () => {
@@ -65,11 +65,11 @@ describe('Multi-file store mutations', () => {
it('toggles editMode', () => {
mutations.TOGGLE_EDIT_MODE(localState);
- expect(localState.editMode).toBeTruthy();
+ expect(localState.editMode).toBeFalsy();
mutations.TOGGLE_EDIT_MODE(localState);
- expect(localState.editMode).toBeFalsy();
+ expect(localState.editMode).toBeTruthy();
});
});
@@ -85,14 +85,6 @@ describe('Multi-file store mutations', () => {
});
});
- describe('SET_COMMIT_REF', () => {
- it('sets currentRef', () => {
- mutations.SET_COMMIT_REF(localState, '123');
-
- expect(localState.currentRef).toBe('123');
- });
- });
-
describe('SET_ROOT', () => {
it('sets isRoot & initialRoot', () => {
mutations.SET_ROOT(localState, true);
@@ -107,11 +99,27 @@ describe('Multi-file store mutations', () => {
});
});
- describe('SET_PREVIOUS_URL', () => {
- it('sets previousUrl', () => {
- mutations.SET_PREVIOUS_URL(localState, 'testing');
+ describe('SET_LEFT_PANEL_COLLAPSED', () => {
+ it('sets left panel collapsed', () => {
+ mutations.SET_LEFT_PANEL_COLLAPSED(localState, true);
+
+ expect(localState.leftPanelCollapsed).toBeTruthy();
+
+ mutations.SET_LEFT_PANEL_COLLAPSED(localState, false);
+
+ expect(localState.leftPanelCollapsed).toBeFalsy();
+ });
+ });
+
+ describe('SET_RIGHT_PANEL_COLLAPSED', () => {
+ it('sets right panel collapsed', () => {
+ mutations.SET_RIGHT_PANEL_COLLAPSED(localState, true);
+
+ expect(localState.rightPanelCollapsed).toBeTruthy();
+
+ mutations.SET_RIGHT_PANEL_COLLAPSED(localState, false);
- expect(localState.previousUrl).toBe('testing');
+ expect(localState.rightPanelCollapsed).toBeFalsy();
});
});
});
diff --git a/spec/javascripts/repo/stores/utils_spec.js b/spec/javascripts/repo/stores/utils_spec.js
index 37287c587d7..89745a2029e 100644
--- a/spec/javascripts/repo/stores/utils_spec.js
+++ b/spec/javascripts/repo/stores/utils_spec.js
@@ -1,4 +1,6 @@
-import * as utils from '~/repo/stores/utils';
+import * as utils from '~/ide/stores/utils';
+import state from '~/ide/stores/state';
+import { file } from '../helpers';
describe('Multi-file store utils', () => {
describe('setPageTitle', () => {
@@ -9,13 +11,28 @@ describe('Multi-file store utils', () => {
});
});
- describe('pushState', () => {
- it('calls history.pushState', () => {
- spyOn(history, 'pushState');
+ describe('treeList', () => {
+ let localState;
- utils.pushState('test');
+ beforeEach(() => {
+ localState = state();
+ });
+
+ it('returns flat tree list', () => {
+ localState.trees = [];
+ localState.trees['abcproject/mybranch'] = {
+ tree: [],
+ };
+ const baseTree = localState.trees['abcproject/mybranch'].tree;
+ baseTree.push(file('1'));
+ baseTree[0].tree.push(file('2'));
+ baseTree[0].tree[0].tree.push(file('3'));
+
+ const treeList = utils.treeList(localState, 'abcproject/mybranch');
- expect(history.pushState).toHaveBeenCalledWith({ url: 'test' }, '', 'test');
+ expect(treeList.length).toBe(3);
+ expect(treeList[1].name).toBe(baseTree[0].tree[0].name);
+ expect(treeList[2].name).toBe(baseTree[0].tree[0].tree[0].name);
});
});
@@ -52,10 +69,10 @@ describe('Multi-file store utils', () => {
});
describe('findIndexOfFile', () => {
- let state;
+ let localState;
beforeEach(() => {
- state = [{
+ localState = [{
path: '1',
}, {
path: '2',
@@ -63,7 +80,7 @@ describe('Multi-file store utils', () => {
});
it('finds in the index of an entry by path', () => {
- const index = utils.findIndexOfFile(state, {
+ const index = utils.findIndexOfFile(localState, {
path: '2',
});
@@ -72,10 +89,10 @@ describe('Multi-file store utils', () => {
});
describe('findEntry', () => {
- let state;
+ let localState;
beforeEach(() => {
- state = {
+ localState = {
tree: [{
type: 'tree',
name: 'test',
@@ -87,14 +104,14 @@ describe('Multi-file store utils', () => {
});
it('returns an entry found by name', () => {
- const foundEntry = utils.findEntry(state, 'tree', 'test');
+ const foundEntry = utils.findEntry(localState.tree, 'tree', 'test');
expect(foundEntry.type).toBe('tree');
expect(foundEntry.name).toBe('test');
});
it('returns undefined when no entry found', () => {
- const foundEntry = utils.findEntry(state, 'blob', 'test');
+ const foundEntry = utils.findEntry(localState.tree, 'blob', 'test');
expect(foundEntry).toBeUndefined();
});
diff --git a/spec/javascripts/right_sidebar_spec.js b/spec/javascripts/right_sidebar_spec.js
index 5505f983d71..3267e29585b 100644
--- a/spec/javascripts/right_sidebar_spec.js
+++ b/spec/javascripts/right_sidebar_spec.js
@@ -1,8 +1,7 @@
/* eslint-disable space-before-function-paren, no-var, one-var, one-var-declaration-per-line, new-parens, no-return-assign, new-cap, vars-on-top, max-len */
-/* global Sidebar */
import '~/commons/bootstrap';
-import '~/right_sidebar';
+import Sidebar from '~/right_sidebar';
(function() {
var $aside, $icon, $labelsIcon, $page, $toggle, assertSidebarState;
@@ -41,7 +40,7 @@ import '~/right_sidebar';
loadFixtures(fixtureName);
this.sidebar = new Sidebar;
$aside = $('.right-sidebar');
- $page = $('.page-with-sidebar');
+ $page = $('.layout-page');
$icon = $aside.find('i');
$toggle = $aside.find('.js-sidebar-toggle');
return $labelsIcon = $aside.find('.sidebar-collapsed-icon');
diff --git a/spec/javascripts/search_autocomplete_spec.js b/spec/javascripts/search_autocomplete_spec.js
index fdfc59a6f12..206f95abc1a 100644
--- a/spec/javascripts/search_autocomplete_spec.js
+++ b/spec/javascripts/search_autocomplete_spec.js
@@ -1,8 +1,9 @@
/* eslint-disable space-before-function-paren, max-len, no-var, one-var, one-var-declaration-per-line, no-unused-expressions, consistent-return, no-param-reassign, default-case, no-return-assign, comma-dangle, object-shorthand, prefer-template, quotes, new-parens, vars-on-top, new-cap, max-len */
import '~/gl_dropdown';
-import '~/search_autocomplete';
+import SearchAutocomplete from '~/search_autocomplete';
import '~/lib/utils/common_utils';
+import * as urlUtils from '~/lib/utils/url_utility';
(function() {
var assertLinks, dashboardIssuesPath, dashboardMRsPath, groupIssuesPath, groupMRsPath, groupName, mockDashboardOptions, mockGroupOptions, mockProjectOptions, projectIssuesPath, projectMRsPath, projectName, userId, widget;
@@ -121,13 +122,13 @@ import '~/lib/utils/common_utils';
loadFixtures('static/search_autocomplete.html.raw');
// Prevent turbolinks from triggering within gl_dropdown
- spyOn(window.gl.utils, 'visitUrl').and.returnValue(true);
+ spyOn(urlUtils, 'visitUrl').and.returnValue(true);
window.gon = {};
window.gon.current_user_id = userId;
window.gon.current_username = userName;
- return widget = new gl.SearchAutocomplete;
+ return widget = new SearchAutocomplete();
});
afterEach(function() {
diff --git a/spec/javascripts/sidebar/sidebar_assignees_spec.js b/spec/javascripts/sidebar/sidebar_assignees_spec.js
index 929ba75e67d..b97e24d9dcf 100644
--- a/spec/javascripts/sidebar/sidebar_assignees_spec.js
+++ b/spec/javascripts/sidebar/sidebar_assignees_spec.js
@@ -4,20 +4,29 @@ import SidebarMediator from '~/sidebar/sidebar_mediator';
import SidebarService from '~/sidebar/services/sidebar_service';
import SidebarStore from '~/sidebar/stores/sidebar_store';
import Mock from './mock_data';
+import mountComponent from '../helpers/vue_mount_component_helper';
describe('sidebar assignees', () => {
- let component;
- let SidebarAssigneeComponent;
+ let vm;
+ let mediator;
+ let sidebarAssigneesEl;
preloadFixtures('issues/open-issue.html.raw');
beforeEach(() => {
Vue.http.interceptors.push(Mock.sidebarMockInterceptor);
- SidebarAssigneeComponent = Vue.extend(SidebarAssignees);
- spyOn(SidebarMediator.prototype, 'saveAssignees').and.callThrough();
- spyOn(SidebarMediator.prototype, 'assignYourself').and.callThrough();
- this.mediator = new SidebarMediator(Mock.mediator);
+
loadFixtures('issues/open-issue.html.raw');
- this.sidebarAssigneesEl = document.querySelector('#js-vue-sidebar-assignees');
+
+ mediator = new SidebarMediator(Mock.mediator);
+ spyOn(mediator, 'saveAssignees').and.callThrough();
+ spyOn(mediator, 'assignYourself').and.callThrough();
+
+ const SidebarAssigneeComponent = Vue.extend(SidebarAssignees);
+ sidebarAssigneesEl = document.querySelector('#js-vue-sidebar-assignees');
+ vm = mountComponent(SidebarAssigneeComponent, {
+ mediator,
+ field: sidebarAssigneesEl.dataset.field,
+ }, sidebarAssigneesEl);
});
afterEach(() => {
@@ -28,30 +37,24 @@ describe('sidebar assignees', () => {
});
it('calls the mediator when saves the assignees', () => {
- component = new SidebarAssigneeComponent()
- .$mount(this.sidebarAssigneesEl);
- component.saveAssignees();
-
- expect(SidebarMediator.prototype.saveAssignees).toHaveBeenCalled();
+ vm.saveAssignees();
+ expect(mediator.saveAssignees).toHaveBeenCalled();
});
it('calls the mediator when "assignSelf" method is called', () => {
- component = new SidebarAssigneeComponent()
- .$mount(this.sidebarAssigneesEl);
- component.assignSelf();
+ vm.assignSelf();
- expect(SidebarMediator.prototype.assignYourself).toHaveBeenCalled();
- expect(this.mediator.store.assignees.length).toEqual(1);
+ expect(mediator.assignYourself).toHaveBeenCalled();
+ expect(mediator.store.assignees.length).toEqual(1);
});
it('hides assignees until fetched', (done) => {
- component = new SidebarAssigneeComponent().$mount(this.sidebarAssigneesEl);
- const currentAssignee = this.sidebarAssigneesEl.querySelector('.value');
+ const currentAssignee = sidebarAssigneesEl.querySelector('.value');
expect(currentAssignee).toBe(null);
- component.store.isFetching.assignees = false;
+ vm.store.isFetching.assignees = false;
Vue.nextTick(() => {
- expect(component.$el.querySelector('.value')).toBeVisible();
+ expect(vm.$el.querySelector('.value')).toBeVisible();
done();
});
});
diff --git a/spec/javascripts/sidebar/sidebar_mediator_spec.js b/spec/javascripts/sidebar/sidebar_mediator_spec.js
index 14c34d5a78c..9efd109b996 100644
--- a/spec/javascripts/sidebar/sidebar_mediator_spec.js
+++ b/spec/javascripts/sidebar/sidebar_mediator_spec.js
@@ -1,4 +1,5 @@
import Vue from 'vue';
+import * as urlUtils from '~/lib/utils/url_utility';
import SidebarMediator from '~/sidebar/sidebar_mediator';
import SidebarStore from '~/sidebar/stores/sidebar_store';
import SidebarService from '~/sidebar/services/sidebar_service';
@@ -85,12 +86,12 @@ describe('Sidebar mediator', () => {
const moveToProjectId = 7;
this.mediator.store.setMoveToProjectId(moveToProjectId);
spyOn(this.mediator.service, 'moveIssue').and.callThrough();
- spyOn(gl.utils, 'visitUrl');
+ spyOn(urlUtils, 'visitUrl');
this.mediator.moveIssue()
.then(() => {
expect(this.mediator.service.moveIssue).toHaveBeenCalledWith(moveToProjectId);
- expect(gl.utils.visitUrl).toHaveBeenCalledWith('/root/some-project/issues/5');
+ expect(urlUtils.visitUrl).toHaveBeenCalledWith('/root/some-project/issues/5');
})
.then(done)
.catch(done.fail);
diff --git a/spec/javascripts/sidebar/sidebar_subscriptions_spec.js b/spec/javascripts/sidebar/sidebar_subscriptions_spec.js
index 7adf22b0f1f..a6113cb0bae 100644
--- a/spec/javascripts/sidebar/sidebar_subscriptions_spec.js
+++ b/spec/javascripts/sidebar/sidebar_subscriptions_spec.js
@@ -26,11 +26,14 @@ describe('Sidebar Subscriptions', function () {
});
it('calls the mediator toggleSubscription on event', () => {
- spyOn(SidebarMediator.prototype, 'toggleSubscription').and.returnValue(Promise.resolve());
- vm = mountComponent(SidebarSubscriptions, {});
+ const mediator = new SidebarMediator();
+ spyOn(mediator, 'toggleSubscription').and.returnValue(Promise.resolve());
+ vm = mountComponent(SidebarSubscriptions, {
+ mediator,
+ });
eventHub.$emit('toggleSubscription');
- expect(SidebarMediator.prototype.toggleSubscription).toHaveBeenCalled();
+ expect(mediator.toggleSubscription).toHaveBeenCalled();
});
});
diff --git a/spec/javascripts/syntax_highlight_spec.js b/spec/javascripts/syntax_highlight_spec.js
index 946f98379ce..763a15e710b 100644
--- a/spec/javascripts/syntax_highlight_spec.js
+++ b/spec/javascripts/syntax_highlight_spec.js
@@ -1,44 +1,42 @@
/* eslint-disable space-before-function-paren, no-var, no-return-assign, quotes */
-import '~/syntax_highlight';
+import syntaxHighlight from '~/syntax_highlight';
-(function() {
- describe('Syntax Highlighter', function() {
- var stubUserColorScheme;
- stubUserColorScheme = function(value) {
- if (window.gon == null) {
- window.gon = {};
- }
- return window.gon.user_color_scheme = value;
- };
- describe('on a js-syntax-highlight element', function() {
- beforeEach(function() {
- return setFixtures('<div class="js-syntax-highlight"></div>');
- });
- return it('applies syntax highlighting', function() {
- stubUserColorScheme('monokai');
- $('.js-syntax-highlight').syntaxHighlight();
- return expect($('.js-syntax-highlight')).toHaveClass('monokai');
- });
+describe('Syntax Highlighter', function() {
+ var stubUserColorScheme;
+ stubUserColorScheme = function(value) {
+ if (window.gon == null) {
+ window.gon = {};
+ }
+ return window.gon.user_color_scheme = value;
+ };
+ describe('on a js-syntax-highlight element', function() {
+ beforeEach(function() {
+ return setFixtures('<div class="js-syntax-highlight"></div>');
});
- return describe('on a parent element', function() {
- beforeEach(function() {
- return setFixtures("<div class=\"parent\">\n <div class=\"js-syntax-highlight\"></div>\n <div class=\"foo\"></div>\n <div class=\"js-syntax-highlight\"></div>\n</div>");
- });
- it('applies highlighting to all applicable children', function() {
- stubUserColorScheme('monokai');
- $('.parent').syntaxHighlight();
- expect($('.parent, .foo')).not.toHaveClass('monokai');
- return expect($('.monokai').length).toBe(2);
- });
- return it('prevents an infinite loop when no matches exist', function() {
- var highlight;
- setFixtures('<div></div>');
- highlight = function() {
- return $('div').syntaxHighlight();
- };
- return expect(highlight).not.toThrow();
- });
+ return it('applies syntax highlighting', function() {
+ stubUserColorScheme('monokai');
+ syntaxHighlight($('.js-syntax-highlight'));
+ return expect($('.js-syntax-highlight')).toHaveClass('monokai');
});
});
-}).call(window);
+ return describe('on a parent element', function() {
+ beforeEach(function() {
+ return setFixtures("<div class=\"parent\">\n <div class=\"js-syntax-highlight\"></div>\n <div class=\"foo\"></div>\n <div class=\"js-syntax-highlight\"></div>\n</div>");
+ });
+ it('applies highlighting to all applicable children', function() {
+ stubUserColorScheme('monokai');
+ syntaxHighlight($('.parent'));
+ expect($('.parent, .foo')).not.toHaveClass('monokai');
+ return expect($('.monokai').length).toBe(2);
+ });
+ return it('prevents an infinite loop when no matches exist', function() {
+ var highlight;
+ setFixtures('<div></div>');
+ highlight = function() {
+ return syntaxHighlight($('div'));
+ };
+ return expect(highlight).not.toThrow();
+ });
+ });
+});
diff --git a/spec/javascripts/test_bundle.js b/spec/javascripts/test_bundle.js
index fd7aa332d17..6897c991066 100644
--- a/spec/javascripts/test_bundle.js
+++ b/spec/javascripts/test_bundle.js
@@ -17,6 +17,12 @@ Vue.config.warnHandler = (msg, vm, trace) => {
fail(`${msg}${trace}`);
};
+let hasVueErrors = false;
+Vue.config.errorHandler = function (err) {
+ hasVueErrors = true;
+ fail(err);
+};
+
Vue.use(VueResource);
// enable test fixtures
@@ -72,7 +78,7 @@ testsContext.keys().forEach(function (path) {
describe('test errors', () => {
beforeAll((done) => {
- if (hasUnhandledPromiseRejections || hasVueWarnings) {
+ if (hasUnhandledPromiseRejections || hasVueWarnings || hasVueErrors) {
setTimeout(done, 1000);
} else {
done();
@@ -86,6 +92,10 @@ describe('test errors', () => {
it('has no Vue warnings', () => {
expect(hasVueWarnings).toBe(false);
});
+
+ it('has no Vue error', () => {
+ expect(hasVueErrors).toBe(false);
+ });
});
// if we're generating coverage reports, make sure to include all files so
diff --git a/spec/javascripts/todos_spec.js b/spec/javascripts/todos_spec.js
index 7d3c9319a11..59e16f0786e 100644
--- a/spec/javascripts/todos_spec.js
+++ b/spec/javascripts/todos_spec.js
@@ -1,3 +1,4 @@
+import * as urlUtils from '~/lib/utils/url_utility';
import Todos from '~/todos';
import '~/lib/utils/common_utils';
@@ -16,7 +17,7 @@ describe('Todos', () => {
it('opens the todo url', (done) => {
const todoLink = todoItem.dataset.url;
- spyOn(gl.utils, 'visitUrl').and.callFake((url) => {
+ spyOn(urlUtils, 'visitUrl').and.callFake((url) => {
expect(url).toEqual(todoLink);
done();
});
@@ -31,7 +32,7 @@ describe('Todos', () => {
beforeEach(() => {
metakeyEvent = $.Event('click', { keyCode: 91, ctrlKey: true });
- visitUrlSpy = spyOn(gl.utils, 'visitUrl').and.callFake(() => {});
+ visitUrlSpy = spyOn(urlUtils, 'visitUrl').and.callFake(() => {});
windowOpenSpy = spyOn(window, 'open').and.callFake(() => {});
});
diff --git a/spec/javascripts/vue_mr_widget/components/mr_widget_deployment_spec.js b/spec/javascripts/vue_mr_widget/components/mr_widget_deployment_spec.js
index 7ee998c8fce..6a59dc3c87e 100644
--- a/spec/javascripts/vue_mr_widget/components/mr_widget_deployment_spec.js
+++ b/spec/javascripts/vue_mr_widget/components/mr_widget_deployment_spec.js
@@ -1,6 +1,8 @@
import Vue from 'vue';
+import * as urlUtils from '~/lib/utils/url_utility';
import deploymentComponent from '~/vue_merge_request_widget/components/mr_widget_deployment';
import MRWidgetService from '~/vue_merge_request_widget/services/mr_widget_service';
+import { getTimeago } from '~/lib/utils/datetime_utility';
const deploymentMockData = [
{
@@ -48,7 +50,7 @@ describe('MRWidgetDeployment', () => {
describe('formatDate', () => {
it('should work', () => {
- const readable = gl.utils.getTimeago().format(deployment.deployed_at);
+ const readable = getTimeago().format(deployment.deployed_at);
expect(vm.formatDate(deployment.deployed_at)).toEqual(readable);
});
});
@@ -93,10 +95,8 @@ describe('MRWidgetDeployment', () => {
const url = '/foo/bar';
const returnPromise = () => new Promise((resolve) => {
resolve({
- json() {
- return {
- redirect_url: url,
- };
+ data: {
+ redirect_url: url,
},
});
});
@@ -108,13 +108,13 @@ describe('MRWidgetDeployment', () => {
it('should show a confirm dialog and call service.stopEnvironment when confirmed', (done) => {
spyOn(window, 'confirm').and.returnValue(true);
spyOn(MRWidgetService, 'stopEnvironment').and.returnValue(returnPromise(true));
- spyOn(gl.utils, 'visitUrl').and.returnValue(true);
+ spyOn(urlUtils, 'visitUrl').and.returnValue(true);
vm = mockStopEnvironment();
expect(window.confirm).toHaveBeenCalled();
expect(MRWidgetService.stopEnvironment).toHaveBeenCalledWith(deploymentMockData.stop_url);
setTimeout(() => {
- expect(gl.utils.visitUrl).toHaveBeenCalledWith(url);
+ expect(urlUtils.visitUrl).toHaveBeenCalledWith(url);
done();
}, 333);
});
diff --git a/spec/javascripts/vue_mr_widget/components/mr_widget_memory_usage_spec.js b/spec/javascripts/vue_mr_widget/components/mr_widget_memory_usage_spec.js
index 2ae3adc1f93..07ed7f7f532 100644
--- a/spec/javascripts/vue_mr_widget/components/mr_widget_memory_usage_spec.js
+++ b/spec/javascripts/vue_mr_widget/components/mr_widget_memory_usage_spec.js
@@ -155,9 +155,7 @@ describe('MemoryUsage', () => {
describe('loadMetrics', () => {
const returnServicePromise = () => new Promise((resolve) => {
resolve({
- json() {
- return metricsMockData;
- },
+ data: metricsMockData,
});
});
diff --git a/spec/javascripts/vue_mr_widget/components/mr_widget_rebase_spec.js b/spec/javascripts/vue_mr_widget/components/mr_widget_rebase_spec.js
new file mode 100644
index 00000000000..66ecaa316c8
--- /dev/null
+++ b/spec/javascripts/vue_mr_widget/components/mr_widget_rebase_spec.js
@@ -0,0 +1,115 @@
+import Vue from 'vue';
+import eventHub from '~/vue_merge_request_widget/event_hub';
+import component from '~/vue_merge_request_widget/components/states/mr_widget_rebase.vue';
+import mountComponent from '../../helpers/vue_mount_component_helper';
+
+describe('Merge request widget rebase component', () => {
+ let Component;
+ let vm;
+ beforeEach(() => {
+ Component = Vue.extend(component);
+ });
+
+ afterEach(() => {
+ vm.$destroy();
+ });
+
+ describe('While rebasing', () => {
+ it('should show progress message', () => {
+ vm = mountComponent(Component, {
+ mr: { rebaseInProgress: true },
+ service: {},
+ });
+
+ expect(
+ vm.$el.querySelector('.rebase-state-find-class-convention span').textContent.trim(),
+ ).toContain('Rebase in progress');
+ });
+ });
+
+ describe('With permissions', () => {
+ beforeEach(() => {
+ vm = mountComponent(Component, {
+ mr: {
+ rebaseInProgress: false,
+ canPushToSourceBranch: true,
+ },
+ service: {},
+ });
+ });
+
+ it('it should render rebase button and warning message', () => {
+ const text = vm.$el.querySelector('.rebase-state-find-class-convention span').textContent.trim();
+ expect(text).toContain('Fast-forward merge is not possible.');
+ expect(text).toContain('Rebase the source branch onto the target branch or merge target');
+ expect(text).toContain('branch into source branch to allow this merge request to be merged.');
+ });
+
+ it('it should render error message when it fails', (done) => {
+ vm.rebasingError = 'Something went wrong!';
+
+ Vue.nextTick(() => {
+ expect(
+ vm.$el.querySelector('.rebase-state-find-class-convention span').textContent.trim(),
+ ).toContain('Something went wrong!');
+ done();
+ });
+ });
+ });
+
+ describe('Without permissions', () => {
+ it('should render a message explaining user does not have permissions', () => {
+ vm = mountComponent(Component, {
+ mr: {
+ rebaseInProgress: false,
+ canPushToSourceBranch: false,
+ targetBranch: 'foo',
+ },
+ service: {},
+ });
+
+ const text = vm.$el.querySelector('.rebase-state-find-class-convention span').textContent.trim();
+
+ expect(text).toContain('Fast-forward merge is not possible.');
+ expect(text).toContain('Rebase the source branch onto');
+ expect(text).toContain('foo');
+ expect(text).toContain('to allow this merge request to be merged.');
+ });
+ });
+
+ describe('methods', () => {
+ it('checkRebaseStatus', (done) => {
+ spyOn(eventHub, '$emit');
+ vm = mountComponent(Component, {
+ mr: {},
+ service: {
+ rebase() {
+ return Promise.resolve();
+ },
+ poll() {
+ return Promise.resolve({
+ data: {
+ rebase_in_progress: false,
+ merge_error: null,
+ },
+ });
+ },
+ },
+ });
+
+ vm.rebase();
+
+ // Wait for the rebase request
+ vm.$nextTick()
+ // Wait for the polling request
+ .then(vm.$nextTick())
+ // Wait for the eventHub to be called
+ .then(vm.$nextTick())
+ .then(() => {
+ expect(eventHub.$emit).toHaveBeenCalledWith('MRWidgetUpdateRequested');
+ })
+ .then(done)
+ .catch(done.fail);
+ });
+ });
+});
diff --git a/spec/javascripts/vue_mr_widget/components/states/mr_widget_closed_spec.js b/spec/javascripts/vue_mr_widget/components/states/mr_widget_closed_spec.js
index d23b558f4ea..1bf97bbf093 100644
--- a/spec/javascripts/vue_mr_widget/components/states/mr_widget_closed_spec.js
+++ b/spec/javascripts/vue_mr_widget/components/states/mr_widget_closed_spec.js
@@ -4,13 +4,16 @@ import closedComponent from '~/vue_merge_request_widget/components/states/mr_wid
const mr = {
targetBranch: 'good-branch',
targetBranchPath: '/good-branch',
- closedEvent: {
- author: {
+ metrics: {
+ mergedBy: {},
+ mergedAt: 'mergedUpdatedAt',
+ closedBy: {
name: 'Fatih Acet',
username: 'fatihacet',
},
- updatedAt: 'closedEventUpdatedAt',
- formattedUpdatedAt: '',
+ closedAt: 'closedEventUpdatedAt',
+ readableMergedAt: '',
+ readableClosedAt: '',
},
updatedAt: 'mrUpdatedAt',
closedAt: '1 day ago',
@@ -56,7 +59,7 @@ describe('MRWidgetClosed', () => {
it('should have correct elements', () => {
expect(el.querySelector('h4').textContent).toContain('Closed by');
- expect(el.querySelector('h4').textContent).toContain(mr.closedEvent.author.name);
+ expect(el.querySelector('h4').textContent).toContain(mr.metrics.closedBy.name);
expect(el.textContent).toContain('The changes were not merged into');
expect(el.querySelector('.label-branch').getAttribute('href')).toEqual(mr.targetBranchPath);
expect(el.querySelector('.label-branch').textContent).toContain(mr.targetBranch);
diff --git a/spec/javascripts/vue_mr_widget/components/states/mr_widget_merge_when_pipeline_succeeds_spec.js b/spec/javascripts/vue_mr_widget/components/states/mr_widget_merge_when_pipeline_succeeds_spec.js
index 9a71d0b47d7..5f4df15bcd6 100644
--- a/spec/javascripts/vue_mr_widget/components/states/mr_widget_merge_when_pipeline_succeeds_spec.js
+++ b/spec/javascripts/vue_mr_widget/components/states/mr_widget_merge_when_pipeline_succeeds_spec.js
@@ -108,9 +108,7 @@ describe('MRWidgetMergeWhenPipelineSucceeds', () => {
spyOn(eventHub, '$emit');
spyOn(vm.service, 'cancelAutomaticMerge').and.returnValue(new Promise((resolve) => {
resolve({
- json() {
- return mrObj;
- },
+ data: mrObj,
});
}));
@@ -129,10 +127,8 @@ describe('MRWidgetMergeWhenPipelineSucceeds', () => {
spyOn(eventHub, '$emit');
spyOn(vm.service.mergeResource, 'save').and.returnValue(new Promise((resolve) => {
resolve({
- json() {
- return {
- status: 'merge_when_pipeline_succeeds',
- };
+ data: {
+ status: 'merge_when_pipeline_succeeds',
},
});
}));
diff --git a/spec/javascripts/vue_mr_widget/components/states/mr_widget_merged_spec.js b/spec/javascripts/vue_mr_widget/components/states/mr_widget_merged_spec.js
index 2714e8294fa..2dc3b72ea40 100644
--- a/spec/javascripts/vue_mr_widget/components/states/mr_widget_merged_spec.js
+++ b/spec/javascripts/vue_mr_widget/components/states/mr_widget_merged_spec.js
@@ -14,10 +14,13 @@ const createComponent = () => {
canRevertInCurrentMR: true,
canRemoveSourceBranch: true,
sourceBranchRemoved: true,
- mergedEvent: {
- author: {},
- updatedAt: 'mergedUpdatedAt',
- formattedUpdatedAt: '',
+ metrics: {
+ mergedBy: {},
+ mergedAt: 'mergedUpdatedAt',
+ readableMergedAt: '',
+ closedBy: {},
+ closedAt: 'mergedUpdatedAt',
+ readableClosedAt: '',
},
updatedAt: 'mrUpdatedAt',
targetBranch,
@@ -111,10 +114,8 @@ describe('MRWidgetMerged', () => {
spyOn(eventHub, '$emit');
spyOn(vm.service, 'removeSourceBranch').and.returnValue(new Promise((resolve) => {
resolve({
- json() {
- return {
- message: 'Branch was removed',
- };
+ data: {
+ message: 'Branch was removed',
},
});
}));
diff --git a/spec/javascripts/vue_mr_widget/components/states/mr_widget_ready_to_merge_spec.js b/spec/javascripts/vue_mr_widget/components/states/mr_widget_ready_to_merge_spec.js
index df3d29ee1f9..1127576617b 100644
--- a/spec/javascripts/vue_mr_widget/components/states/mr_widget_ready_to_merge_spec.js
+++ b/spec/javascripts/vue_mr_widget/components/states/mr_widget_ready_to_merge_spec.js
@@ -292,8 +292,8 @@ describe('MRWidgetReadyToMerge', () => {
describe('handleMergeButtonClick', () => {
const returnPromise = status => new Promise((resolve) => {
resolve({
- json() {
- return { status };
+ data: {
+ status,
},
});
});
@@ -364,8 +364,9 @@ describe('MRWidgetReadyToMerge', () => {
describe('handleMergePolling', () => {
const returnPromise = state => new Promise((resolve) => {
resolve({
- json() {
- return { state, source_branch_exists: true };
+ data: {
+ state,
+ source_branch_exists: true,
},
});
});
@@ -422,8 +423,8 @@ describe('MRWidgetReadyToMerge', () => {
describe('handleRemoveBranchPolling', () => {
const returnPromise = state => new Promise((resolve) => {
resolve({
- json() {
- return { source_branch_exists: state };
+ data: {
+ source_branch_exists: state,
},
});
});
diff --git a/spec/javascripts/vue_mr_widget/components/states/mr_widget_wip_spec.js b/spec/javascripts/vue_mr_widget/components/states/mr_widget_wip_spec.js
index 2cb3aaa6951..98ab61a0367 100644
--- a/spec/javascripts/vue_mr_widget/components/states/mr_widget_wip_spec.js
+++ b/spec/javascripts/vue_mr_widget/components/states/mr_widget_wip_spec.js
@@ -50,9 +50,7 @@ describe('MRWidgetWIP', () => {
spyOn(eventHub, '$emit');
spyOn(vm.service, 'removeWIP').and.returnValue(new Promise((resolve) => {
resolve({
- json() {
- return mrObj;
- },
+ data: mrObj,
});
}));
diff --git a/spec/javascripts/vue_mr_widget/mock_data.js b/spec/javascripts/vue_mr_widget/mock_data.js
index 1ad7c2d8efa..ca29c9fee32 100644
--- a/spec/javascripts/vue_mr_widget/mock_data.js
+++ b/spec/javascripts/vue_mr_widget/mock_data.js
@@ -33,8 +33,8 @@ export default {
"source_project_id": 19,
"target_branch": "master",
"target_project_id": 19,
- "merge_event": {
- "author": {
+ "metrics": {
+ "merged_by": {
"name": "Administrator",
"username": "root",
"id": 1,
@@ -42,9 +42,10 @@ export default {
"avatar_url": "http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon",
"web_url": "http://localhost:3000/root"
},
- "updated_at": "2017-04-07T15:39:25.696Z"
+ "merged_at": "2017-04-07T15:39:25.696Z",
+ "closed_by": null,
+ "closed_at": null
},
- "closed_event": null,
"author": {
"name": "Administrator",
"username": "root",
diff --git a/spec/javascripts/vue_mr_widget/mr_widget_options_spec.js b/spec/javascripts/vue_mr_widget/mr_widget_options_spec.js
index 9e6d0aa472c..cd00d0a39a3 100644
--- a/spec/javascripts/vue_mr_widget/mr_widget_options_spec.js
+++ b/spec/javascripts/vue_mr_widget/mr_widget_options_spec.js
@@ -2,15 +2,13 @@ import Vue from 'vue';
import mrWidgetOptions from '~/vue_merge_request_widget/mr_widget_options';
import eventHub from '~/vue_merge_request_widget/event_hub';
import notify from '~/lib/utils/notify';
+import { stateKey } from '~/vue_merge_request_widget/stores/state_maps';
import mockData from './mock_data';
import mountComponent from '../helpers/vue_mount_component_helper';
const returnPromise = data => new Promise((resolve) => {
resolve({
- json() {
- return data;
- },
- body: data,
+ data,
});
});
@@ -344,4 +342,31 @@ describe('mrWidgetOptions', () => {
expect(comps['mr-widget-merge-when-pipeline-succeeds']).toBeDefined();
});
});
+
+ describe('rendering relatedLinks', () => {
+ beforeEach((done) => {
+ vm.mr.relatedLinks = {
+ assignToMe: null,
+ closing: `
+ <a class="close-related-link" href="#'>
+ Close
+ </a>
+ `,
+ mentioned: '',
+ };
+ Vue.nextTick(done);
+ });
+
+ it('renders if there are relatedLinks', () => {
+ expect(vm.$el.querySelector('.close-related-link')).toBeDefined();
+ });
+
+ it('does not render if state is nothingToMerge', (done) => {
+ vm.mr.state = stateKey.nothingToMerge;
+ Vue.nextTick(() => {
+ expect(vm.$el.querySelector('.close-related-link')).toBeNull();
+ done();
+ });
+ });
+ });
});
diff --git a/spec/javascripts/vue_mr_widget/stores/mr_widget_store_spec.js b/spec/javascripts/vue_mr_widget/stores/mr_widget_store_spec.js
index 8e5614b20f0..33d052aceb2 100644
--- a/spec/javascripts/vue_mr_widget/stores/mr_widget_store_spec.js
+++ b/spec/javascripts/vue_mr_widget/stores/mr_widget_store_spec.js
@@ -1,4 +1,5 @@
import MergeRequestStore from '~/vue_merge_request_widget/stores/mr_widget_store';
+import { stateKey } from '~/vue_merge_request_widget/stores/state_maps';
import mockData from '../mock_data';
describe('MergeRequestStore', () => {
@@ -52,5 +53,17 @@ describe('MergeRequestStore', () => {
expect(store.isPipelineSkipped).toBe(false);
});
});
+
+ describe('isNothingToMergeState', () => {
+ it('returns true when nothingToMerge', () => {
+ store.state = stateKey.nothingToMerge;
+ expect(store.isNothingToMergeState).toEqual(true);
+ });
+
+ it('returns false when not nothingToMerge', () => {
+ store.state = 'state';
+ expect(store.isNothingToMergeState).toEqual(false);
+ });
+ });
});
});
diff --git a/spec/javascripts/vue_shared/components/commit_spec.js b/spec/javascripts/vue_shared/components/commit_spec.js
index d5754aaa9e7..fdead874209 100644
--- a/spec/javascripts/vue_shared/components/commit_spec.js
+++ b/spec/javascripts/vue_shared/components/commit_spec.js
@@ -10,7 +10,7 @@ describe('Commit component', () => {
CommitComponent = Vue.extend(commitComp);
});
- it('should render a code-fork icon if it does not represent a tag', () => {
+ it('should render a fork icon if it does not represent a tag', () => {
component = new CommitComponent({
propsData: {
tag: false,
@@ -30,7 +30,7 @@ describe('Commit component', () => {
},
}).$mount();
- expect(component.$el.querySelector('.icon-container i').classList).toContain('fa-code-fork');
+ expect(component.$el.querySelector('.icon-container').children).toContain('svg');
});
describe('Given all the props', () => {
diff --git a/spec/javascripts/vue_shared/components/expand_button_spec.js b/spec/javascripts/vue_shared/components/expand_button_spec.js
new file mode 100644
index 00000000000..a33ab689dd1
--- /dev/null
+++ b/spec/javascripts/vue_shared/components/expand_button_spec.js
@@ -0,0 +1,32 @@
+import Vue from 'vue';
+import expandButton from '~/vue_shared/components/expand_button.vue';
+import mountComponent from '../../helpers/vue_mount_component_helper';
+
+describe('expand button', () => {
+ let vm;
+
+ beforeEach(() => {
+ const Component = Vue.extend(expandButton);
+ vm = mountComponent(Component, {
+ slots: {
+ expanded: '<p>Expanded!</p>',
+ },
+ });
+ });
+
+ afterEach(() => {
+ vm.$destroy();
+ });
+
+ it('renders a collpased button', () => {
+ expect(vm.$el.textContent.trim()).toEqual('...');
+ });
+
+ it('hides expander on click', (done) => {
+ vm.$el.querySelector('button').click();
+ vm.$nextTick(() => {
+ expect(vm.$el.querySelector('button').getAttribute('style')).toEqual('display: none;');
+ done();
+ });
+ });
+});
diff --git a/spec/javascripts/vue_shared/components/file_icon_spec.js b/spec/javascripts/vue_shared/components/file_icon_spec.js
new file mode 100644
index 00000000000..d99b17bdc79
--- /dev/null
+++ b/spec/javascripts/vue_shared/components/file_icon_spec.js
@@ -0,0 +1,83 @@
+import Vue from 'vue';
+import fileIcon from '~/vue_shared/components/file_icon.vue';
+import mountComponent from '../../helpers/vue_mount_component_helper';
+
+describe('File Icon component', () => {
+ let vm;
+ let FileIcon;
+
+ beforeEach(() => {
+ FileIcon = Vue.extend(fileIcon);
+ });
+
+ afterEach(() => {
+ vm.$destroy();
+ });
+
+ it('should render a span element with an svg', () => {
+ vm = mountComponent(FileIcon, {
+ fileName: 'test.js',
+ });
+
+ expect(vm.$el.tagName).toEqual('SPAN');
+ expect(vm.$el.querySelector('span > svg')).toBeDefined();
+ });
+
+ it('should render a javascript icon based on file ending', () => {
+ vm = mountComponent(FileIcon, {
+ fileName: 'test.js',
+ });
+
+ expect(vm.$el.firstChild.firstChild.getAttribute('xlink:href')).toBe(`${gon.sprite_file_icons}#javascript`);
+ });
+
+ it('should render a image icon based on file ending', () => {
+ vm = mountComponent(FileIcon, {
+ fileName: 'test.png',
+ });
+
+ expect(vm.$el.firstChild.firstChild.getAttribute('xlink:href')).toBe(`${gon.sprite_file_icons}#image`);
+ });
+
+ it('should render a webpack icon based on file namer', () => {
+ vm = mountComponent(FileIcon, {
+ fileName: 'webpack.js',
+ });
+
+ expect(vm.$el.firstChild.firstChild.getAttribute('xlink:href')).toBe(`${gon.sprite_file_icons}#webpack`);
+ });
+
+ it('should render a standard folder icon', () => {
+ vm = mountComponent(FileIcon, {
+ fileName: 'js',
+ folder: true,
+ });
+
+ expect(vm.$el.querySelector('span > svg > use').getAttribute('xlink:href')).toBe(`${gon.sprite_file_icons}#folder`);
+ });
+
+ it('should render a loading icon', () => {
+ vm = mountComponent(FileIcon, {
+ fileName: 'test.js',
+ loading: true,
+ });
+
+ expect(
+ vm.$el.querySelector('i').getAttribute('class'),
+ ).toEqual('fa fa-spin fa-spinner fa-1x');
+ });
+
+ it('should add a special class and a size class', () => {
+ vm = mountComponent(FileIcon, {
+ fileName: 'test.js',
+ cssClasses: 'extraclasses',
+ size: 120,
+ });
+
+ const classList = vm.$el.firstChild.classList;
+ const containsSizeClass = classList.contains('s120');
+ const containsCustomClass = classList.contains('extraclasses');
+ expect(containsSizeClass).toBe(true);
+ expect(containsCustomClass).toBe(true);
+ });
+});
diff --git a/spec/javascripts/vue_shared/components/header_ci_component_spec.js b/spec/javascripts/vue_shared/components/header_ci_component_spec.js
index b4553acb341..b378a0bd896 100644
--- a/spec/javascripts/vue_shared/components/header_ci_component_spec.js
+++ b/spec/javascripts/vue_shared/components/header_ci_component_spec.js
@@ -1,5 +1,6 @@
import Vue from 'vue';
import headerCi from '~/vue_shared/components/header_ci_component.vue';
+import mountComponent from '../../helpers/vue_mount_component_helper';
describe('Header CI Component', () => {
let HeaderCi;
@@ -8,7 +9,6 @@ describe('Header CI Component', () => {
beforeEach(() => {
HeaderCi = Vue.extend(headerCi);
-
props = {
status: {
group: 'failed',
@@ -45,54 +45,65 @@ describe('Header CI Component', () => {
],
hasSidebarButton: true,
};
-
- vm = new HeaderCi({
- propsData: props,
- }).$mount();
});
afterEach(() => {
vm.$destroy();
});
- it('should render status badge', () => {
- expect(vm.$el.querySelector('.ci-failed')).toBeDefined();
- expect(vm.$el.querySelector('.ci-status-icon-failed svg')).toBeDefined();
- expect(
- vm.$el.querySelector('.ci-failed').getAttribute('href'),
- ).toEqual(props.status.details_path);
- });
+ describe('render', () => {
+ beforeEach(() => {
+ vm = mountComponent(HeaderCi, props);
+ });
- it('should render item name and id', () => {
- expect(vm.$el.querySelector('strong').textContent.trim()).toEqual('job #123');
- });
+ it('should render status badge', () => {
+ expect(vm.$el.querySelector('.ci-failed')).toBeDefined();
+ expect(vm.$el.querySelector('.ci-status-icon-failed svg')).toBeDefined();
+ expect(
+ vm.$el.querySelector('.ci-failed').getAttribute('href'),
+ ).toEqual(props.status.details_path);
+ });
- it('should render timeago date', () => {
- expect(vm.$el.querySelector('time')).toBeDefined();
- });
+ it('should render item name and id', () => {
+ expect(vm.$el.querySelector('strong').textContent.trim()).toEqual('job #123');
+ });
- it('should render user icon and name', () => {
- expect(vm.$el.querySelector('.js-user-link').textContent.trim()).toEqual(props.user.name);
- });
+ it('should render timeago date', () => {
+ expect(vm.$el.querySelector('time')).toBeDefined();
+ });
- it('should render provided actions', () => {
- expect(vm.$el.querySelector('.btn').tagName).toEqual('BUTTON');
- expect(vm.$el.querySelector('.btn').textContent.trim()).toEqual(props.actions[0].label);
- expect(vm.$el.querySelector('.link').tagName).toEqual('A');
- expect(vm.$el.querySelector('.link').textContent.trim()).toEqual(props.actions[1].label);
- expect(vm.$el.querySelector('.link').getAttribute('href')).toEqual(props.actions[0].path);
- });
+ it('should render user icon and name', () => {
+ expect(vm.$el.querySelector('.js-user-link').textContent.trim()).toEqual(props.user.name);
+ });
+
+ it('should render provided actions', () => {
+ expect(vm.$el.querySelector('.btn').tagName).toEqual('BUTTON');
+ expect(vm.$el.querySelector('.btn').textContent.trim()).toEqual(props.actions[0].label);
+ expect(vm.$el.querySelector('.link').tagName).toEqual('A');
+ expect(vm.$el.querySelector('.link').textContent.trim()).toEqual(props.actions[1].label);
+ expect(vm.$el.querySelector('.link').getAttribute('href')).toEqual(props.actions[0].path);
+ });
- it('should show loading icon', (done) => {
- vm.actions[0].isLoading = true;
+ it('should show loading icon', (done) => {
+ vm.actions[0].isLoading = true;
- Vue.nextTick(() => {
- expect(vm.$el.querySelector('.btn .fa-spinner').getAttribute('style')).toBeFalsy();
- done();
+ Vue.nextTick(() => {
+ expect(vm.$el.querySelector('.btn .fa-spinner').getAttribute('style')).toBeFalsy();
+ done();
+ });
+ });
+
+ it('should render sidebar toggle button', () => {
+ expect(vm.$el.querySelector('.js-sidebar-build-toggle')).toBeDefined();
});
});
- it('should render sidebar toggle button', () => {
- expect(vm.$el.querySelector('.js-sidebar-build-toggle')).toBeDefined();
+ describe('shouldRenderTriggeredLabel', () => {
+ it('should rendered created keyword when the shouldRenderTriggeredLabel is false', () => {
+ vm = mountComponent(HeaderCi, { ...props, shouldRenderTriggeredLabel: false });
+
+ expect(vm.$el.textContent).toContain('created');
+ expect(vm.$el.textContent).not.toContain('triggered');
+ });
});
});
diff --git a/spec/javascripts/vue_shared/components/modal_spec.js b/spec/javascripts/vue_shared/components/modal_spec.js
new file mode 100644
index 00000000000..fe75a86cac8
--- /dev/null
+++ b/spec/javascripts/vue_shared/components/modal_spec.js
@@ -0,0 +1,66 @@
+import Vue from 'vue';
+import modal from '~/vue_shared/components/modal.vue';
+import mountComponent from '../../helpers/vue_mount_component_helper';
+
+const modalComponent = Vue.extend(modal);
+
+describe('Modal', () => {
+ let vm;
+
+ afterEach(() => {
+ vm.$destroy();
+ });
+
+ describe('props', () => {
+ describe('without primaryButtonLabel', () => {
+ beforeEach(() => {
+ vm = mountComponent(modalComponent, {
+ primaryButtonLabel: null,
+ });
+ });
+
+ it('does not render a primary button', () => {
+ expect(vm.$el.querySelector('.js-primary-button')).toBeNull();
+ });
+ });
+
+ describe('with id', () => {
+ it('does not render a primary button', () => {
+ beforeEach(() => {
+ vm = mountComponent(modalComponent, {
+ id: 'my-modal',
+ });
+ });
+
+ it('assigns the id to the modal', () => {
+ expect(vm.$el.querySelector('#my-modal.modal')).not.toBeNull();
+ });
+
+ it('does not show the modal immediately', () => {
+ expect(vm.$el.querySelector('#my-modal.modal')).not.toHaveClass('show');
+ });
+
+ it('does not show a backdrop', () => {
+ expect(vm.$el.querySelector('modal-backdrop')).toBeNull();
+ });
+ });
+ });
+
+ it('works with data-toggle="modal"', (done) => {
+ setFixtures(`
+ <button id="modal-button" data-toggle="modal" data-target="#my-modal"></button>
+ <div id="modal-container"></div>
+ `);
+
+ const modalContainer = document.getElementById('modal-container');
+ const modalButton = document.getElementById('modal-button');
+ vm = mountComponent(modalComponent, {
+ id: 'my-modal',
+ }, modalContainer);
+ const modalElement = vm.$el.querySelector('#my-modal');
+ $(modalElement).on('shown.bs.modal', () => done());
+
+ modalButton.click();
+ });
+ });
+});
diff --git a/spec/javascripts/vue_shared/components/panel_resizer_spec.js b/spec/javascripts/vue_shared/components/panel_resizer_spec.js
new file mode 100644
index 00000000000..70ce3dffaba
--- /dev/null
+++ b/spec/javascripts/vue_shared/components/panel_resizer_spec.js
@@ -0,0 +1,59 @@
+import Vue from 'vue';
+import panelResizer from '~/vue_shared/components/panel_resizer.vue';
+import mountComponent from '../../helpers/vue_mount_component_helper';
+
+describe('Panel Resizer component', () => {
+ let vm;
+ let PanelResizer;
+
+ const triggerEvent = (eventName, el = vm.$el, clientX = 0) => {
+ const event = document.createEvent('MouseEvents');
+ event.initMouseEvent(eventName, true, true, window, 1, clientX, 0, clientX, 0, false, false,
+ false, false, 0, null);
+
+ el.dispatchEvent(event);
+ };
+
+ beforeEach(() => {
+ PanelResizer = Vue.extend(panelResizer);
+ });
+
+ afterEach(() => {
+ vm.$destroy();
+ });
+
+ it('should render a div element with the correct classes and styles', () => {
+ vm = mountComponent(PanelResizer, {
+ startSize: 100,
+ side: 'left',
+ });
+
+ expect(vm.$el.tagName).toEqual('DIV');
+ expect(vm.$el.getAttribute('class')).toBe('dragHandle dragleft');
+ expect(vm.$el.getAttribute('style')).toBe('cursor: ew-resize;');
+ });
+
+ it('should render a div element with the correct classes for a right side panel', () => {
+ vm = mountComponent(PanelResizer, {
+ startSize: 100,
+ side: 'right',
+ });
+
+ expect(vm.$el.tagName).toEqual('DIV');
+ expect(vm.$el.getAttribute('class')).toBe('dragHandle dragright');
+ });
+
+ it('drag the resizer', () => {
+ vm = mountComponent(PanelResizer, {
+ startSize: 100,
+ side: 'left',
+ });
+
+ spyOn(vm, '$emit');
+ triggerEvent('mousedown', vm.$el);
+ triggerEvent('mousemove', document);
+ triggerEvent('mouseup', document);
+ expect(vm.$emit.calls.allArgs()).toEqual([['resize-start', 100], ['update:size', 100], ['resize-end', 100]]);
+ expect(vm.size).toBe(100);
+ });
+});
diff --git a/spec/javascripts/vue_shared/components/table_pagination_spec.js b/spec/javascripts/vue_shared/components/table_pagination_spec.js
index b0b78e34e0f..1465ef5855f 100644
--- a/spec/javascripts/vue_shared/components/table_pagination_spec.js
+++ b/spec/javascripts/vue_shared/components/table_pagination_spec.js
@@ -19,6 +19,22 @@ describe('Pagination component', () => {
});
describe('render', () => {
+ it('should not render anything', () => {
+ component = mountComponet({
+ pageInfo: {
+ nextPage: 1,
+ page: 1,
+ perPage: 20,
+ previousPage: null,
+ total: 15,
+ totalPages: 1,
+ },
+ change: spy,
+ });
+
+ expect(component.$el.innerHTML).not.toBeDefined();
+ });
+
describe('prev button', () => {
it('should be disabled and non clickable', () => {
component = mountComponet({
diff --git a/spec/javascripts/vue_shared/components/time_ago_tooltip_spec.js b/spec/javascripts/vue_shared/components/time_ago_tooltip_spec.js
index b4c1f70ed1e..b4fb568f1d4 100644
--- a/spec/javascripts/vue_shared/components/time_ago_tooltip_spec.js
+++ b/spec/javascripts/vue_shared/components/time_ago_tooltip_spec.js
@@ -1,6 +1,6 @@
import Vue from 'vue';
import timeagoTooltip from '~/vue_shared/components/time_ago_tooltip.vue';
-import '~/lib/utils/datetime_utility';
+import { formatDate, getTimeago } from '~/lib/utils/datetime_utility';
describe('Time ago with tooltip component', () => {
let TimeagoTooltip;
@@ -24,10 +24,10 @@ describe('Time ago with tooltip component', () => {
expect(vm.$el.tagName).toEqual('TIME');
expect(
vm.$el.getAttribute('data-original-title'),
- ).toEqual(gl.utils.formatDate('2017-05-08T14:57:39.781Z'));
+ ).toEqual(formatDate('2017-05-08T14:57:39.781Z'));
expect(vm.$el.getAttribute('data-placement')).toEqual('top');
- const timeago = gl.utils.getTimeago();
+ const timeago = getTimeago();
expect(vm.$el.textContent.trim()).toEqual(timeago.format('2017-05-08T14:57:39.781Z'));
});
diff --git a/spec/javascripts/vue_shared/components/toggle_button_spec.js b/spec/javascripts/vue_shared/components/toggle_button_spec.js
index 447d74d4e08..859995d33fa 100644
--- a/spec/javascripts/vue_shared/components/toggle_button_spec.js
+++ b/spec/javascripts/vue_shared/components/toggle_button_spec.js
@@ -30,9 +30,9 @@ describe('Toggle Button', () => {
expect(vm.$el.querySelector('input').getAttribute('value')).toEqual('true');
});
- it('renders Enabled and Disabled text data attributes', () => {
- expect(vm.$el.querySelector('button').getAttribute('data-enabled-text')).toEqual('Enabled');
- expect(vm.$el.querySelector('button').getAttribute('data-disabled-text')).toEqual('Disabled');
+ it('renders input status icon', () => {
+ expect(vm.$el.querySelectorAll('span.toggle-icon').length).toEqual(1);
+ expect(vm.$el.querySelectorAll('svg.s16.toggle-icon-svg').length).toEqual(1);
});
});
@@ -49,6 +49,14 @@ describe('Toggle Button', () => {
expect(vm.$el.querySelector('button').classList.contains('is-checked')).toEqual(true);
});
+ it('sets aria-label representing toggle state', () => {
+ vm.value = true;
+ expect(vm.ariaLabel).toEqual('Toggle Status: ON');
+
+ vm.value = false;
+ expect(vm.ariaLabel).toEqual('Toggle Status: OFF');
+ });
+
it('emits change event when clicked', () => {
vm.$el.querySelector('button').click();
diff --git a/spec/lib/banzai/filter/mermaid_filter_spec.rb b/spec/lib/banzai/filter/mermaid_filter_spec.rb
index 532d25e121d..f6474c8936d 100644
--- a/spec/lib/banzai/filter/mermaid_filter_spec.rb
+++ b/spec/lib/banzai/filter/mermaid_filter_spec.rb
@@ -3,9 +3,9 @@ require 'spec_helper'
describe Banzai::Filter::MermaidFilter do
include FilterSpecHelper
- it 'adds `js-render-mermaid` class to the `pre` tag' do
+ it 'adds `js-render-mermaid` class to the `code` tag' do
doc = filter("<pre class='code highlight js-syntax-highlight mermaid' lang='mermaid' v-pre='true'><code>graph TD;\n A--&gt;B;\n</code></pre>")
- result = doc.xpath('descendant-or-self::pre').first
+ result = doc.css('code').first
expect(result[:class]).to include('js-render-mermaid')
end
diff --git a/spec/lib/banzai/filter/redactor_filter_spec.rb b/spec/lib/banzai/filter/redactor_filter_spec.rb
index 68643effb66..5a7858e77f3 100644
--- a/spec/lib/banzai/filter/redactor_filter_spec.rb
+++ b/spec/lib/banzai/filter/redactor_filter_spec.rb
@@ -46,7 +46,7 @@ describe Banzai::Filter::RedactorFilter do
it 'allows permitted Project references' do
user = create(:user)
project = create(:project)
- project.team << [user, :master]
+ project.add_master(user)
link = reference_link(project: project.id, reference_type: 'test')
doc = filter(link, current_user: user)
@@ -94,7 +94,7 @@ describe Banzai::Filter::RedactorFilter do
it 'removes references for project members with guest role' do
member = create(:user)
project = create(:project, :public)
- project.team << [member, :guest]
+ project.add_guest(member)
issue = create(:issue, :confidential, project: project)
link = reference_link(project: project.id, issue: issue.id, reference_type: 'issue')
@@ -128,7 +128,7 @@ describe Banzai::Filter::RedactorFilter do
it 'allows references for project members' do
member = create(:user)
project = create(:project, :public)
- project.team << [member, :developer]
+ project.add_developer(member)
issue = create(:issue, :confidential, project: project)
link = reference_link(project: project.id, issue: issue.id, reference_type: 'issue')
diff --git a/spec/lib/banzai/filter/relative_link_filter_spec.rb b/spec/lib/banzai/filter/relative_link_filter_spec.rb
index 08beede62db..f38f0776303 100644
--- a/spec/lib/banzai/filter/relative_link_filter_spec.rb
+++ b/spec/lib/banzai/filter/relative_link_filter_spec.rb
@@ -5,6 +5,7 @@ describe Banzai::Filter::RelativeLinkFilter do
contexts.reverse_merge!({
commit: commit,
project: project,
+ group: group,
project_wiki: project_wiki,
ref: ref,
requested_path: requested_path
@@ -25,7 +26,12 @@ describe Banzai::Filter::RelativeLinkFilter do
%(<a href="#{path}">#{path}</a>)
end
+ def nested(element)
+ %(<div>#{element}</div>)
+ end
+
let(:project) { create(:project, :repository) }
+ let(:group) { nil }
let(:project_path) { project.full_path }
let(:ref) { 'markdown' }
let(:commit) { project.commit(ref) }
@@ -70,6 +76,11 @@ describe Banzai::Filter::RelativeLinkFilter do
expect { filter(act) }.not_to raise_error
end
+ it 'does not raise an exception with a garbled path' do
+ act = link("open(/var/tmp/):%20/location%0Afrom:%20/test")
+ expect { filter(act) }.not_to raise_error
+ end
+
it 'ignores ref if commit is passed' do
doc = filter(link('non/existent.file'), commit: project.commit('empty-branch') )
expect(doc.at_css('a')['href'])
@@ -223,4 +234,79 @@ describe Banzai::Filter::RelativeLinkFilter do
let(:commit) { nil } # force filter to use ref instead of commit
include_examples :valid_repository
end
+
+ context 'with a /upload/ URL' do
+ # not needed
+ let(:commit) { nil }
+ let(:ref) { nil }
+ let(:requested_path) { nil }
+
+ context 'to a project upload' do
+ it 'rebuilds relative URL for a link' do
+ doc = filter(link('/uploads/e90decf88d8f96fe9e1389afc2e4a91f/test.jpg'))
+ expect(doc.at_css('a')['href'])
+ .to eq "/#{project.full_path}/uploads/e90decf88d8f96fe9e1389afc2e4a91f/test.jpg"
+
+ doc = filter(nested(link('/uploads/e90decf88d8f96fe9e1389afc2e4a91f/test.jpg')))
+ expect(doc.at_css('a')['href'])
+ .to eq "/#{project.full_path}/uploads/e90decf88d8f96fe9e1389afc2e4a91f/test.jpg"
+ end
+
+ it 'rebuilds relative URL for an image' do
+ doc = filter(image('/uploads/e90decf88d8f96fe9e1389afc2e4a91f/test.jpg'))
+ expect(doc.at_css('img')['src'])
+ .to eq "/#{project.full_path}/uploads/e90decf88d8f96fe9e1389afc2e4a91f/test.jpg"
+
+ doc = filter(nested(image('/uploads/e90decf88d8f96fe9e1389afc2e4a91f/test.jpg')))
+ expect(doc.at_css('img')['src'])
+ .to eq "/#{project.full_path}/uploads/e90decf88d8f96fe9e1389afc2e4a91f/test.jpg"
+ end
+
+ it 'does not modify absolute URL' do
+ doc = filter(link('http://example.com'))
+ expect(doc.at_css('a')['href']).to eq 'http://example.com'
+ end
+
+ it 'supports Unicode filenames' do
+ path = '/uploads/한글.png'
+ escaped = Addressable::URI.escape(path)
+
+ # Stub these methods so the file doesn't actually need to be in the repo
+ allow_any_instance_of(described_class)
+ .to receive(:file_exists?).and_return(true)
+ allow_any_instance_of(described_class)
+ .to receive(:image?).with(path).and_return(true)
+
+ doc = filter(image(escaped))
+ expect(doc.at_css('img')['src']).to match "/#{project.full_path}/uploads/%ED%95%9C%EA%B8%80.png"
+ end
+ end
+
+ context 'to a group upload' do
+ let(:upload_link) { link('/uploads/e90decf88d8f96fe9e1389afc2e4a91f/test.jpg') }
+ let(:group) { create(:group) }
+ let(:project) { nil }
+ let(:relative_path) { "/groups/#{group.full_path}/-/uploads/e90decf88d8f96fe9e1389afc2e4a91f/test.jpg" }
+
+ it 'rewrites the link correctly' do
+ doc = filter(upload_link)
+
+ expect(doc.at_css('a')['href']).to eq(relative_path)
+ end
+
+ it 'rewrites the link correctly for subgroup' do
+ group.update!(parent: create(:group))
+
+ doc = filter(upload_link)
+
+ expect(doc.at_css('a')['href']).to eq(relative_path)
+ end
+
+ it 'does not modify absolute URL' do
+ doc = filter(link('http://example.com'))
+
+ expect(doc.at_css('a')['href']).to eq 'http://example.com'
+ end
+ end
+ end
end
diff --git a/spec/lib/banzai/filter/table_of_contents_filter_spec.rb b/spec/lib/banzai/filter/table_of_contents_filter_spec.rb
index 85eddde732e..0cfef4ff5bf 100644
--- a/spec/lib/banzai/filter/table_of_contents_filter_spec.rb
+++ b/spec/lib/banzai/filter/table_of_contents_filter_spec.rb
@@ -65,6 +65,13 @@ describe Banzai::Filter::TableOfContentsFilter do
expect(doc.css('h2 a').first.attr('href')).to eq '#one-1'
end
+ it 'prepends a prefix to digits-only ids' do
+ doc = filter(header(1, "123") + header(2, "1.0"))
+
+ expect(doc.css('h1 a').first.attr('href')).to eq '#anchor-123'
+ expect(doc.css('h2 a').first.attr('href')).to eq '#anchor-10'
+ end
+
it 'supports Unicode' do
doc = filter(header(1, '한글'))
expect(doc.css('h1 a').first.attr('id')).to eq 'user-content-한글'
diff --git a/spec/lib/banzai/filter/upload_link_filter_spec.rb b/spec/lib/banzai/filter/upload_link_filter_spec.rb
deleted file mode 100644
index 76bc0c36ab7..00000000000
--- a/spec/lib/banzai/filter/upload_link_filter_spec.rb
+++ /dev/null
@@ -1,133 +0,0 @@
-require 'spec_helper'
-
-describe Banzai::Filter::UploadLinkFilter do
- def filter(doc, contexts = {})
- contexts.reverse_merge!({
- project: project
- })
-
- raw_filter(doc, contexts)
- end
-
- def raw_filter(doc, contexts = {})
- described_class.call(doc, contexts)
- end
-
- def image(path)
- %(<img src="#{path}" />)
- end
-
- def link(path)
- %(<a href="#{path}">#{path}</a>)
- end
-
- def nested_image(path)
- %(<div><img src="#{path}" /></div>)
- end
-
- def nested_link(path)
- %(<div><a href="#{path}">#{path}</a></div>)
- end
-
- let(:project) { create(:project) }
-
- shared_examples :preserve_unchanged do
- it 'does not modify any relative URL in anchor' do
- doc = filter(link('README.md'))
- expect(doc.at_css('a')['href']).to eq 'README.md'
- end
-
- it 'does not modify any relative URL in image' do
- doc = filter(image('files/images/logo-black.png'))
- expect(doc.at_css('img')['src']).to eq 'files/images/logo-black.png'
- end
- end
-
- it 'does not raise an exception on invalid URIs' do
- act = link("://foo")
- expect { filter(act) }.not_to raise_error
- end
-
- context 'with a valid repository' do
- it 'rebuilds relative URL for a link' do
- doc = filter(link('/uploads/e90decf88d8f96fe9e1389afc2e4a91f/test.jpg'))
- expect(doc.at_css('a')['href'])
- .to eq "#{Gitlab.config.gitlab.url}/#{project.full_path}/uploads/e90decf88d8f96fe9e1389afc2e4a91f/test.jpg"
-
- doc = filter(nested_link('/uploads/e90decf88d8f96fe9e1389afc2e4a91f/test.jpg'))
- expect(doc.at_css('a')['href'])
- .to eq "#{Gitlab.config.gitlab.url}/#{project.full_path}/uploads/e90decf88d8f96fe9e1389afc2e4a91f/test.jpg"
- end
-
- it 'rebuilds relative URL for an image' do
- doc = filter(image('/uploads/e90decf88d8f96fe9e1389afc2e4a91f/test.jpg'))
- expect(doc.at_css('img')['src'])
- .to eq "#{Gitlab.config.gitlab.url}/#{project.full_path}/uploads/e90decf88d8f96fe9e1389afc2e4a91f/test.jpg"
-
- doc = filter(nested_image('/uploads/e90decf88d8f96fe9e1389afc2e4a91f/test.jpg'))
- expect(doc.at_css('img')['src'])
- .to eq "#{Gitlab.config.gitlab.url}/#{project.full_path}/uploads/e90decf88d8f96fe9e1389afc2e4a91f/test.jpg"
- end
-
- it 'does not modify absolute URL' do
- doc = filter(link('http://example.com'))
- expect(doc.at_css('a')['href']).to eq 'http://example.com'
- end
-
- it 'supports Unicode filenames' do
- path = '/uploads/한글.png'
- escaped = Addressable::URI.escape(path)
-
- # Stub these methods so the file doesn't actually need to be in the repo
- allow_any_instance_of(described_class)
- .to receive(:file_exists?).and_return(true)
- allow_any_instance_of(described_class)
- .to receive(:image?).with(path).and_return(true)
-
- doc = filter(image(escaped))
- expect(doc.at_css('img')['src']).to match "#{Gitlab.config.gitlab.url}/#{project.full_path}/uploads/%ED%95%9C%EA%B8%80.png"
- end
- end
-
- context 'in group context' do
- let(:upload_link) { link('/uploads/e90decf88d8f96fe9e1389afc2e4a91f/test.jpg') }
- let(:group) { create(:group) }
- let(:filter_context) { { project: nil, group: group } }
- let(:relative_path) { "groups/#{group.full_path}/-/uploads/e90decf88d8f96fe9e1389afc2e4a91f/test.jpg" }
-
- it 'rewrites the link correctly' do
- doc = raw_filter(upload_link, filter_context)
-
- expect(doc.at_css('a')['href']).to eq("#{Gitlab.config.gitlab.url}/#{relative_path}")
- end
-
- it 'rewrites the link correctly for subgroup' do
- subgroup = create(:group, parent: group)
- relative_path = "groups/#{subgroup.full_path}/-/uploads/e90decf88d8f96fe9e1389afc2e4a91f/test.jpg"
-
- doc = raw_filter(upload_link, { project: nil, group: subgroup })
-
- expect(doc.at_css('a')['href']).to eq("#{Gitlab.config.gitlab.url}/#{relative_path}")
- end
-
- it 'does not modify absolute URL' do
- doc = filter(link('http://example.com'), filter_context)
-
- expect(doc.at_css('a')['href']).to eq 'http://example.com'
- end
- end
-
- context 'when project or group context does not exist' do
- let(:upload_link) { link('/uploads/e90decf88d8f96fe9e1389afc2e4a91f/test.jpg') }
-
- it 'does not raise error' do
- expect { raw_filter(upload_link, project: nil) }.not_to raise_error
- end
-
- it 'does not rewrite link' do
- doc = raw_filter(upload_link, project: nil)
-
- expect(doc.to_html).to eq upload_link
- end
- end
-end
diff --git a/spec/lib/banzai/filter/user_reference_filter_spec.rb b/spec/lib/banzai/filter/user_reference_filter_spec.rb
index fc03741976e..c76adc262fc 100644
--- a/spec/lib/banzai/filter/user_reference_filter_spec.rb
+++ b/spec/lib/banzai/filter/user_reference_filter_spec.rb
@@ -34,11 +34,11 @@ describe Banzai::Filter::UserReferenceFilter do
let(:reference) { User.reference_prefix + 'all' }
before do
- project.team << [project.creator, :developer]
+ project.add_developer(project.creator)
end
it 'supports a special @all mention' do
- project.team << [user, :developer]
+ project.add_developer(user)
doc = reference_filter("Hey #{reference}", author: user)
expect(doc.css('a').length).to eq 1
@@ -47,7 +47,7 @@ describe Banzai::Filter::UserReferenceFilter do
end
it 'includes a data-author attribute when there is an author' do
- project.team << [user, :developer]
+ project.add_developer(user)
doc = reference_filter(reference, author: user)
expect(doc.css('a').first.attr('data-author')).to eq(user.id.to_s)
diff --git a/spec/lib/banzai/reference_parser/user_parser_spec.rb b/spec/lib/banzai/reference_parser/user_parser_spec.rb
index e49726aca6c..b079a3be029 100644
--- a/spec/lib/banzai/reference_parser/user_parser_spec.rb
+++ b/spec/lib/banzai/reference_parser/user_parser_spec.rb
@@ -63,8 +63,8 @@ describe Banzai::ReferenceParser::UserParser do
let(:contributor) { create(:user) }
before do
- project.team << [user, :developer]
- project.team << [contributor, :developer]
+ project.add_developer(user)
+ project.add_developer(contributor)
end
it 'returns the members of a project' do
@@ -162,7 +162,7 @@ describe Banzai::ReferenceParser::UserParser do
context 'when the link has a data-author attribute' do
it 'returns the nodes when the user is a member of the project' do
other_project = create(:project)
- other_project.team << [user, :developer]
+ other_project.add_developer(user)
link['data-project'] = other_project.id.to_s
link['data-author'] = user.id.to_s
diff --git a/spec/lib/gitlab/action_rate_limiter_spec.rb b/spec/lib/gitlab/action_rate_limiter_spec.rb
new file mode 100644
index 00000000000..542fc03e555
--- /dev/null
+++ b/spec/lib/gitlab/action_rate_limiter_spec.rb
@@ -0,0 +1,29 @@
+require 'spec_helper'
+
+describe Gitlab::ActionRateLimiter do
+ let(:redis) { double('redis') }
+ let(:user) { create(:user) }
+ let(:project) { create(:project) }
+ let(:key) { [user, project] }
+ let(:cache_key) { "action_rate_limiter:test_action:user:#{user.id}:project:#{project.id}" }
+
+ subject { described_class.new(action: :test_action, expiry_time: 100) }
+
+ before do
+ allow(Gitlab::Redis::Cache).to receive(:with).and_yield(redis)
+ end
+
+ it 'increases the throttle count and sets the expire time' do
+ expect(redis).to receive(:incr).with(cache_key).and_return(1)
+ expect(redis).to receive(:expire).with(cache_key, 100)
+
+ expect(subject.throttled?(key, 1)).to be false
+ end
+
+ it 'returns true if the key is throttled' do
+ expect(redis).to receive(:incr).with(cache_key).and_return(2)
+ expect(redis).not_to receive(:expire)
+
+ expect(subject.throttled?(key, 1)).to be true
+ end
+end
diff --git a/spec/lib/gitlab/background_migration/create_fork_network_memberships_range_spec.rb b/spec/lib/gitlab/background_migration/create_fork_network_memberships_range_spec.rb
index 79d2c071446..e1c4f9cfea7 100644
--- a/spec/lib/gitlab/background_migration/create_fork_network_memberships_range_spec.rb
+++ b/spec/lib/gitlab/background_migration/create_fork_network_memberships_range_spec.rb
@@ -2,19 +2,20 @@ require 'spec_helper'
describe Gitlab::BackgroundMigration::CreateForkNetworkMembershipsRange, :migration, schema: 20170929131201 do
let(:migration) { described_class.new }
+ let(:projects) { table(:projects) }
- let(:base1) { create(:project) }
- let(:base1_fork1) { create(:project) }
- let(:base1_fork2) { create(:project) }
+ let(:base1) { projects.create }
+ let(:base1_fork1) { projects.create }
+ let(:base1_fork2) { projects.create }
- let(:base2) { create(:project) }
- let(:base2_fork1) { create(:project) }
- let(:base2_fork2) { create(:project) }
+ let(:base2) { projects.create }
+ let(:base2_fork1) { projects.create }
+ let(:base2_fork2) { projects.create }
- let(:fork_of_fork) { create(:project) }
- let(:fork_of_fork2) { create(:project) }
- let(:second_level_fork) { create(:project) }
- let(:third_level_fork) { create(:project) }
+ let(:fork_of_fork) { projects.create }
+ let(:fork_of_fork2) { projects.create }
+ let(:second_level_fork) { projects.create }
+ let(:third_level_fork) { projects.create }
let(:fork_network1) { fork_networks.find_by(root_project_id: base1.id) }
let(:fork_network2) { fork_networks.find_by(root_project_id: base2.id) }
@@ -97,7 +98,7 @@ describe Gitlab::BackgroundMigration::CreateForkNetworkMembershipsRange, :migrat
end
it 'does not miss members for forks of forks for which the root was deleted' do
- forked_project_links.create(id: 9, forked_from_project_id: base1_fork1.id, forked_to_project_id: create(:project).id)
+ forked_project_links.create(id: 9, forked_from_project_id: base1_fork1.id, forked_to_project_id: projects.create.id)
base1.destroy
expect(migration.missing_members?(7, 10)).to be_falsy
@@ -105,8 +106,8 @@ describe Gitlab::BackgroundMigration::CreateForkNetworkMembershipsRange, :migrat
context 'with more forks' do
before do
- forked_project_links.create(id: 9, forked_from_project_id: fork_of_fork.id, forked_to_project_id: create(:project).id)
- forked_project_links.create(id: 10, forked_from_project_id: fork_of_fork.id, forked_to_project_id: create(:project).id)
+ forked_project_links.create(id: 9, forked_from_project_id: fork_of_fork.id, forked_to_project_id: projects.create.id)
+ forked_project_links.create(id: 10, forked_from_project_id: fork_of_fork.id, forked_to_project_id: projects.create.id)
end
it 'only processes a single batch of links at a time' do
diff --git a/spec/lib/gitlab/background_migration/delete_conflicting_redirect_routes_range_spec.rb b/spec/lib/gitlab/background_migration/delete_conflicting_redirect_routes_range_spec.rb
index 5c471cbdeda..9bae7e53b71 100644
--- a/spec/lib/gitlab/background_migration/delete_conflicting_redirect_routes_range_spec.rb
+++ b/spec/lib/gitlab/background_migration/delete_conflicting_redirect_routes_range_spec.rb
@@ -24,17 +24,12 @@ describe Gitlab::BackgroundMigration::DeleteConflictingRedirectRoutesRange, :mig
redirect_routes.create!(source_id: 1, source_type: 'Namespace', path: 'foo5')
end
- it 'deletes the conflicting redirect_routes in the range' do
+ # No-op. See https://gitlab.com/gitlab-com/infrastructure/issues/3460#note_53223252
+ it 'NO-OP: does not delete any redirect_routes' do
expect(redirect_routes.count).to eq(8)
- expect do
- described_class.new.perform(1, 3)
- end.to change { redirect_routes.where("path like 'foo%'").count }.from(5).to(2)
+ described_class.new.perform(1, 5)
- expect do
- described_class.new.perform(4, 5)
- end.to change { redirect_routes.where("path like 'foo%'").count }.from(2).to(0)
-
- expect(redirect_routes.count).to eq(3)
+ expect(redirect_routes.count).to eq(8)
end
end
diff --git a/spec/lib/gitlab/background_migration/migrate_events_to_push_event_payloads_spec.rb b/spec/lib/gitlab/background_migration/migrate_events_to_push_event_payloads_spec.rb
index cb52d971047..5432d270555 100644
--- a/spec/lib/gitlab/background_migration/migrate_events_to_push_event_payloads_spec.rb
+++ b/spec/lib/gitlab/background_migration/migrate_events_to_push_event_payloads_spec.rb
@@ -225,9 +225,10 @@ describe Gitlab::BackgroundMigration::MigrateEventsToPushEventPayloads, :migrati
let(:user_class) { table(:users) }
let(:author) { build(:user).becomes(user_class).tap(&:save!).becomes(User) }
let(:namespace) { create(:namespace, owner: author) }
- let(:project) { create(:project_empty_repo, namespace: namespace, creator: author) }
+ let(:projects) { table(:projects) }
+ let(:project) { projects.create(namespace_id: namespace.id, creator_id: author.id) }
- # We can not rely on FactoryGirl as the state of Event may change in ways that
+ # We can not rely on FactoryBot as the state of Event may change in ways that
# the background migration does not expect, hence we use the Event class of
# the migration itself.
def create_push_event(project, author, data = nil)
@@ -280,6 +281,17 @@ describe Gitlab::BackgroundMigration::MigrateEventsToPushEventPayloads, :migrati
migration.process_event(event)
end
+
+ it 'handles an error gracefully' do
+ event1 = create_push_event(project, author, { commits: [] })
+
+ expect(migration).to receive(:replicate_event).and_call_original
+ expect(migration).to receive(:create_push_event_payload).and_raise(ActiveRecord::InvalidForeignKey, 'invalid foreign key')
+
+ migration.process_event(event1)
+
+ expect(described_class::EventForMigration.all.count).to eq(0)
+ end
end
describe '#replicate_event' do
@@ -334,9 +346,8 @@ describe Gitlab::BackgroundMigration::MigrateEventsToPushEventPayloads, :migrati
it 'does not create push event payloads for removed events' do
allow(event).to receive(:id).and_return(-1)
- payload = migration.create_push_event_payload(event)
+ expect { migration.create_push_event_payload(event) }.to raise_error(ActiveRecord::InvalidForeignKey)
- expect(payload).to be_nil
expect(PushEventPayload.count).to eq(0)
end
diff --git a/spec/lib/gitlab/background_migration/populate_fork_networks_range_spec.rb b/spec/lib/gitlab/background_migration/populate_fork_networks_range_spec.rb
index e52baf8dde7..8582af96199 100644
--- a/spec/lib/gitlab/background_migration/populate_fork_networks_range_spec.rb
+++ b/spec/lib/gitlab/background_migration/populate_fork_networks_range_spec.rb
@@ -2,10 +2,11 @@ require 'spec_helper'
describe Gitlab::BackgroundMigration::PopulateForkNetworksRange, :migration, schema: 20170929131201 do
let(:migration) { described_class.new }
- let(:base1) { create(:project) }
+ let(:projects) { table(:projects) }
+ let(:base1) { projects.create }
- let(:base2) { create(:project) }
- let(:base2_fork1) { create(:project) }
+ let(:base2) { projects.create }
+ let(:base2_fork1) { projects.create }
let!(:forked_project_links) { table(:forked_project_links) }
let!(:fork_networks) { table(:fork_networks) }
@@ -18,10 +19,10 @@ describe Gitlab::BackgroundMigration::PopulateForkNetworksRange, :migration, sch
# A normal fork link
forked_project_links.create(id: 1,
forked_from_project_id: base1.id,
- forked_to_project_id: create(:project).id)
+ forked_to_project_id: projects.create.id)
forked_project_links.create(id: 2,
forked_from_project_id: base1.id,
- forked_to_project_id: create(:project).id)
+ forked_to_project_id: projects.create.id)
forked_project_links.create(id: 3,
forked_from_project_id: base2.id,
forked_to_project_id: base2_fork1.id)
@@ -29,10 +30,10 @@ describe Gitlab::BackgroundMigration::PopulateForkNetworksRange, :migration, sch
# create a fork of a fork
forked_project_links.create(id: 4,
forked_from_project_id: base2_fork1.id,
- forked_to_project_id: create(:project).id)
+ forked_to_project_id: projects.create.id)
forked_project_links.create(id: 5,
- forked_from_project_id: create(:project).id,
- forked_to_project_id: create(:project).id)
+ forked_from_project_id: projects.create.id,
+ forked_to_project_id: projects.create.id)
# Stub out the calls to the other migrations
allow(BackgroundMigrationWorker).to receive(:perform_in)
@@ -63,7 +64,7 @@ describe Gitlab::BackgroundMigration::PopulateForkNetworksRange, :migration, sch
end
it 'creates a fork network for the fork of which the source was deleted' do
- fork = create(:project)
+ fork = projects.create
forked_project_links.create(id: 6, forked_from_project_id: 99999, forked_to_project_id: fork.id)
migration.perform(5, 8)
diff --git a/spec/lib/gitlab/background_migration/populate_merge_request_metrics_with_events_data_spec.rb b/spec/lib/gitlab/background_migration/populate_merge_request_metrics_with_events_data_spec.rb
new file mode 100644
index 00000000000..dfe3b31f1c0
--- /dev/null
+++ b/spec/lib/gitlab/background_migration/populate_merge_request_metrics_with_events_data_spec.rb
@@ -0,0 +1,124 @@
+require 'rails_helper'
+
+describe Gitlab::BackgroundMigration::PopulateMergeRequestMetricsWithEventsData, :migration, schema: 20171128214150 do
+ describe '#perform' do
+ let(:mr_with_event) { create(:merge_request) }
+ let!(:merged_event) { create(:event, :merged, target: mr_with_event) }
+ let!(:closed_event) { create(:event, :closed, target: mr_with_event) }
+
+ before do
+ # Make sure no metrics are created and kept through after_* callbacks.
+ mr_with_event.metrics.destroy!
+ end
+
+ it 'inserts metrics and updates closed and merged events' do
+ subject.perform(mr_with_event.id, mr_with_event.id)
+
+ mr_with_event.reload
+
+ expect(mr_with_event.metrics).to have_attributes(latest_closed_by_id: closed_event.author_id,
+ merged_by_id: merged_event.author_id)
+ expect(mr_with_event.metrics.latest_closed_at.to_s).to eq(closed_event.updated_at.to_s)
+ end
+ end
+
+ describe '#insert_metrics_for_range' do
+ let!(:mrs_without_metrics) { create_list(:merge_request, 3) }
+ let!(:mrs_with_metrics) { create_list(:merge_request, 2) }
+
+ before do
+ # Make sure no metrics are created and kept through after_* callbacks.
+ mrs_without_metrics.each { |m| m.metrics.destroy! }
+ end
+
+ it 'inserts merge_request_metrics for merge_requests without one' do
+ expect { subject.insert_metrics_for_range(MergeRequest.first.id, MergeRequest.last.id) }
+ .to change(MergeRequest::Metrics, :count).from(2).to(5)
+
+ mrs_without_metrics.each do |mr_without_metrics|
+ expect(mr_without_metrics.reload.metrics).to be_present
+ end
+ end
+
+ it 'does not inserts merge_request_metrics for MRs out of given range' do
+ expect { subject.insert_metrics_for_range(mrs_with_metrics.first.id, mrs_with_metrics.last.id) }
+ .not_to change(MergeRequest::Metrics, :count).from(2)
+ end
+ end
+
+ describe '#update_metrics_with_events_data' do
+ context 'closed events data update' do
+ let(:users) { create_list(:user, 3) }
+ let(:mrs_with_event) { create_list(:merge_request, 3) }
+
+ before do
+ create_list(:event, 2, :closed, author: users.first, target: mrs_with_event.first)
+ create_list(:event, 3, :closed, author: users.second, target: mrs_with_event.second)
+ create(:event, :closed, author: users.third, target: mrs_with_event.third)
+ end
+
+ it 'migrates multiple MR metrics with closed event data' do
+ mr_without_event = create(:merge_request)
+ create(:event, :merged)
+
+ subject.update_metrics_with_events_data(mrs_with_event.first.id, mrs_with_event.last.id)
+
+ mrs_with_event.each do |mr_with_event|
+ latest_event = Event.where(action: 3, target: mr_with_event).last
+
+ mr_with_event.metrics.reload
+
+ expect(mr_with_event.metrics.latest_closed_by).to eq(latest_event.author)
+ expect(mr_with_event.metrics.latest_closed_at.to_s).to eq(latest_event.updated_at.to_s)
+ end
+
+ expect(mr_without_event.metrics.reload).to have_attributes(latest_closed_by_id: nil,
+ latest_closed_at: nil)
+ end
+
+ it 'does not updates metrics out of given range' do
+ out_of_range_mr = create(:merge_request)
+ create(:event, :closed, author: users.last, target: out_of_range_mr)
+
+ expect { subject.perform(mrs_with_event.first.id, mrs_with_event.second.id) }
+ .not_to change { out_of_range_mr.metrics.reload.merged_by }
+ .from(nil)
+ end
+ end
+
+ context 'merged events data update' do
+ let(:users) { create_list(:user, 3) }
+ let(:mrs_with_event) { create_list(:merge_request, 3) }
+
+ before do
+ create_list(:event, 2, :merged, author: users.first, target: mrs_with_event.first)
+ create_list(:event, 3, :merged, author: users.second, target: mrs_with_event.second)
+ create(:event, :merged, author: users.third, target: mrs_with_event.third)
+ end
+
+ it 'migrates multiple MR metrics with merged event data' do
+ mr_without_event = create(:merge_request)
+ create(:event, :merged)
+
+ subject.update_metrics_with_events_data(mrs_with_event.first.id, mrs_with_event.last.id)
+
+ mrs_with_event.each do |mr_with_event|
+ latest_event = Event.where(action: Event::MERGED, target: mr_with_event).last
+
+ expect(mr_with_event.metrics.reload.merged_by).to eq(latest_event.author)
+ end
+
+ expect(mr_without_event.metrics.reload).to have_attributes(merged_by_id: nil)
+ end
+
+ it 'does not updates metrics out of given range' do
+ out_of_range_mr = create(:merge_request)
+ create(:event, :merged, author: users.last, target: out_of_range_mr)
+
+ expect { subject.perform(mrs_with_event.first.id, mrs_with_event.second.id) }
+ .not_to change { out_of_range_mr.metrics.reload.merged_by }
+ .from(nil)
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/background_migration/populate_untracked_uploads_spec.rb b/spec/lib/gitlab/background_migration/populate_untracked_uploads_spec.rb
index b80df6956b0..be45c00dfe6 100644
--- a/spec/lib/gitlab/background_migration/populate_untracked_uploads_spec.rb
+++ b/spec/lib/gitlab/background_migration/populate_untracked_uploads_spec.rb
@@ -182,13 +182,22 @@ describe Gitlab::BackgroundMigration::PopulateUntrackedUploads, :sidekiq do
end
context 'for a pre-Markdown Note attachment file path' do
- class Note < ActiveRecord::Base
- has_many :uploads, as: :model, dependent: :destroy
+ let(:model) { create(:note, :with_attachment) }
+ let!(:expected_upload_attrs) { Upload.where(model_type: 'Note', model_id: model.id).first.attributes.slice('path', 'uploader', 'size', 'checksum') }
+ let!(:untracked_file) { untracked_files_for_uploads.create!(path: expected_upload_attrs['path']) }
+
+ before do
+ Upload.where(model_type: 'Note', model_id: model.id).delete_all
end
- let(:model) { create(:note, :with_attachment) }
+ # Can't use the shared example because Note doesn't have an `uploads` association
+ it 'creates an Upload record' do
+ expect do
+ subject.perform(1, untracked_files_for_uploads.last.id)
+ end.to change { Upload.where(model_type: 'Note', model_id: model.id).count }.from(0).to(1)
- it_behaves_like 'non_markdown_file'
+ expect(Upload.where(model_type: 'Note', model_id: model.id).first.attributes).to include(expected_upload_attrs)
+ end
end
context 'for a user avatar file path' do
diff --git a/spec/lib/gitlab/background_migration/prepare_untracked_uploads_spec.rb b/spec/lib/gitlab/background_migration/prepare_untracked_uploads_spec.rb
index cd3f1a45270..8bb9ebe0419 100644
--- a/spec/lib/gitlab/background_migration/prepare_untracked_uploads_spec.rb
+++ b/spec/lib/gitlab/background_migration/prepare_untracked_uploads_spec.rb
@@ -2,21 +2,10 @@ require 'spec_helper'
describe Gitlab::BackgroundMigration::PrepareUntrackedUploads, :sidekiq do
include TrackUntrackedUploadsHelpers
+ include MigrationsHelpers
let!(:untracked_files_for_uploads) { described_class::UntrackedFile }
- matcher :be_scheduled_migration do |*expected|
- match do |migration|
- BackgroundMigrationWorker.jobs.any? do |job|
- job['args'] == [migration, expected]
- end
- end
-
- failure_message do |migration|
- "Migration `#{migration}` with args `#{expected.inspect}` not scheduled!"
- end
- end
-
before do
DatabaseCleaner.clean
diff --git a/spec/lib/gitlab/bare_repository_import/importer_spec.rb b/spec/lib/gitlab/bare_repository_import/importer_spec.rb
index 7f3bf5fc41c..b5d86df09d2 100644
--- a/spec/lib/gitlab/bare_repository_import/importer_spec.rb
+++ b/spec/lib/gitlab/bare_repository_import/importer_spec.rb
@@ -68,8 +68,14 @@ describe Gitlab::BareRepositoryImport::Importer, repository: true do
expect(Project.find_by_full_path(project_path)).not_to be_nil
end
+ it 'does not schedule an import' do
+ expect_any_instance_of(Project).not_to receive(:import_schedule)
+
+ importer.create_project_if_needed
+ end
+
it 'creates the Git repo in disk' do
- FileUtils.mkdir_p(File.join(base_dir, "#{project_path}.git"))
+ create_bare_repository("#{project_path}.git")
importer.create_project_if_needed
@@ -124,13 +130,31 @@ describe Gitlab::BareRepositoryImport::Importer, repository: true do
end
it 'creates the Git repo in disk' do
- FileUtils.mkdir_p(File.join(base_dir, "#{project_path}.git"))
+ create_bare_repository("#{project_path}.git")
importer.create_project_if_needed
project = Project.find_by_full_path("#{admin.full_path}/#{project_path}")
expect(File).to exist(File.join(project.repository_storage_path, project.disk_path + '.git'))
+ expect(File).to exist(File.join(project.repository_storage_path, project.disk_path + '.wiki.git'))
+ end
+
+ it 'moves an existing project to the correct path' do
+ # This is a quick way to get a valid repository instead of copying an
+ # existing one. Since it's not persisted, the importer will try to
+ # create the project.
+ project = build(:project, :repository)
+ original_commit_count = project.repository.commit_count
+
+ bare_repo = Gitlab::BareRepositoryImport::Repository.new(project.repository_storage_path, project.repository.path)
+ gitlab_importer = described_class.new(admin, bare_repo)
+
+ expect(gitlab_importer).to receive(:create_project).and_call_original
+
+ new_project = gitlab_importer.create_project_if_needed
+
+ expect(new_project.repository.commit_count).to eq(original_commit_count)
end
end
@@ -141,8 +165,11 @@ describe Gitlab::BareRepositoryImport::Importer, repository: true do
it_behaves_like 'importing a repository'
it 'creates the Wiki git repo in disk' do
- FileUtils.mkdir_p(File.join(base_dir, "#{project_path}.git"))
- FileUtils.mkdir_p(File.join(base_dir, "#{project_path}.wiki.git"))
+ create_bare_repository("#{project_path}.git")
+ create_bare_repository("#{project_path}.wiki.git")
+
+ expect(Projects::CreateService).to receive(:new).with(admin, hash_including(skip_wiki: true,
+ import_type: 'bare_repository')).and_call_original
importer.create_project_if_needed
@@ -165,4 +192,9 @@ describe Gitlab::BareRepositoryImport::Importer, repository: true do
end
end
end
+
+ def create_bare_repository(project_path)
+ repo_path = File.join(base_dir, project_path)
+ Gitlab::Git::Repository.create(repo_path, bare: true)
+ end
end
diff --git a/spec/lib/gitlab/bare_repository_import/repository_spec.rb b/spec/lib/gitlab/bare_repository_import/repository_spec.rb
index 2db737f5fb6..9f42cf1dfca 100644
--- a/spec/lib/gitlab/bare_repository_import/repository_spec.rb
+++ b/spec/lib/gitlab/bare_repository_import/repository_spec.rb
@@ -1,51 +1,122 @@
require 'spec_helper'
describe ::Gitlab::BareRepositoryImport::Repository do
- let(:project_repo_path) { described_class.new('/full/path/', '/full/path/to/repo.git') }
+ context 'legacy storage' do
+ subject { described_class.new('/full/path/', '/full/path/to/repo.git') }
- it 'stores the repo path' do
- expect(project_repo_path.repo_path).to eq('/full/path/to/repo.git')
- end
+ it 'stores the repo path' do
+ expect(subject.repo_path).to eq('/full/path/to/repo.git')
+ end
- it 'stores the group path' do
- expect(project_repo_path.group_path).to eq('to')
- end
+ it 'stores the group path' do
+ expect(subject.group_path).to eq('to')
+ end
- it 'stores the project name' do
- expect(project_repo_path.project_name).to eq('repo')
- end
+ it 'stores the project name' do
+ expect(subject.project_name).to eq('repo')
+ end
- it 'stores the wiki path' do
- expect(project_repo_path.wiki_path).to eq('/full/path/to/repo.wiki.git')
- end
+ it 'stores the wiki path' do
+ expect(subject.wiki_path).to eq('/full/path/to/repo.wiki.git')
+ end
- describe '#wiki?' do
- it 'returns true if it is a wiki' do
- wiki_path = described_class.new('/full/path/', '/full/path/to/a/b/my.wiki.git')
+ describe '#processable?' do
+ it 'returns false if it is a wiki' do
+ subject = described_class.new('/full/path/', '/full/path/to/a/b/my.wiki.git')
- expect(wiki_path.wiki?).to eq(true)
+ expect(subject).not_to be_processable
+ end
+
+ it 'returns true if group path is missing' do
+ subject = described_class.new('/full/path/', '/full/path/repo.git')
+
+ expect(subject).to be_processable
+ end
+
+ it 'returns true when group path and project name are present' do
+ expect(subject).to be_processable
+ end
end
- it 'returns false if it is not a wiki' do
- expect(project_repo_path.wiki?).to eq(false)
+ describe '#project_full_path' do
+ it 'returns the project full path with trailing slash in the root path' do
+ expect(subject.project_full_path).to eq('to/repo')
+ end
+
+ it 'returns the project full path with no trailing slash in the root path' do
+ subject = described_class.new('/full/path', '/full/path/to/repo.git')
+
+ expect(subject.project_full_path).to eq('to/repo')
+ end
end
end
- describe '#hashed?' do
- it 'returns true if it is a hashed folder' do
- path = described_class.new('/full/path/', '/full/path/@hashed/my.repo.git')
+ context 'hashed storage' do
+ let(:gitlab_shell) { Gitlab::Shell.new }
+ let(:repository_storage) { 'default' }
+ let(:root_path) { Gitlab.config.repositories.storages[repository_storage]['path'] }
+ let(:hash) { '6b86b273ff34fce19d6b804eff5a3f5747ada4eaa22f1d49c01e52ddb7875b4b' }
+ let(:hashed_path) { "@hashed/6b/86/6b86b273ff34fce19d6b804eff5a3f5747ada4eaa22f1d49c01e52ddb7875b4b" }
+ let(:repo_path) { File.join(root_path, "#{hashed_path}.git") }
+ let(:wiki_path) { File.join(root_path, "#{hashed_path}.wiki.git") }
- expect(path.hashed?).to eq(true)
+ before do
+ gitlab_shell.add_repository(repository_storage, hashed_path)
+ repository = Rugged::Repository.new(repo_path)
+ repository.config['gitlab.fullpath'] = 'to/repo'
end
- it 'returns false if it is not a hashed folder' do
- expect(project_repo_path.hashed?).to eq(false)
+ after do
+ gitlab_shell.remove_repository(root_path, hashed_path)
end
- end
- describe '#project_full_path' do
- it 'returns the project full path' do
- expect(project_repo_path.repo_path).to eq('/full/path/to/repo.git')
+ subject { described_class.new(root_path, repo_path) }
+
+ it 'stores the repo path' do
+ expect(subject.repo_path).to eq(repo_path)
+ end
+
+ it 'stores the wiki path' do
+ expect(subject.wiki_path).to eq(wiki_path)
+ end
+
+ it 'reads the group path from .git/config' do
+ expect(subject.group_path).to eq('to')
+ end
+
+ it 'reads the project name from .git/config' do
+ expect(subject.project_name).to eq('repo')
+ end
+
+ describe '#processable?' do
+ it 'returns false if it is a wiki' do
+ subject = described_class.new(root_path, wiki_path)
+
+ expect(subject).not_to be_processable
+ end
+
+ it 'returns false when group and project name are missing' do
+ repository = Rugged::Repository.new(repo_path)
+ repository.config.delete('gitlab.fullpath')
+
+ expect(subject).not_to be_processable
+ end
+
+ it 'returns true when group path and project name are present' do
+ expect(subject).to be_processable
+ end
+ end
+
+ describe '#project_full_path' do
+ it 'returns the project full path with trailing slash in the root path' do
+ expect(subject.project_full_path).to eq('to/repo')
+ end
+
+ it 'returns the project full path with no trailing slash in the root path' do
+ subject = described_class.new(root_path[0...-1], repo_path)
+
+ expect(subject.project_full_path).to eq('to/repo')
+ end
end
end
end
diff --git a/spec/lib/gitlab/checks/project_moved_spec.rb b/spec/lib/gitlab/checks/project_moved_spec.rb
new file mode 100644
index 00000000000..f90c2d6aded
--- /dev/null
+++ b/spec/lib/gitlab/checks/project_moved_spec.rb
@@ -0,0 +1,87 @@
+require 'rails_helper'
+
+describe Gitlab::Checks::ProjectMoved, :clean_gitlab_redis_shared_state do
+ let(:user) { create(:user) }
+ let(:project) { create(:project) }
+
+ describe '.fetch_redirct_message' do
+ context 'with a redirect message queue' do
+ it 'should return the redirect message' do
+ project_moved = described_class.new(project, user, 'foo/bar', 'http')
+ project_moved.add_redirect_message
+
+ expect(described_class.fetch_redirect_message(user.id, project.id)).to eq(project_moved.redirect_message)
+ end
+
+ it 'should delete the redirect message from redis' do
+ project_moved = described_class.new(project, user, 'foo/bar', 'http')
+ project_moved.add_redirect_message
+
+ expect(Gitlab::Redis::SharedState.with { |redis| redis.get("redirect_namespace:#{user.id}:#{project.id}") }).not_to be_nil
+ described_class.fetch_redirect_message(user.id, project.id)
+ expect(Gitlab::Redis::SharedState.with { |redis| redis.get("redirect_namespace:#{user.id}:#{project.id}") }).to be_nil
+ end
+ end
+
+ context 'with no redirect message queue' do
+ it 'should return nil' do
+ expect(described_class.fetch_redirect_message(1, 2)).to be_nil
+ end
+ end
+ end
+
+ describe '#add_redirect_message' do
+ it 'should queue a redirect message' do
+ project_moved = described_class.new(project, user, 'foo/bar', 'http')
+ expect(project_moved.add_redirect_message).to eq("OK")
+ end
+
+ it 'should handle anonymous clones' do
+ project_moved = described_class.new(project, nil, 'foo/bar', 'http')
+
+ expect(project_moved.add_redirect_message).to eq(nil)
+ end
+ end
+
+ describe '#redirect_message' do
+ context 'when the push is rejected' do
+ it 'should return a redirect message telling the user to try again' do
+ project_moved = described_class.new(project, user, 'foo/bar', 'http')
+ message = "Project 'foo/bar' was moved to '#{project.full_path}'." +
+ "\n\nPlease update your Git remote:" +
+ "\n\n git remote set-url origin #{project.http_url_to_repo} and try again.\n"
+
+ expect(project_moved.redirect_message(rejected: true)).to eq(message)
+ end
+ end
+
+ context 'when the push is not rejected' do
+ it 'should return a redirect message' do
+ project_moved = described_class.new(project, user, 'foo/bar', 'http')
+ message = "Project 'foo/bar' was moved to '#{project.full_path}'." +
+ "\n\nPlease update your Git remote:" +
+ "\n\n git remote set-url origin #{project.http_url_to_repo}\n"
+
+ expect(project_moved.redirect_message).to eq(message)
+ end
+ end
+ end
+
+ describe '#permanent_redirect?' do
+ context 'with a permanent RedirectRoute' do
+ it 'should return true' do
+ project.route.create_redirect('foo/bar', permanent: true)
+ project_moved = described_class.new(project, user, 'foo/bar', 'http')
+ expect(project_moved.permanent_redirect?).to be_truthy
+ end
+ end
+
+ context 'without a permanent RedirectRoute' do
+ it 'should return false' do
+ project.route.create_redirect('foo/bar')
+ project_moved = described_class.new(project, user, 'foo/bar', 'http')
+ expect(project_moved.permanent_redirect?).to be_falsy
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/ci/ansi2html_spec.rb b/spec/lib/gitlab/ci/ansi2html_spec.rb
index 33540eab5d6..05e2d94cbd6 100644
--- a/spec/lib/gitlab/ci/ansi2html_spec.rb
+++ b/spec/lib/gitlab/ci/ansi2html_spec.rb
@@ -120,6 +120,10 @@ describe Gitlab::Ci::Ansi2html do
expect(convert_html("\e[48;5;240mHello")).to eq('<span class="xterm-bg-240">Hello</span>')
end
+ it "can print 256 xterm fg bold colors" do
+ expect(convert_html("\e[38;5;16;1mHello")).to eq('<span class="xterm-fg-16 term-bold">Hello</span>')
+ end
+
it "can print 256 xterm bg colors on normal magenta foreground" do
expect(convert_html("\e[48;5;16;35mHello")).to eq('<span class="term-fg-magenta xterm-bg-16">Hello</span>')
end
diff --git a/spec/lib/gitlab/ci/pipeline/chain/build_spec.rb b/spec/lib/gitlab/ci/pipeline/chain/build_spec.rb
index 0f1d72080c5..3ae7053a995 100644
--- a/spec/lib/gitlab/ci/pipeline/chain/build_spec.rb
+++ b/spec/lib/gitlab/ci/pipeline/chain/build_spec.rb
@@ -6,46 +6,81 @@ describe Gitlab::Ci::Pipeline::Chain::Build do
let(:pipeline) { Ci::Pipeline.new }
let(:command) do
- double('command', source: :push,
- origin_ref: 'master',
- checkout_sha: project.commit.id,
- after_sha: nil,
- before_sha: nil,
- trigger_request: nil,
- schedule: nil,
- project: project,
- current_user: user)
+ Gitlab::Ci::Pipeline::Chain::Command.new(
+ source: :push,
+ origin_ref: 'master',
+ checkout_sha: project.commit.id,
+ after_sha: nil,
+ before_sha: nil,
+ trigger_request: nil,
+ schedule: nil,
+ project: project,
+ current_user: user)
end
let(:step) { described_class.new(pipeline, command) }
before do
stub_repository_ci_yaml_file(sha: anything)
-
- step.perform!
end
it 'never breaks the chain' do
+ step.perform!
+
expect(step.break?).to be false
end
it 'fills pipeline object with data' do
+ step.perform!
+
expect(pipeline.sha).not_to be_empty
expect(pipeline.sha).to eq project.commit.id
expect(pipeline.ref).to eq 'master'
+ expect(pipeline.tag).to be false
expect(pipeline.user).to eq user
expect(pipeline.project).to eq project
end
it 'sets a valid config source' do
+ step.perform!
+
expect(pipeline.repository_source?).to be true
end
it 'returns a valid pipeline' do
+ step.perform!
+
expect(pipeline).to be_valid
end
it 'does not persist a pipeline' do
+ step.perform!
+
expect(pipeline).not_to be_persisted
end
+
+ context 'when pipeline is running for a tag' do
+ let(:command) do
+ Gitlab::Ci::Pipeline::Chain::Command.new(
+ source: :push,
+ origin_ref: 'mytag',
+ checkout_sha: project.commit.id,
+ after_sha: nil,
+ before_sha: nil,
+ trigger_request: nil,
+ schedule: nil,
+ project: project,
+ current_user: user)
+ end
+
+ before do
+ allow_any_instance_of(Repository).to receive(:tag_exists?).with('mytag').and_return(true)
+
+ step.perform!
+ end
+
+ it 'correctly indicated that this is a tagged pipeline' do
+ expect(pipeline).to be_tag
+ end
+ end
end
diff --git a/spec/lib/gitlab/ci/pipeline/chain/command_spec.rb b/spec/lib/gitlab/ci/pipeline/chain/command_spec.rb
new file mode 100644
index 00000000000..75a177d2d1f
--- /dev/null
+++ b/spec/lib/gitlab/ci/pipeline/chain/command_spec.rb
@@ -0,0 +1,185 @@
+require 'spec_helper'
+
+describe Gitlab::Ci::Pipeline::Chain::Command do
+ set(:project) { create(:project, :repository) }
+
+ describe '#initialize' do
+ subject do
+ described_class.new(origin_ref: 'master')
+ end
+
+ it 'properly initialises object from hash' do
+ expect(subject.origin_ref).to eq('master')
+ end
+ end
+
+ context 'handling of origin_ref' do
+ let(:command) { described_class.new(project: project, origin_ref: origin_ref) }
+
+ describe '#branch_exists?' do
+ subject { command.branch_exists? }
+
+ context 'for existing branch' do
+ let(:origin_ref) { 'master' }
+
+ it { is_expected.to eq(true) }
+ end
+
+ context 'for invalid branch' do
+ let(:origin_ref) { 'something' }
+
+ it { is_expected.to eq(false) }
+ end
+ end
+
+ describe '#tag_exists?' do
+ subject { command.tag_exists? }
+
+ context 'for existing ref' do
+ let(:origin_ref) { 'v1.0.0' }
+
+ it { is_expected.to eq(true) }
+ end
+
+ context 'for invalid ref' do
+ let(:origin_ref) { 'something' }
+
+ it { is_expected.to eq(false) }
+ end
+ end
+
+ describe '#ref' do
+ subject { command.ref }
+
+ context 'for regular ref' do
+ let(:origin_ref) { 'master' }
+
+ it { is_expected.to eq('master') }
+ end
+
+ context 'for branch ref' do
+ let(:origin_ref) { 'refs/heads/master' }
+
+ it { is_expected.to eq('master') }
+ end
+
+ context 'for tag ref' do
+ let(:origin_ref) { 'refs/tags/1.0.0' }
+
+ it { is_expected.to eq('1.0.0') }
+ end
+
+ context 'for other refs' do
+ let(:origin_ref) { 'refs/merge-requests/11/head' }
+
+ it { is_expected.to eq('refs/merge-requests/11/head') }
+ end
+ end
+ end
+
+ describe '#sha' do
+ subject { command.sha }
+
+ context 'when invalid checkout_sha is specified' do
+ let(:command) { described_class.new(project: project, checkout_sha: 'aaa') }
+
+ it 'returns empty value' do
+ is_expected.to be_nil
+ end
+ end
+
+ context 'when a valid checkout_sha is specified' do
+ let(:command) { described_class.new(project: project, checkout_sha: project.commit.id) }
+
+ it 'returns checkout_sha' do
+ is_expected.to eq(project.commit.id)
+ end
+ end
+
+ context 'when a valid after_sha is specified' do
+ let(:command) { described_class.new(project: project, after_sha: project.commit.id) }
+
+ it 'returns after_sha' do
+ is_expected.to eq(project.commit.id)
+ end
+ end
+
+ context 'when a valid origin_ref is specified' do
+ let(:command) { described_class.new(project: project, origin_ref: 'HEAD') }
+
+ it 'returns SHA for given ref' do
+ is_expected.to eq(project.commit.id)
+ end
+ end
+ end
+
+ describe '#origin_sha' do
+ subject { command.origin_sha }
+
+ context 'when using checkout_sha and after_sha' do
+ let(:command) { described_class.new(project: project, checkout_sha: 'aaa', after_sha: 'bbb') }
+
+ it 'uses checkout_sha' do
+ is_expected.to eq('aaa')
+ end
+ end
+
+ context 'when using after_sha only' do
+ let(:command) { described_class.new(project: project, after_sha: 'bbb') }
+
+ it 'uses after_sha' do
+ is_expected.to eq('bbb')
+ end
+ end
+ end
+
+ describe '#before_sha' do
+ subject { command.before_sha }
+
+ context 'when using checkout_sha and before_sha' do
+ let(:command) { described_class.new(project: project, checkout_sha: 'aaa', before_sha: 'bbb') }
+
+ it 'uses before_sha' do
+ is_expected.to eq('bbb')
+ end
+ end
+
+ context 'when using checkout_sha only' do
+ let(:command) { described_class.new(project: project, checkout_sha: 'aaa') }
+
+ it 'uses checkout_sha' do
+ is_expected.to eq('aaa')
+ end
+ end
+
+ context 'when checkout_sha and before_sha are empty' do
+ let(:command) { described_class.new(project: project) }
+
+ it 'uses BLANK_SHA' do
+ is_expected.to eq(Gitlab::Git::BLANK_SHA)
+ end
+ end
+ end
+
+ describe '#protected_ref?' do
+ let(:command) { described_class.new(project: project, origin_ref: 'my-branch') }
+
+ subject { command.protected_ref? }
+
+ context 'when a ref is protected' do
+ before do
+ expect_any_instance_of(Project).to receive(:protected_for?).with('my-branch').and_return(true)
+ end
+
+ it { is_expected.to eq(true) }
+ end
+
+ context 'when a ref is unprotected' do
+ before do
+ expect_any_instance_of(Project).to receive(:protected_for?).with('my-branch').and_return(false)
+ end
+
+ it { is_expected.to eq(false) }
+ end
+ end
+end
diff --git a/spec/lib/gitlab/ci/pipeline/chain/create_spec.rb b/spec/lib/gitlab/ci/pipeline/chain/create_spec.rb
index f54e2326b06..1b03227d67b 100644
--- a/spec/lib/gitlab/ci/pipeline/chain/create_spec.rb
+++ b/spec/lib/gitlab/ci/pipeline/chain/create_spec.rb
@@ -10,9 +10,9 @@ describe Gitlab::Ci::Pipeline::Chain::Create do
end
let(:command) do
- double('command', project: project,
- current_user: user,
- seeds_block: nil)
+ Gitlab::Ci::Pipeline::Chain::Command.new(
+ project: project,
+ current_user: user, seeds_block: nil)
end
let(:step) { described_class.new(pipeline, command) }
diff --git a/spec/lib/gitlab/ci/pipeline/chain/sequence_spec.rb b/spec/lib/gitlab/ci/pipeline/chain/sequence_spec.rb
index e165e0fac2a..eca23694a2b 100644
--- a/spec/lib/gitlab/ci/pipeline/chain/sequence_spec.rb
+++ b/spec/lib/gitlab/ci/pipeline/chain/sequence_spec.rb
@@ -5,7 +5,7 @@ describe Gitlab::Ci::Pipeline::Chain::Sequence do
set(:user) { create(:user) }
let(:pipeline) { build_stubbed(:ci_pipeline) }
- let(:command) { double('command' ) }
+ let(:command) { Gitlab::Ci::Pipeline::Chain::Command.new }
let(:first_step) { spy('first step') }
let(:second_step) { spy('second step') }
let(:sequence) { [first_step, second_step] }
diff --git a/spec/lib/gitlab/ci/pipeline/chain/skip_spec.rb b/spec/lib/gitlab/ci/pipeline/chain/skip_spec.rb
index 32bd5de829b..dc13cae961c 100644
--- a/spec/lib/gitlab/ci/pipeline/chain/skip_spec.rb
+++ b/spec/lib/gitlab/ci/pipeline/chain/skip_spec.rb
@@ -6,10 +6,11 @@ describe Gitlab::Ci::Pipeline::Chain::Skip do
set(:pipeline) { create(:ci_pipeline, project: project) }
let(:command) do
- double('command', project: project,
- current_user: user,
- ignore_skip_ci: false,
- save_incompleted: true)
+ Gitlab::Ci::Pipeline::Chain::Command.new(
+ project: project,
+ current_user: user,
+ ignore_skip_ci: false,
+ save_incompleted: true)
end
let(:step) { described_class.new(pipeline, command) }
diff --git a/spec/lib/gitlab/ci/pipeline/chain/validate/abilities_spec.rb b/spec/lib/gitlab/ci/pipeline/chain/validate/abilities_spec.rb
index 0bbdd23f4d6..a973ccda8de 100644
--- a/spec/lib/gitlab/ci/pipeline/chain/validate/abilities_spec.rb
+++ b/spec/lib/gitlab/ci/pipeline/chain/validate/abilities_spec.rb
@@ -5,11 +5,12 @@ describe Gitlab::Ci::Pipeline::Chain::Validate::Abilities do
set(:user) { create(:user) }
let(:pipeline) do
- build_stubbed(:ci_pipeline, ref: ref, project: project)
+ build_stubbed(:ci_pipeline, project: project)
end
let(:command) do
- double('command', project: project, current_user: user)
+ Gitlab::Ci::Pipeline::Chain::Command.new(
+ project: project, current_user: user, origin_ref: ref)
end
let(:step) { described_class.new(pipeline, command) }
diff --git a/spec/lib/gitlab/ci/pipeline/chain/validate/config_spec.rb b/spec/lib/gitlab/ci/pipeline/chain/validate/config_spec.rb
index 8357af38f92..5c12c6e6392 100644
--- a/spec/lib/gitlab/ci/pipeline/chain/validate/config_spec.rb
+++ b/spec/lib/gitlab/ci/pipeline/chain/validate/config_spec.rb
@@ -5,9 +5,10 @@ describe Gitlab::Ci::Pipeline::Chain::Validate::Config do
set(:user) { create(:user) }
let(:command) do
- double('command', project: project,
- current_user: user,
- save_incompleted: true)
+ Gitlab::Ci::Pipeline::Chain::Command.new(
+ project: project,
+ current_user: user,
+ save_incompleted: true)
end
let!(:step) { described_class.new(pipeline, command) }
diff --git a/spec/lib/gitlab/ci/pipeline/chain/validate/repository_spec.rb b/spec/lib/gitlab/ci/pipeline/chain/validate/repository_spec.rb
index bb356efe9ad..fb1b53fc55c 100644
--- a/spec/lib/gitlab/ci/pipeline/chain/validate/repository_spec.rb
+++ b/spec/lib/gitlab/ci/pipeline/chain/validate/repository_spec.rb
@@ -3,10 +3,7 @@ require 'spec_helper'
describe Gitlab::Ci::Pipeline::Chain::Validate::Repository do
set(:project) { create(:project, :repository) }
set(:user) { create(:user) }
-
- let(:command) do
- double('command', project: project, current_user: user)
- end
+ let(:pipeline) { build_stubbed(:ci_pipeline) }
let!(:step) { described_class.new(pipeline, command) }
@@ -14,9 +11,10 @@ describe Gitlab::Ci::Pipeline::Chain::Validate::Repository do
step.perform!
end
- context 'when pipeline ref and sha exists' do
- let(:pipeline) do
- build_stubbed(:ci_pipeline, ref: 'master', sha: '123', project: project)
+ context 'when ref and sha exists' do
+ let(:command) do
+ Gitlab::Ci::Pipeline::Chain::Command.new(
+ project: project, current_user: user, origin_ref: 'master', checkout_sha: project.commit.id)
end
it 'does not break the chain' do
@@ -28,9 +26,10 @@ describe Gitlab::Ci::Pipeline::Chain::Validate::Repository do
end
end
- context 'when pipeline ref does not exist' do
- let(:pipeline) do
- build_stubbed(:ci_pipeline, ref: 'something', project: project)
+ context 'when ref does not exist' do
+ let(:command) do
+ Gitlab::Ci::Pipeline::Chain::Command.new(
+ project: project, current_user: user, origin_ref: 'something')
end
it 'breaks the chain' do
@@ -43,9 +42,10 @@ describe Gitlab::Ci::Pipeline::Chain::Validate::Repository do
end
end
- context 'when pipeline does not have SHA set' do
- let(:pipeline) do
- build_stubbed(:ci_pipeline, ref: 'master', sha: nil, project: project)
+ context 'when does not have existing SHA set' do
+ let(:command) do
+ Gitlab::Ci::Pipeline::Chain::Command.new(
+ project: project, current_user: user, origin_ref: 'master', checkout_sha: 'something')
end
it 'breaks the chain' do
diff --git a/spec/lib/gitlab/ci/status/build/common_spec.rb b/spec/lib/gitlab/ci/status/build/common_spec.rb
index 03d1f46b517..2cce7a23ea7 100644
--- a/spec/lib/gitlab/ci/status/build/common_spec.rb
+++ b/spec/lib/gitlab/ci/status/build/common_spec.rb
@@ -18,7 +18,7 @@ describe Gitlab::Ci::Status::Build::Common do
describe '#has_details?' do
context 'when user has access to read build' do
before do
- project.team << [user, :developer]
+ project.add_developer(user)
end
it { is_expected.to have_details }
diff --git a/spec/lib/gitlab/ci/status/external/common_spec.rb b/spec/lib/gitlab/ci/status/external/common_spec.rb
index b38fbee2486..40871f86568 100644
--- a/spec/lib/gitlab/ci/status/external/common_spec.rb
+++ b/spec/lib/gitlab/ci/status/external/common_spec.rb
@@ -29,7 +29,7 @@ describe Gitlab::Ci::Status::External::Common do
describe '#has_details?' do
context 'when user has access to read commit status' do
before do
- project.team << [user, :developer]
+ project.add_developer(user)
end
it { is_expected.to have_details }
diff --git a/spec/lib/gitlab/ci/status/external/factory_spec.rb b/spec/lib/gitlab/ci/status/external/factory_spec.rb
index c96fd53e730..529d02a3e39 100644
--- a/spec/lib/gitlab/ci/status/external/factory_spec.rb
+++ b/spec/lib/gitlab/ci/status/external/factory_spec.rb
@@ -8,7 +8,7 @@ describe Gitlab::Ci::Status::External::Factory do
let(:external_url) { 'http://gitlab.com/status' }
before do
- project.team << [user, :developer]
+ project.add_developer(user)
end
context 'when external status has a simple core status' do
diff --git a/spec/lib/gitlab/ci/status/pipeline/common_spec.rb b/spec/lib/gitlab/ci/status/pipeline/common_spec.rb
index 4a5b45e7cae..57df8325635 100644
--- a/spec/lib/gitlab/ci/status/pipeline/common_spec.rb
+++ b/spec/lib/gitlab/ci/status/pipeline/common_spec.rb
@@ -18,7 +18,7 @@ describe Gitlab::Ci::Status::Pipeline::Common do
describe '#has_details?' do
context 'when user has access to read pipeline' do
before do
- project.team << [user, :developer]
+ project.add_developer(user)
end
it { is_expected.to have_details }
diff --git a/spec/lib/gitlab/ci/status/pipeline/factory_spec.rb b/spec/lib/gitlab/ci/status/pipeline/factory_spec.rb
index dd754b849b2..defb3fdc0df 100644
--- a/spec/lib/gitlab/ci/status/pipeline/factory_spec.rb
+++ b/spec/lib/gitlab/ci/status/pipeline/factory_spec.rb
@@ -7,7 +7,7 @@ describe Gitlab::Ci::Status::Pipeline::Factory do
let(:factory) { described_class.new(pipeline, user) }
before do
- project.team << [user, :developer]
+ project.add_developer(user)
end
context 'when pipeline has a core status' do
diff --git a/spec/lib/gitlab/ci/status/stage/common_spec.rb b/spec/lib/gitlab/ci/status/stage/common_spec.rb
index f5f03ac0395..6ec35f8da7e 100644
--- a/spec/lib/gitlab/ci/status/stage/common_spec.rb
+++ b/spec/lib/gitlab/ci/status/stage/common_spec.rb
@@ -27,7 +27,7 @@ describe Gitlab::Ci::Status::Stage::Common do
context 'when user has permission to read pipeline' do
before do
- project.team << [user, :master]
+ project.add_master(user)
end
it 'has details' do
diff --git a/spec/lib/gitlab/ci/status/stage/factory_spec.rb b/spec/lib/gitlab/ci/status/stage/factory_spec.rb
index 432b07e4902..dee4f4efd1b 100644
--- a/spec/lib/gitlab/ci/status/stage/factory_spec.rb
+++ b/spec/lib/gitlab/ci/status/stage/factory_spec.rb
@@ -18,7 +18,7 @@ describe Gitlab::Ci::Status::Stage::Factory do
end
before do
- project.team << [user, :developer]
+ project.add_developer(user)
end
context 'when stage has a core status' do
diff --git a/spec/lib/gitlab/closing_issue_extractor_spec.rb b/spec/lib/gitlab/closing_issue_extractor_spec.rb
index ef7d766a13d..8c79ef54c6c 100644
--- a/spec/lib/gitlab/closing_issue_extractor_spec.rb
+++ b/spec/lib/gitlab/closing_issue_extractor_spec.rb
@@ -13,8 +13,8 @@ describe Gitlab::ClosingIssueExtractor do
subject { described_class.new(project, project.creator) }
before do
- project.team << [project.creator, :developer]
- project2.team << [project.creator, :master]
+ project.add_developer(project.creator)
+ project2.add_master(project.creator)
end
describe "#closed_by_message" do
@@ -297,7 +297,7 @@ describe Gitlab::ClosingIssueExtractor do
context 'with an external issue tracker reference' do
it 'extracts the referenced issue' do
jira_project = create(:jira_project, name: 'JIRA_EXT1')
- jira_project.team << [jira_project.creator, :master]
+ jira_project.add_master(jira_project.creator)
jira_issue = ExternalIssue.new("#{jira_project.name}-1", project: jira_project)
closing_issue_extractor = described_class.new(jira_project, jira_project.creator)
message = "Resolve #{jira_issue.to_reference}"
diff --git a/spec/lib/gitlab/cycle_analytics/base_event_fetcher_spec.rb b/spec/lib/gitlab/cycle_analytics/base_event_fetcher_spec.rb
index 0560c47f03f..3fe0493ed9b 100644
--- a/spec/lib/gitlab/cycle_analytics/base_event_fetcher_spec.rb
+++ b/spec/lib/gitlab/cycle_analytics/base_event_fetcher_spec.rb
@@ -23,6 +23,8 @@ describe Gitlab::CycleAnalytics::BaseEventFetcher do
allow_any_instance_of(described_class).to receive(:serialize) do |event|
event
end
+ allow_any_instance_of(described_class)
+ .to receive(:allowed_ids).and_return(nil)
stub_const('Gitlab::CycleAnalytics::BaseEventFetcher::MAX_EVENTS', max_events)
diff --git a/spec/lib/gitlab/cycle_analytics/events_spec.rb b/spec/lib/gitlab/cycle_analytics/events_spec.rb
index 28ea7d4c303..38a47a159e1 100644
--- a/spec/lib/gitlab/cycle_analytics/events_spec.rb
+++ b/spec/lib/gitlab/cycle_analytics/events_spec.rb
@@ -122,17 +122,18 @@ describe 'cycle analytics events' do
let(:stage) { :test }
let(:merge_request) { MergeRequest.first }
+
let!(:pipeline) do
create(:ci_pipeline,
ref: merge_request.source_branch,
sha: merge_request.diff_head_sha,
- project: context.project,
+ project: project,
head_pipeline_of: merge_request)
end
before do
- create(:ci_build, pipeline: pipeline, status: :success, author: user)
- create(:ci_build, pipeline: pipeline, status: :success, author: user)
+ create(:ci_build, :success, pipeline: pipeline, author: user)
+ create(:ci_build, :success, pipeline: pipeline, author: user)
pipeline.run!
pipeline.succeed!
@@ -219,17 +220,18 @@ describe 'cycle analytics events' do
describe '#staging_events' do
let(:stage) { :staging }
let(:merge_request) { MergeRequest.first }
+
let!(:pipeline) do
create(:ci_pipeline,
ref: merge_request.source_branch,
sha: merge_request.diff_head_sha,
- project: context.project,
+ project: project,
head_pipeline_of: merge_request)
end
before do
- create(:ci_build, pipeline: pipeline, status: :success, author: user)
- create(:ci_build, pipeline: pipeline, status: :success, author: user)
+ create(:ci_build, :success, pipeline: pipeline, author: user)
+ create(:ci_build, :success, pipeline: pipeline, author: user)
pipeline.run!
pipeline.succeed!
diff --git a/spec/lib/gitlab/cycle_analytics/permissions_spec.rb b/spec/lib/gitlab/cycle_analytics/permissions_spec.rb
index 2a0dd7be439..6de4bd3dc7c 100644
--- a/spec/lib/gitlab/cycle_analytics/permissions_spec.rb
+++ b/spec/lib/gitlab/cycle_analytics/permissions_spec.rb
@@ -38,7 +38,7 @@ describe Gitlab::CycleAnalytics::Permissions do
context 'user is master' do
before do
- project.team << [user, :master]
+ project.add_master(user)
end
it 'has permissions to issue stage' do
@@ -72,7 +72,7 @@ describe Gitlab::CycleAnalytics::Permissions do
context 'user has no build permissions' do
before do
- project.team << [user, :guest]
+ project.add_guest(user)
end
it 'has permissions to issue stage' do
@@ -90,7 +90,7 @@ describe Gitlab::CycleAnalytics::Permissions do
context 'user has no merge request permissions' do
before do
- project.team << [user, :guest]
+ project.add_guest(user)
end
it 'has permissions to issue stage' do
@@ -108,7 +108,7 @@ describe Gitlab::CycleAnalytics::Permissions do
context 'user has no issue permissions' do
before do
- project.team << [user, :developer]
+ project.add_developer(user)
project.project_feature.update_attribute(:issues_access_level, ProjectFeature::DISABLED)
end
diff --git a/spec/lib/gitlab/database/migration_helpers_spec.rb b/spec/lib/gitlab/database/migration_helpers_spec.rb
index 664ba0f7234..43761c2fe0c 100644
--- a/spec/lib/gitlab/database/migration_helpers_spec.rb
+++ b/spec/lib/gitlab/database/migration_helpers_spec.rb
@@ -902,7 +902,7 @@ describe Gitlab::Database::MigrationHelpers do
describe '#check_trigger_permissions!' do
it 'does nothing when the user has the correct permissions' do
expect { model.check_trigger_permissions!('users') }
- .not_to raise_error(RuntimeError)
+ .not_to raise_error
end
it 'raises RuntimeError when the user does not have the correct permissions' do
@@ -1006,12 +1006,12 @@ describe Gitlab::Database::MigrationHelpers do
context 'with batch_size option' do
it 'queues jobs correctly' do
Sidekiq::Testing.fake! do
- model.queue_background_migration_jobs_by_range_at_intervals(User, 'FooJob', 10.seconds, batch_size: 2)
+ model.queue_background_migration_jobs_by_range_at_intervals(User, 'FooJob', 10.minutes, batch_size: 2)
expect(BackgroundMigrationWorker.jobs[0]['args']).to eq(['FooJob', [id1, id2]])
- expect(BackgroundMigrationWorker.jobs[0]['at']).to eq(10.seconds.from_now.to_f)
+ expect(BackgroundMigrationWorker.jobs[0]['at']).to eq(10.minutes.from_now.to_f)
expect(BackgroundMigrationWorker.jobs[1]['args']).to eq(['FooJob', [id3, id3]])
- expect(BackgroundMigrationWorker.jobs[1]['at']).to eq(20.seconds.from_now.to_f)
+ expect(BackgroundMigrationWorker.jobs[1]['at']).to eq(20.minutes.from_now.to_f)
end
end
end
@@ -1019,10 +1019,10 @@ describe Gitlab::Database::MigrationHelpers do
context 'without batch_size option' do
it 'queues jobs correctly' do
Sidekiq::Testing.fake! do
- model.queue_background_migration_jobs_by_range_at_intervals(User, 'FooJob', 10.seconds)
+ model.queue_background_migration_jobs_by_range_at_intervals(User, 'FooJob', 10.minutes)
expect(BackgroundMigrationWorker.jobs[0]['args']).to eq(['FooJob', [id1, id3]])
- expect(BackgroundMigrationWorker.jobs[0]['at']).to eq(10.seconds.from_now.to_f)
+ expect(BackgroundMigrationWorker.jobs[0]['at']).to eq(10.minutes.from_now.to_f)
end
end
end
@@ -1036,4 +1036,93 @@ describe Gitlab::Database::MigrationHelpers do
end
end
end
+
+ describe '#change_column_type_using_background_migration' do
+ let!(:issue) { create(:issue) }
+
+ let(:issue_model) do
+ Class.new(ActiveRecord::Base) do
+ self.table_name = 'issues'
+ include EachBatch
+ end
+ end
+
+ it 'changes the type of a column using a background migration' do
+ expect(model)
+ .to receive(:add_column)
+ .with('issues', 'closed_at_for_type_change', :datetime_with_timezone)
+
+ expect(model)
+ .to receive(:install_rename_triggers)
+ .with('issues', :closed_at, 'closed_at_for_type_change')
+
+ expect(BackgroundMigrationWorker)
+ .to receive(:perform_in)
+ .ordered
+ .with(
+ 10.minutes,
+ 'CopyColumn',
+ ['issues', :closed_at, 'closed_at_for_type_change', issue.id, issue.id]
+ )
+
+ expect(BackgroundMigrationWorker)
+ .to receive(:perform_in)
+ .ordered
+ .with(
+ 1.hour + 10.minutes,
+ 'CleanupConcurrentTypeChange',
+ ['issues', :closed_at, 'closed_at_for_type_change']
+ )
+
+ expect(Gitlab::BackgroundMigration)
+ .to receive(:steal)
+ .ordered
+ .with('CopyColumn')
+
+ expect(Gitlab::BackgroundMigration)
+ .to receive(:steal)
+ .ordered
+ .with('CleanupConcurrentTypeChange')
+
+ model.change_column_type_using_background_migration(
+ issue_model.all,
+ :closed_at,
+ :datetime_with_timezone
+ )
+ end
+ end
+
+ describe '#perform_background_migration_inline?' do
+ it 'returns true in a test environment' do
+ allow(Rails.env)
+ .to receive(:test?)
+ .and_return(true)
+
+ expect(model.perform_background_migration_inline?).to eq(true)
+ end
+
+ it 'returns true in a development environment' do
+ allow(Rails.env)
+ .to receive(:test?)
+ .and_return(false)
+
+ allow(Rails.env)
+ .to receive(:development?)
+ .and_return(true)
+
+ expect(model.perform_background_migration_inline?).to eq(true)
+ end
+
+ it 'returns false in a production environment' do
+ allow(Rails.env)
+ .to receive(:test?)
+ .and_return(false)
+
+ allow(Rails.env)
+ .to receive(:development?)
+ .and_return(false)
+
+ expect(model.perform_background_migration_inline?).to eq(false)
+ end
+ end
end
diff --git a/spec/lib/gitlab/diff/file_collection/merge_request_diff_spec.rb b/spec/lib/gitlab/diff/file_collection/merge_request_diff_spec.rb
index d81774c8b8f..a067c42b75b 100644
--- a/spec/lib/gitlab/diff/file_collection/merge_request_diff_spec.rb
+++ b/spec/lib/gitlab/diff/file_collection/merge_request_diff_spec.rb
@@ -19,4 +19,18 @@ describe Gitlab::Diff::FileCollection::MergeRequestDiff do
diff_files
end
+
+ shared_examples 'initializes a DiffCollection' do
+ it 'returns a valid instance of a DiffCollection' do
+ expect(diff_files).to be_a(Gitlab::Git::DiffCollection)
+ end
+ end
+
+ context 'with Gitaly disabled', :disable_gitaly do
+ it_behaves_like 'initializes a DiffCollection'
+ end
+
+ context 'with Gitaly enabled' do
+ it_behaves_like 'initializes a DiffCollection'
+ end
end
diff --git a/spec/lib/gitlab/diff/file_spec.rb b/spec/lib/gitlab/diff/file_spec.rb
index ff9acfd08b9..9204ea37963 100644
--- a/spec/lib/gitlab/diff/file_spec.rb
+++ b/spec/lib/gitlab/diff/file_spec.rb
@@ -431,4 +431,29 @@ describe Gitlab::Diff::File do
end
end
end
+
+ context 'when neither blob exists' do
+ let(:blank_diff_refs) { Gitlab::Diff::DiffRefs.new(base_sha: Gitlab::Git::BLANK_SHA, head_sha: Gitlab::Git::BLANK_SHA) }
+ let(:diff_file) { described_class.new(diff, diff_refs: blank_diff_refs, repository: project.repository) }
+
+ describe '#blob' do
+ it 'returns a concrete nil so it can be used in boolean expressions' do
+ actual = diff_file.blob && true
+
+ expect(actual).to be_nil
+ end
+ end
+
+ describe '#binary?' do
+ it 'returns false' do
+ expect(diff_file).not_to be_binary
+ end
+ end
+
+ describe '#size' do
+ it 'returns zero' do
+ expect(diff_file.size).to be_zero
+ end
+ end
+ end
end
diff --git a/spec/lib/gitlab/email/handler/create_merge_request_handler_spec.rb b/spec/lib/gitlab/email/handler/create_merge_request_handler_spec.rb
index 51ce3116880..dc1a93367a4 100644
--- a/spec/lib/gitlab/email/handler/create_merge_request_handler_spec.rb
+++ b/spec/lib/gitlab/email/handler/create_merge_request_handler_spec.rb
@@ -49,6 +49,7 @@ describe Gitlab::Email::Handler::CreateMergeRequestHandler do
expect(merge_request.author).to eq(user)
expect(merge_request.source_branch).to eq('feature')
expect(merge_request.title).to eq('Feature added')
+ expect(merge_request.description).to eq('Merge request description')
expect(merge_request.target_branch).to eq(project.default_branch)
end
end
@@ -79,6 +80,17 @@ describe Gitlab::Email::Handler::CreateMergeRequestHandler do
expect { receiver.execute }.to raise_error(Gitlab::Email::InvalidMergeRequestError)
end
end
+
+ context "when the message body is blank" do
+ let(:email_raw) { fixture_file("emails/valid_new_merge_request_no_description.eml") }
+
+ it "creates a new merge request with description set from the last commit" do
+ expect { receiver.execute }.to change { project.merge_requests.count }.by(1)
+ merge_request = project.merge_requests.last
+
+ expect(merge_request.description).to eq('Signed-off-by: Dmitriy Zaporozhets <dmitriy.zaporozhets@gmail.com>')
+ end
+ end
end
end
end
diff --git a/spec/lib/gitlab/email/handler/create_note_handler_spec.rb b/spec/lib/gitlab/email/handler/create_note_handler_spec.rb
index d0fa16ce4d1..031efcf1291 100644
--- a/spec/lib/gitlab/email/handler/create_note_handler_spec.rb
+++ b/spec/lib/gitlab/email/handler/create_note_handler_spec.rb
@@ -66,7 +66,7 @@ describe Gitlab::Email::Handler::CreateNoteHandler do
context 'and current user can update noteable' do
before do
- project.team << [user, :developer]
+ project.add_developer(user)
end
it 'does not raise an error' do
@@ -99,7 +99,7 @@ describe Gitlab::Email::Handler::CreateNoteHandler do
context 'and current user can update noteable' do
before do
- project.team << [user, :developer]
+ project.add_developer(user)
end
it 'post a note and updates the noteable' do
diff --git a/spec/lib/gitlab/encoding_helper_spec.rb b/spec/lib/gitlab/encoding_helper_spec.rb
index f6e5c55240f..4e9367323cb 100644
--- a/spec/lib/gitlab/encoding_helper_spec.rb
+++ b/spec/lib/gitlab/encoding_helper_spec.rb
@@ -120,6 +120,24 @@ describe Gitlab::EncodingHelper do
it 'returns empty string on conversion errors' do
expect { ext_class.encode_utf8('') }.not_to raise_error(ArgumentError)
end
+
+ context 'with strings that can be forcefully encoded into utf8' do
+ let(:test_string) do
+ "refs/heads/FixSymbolsTitleDropdown".encode("ASCII-8BIT")
+ end
+ let(:expected_string) do
+ "refs/heads/FixSymbolsTitleDropdown".encode("UTF-8")
+ end
+
+ subject { ext_class.encode_utf8(test_string) }
+
+ it "doesn't use CharlockHolmes if the encoding can be forced into utf_8" do
+ expect(CharlockHolmes::EncodingDetector).not_to receive(:detect)
+
+ expect(subject).to eq(expected_string)
+ expect(subject.encoding.name).to eq('UTF-8')
+ end
+ end
end
describe '#clean' do
@@ -145,4 +163,18 @@ describe Gitlab::EncodingHelper do
end
end
end
+
+ describe 'encode_binary' do
+ [
+ [nil, ""],
+ ["", ""],
+ [" ", " "],
+ %w(a1 a1),
+ ["编码", "\xE7\xBC\x96\xE7\xA0\x81".b]
+ ].each do |input, result|
+ it "encodes #{input.inspect} to #{result.inspect}" do
+ expect(ext_class.encode_binary(input)).to eq(result)
+ end
+ end
+ end
end
diff --git a/spec/lib/gitlab/exclusive_lease_spec.rb b/spec/lib/gitlab/exclusive_lease_spec.rb
index 7322a326b01..6193e177668 100644
--- a/spec/lib/gitlab/exclusive_lease_spec.rb
+++ b/spec/lib/gitlab/exclusive_lease_spec.rb
@@ -73,4 +73,19 @@ describe Gitlab::ExclusiveLease, :clean_gitlab_redis_shared_state do
described_class.new(key, timeout: 3600).try_obtain
end
end
+
+ describe '#ttl' do
+ it 'returns the TTL of the Redis key' do
+ lease = described_class.new('kittens', timeout: 100)
+ lease.try_obtain
+
+ expect(lease.ttl <= 100).to eq(true)
+ end
+
+ it 'returns nil when the lease does not exist' do
+ lease = described_class.new('kittens', timeout: 10)
+
+ expect(lease.ttl).to be_nil
+ end
+ end
end
diff --git a/spec/lib/gitlab/gfm/reference_rewriter_spec.rb b/spec/lib/gitlab/gfm/reference_rewriter_spec.rb
index 7dc06c90078..4d2f08f95fc 100644
--- a/spec/lib/gitlab/gfm/reference_rewriter_spec.rb
+++ b/spec/lib/gitlab/gfm/reference_rewriter_spec.rb
@@ -10,7 +10,7 @@ describe Gitlab::Gfm::ReferenceRewriter do
let(:text) { 'some text' }
before do
- old_project.team << [user, :reporter]
+ old_project.add_reporter(user)
end
describe '#rewrite' do
diff --git a/spec/lib/gitlab/git/blob_spec.rb b/spec/lib/gitlab/git/blob_spec.rb
index c04a9688503..07eb5b82d5f 100644
--- a/spec/lib/gitlab/git/blob_spec.rb
+++ b/spec/lib/gitlab/git/blob_spec.rb
@@ -146,7 +146,7 @@ describe Gitlab::Git::Blob, seed_helper: true do
context 'when sha references a tree' do
it 'returns nil' do
- tree = Gitlab::Git::Commit.find(repository, 'master').tree
+ tree = repository.rugged.rev_parse('master^{tree}')
blob = Gitlab::Git::Blob.raw(repository, tree.oid)
@@ -202,16 +202,6 @@ describe Gitlab::Git::Blob, seed_helper: true do
context 'limiting' do
subject { described_class.batch(repository, blob_references, blob_size_limit: blob_size_limit) }
- context 'default' do
- let(:blob_size_limit) { nil }
-
- it 'limits to MAX_DATA_DISPLAY_SIZE' do
- stub_const('Gitlab::Git::Blob::MAX_DATA_DISPLAY_SIZE', 100)
-
- expect(subject.first.data.size).to eq(100)
- end
- end
-
context 'positive' do
let(:blob_size_limit) { 10 }
@@ -221,7 +211,10 @@ describe Gitlab::Git::Blob, seed_helper: true do
context 'zero' do
let(:blob_size_limit) { 0 }
- it { expect(subject.first.data).to eq('') }
+ it 'only loads the metadata' do
+ expect(subject.first.size).not_to be(0)
+ expect(subject.first.data).to eq('')
+ end
end
context 'negative' do
@@ -237,7 +230,7 @@ describe Gitlab::Git::Blob, seed_helper: true do
end
describe '.batch_lfs_pointers' do
- let(:tree_object) { Gitlab::Git::Commit.find(repository, 'master').tree }
+ let(:tree_object) { repository.rugged.rev_parse('master^{tree}') }
let(:non_lfs_blob) do
Gitlab::Git::Blob.find(
diff --git a/spec/lib/gitlab/git/commit_spec.rb b/spec/lib/gitlab/git/commit_spec.rb
index 5ed639543e0..6a07a3ca8b8 100644
--- a/spec/lib/gitlab/git/commit_spec.rb
+++ b/spec/lib/gitlab/git/commit_spec.rb
@@ -55,7 +55,6 @@ describe Gitlab::Git::Commit, seed_helper: true do
it { expect(@commit.parents).to eq(@gitlab_parents) }
it { expect(@commit.parent_id).to eq(@parents.first.oid) }
it { expect(@commit.no_commit_message).to eq("--no commit message") }
- it { expect(@commit.tree).to eq(@tree) }
after do
# Erase the new commit so other tests get the original repo
@@ -428,6 +427,11 @@ describe Gitlab::Git::Commit, seed_helper: true do
subject { super().deletions }
it { is_expected.to eq(6) }
end
+
+ describe '#total' do
+ subject { super().total }
+ it { is_expected.to eq(17) }
+ end
end
describe '#stats with gitaly on' do
diff --git a/spec/lib/gitlab/git/gitlab_projects_spec.rb b/spec/lib/gitlab/git/gitlab_projects_spec.rb
new file mode 100644
index 00000000000..a798b188a0d
--- /dev/null
+++ b/spec/lib/gitlab/git/gitlab_projects_spec.rb
@@ -0,0 +1,337 @@
+require 'spec_helper'
+
+describe Gitlab::Git::GitlabProjects do
+ after do
+ TestEnv.clean_test_path
+ end
+
+ let(:project) { create(:project, :repository) }
+
+ if $VERBOSE
+ let(:logger) { Logger.new(STDOUT) }
+ else
+ let(:logger) { double('logger').as_null_object }
+ end
+
+ let(:tmp_repos_path) { TestEnv.repos_path }
+ let(:repo_name) { project.disk_path + '.git' }
+ let(:tmp_repo_path) { File.join(tmp_repos_path, repo_name) }
+ let(:gl_projects) { build_gitlab_projects(tmp_repos_path, repo_name) }
+
+ describe '#initialize' do
+ it { expect(gl_projects.shard_path).to eq(tmp_repos_path) }
+ it { expect(gl_projects.repository_relative_path).to eq(repo_name) }
+ it { expect(gl_projects.repository_absolute_path).to eq(File.join(tmp_repos_path, repo_name)) }
+ it { expect(gl_projects.logger).to eq(logger) }
+ end
+
+ describe '#mv_project' do
+ let(:new_repo_path) { File.join(tmp_repos_path, 'repo.git') }
+
+ it 'moves a repo directory' do
+ expect(File.exist?(tmp_repo_path)).to be_truthy
+
+ message = "Moving repository from <#{tmp_repo_path}> to <#{new_repo_path}>."
+ expect(logger).to receive(:info).with(message)
+
+ expect(gl_projects.mv_project('repo.git')).to be_truthy
+
+ expect(File.exist?(tmp_repo_path)).to be_falsy
+ expect(File.exist?(new_repo_path)).to be_truthy
+ end
+
+ it "fails if the source path doesn't exist" do
+ expected_source_path = File.join(tmp_repos_path, 'bad-src.git')
+ expect(logger).to receive(:error).with("mv-project failed: source path <#{expected_source_path}> does not exist.")
+
+ result = build_gitlab_projects(tmp_repos_path, 'bad-src.git').mv_project('repo.git')
+ expect(result).to be_falsy
+ end
+
+ it 'fails if the destination path already exists' do
+ FileUtils.mkdir_p(File.join(tmp_repos_path, 'already-exists.git'))
+
+ expected_distination_path = File.join(tmp_repos_path, 'already-exists.git')
+ message = "mv-project failed: destination path <#{expected_distination_path}> already exists."
+ expect(logger).to receive(:error).with(message)
+
+ expect(gl_projects.mv_project('already-exists.git')).to be_falsy
+ end
+ end
+
+ describe '#rm_project' do
+ it 'removes a repo directory' do
+ expect(File.exist?(tmp_repo_path)).to be_truthy
+ expect(logger).to receive(:info).with("Removing repository <#{tmp_repo_path}>.")
+
+ expect(gl_projects.rm_project).to be_truthy
+
+ expect(File.exist?(tmp_repo_path)).to be_falsy
+ end
+ end
+
+ describe '#push_branches' do
+ let(:remote_name) { 'remote-name' }
+ let(:branch_name) { 'master' }
+ let(:cmd) { %W(git push -- #{remote_name} #{branch_name}) }
+ let(:force) { false }
+
+ subject { gl_projects.push_branches(remote_name, 600, force, [branch_name]) }
+
+ it 'executes the command' do
+ stub_spawn(cmd, 600, tmp_repo_path, success: true)
+
+ is_expected.to be_truthy
+ end
+
+ it 'fails' do
+ stub_spawn(cmd, 600, tmp_repo_path, success: false)
+
+ is_expected.to be_falsy
+ end
+
+ context 'with --force' do
+ let(:cmd) { %W(git push --force -- #{remote_name} #{branch_name}) }
+ let(:force) { true }
+
+ it 'executes the command' do
+ stub_spawn(cmd, 600, tmp_repo_path, success: true)
+
+ is_expected.to be_truthy
+ end
+ end
+ end
+
+ describe '#fetch_remote' do
+ let(:remote_name) { 'remote-name' }
+ let(:branch_name) { 'master' }
+ let(:force) { false }
+ let(:tags) { true }
+ let(:args) { { force: force, tags: tags }.merge(extra_args) }
+ let(:extra_args) { {} }
+ let(:cmd) { %W(git fetch #{remote_name} --prune --quiet --tags) }
+
+ subject { gl_projects.fetch_remote(remote_name, 600, args) }
+
+ def stub_tempfile(name, filename, opts = {})
+ chmod = opts.delete(:chmod)
+ file = StringIO.new
+
+ allow(file).to receive(:close!)
+ allow(file).to receive(:path).and_return(name)
+
+ expect(Tempfile).to receive(:new).with(filename).and_return(file)
+ expect(file).to receive(:chmod).with(chmod) if chmod
+
+ file
+ end
+
+ context 'with default args' do
+ it 'executes the command' do
+ stub_spawn(cmd, 600, tmp_repo_path, {}, success: true)
+
+ is_expected.to be_truthy
+ end
+
+ it 'fails' do
+ stub_spawn(cmd, 600, tmp_repo_path, {}, success: false)
+
+ is_expected.to be_falsy
+ end
+ end
+
+ context 'with --force' do
+ let(:force) { true }
+ let(:cmd) { %W(git fetch #{remote_name} --prune --quiet --force --tags) }
+
+ it 'executes the command with forced option' do
+ stub_spawn(cmd, 600, tmp_repo_path, {}, success: true)
+
+ is_expected.to be_truthy
+ end
+ end
+
+ context 'with --no-tags' do
+ let(:tags) { false }
+ let(:cmd) { %W(git fetch #{remote_name} --prune --quiet --no-tags) }
+
+ it 'executes the command' do
+ stub_spawn(cmd, 600, tmp_repo_path, {}, success: true)
+
+ is_expected.to be_truthy
+ end
+ end
+
+ describe 'with an SSH key' do
+ let(:extra_args) { { ssh_key: 'SSH KEY' } }
+
+ it 'sets GIT_SSH to a custom script' do
+ script = stub_tempfile('scriptFile', 'gitlab-shell-ssh-wrapper', chmod: 0o755)
+ key = stub_tempfile('/tmp files/keyFile', 'gitlab-shell-key-file', chmod: 0o400)
+
+ stub_spawn(cmd, 600, tmp_repo_path, { 'GIT_SSH' => 'scriptFile' }, success: true)
+
+ is_expected.to be_truthy
+
+ expect(script.string).to eq("#!/bin/sh\nexec ssh '-oIdentityFile=\"/tmp files/keyFile\"' '-oIdentitiesOnly=\"yes\"' \"$@\"")
+ expect(key.string).to eq('SSH KEY')
+ end
+ end
+
+ describe 'with known_hosts data' do
+ let(:extra_args) { { known_hosts: 'KNOWN HOSTS' } }
+
+ it 'sets GIT_SSH to a custom script' do
+ script = stub_tempfile('scriptFile', 'gitlab-shell-ssh-wrapper', chmod: 0o755)
+ key = stub_tempfile('/tmp files/knownHosts', 'gitlab-shell-known-hosts', chmod: 0o400)
+
+ stub_spawn(cmd, 600, tmp_repo_path, { 'GIT_SSH' => 'scriptFile' }, success: true)
+
+ is_expected.to be_truthy
+
+ expect(script.string).to eq("#!/bin/sh\nexec ssh '-oStrictHostKeyChecking=\"yes\"' '-oUserKnownHostsFile=\"/tmp files/knownHosts\"' \"$@\"")
+ expect(key.string).to eq('KNOWN HOSTS')
+ end
+ end
+ end
+
+ describe '#import_project' do
+ let(:project) { create(:project) }
+ let(:import_url) { TestEnv.factory_repo_path_bare }
+ let(:cmd) { %W(git clone --bare -- #{import_url} #{tmp_repo_path}) }
+ let(:timeout) { 600 }
+
+ subject { gl_projects.import_project(import_url, timeout) }
+
+ context 'success import' do
+ it 'imports a repo' do
+ expect(File.exist?(File.join(tmp_repo_path, 'HEAD'))).to be_falsy
+
+ message = "Importing project from <#{import_url}> to <#{tmp_repo_path}>."
+ expect(logger).to receive(:info).with(message)
+
+ is_expected.to be_truthy
+
+ expect(File.exist?(File.join(tmp_repo_path, 'HEAD'))).to be_truthy
+ end
+ end
+
+ context 'already exists' do
+ it "doesn't import" do
+ FileUtils.mkdir_p(tmp_repo_path)
+
+ is_expected.to be_falsy
+ end
+ end
+
+ context 'timeout' do
+ it 'does not import a repo' do
+ stub_spawn_timeout(cmd, timeout, nil)
+
+ message = "Importing project from <#{import_url}> to <#{tmp_repo_path}> failed."
+ expect(logger).to receive(:error).with(message)
+
+ is_expected.to be_falsy
+
+ expect(gl_projects.output).to eq("Timed out\n")
+ expect(File.exist?(File.join(tmp_repo_path, 'HEAD'))).to be_falsy
+ end
+ end
+ end
+
+ describe '#fork_repository' do
+ let(:dest_repos_path) { tmp_repos_path }
+ let(:dest_repo_name) { File.join('@hashed', 'aa', 'bb', 'xyz.git') }
+ let(:dest_repo) { File.join(dest_repos_path, dest_repo_name) }
+
+ subject { gl_projects.fork_repository(dest_repos_path, dest_repo_name) }
+
+ before do
+ FileUtils.mkdir_p(dest_repos_path)
+ end
+
+ after do
+ FileUtils.rm_rf(dest_repos_path)
+ end
+
+ shared_examples 'forking a repository' do
+ it 'forks the repository' do
+ is_expected.to be_truthy
+
+ expect(File.exist?(dest_repo)).to be_truthy
+ expect(File.exist?(File.join(dest_repo, 'hooks', 'pre-receive'))).to be_truthy
+ expect(File.exist?(File.join(dest_repo, 'hooks', 'post-receive'))).to be_truthy
+ end
+
+ it 'does not fork if a project of the same name already exists' do
+ # create a fake project at the intended destination
+ FileUtils.mkdir_p(dest_repo)
+
+ is_expected.to be_falsy
+ end
+ end
+
+ context 'when Gitaly fork_repository feature is enabled' do
+ it_behaves_like 'forking a repository'
+ end
+
+ context 'when Gitaly fork_repository feature is disabled', :disable_gitaly do
+ it_behaves_like 'forking a repository'
+
+ # We seem to be stuck to having only one working Gitaly storage in tests, changing
+ # that is not very straight-forward so I'm leaving this test here for now till
+ # https://gitlab.com/gitlab-org/gitlab-ce/issues/41393 is fixed.
+ context 'different storages' do
+ let(:dest_repos_path) { File.join(File.dirname(tmp_repos_path), 'alternative') }
+
+ it 'forks the repo' do
+ is_expected.to be_truthy
+
+ expect(File.exist?(dest_repo)).to be_truthy
+ expect(File.exist?(File.join(dest_repo, 'hooks', 'pre-receive'))).to be_truthy
+ expect(File.exist?(File.join(dest_repo, 'hooks', 'post-receive'))).to be_truthy
+ end
+ end
+
+ describe 'log messages' do
+ describe 'successful fork' do
+ it do
+ message = "Forking repository from <#{tmp_repo_path}> to <#{dest_repo}>."
+ expect(logger).to receive(:info).with(message)
+
+ subject
+ end
+ end
+
+ describe 'failed fork due existing destination' do
+ it do
+ FileUtils.mkdir_p(dest_repo)
+ message = "fork-repository failed: destination repository <#{dest_repo}> already exists."
+ expect(logger).to receive(:error).with(message)
+
+ subject
+ end
+ end
+ end
+ end
+ end
+
+ def build_gitlab_projects(*args)
+ described_class.new(
+ *args,
+ global_hooks_path: Gitlab.config.gitlab_shell.hooks_path,
+ logger: logger
+ )
+ end
+
+ def stub_spawn(*args, success: true)
+ exitstatus = success ? 0 : nil
+ expect(gl_projects).to receive(:popen_with_timeout).with(*args)
+ .and_return(["output", exitstatus])
+ end
+
+ def stub_spawn_timeout(*args)
+ expect(gl_projects).to receive(:popen_with_timeout).with(*args)
+ .and_raise(Timeout::Error)
+ end
+end
diff --git a/spec/lib/gitlab/git/repository_spec.rb b/spec/lib/gitlab/git/repository_spec.rb
index f19b65a5f71..f346a345f00 100644
--- a/spec/lib/gitlab/git/repository_spec.rb
+++ b/spec/lib/gitlab/git/repository_spec.rb
@@ -18,6 +18,52 @@ describe Gitlab::Git::Repository, seed_helper: true do
end
let(:repository) { Gitlab::Git::Repository.new('default', TEST_REPO_PATH, '') }
+ let(:storage_path) { TestEnv.repos_path }
+
+ describe '.create_hooks' do
+ let(:repo_path) { File.join(storage_path, 'hook-test.git') }
+ let(:hooks_dir) { File.join(repo_path, 'hooks') }
+ let(:target_hooks_dir) { Gitlab.config.gitlab_shell.hooks_path }
+ let(:existing_target) { File.join(repo_path, 'foobar') }
+
+ before do
+ FileUtils.rm_rf(repo_path)
+ FileUtils.mkdir_p(repo_path)
+ end
+
+ context 'hooks is a directory' do
+ let(:existing_file) { File.join(hooks_dir, 'my-file') }
+
+ before do
+ FileUtils.mkdir_p(hooks_dir)
+ FileUtils.touch(existing_file)
+ described_class.create_hooks(repo_path, target_hooks_dir)
+ end
+
+ it { expect(File.readlink(hooks_dir)).to eq(target_hooks_dir) }
+ it { expect(Dir[File.join(repo_path, "hooks.old.*/my-file")].count).to eq(1) }
+ end
+
+ context 'hooks is a valid symlink' do
+ before do
+ FileUtils.mkdir_p existing_target
+ File.symlink(existing_target, hooks_dir)
+ described_class.create_hooks(repo_path, target_hooks_dir)
+ end
+
+ it { expect(File.readlink(hooks_dir)).to eq(target_hooks_dir) }
+ end
+
+ context 'hooks is a broken symlink' do
+ before do
+ FileUtils.rm_f(existing_target)
+ File.symlink(existing_target, hooks_dir)
+ described_class.create_hooks(repo_path, target_hooks_dir)
+ end
+
+ it { expect(File.readlink(hooks_dir)).to eq(target_hooks_dir) }
+ end
+ end
describe "Respond to" do
subject { repository }
@@ -588,44 +634,54 @@ describe Gitlab::Git::Repository, seed_helper: true do
end
end
- describe '#fetch_as_mirror_without_shell' do
+ describe '#fetch_repository_as_mirror' do
let(:new_repository) do
Gitlab::Git::Repository.new('default', 'my_project.git', '')
end
- subject { new_repository.fetch_as_mirror_without_shell(repository.path) }
+ subject { new_repository.fetch_repository_as_mirror(repository) }
before do
Gitlab::Shell.new.add_repository('default', 'my_project')
end
after do
- Gitlab::Shell.new.remove_repository(TestEnv.repos_path, 'my_project')
+ Gitlab::Shell.new.remove_repository(storage_path, 'my_project')
end
- it 'fetches a url as a mirror remote' do
- subject
+ shared_examples 'repository mirror fecthing' do
+ it 'fetches a repository as a mirror remote' do
+ subject
- expect(refs(new_repository.path)).to eq(refs(repository.path))
- end
+ expect(refs(new_repository.path)).to eq(refs(repository.path))
+ end
- context 'with keep-around refs' do
- let(:sha) { SeedRepo::Commit::ID }
- let(:keep_around_ref) { "refs/keep-around/#{sha}" }
- let(:tmp_ref) { "refs/tmp/#{SecureRandom.hex}" }
+ context 'with keep-around refs' do
+ let(:sha) { SeedRepo::Commit::ID }
+ let(:keep_around_ref) { "refs/keep-around/#{sha}" }
+ let(:tmp_ref) { "refs/tmp/#{SecureRandom.hex}" }
- before do
- repository.rugged.references.create(keep_around_ref, sha, force: true)
- repository.rugged.references.create(tmp_ref, sha, force: true)
- end
+ before do
+ repository.rugged.references.create(keep_around_ref, sha, force: true)
+ repository.rugged.references.create(tmp_ref, sha, force: true)
+ end
- it 'includes the temporary and keep-around refs' do
- subject
+ it 'includes the temporary and keep-around refs' do
+ subject
- expect(refs(new_repository.path)).to include(keep_around_ref)
- expect(refs(new_repository.path)).to include(tmp_ref)
+ expect(refs(new_repository.path)).to include(keep_around_ref)
+ expect(refs(new_repository.path)).to include(tmp_ref)
+ end
end
end
+
+ context 'with gitaly enabled' do
+ it_behaves_like 'repository mirror fecthing'
+ end
+
+ context 'with gitaly enabled', :skip_gitaly_mock do
+ it_behaves_like 'repository mirror fecthing'
+ end
end
describe '#remote_tags' do
@@ -656,21 +712,6 @@ describe Gitlab::Git::Repository, seed_helper: true do
end
end
- describe '#remote_exists?' do
- before(:all) do
- @repo = Gitlab::Git::Repository.new('default', TEST_MUTABLE_REPO_PATH, '')
- @repo.add_remote("new_remote", SeedHelper::GITLAB_GIT_TEST_REPO_URL)
- end
-
- it 'returns true for an existing remote' do
- expect(@repo.remote_exists?('new_remote')).to eq(true)
- end
-
- it 'returns false for a non-existing remote' do
- expect(@repo.remote_exists?('foo')).to eq(false)
- end
- end
-
describe "#log" do
let(:commit_with_old_name) do
Gitlab::Git::Commit.decorate(repository, @commit_with_old_name_id)
@@ -985,7 +1026,7 @@ describe Gitlab::Git::Repository, seed_helper: true do
shared_examples 'extended commit counting' do
context 'with after timestamp' do
it 'returns the number of commits after timestamp' do
- options = { ref: 'master', limit: nil, after: Time.iso8601('2013-03-03T20:15:01+00:00') }
+ options = { ref: 'master', after: Time.iso8601('2013-03-03T20:15:01+00:00') }
expect(repository.count_commits(options)).to eq(25)
end
@@ -993,19 +1034,65 @@ describe Gitlab::Git::Repository, seed_helper: true do
context 'with before timestamp' do
it 'returns the number of commits before timestamp' do
- options = { ref: 'feature', limit: nil, before: Time.iso8601('2015-03-03T20:15:01+00:00') }
+ options = { ref: 'feature', before: Time.iso8601('2015-03-03T20:15:01+00:00') }
expect(repository.count_commits(options)).to eq(9)
end
end
+ context 'with max_count' do
+ it 'returns the number of commits with path ' do
+ options = { ref: 'master', max_count: 5 }
+
+ expect(repository.count_commits(options)).to eq(5)
+ end
+ end
+
context 'with path' do
it 'returns the number of commits with path ' do
- options = { ref: 'master', limit: nil, path: "encoding" }
+ options = { ref: 'master', path: 'encoding' }
expect(repository.count_commits(options)).to eq(2)
end
end
+
+ context 'with option :from and option :to' do
+ it 'returns the number of commits ahead for fix-mode..fix-blob-path' do
+ options = { from: 'fix-mode', to: 'fix-blob-path' }
+
+ expect(repository.count_commits(options)).to eq(2)
+ end
+
+ it 'returns the number of commits ahead for fix-blob-path..fix-mode' do
+ options = { from: 'fix-blob-path', to: 'fix-mode' }
+
+ expect(repository.count_commits(options)).to eq(1)
+ end
+
+ context 'with option :left_right' do
+ it 'returns the number of commits for fix-mode...fix-blob-path' do
+ options = { from: 'fix-mode', to: 'fix-blob-path', left_right: true }
+
+ expect(repository.count_commits(options)).to eq([1, 2])
+ end
+
+ context 'with max_count' do
+ it 'returns the number of commits with path ' do
+ options = { from: 'fix-mode', to: 'fix-blob-path', left_right: true, max_count: 1 }
+
+ expect(repository.count_commits(options)).to eq([1, 1])
+ end
+ end
+ end
+ end
+
+ context 'with max_count' do
+ it 'returns the number of commits up to the passed limit' do
+ options = { ref: 'master', max_count: 10, after: Time.iso8601('2013-03-03T20:15:01+00:00') }
+
+ expect(repository.count_commits(options)).to eq(10)
+ end
+ end
end
context 'when Gitaly count_commits feature is enabled' do
@@ -1625,7 +1712,7 @@ describe Gitlab::Git::Repository, seed_helper: true do
let(:branch_name) { "to-be-deleted-soon" }
before do
- project.team << [user, :developer]
+ project.add_developer(user)
repository.create_branch(branch_name)
end
@@ -1662,21 +1749,6 @@ describe Gitlab::Git::Repository, seed_helper: true do
end
end
- describe '#fetch_remote_without_shell' do
- let(:git_path) { Gitlab.config.git.bin_path }
- let(:remote_name) { 'my_remote' }
-
- subject { repository.fetch_remote_without_shell(remote_name) }
-
- it 'fetches the remote and returns true if the command was successful' do
- expect(repository).to receive(:popen)
- .with(%W(#{git_path} fetch #{remote_name}), repository.path, {})
- .and_return(['', 0])
-
- expect(subject).to be(true)
- end
- end
-
describe '#merge' do
let(:repository) do
Gitlab::Git::Repository.new('default', TEST_MUTABLE_REPO_PATH, '')
@@ -1704,6 +1776,20 @@ describe Gitlab::Git::Repository, seed_helper: true do
expect(result.repo_created).to eq(false)
expect(result.branch_created).to eq(false)
end
+
+ it 'returns nil if there was a concurrent branch update' do
+ concurrent_update_id = '33f3729a45c02fc67d00adb1b8bca394b0e761d9'
+ result = repository.merge(user, source_sha, target_branch, 'Test merge') do
+ # This ref update should make the merge fail
+ repository.write_ref(Gitlab::Git::BRANCH_REF_PREFIX + target_branch, concurrent_update_id)
+ end
+
+ # This 'nil' signals that the merge was not applied
+ expect(result).to be_nil
+
+ # Our concurrent ref update should not have been undone
+ expect(repository.find_branch(target_branch).target).to eq(concurrent_update_id)
+ end
end
context 'with gitaly' do
@@ -1813,6 +1899,166 @@ describe Gitlab::Git::Repository, seed_helper: true do
end
end
+ describe 'remotes' do
+ let(:repository) do
+ Gitlab::Git::Repository.new('default', TEST_MUTABLE_REPO_PATH, '')
+ end
+ let(:remote_name) { 'my-remote' }
+
+ after do
+ ensure_seeds
+ end
+
+ describe '#add_remote' do
+ let(:url) { 'http://my-repo.git' }
+ let(:mirror_refmap) { '+refs/*:refs/*' }
+
+ it 'creates a new remote via Gitaly' do
+ expect_any_instance_of(Gitlab::GitalyClient::RemoteService)
+ .to receive(:add_remote).with(remote_name, url, mirror_refmap)
+
+ repository.add_remote(remote_name, url, mirror_refmap: mirror_refmap)
+ end
+
+ context 'with Gitaly disabled', :skip_gitaly_mock do
+ it 'creates a new remote via Rugged' do
+ expect_any_instance_of(Rugged::RemoteCollection).to receive(:create)
+ .with(remote_name, url)
+ expect_any_instance_of(Rugged::Config).to receive(:[]=)
+ .with("remote.#{remote_name}.mirror", true)
+ expect_any_instance_of(Rugged::Config).to receive(:[]=)
+ .with("remote.#{remote_name}.prune", true)
+ expect_any_instance_of(Rugged::Config).to receive(:[]=)
+ .with("remote.#{remote_name}.fetch", mirror_refmap)
+
+ repository.add_remote(remote_name, url, mirror_refmap: mirror_refmap)
+ end
+ end
+ end
+
+ describe '#remove_remote' do
+ it 'removes the remote via Gitaly' do
+ expect_any_instance_of(Gitlab::GitalyClient::RemoteService)
+ .to receive(:remove_remote).with(remote_name)
+
+ repository.remove_remote(remote_name)
+ end
+
+ context 'with Gitaly disabled', :skip_gitaly_mock do
+ it 'removes the remote via Rugged' do
+ expect_any_instance_of(Rugged::RemoteCollection).to receive(:delete)
+ .with(remote_name)
+
+ repository.remove_remote(remote_name)
+ end
+ end
+ end
+ end
+
+ describe '#gitlab_projects' do
+ subject { repository.gitlab_projects }
+
+ it { expect(subject.shard_path).to eq(storage_path) }
+ it { expect(subject.repository_relative_path).to eq(repository.relative_path) }
+ end
+
+ context 'gitlab_projects commands' do
+ let(:gitlab_projects) { repository.gitlab_projects }
+ let(:timeout) { Gitlab.config.gitlab_shell.git_timeout }
+
+ describe '#push_remote_branches' do
+ subject do
+ repository.push_remote_branches('downstream-remote', ['master'])
+ end
+
+ it 'executes the command' do
+ expect(gitlab_projects).to receive(:push_branches)
+ .with('downstream-remote', timeout, true, ['master'])
+ .and_return(true)
+
+ is_expected.to be_truthy
+ end
+
+ it 'raises an error if the command fails' do
+ allow(gitlab_projects).to receive(:output) { 'error' }
+ expect(gitlab_projects).to receive(:push_branches)
+ .with('downstream-remote', timeout, true, ['master'])
+ .and_return(false)
+
+ expect { subject }.to raise_error(Gitlab::Git::CommandError, 'error')
+ end
+ end
+
+ describe '#delete_remote_branches' do
+ subject do
+ repository.delete_remote_branches('downstream-remote', ['master'])
+ end
+
+ it 'executes the command' do
+ expect(gitlab_projects).to receive(:delete_remote_branches)
+ .with('downstream-remote', ['master'])
+ .and_return(true)
+
+ is_expected.to be_truthy
+ end
+
+ it 'raises an error if the command fails' do
+ allow(gitlab_projects).to receive(:output) { 'error' }
+ expect(gitlab_projects).to receive(:delete_remote_branches)
+ .with('downstream-remote', ['master'])
+ .and_return(false)
+
+ expect { subject }.to raise_error(Gitlab::Git::CommandError, 'error')
+ end
+ end
+
+ describe '#delete_remote_branches' do
+ subject do
+ repository.delete_remote_branches('downstream-remote', ['master'])
+ end
+
+ it 'executes the command' do
+ expect(gitlab_projects).to receive(:delete_remote_branches)
+ .with('downstream-remote', ['master'])
+ .and_return(true)
+
+ is_expected.to be_truthy
+ end
+
+ it 'raises an error if the command fails' do
+ allow(gitlab_projects).to receive(:output) { 'error' }
+ expect(gitlab_projects).to receive(:delete_remote_branches)
+ .with('downstream-remote', ['master'])
+ .and_return(false)
+
+ expect { subject }.to raise_error(Gitlab::Git::CommandError, 'error')
+ end
+ end
+
+ describe '#delete_remote_branches' do
+ subject do
+ repository.delete_remote_branches('downstream-remote', ['master'])
+ end
+
+ it 'executes the command' do
+ expect(gitlab_projects).to receive(:delete_remote_branches)
+ .with('downstream-remote', ['master'])
+ .and_return(true)
+
+ is_expected.to be_truthy
+ end
+
+ it 'raises an error if the command fails' do
+ allow(gitlab_projects).to receive(:output) { 'error' }
+ expect(gitlab_projects).to receive(:delete_remote_branches)
+ .with('downstream-remote', ['master'])
+ .and_return(false)
+
+ expect { subject }.to raise_error(Gitlab::Git::CommandError, 'error')
+ end
+ end
+ end
+
def create_remote_branch(repository, remote_name, branch_name, source_branch_name)
source_branch = repository.branches.find { |branch| branch.name == source_branch_name }
rugged = repository.rugged
diff --git a/spec/lib/gitlab/git_access_spec.rb b/spec/lib/gitlab/git_access_spec.rb
index c9643c5da47..4290fbb0087 100644
--- a/spec/lib/gitlab/git_access_spec.rb
+++ b/spec/lib/gitlab/git_access_spec.rb
@@ -193,7 +193,15 @@ describe Gitlab::GitAccess do
let(:actor) { build(:rsa_deploy_key_2048, user: user) }
end
- describe '#check_project_moved!' do
+ shared_examples 'check_project_moved' do
+ it 'enqueues a redirected message' do
+ push_access_check
+
+ expect(Gitlab::Checks::ProjectMoved.fetch_redirect_message(user.id, project.id)).not_to be_nil
+ end
+ end
+
+ describe '#check_project_moved!', :clean_gitlab_redis_shared_state do
before do
project.add_master(user)
end
@@ -207,7 +215,40 @@ describe Gitlab::GitAccess do
end
end
- context 'when a redirect was followed to find the project' do
+ context 'when a permanent redirect and ssh protocol' do
+ let(:redirected_path) { 'some/other-path' }
+
+ before do
+ allow_any_instance_of(Gitlab::Checks::ProjectMoved).to receive(:permanent_redirect?).and_return(true)
+ end
+
+ it 'allows push and pull access' do
+ aggregate_failures do
+ expect { push_access_check }.not_to raise_error
+ end
+ end
+
+ it_behaves_like 'check_project_moved'
+ end
+
+ context 'with a permanent redirect and http protocol' do
+ let(:redirected_path) { 'some/other-path' }
+ let(:protocol) { 'http' }
+
+ before do
+ allow_any_instance_of(Gitlab::Checks::ProjectMoved).to receive(:permanent_redirect?).and_return(true)
+ end
+
+ it 'allows_push and pull access' do
+ aggregate_failures do
+ expect { push_access_check }.not_to raise_error
+ end
+ end
+
+ it_behaves_like 'check_project_moved'
+ end
+
+ context 'with a temporal redirect and ssh protocol' do
let(:redirected_path) { 'some/other-path' }
it 'blocks push and pull access' do
@@ -219,23 +260,22 @@ describe Gitlab::GitAccess do
expect { pull_access_check }.to raise_error(described_class::ProjectMovedError, /git remote set-url origin #{project.ssh_url_to_repo}/)
end
end
+ end
- context 'http protocol' do
- let(:protocol) { 'http' }
+ context 'with a temporal redirect and http protocol' do
+ let(:redirected_path) { 'some/other-path' }
+ let(:protocol) { 'http' }
- it 'includes the path to the project using HTTP' do
- aggregate_failures do
- expect { push_access_check }.to raise_error(described_class::ProjectMovedError, /git remote set-url origin #{project.http_url_to_repo}/)
- expect { pull_access_check }.to raise_error(described_class::ProjectMovedError, /git remote set-url origin #{project.http_url_to_repo}/)
- end
- end
+ it 'does not allow to push and pull access' do
+ expect { push_access_check }.to raise_error(described_class::ProjectMovedError, /git remote set-url origin #{project.http_url_to_repo}/)
+ expect { pull_access_check }.to raise_error(described_class::ProjectMovedError, /git remote set-url origin #{project.http_url_to_repo}/)
end
end
end
describe '#check_command_disabled!' do
before do
- project.team << [user, :master]
+ project.add_master(user)
end
context 'over http' do
@@ -364,7 +404,7 @@ describe Gitlab::GitAccess do
describe 'reporter user' do
before do
- project.team << [user, :reporter]
+ project.add_reporter(user)
end
context 'pull code' do
@@ -377,7 +417,7 @@ describe Gitlab::GitAccess do
context 'when member of the project' do
before do
- project.team << [user, :reporter]
+ project.add_reporter(user)
end
context 'pull code' do
@@ -457,7 +497,7 @@ describe Gitlab::GitAccess do
if role == :admin
user.update_attribute(:admin, true)
else
- project.team << [user, role]
+ project.add_role(user, role)
end
aggregate_failures do
@@ -618,7 +658,7 @@ describe Gitlab::GitAccess do
context 'when project is authorized' do
before do
- project.team << [user, :reporter]
+ project.add_reporter(user)
end
it { expect { push_access_check }.to raise_unauthorized(described_class::ERROR_MESSAGES[:upload]) }
diff --git a/spec/lib/gitlab/git_access_wiki_spec.rb b/spec/lib/gitlab/git_access_wiki_spec.rb
index 1056074264a..186b2d9279d 100644
--- a/spec/lib/gitlab/git_access_wiki_spec.rb
+++ b/spec/lib/gitlab/git_access_wiki_spec.rb
@@ -18,7 +18,7 @@ describe Gitlab::GitAccessWiki do
context 'when user can :create_wiki' do
before do
create(:protected_branch, name: 'master', project: project)
- project.team << [user, :developer]
+ project.add_developer(user)
end
subject { access.check('git-receive-pack', changes) }
@@ -41,7 +41,7 @@ describe Gitlab::GitAccessWiki do
subject { access.check('git-upload-pack', '_any') }
before do
- project.team << [user, :developer]
+ project.add_developer(user)
end
context 'when wiki feature is enabled' do
diff --git a/spec/lib/gitlab/gitaly_client/conflicts_service_spec.rb b/spec/lib/gitlab/gitaly_client/conflicts_service_spec.rb
new file mode 100644
index 00000000000..b9641de7eda
--- /dev/null
+++ b/spec/lib/gitlab/gitaly_client/conflicts_service_spec.rb
@@ -0,0 +1,90 @@
+require 'spec_helper'
+
+describe Gitlab::GitalyClient::ConflictsService do
+ let(:project) { create(:project, :repository) }
+ let(:target_project) { create(:project, :repository) }
+ let(:source_repository) { project.repository.raw }
+ let(:target_repository) { target_project.repository.raw }
+ let(:target_gitaly_repository) { target_repository.gitaly_repository }
+ let(:our_commit_oid) { 'f00' }
+ let(:their_commit_oid) { 'f44' }
+ let(:client) do
+ described_class.new(target_repository, our_commit_oid, their_commit_oid)
+ end
+
+ describe '#list_conflict_files' do
+ let(:request) do
+ Gitaly::ListConflictFilesRequest.new(
+ repository: target_gitaly_repository, our_commit_oid: our_commit_oid,
+ their_commit_oid: their_commit_oid
+ )
+ end
+ let(:our_path) { 'our/path' }
+ let(:their_path) { 'their/path' }
+ let(:our_mode) { 0744 }
+ let(:header) do
+ double(repository: target_gitaly_repository, commit_oid: our_commit_oid,
+ our_path: our_path, our_mode: 0744, their_path: their_path)
+ end
+ let(:response) do
+ [
+ double(files: [double(header: header), double(content: 'foo', header: nil)]),
+ double(files: [double(content: 'bar', header: nil)])
+ ]
+ end
+ let(:file) { subject[0] }
+
+ subject { client.list_conflict_files }
+
+ it 'sends an RPC request' do
+ expect_any_instance_of(Gitaly::ConflictsService::Stub).to receive(:list_conflict_files)
+ .with(request, kind_of(Hash)).and_return([])
+
+ subject
+ end
+
+ it 'forms a Gitlab::Git::ConflictFile collection from the response' do
+ allow_any_instance_of(Gitaly::ConflictsService::Stub).to receive(:list_conflict_files)
+ .with(request, kind_of(Hash)).and_return(response)
+
+ expect(subject.size).to be(1)
+ expect(file.content).to eq('foobar')
+ expect(file.their_path).to eq(their_path)
+ expect(file.our_path).to eq(our_path)
+ expect(file.our_mode).to be(our_mode)
+ expect(file.repository).to eq(target_repository)
+ expect(file.commit_oid).to eq(our_commit_oid)
+ end
+ end
+
+ describe '#resolve_conflicts' do
+ let(:user) { create(:user) }
+ let(:files) do
+ [{ old_path: 'some/path', new_path: 'some/path', content: '' }]
+ end
+ let(:source_branch) { 'master' }
+ let(:target_branch) { 'feature' }
+ let(:commit_message) { 'Solving conflicts' }
+ let(:resolution) do
+ Gitlab::Git::Conflict::Resolution.new(user, files, commit_message)
+ end
+
+ subject do
+ client.resolve_conflicts(source_repository, resolution, source_branch, target_branch)
+ end
+
+ it 'sends an RPC request' do
+ expect_any_instance_of(Gitaly::ConflictsService::Stub).to receive(:resolve_conflicts)
+ .with(kind_of(Enumerator), kind_of(Hash)).and_return(double(resolution_error: ""))
+
+ subject
+ end
+
+ it 'raises a relevant exception if resolution_error is present' do
+ expect_any_instance_of(Gitaly::ConflictsService::Stub).to receive(:resolve_conflicts)
+ .with(kind_of(Enumerator), kind_of(Hash)).and_return(double(resolution_error: "something happened"))
+
+ expect { subject }.to raise_error(Gitlab::Git::Conflict::Resolver::ResolutionError)
+ end
+ end
+end
diff --git a/spec/lib/gitlab/gitaly_client/remote_service_spec.rb b/spec/lib/gitlab/gitaly_client/remote_service_spec.rb
new file mode 100644
index 00000000000..9d540446532
--- /dev/null
+++ b/spec/lib/gitlab/gitaly_client/remote_service_spec.rb
@@ -0,0 +1,47 @@
+require 'spec_helper'
+
+describe Gitlab::GitalyClient::RemoteService do
+ let(:project) { create(:project) }
+ let(:storage_name) { project.repository_storage }
+ let(:relative_path) { project.disk_path + '.git' }
+ let(:remote_name) { 'my-remote' }
+ let(:client) { described_class.new(project.repository) }
+
+ describe '#add_remote' do
+ let(:url) { 'http://my-repo.git' }
+ let(:mirror_refmap) { :all_refs }
+
+ it 'sends an add_remote message' do
+ expect_any_instance_of(Gitaly::RemoteService::Stub)
+ .to receive(:add_remote)
+ .with(gitaly_request_with_path(storage_name, relative_path), kind_of(Hash))
+ .and_return(double(:add_remote_response))
+
+ client.add_remote(remote_name, url, mirror_refmap)
+ end
+ end
+
+ describe '#remove_remote' do
+ it 'sends an remove_remote message and returns the result value' do
+ expect_any_instance_of(Gitaly::RemoteService::Stub)
+ .to receive(:remove_remote)
+ .with(gitaly_request_with_path(storage_name, relative_path), kind_of(Hash))
+ .and_return(double(result: true))
+
+ expect(client.remove_remote(remote_name)).to be(true)
+ end
+ end
+
+ describe '#fetch_internal_remote' do
+ let(:remote_repository) { Gitlab::Git::Repository.new('default', TEST_MUTABLE_REPO_PATH, '') }
+
+ it 'sends an fetch_internal_remote message and returns the result value' do
+ expect_any_instance_of(Gitaly::RemoteService::Stub)
+ .to receive(:fetch_internal_remote)
+ .with(gitaly_request_with_path(storage_name, relative_path), kind_of(Hash))
+ .and_return(double(result: true))
+
+ expect(client.fetch_internal_remote(remote_repository)).to be(true)
+ end
+ end
+end
diff --git a/spec/lib/gitlab/gitaly_client_spec.rb b/spec/lib/gitlab/gitaly_client_spec.rb
index a871ed0df0e..309b7338ef0 100644
--- a/spec/lib/gitlab/gitaly_client_spec.rb
+++ b/spec/lib/gitlab/gitaly_client_spec.rb
@@ -38,20 +38,6 @@ describe Gitlab::GitalyClient, skip_gitaly_mock: true do
end
end
- describe 'encode' do
- [
- [nil, ""],
- ["", ""],
- [" ", " "],
- %w(a1 a1),
- ["编码", "\xE7\xBC\x96\xE7\xA0\x81".b]
- ].each do |input, result|
- it "encodes #{input.inspect} to #{result.inspect}" do
- expect(described_class.encode(input)).to eq result
- end
- end
- end
-
describe 'allow_n_plus_1_calls' do
context 'when RequestStore is enabled', :request_store do
it 'returns the result of the allow_n_plus_1_calls block' do
diff --git a/spec/lib/gitlab/github_import/importer/repository_importer_spec.rb b/spec/lib/gitlab/github_import/importer/repository_importer_spec.rb
index 168e5d07504..46a57e08963 100644
--- a/spec/lib/gitlab/github_import/importer/repository_importer_spec.rb
+++ b/spec/lib/gitlab/github_import/importer/repository_importer_spec.rb
@@ -70,7 +70,7 @@ describe Gitlab::GithubImport::Importer::RepositoryImporter do
describe '#execute' do
it 'imports the repository and wiki' do
- expect(repository)
+ expect(project)
.to receive(:empty_repo?)
.and_return(true)
@@ -93,7 +93,7 @@ describe Gitlab::GithubImport::Importer::RepositoryImporter do
end
it 'does not import the repository if it already exists' do
- expect(repository)
+ expect(project)
.to receive(:empty_repo?)
.and_return(false)
@@ -115,7 +115,7 @@ describe Gitlab::GithubImport::Importer::RepositoryImporter do
end
it 'does not import the wiki if it is disabled' do
- expect(repository)
+ expect(project)
.to receive(:empty_repo?)
.and_return(true)
@@ -137,7 +137,7 @@ describe Gitlab::GithubImport::Importer::RepositoryImporter do
end
it 'does not import the wiki if the repository could not be imported' do
- expect(repository)
+ expect(project)
.to receive(:empty_repo?)
.and_return(true)
diff --git a/spec/lib/gitlab/google_code_import/importer_spec.rb b/spec/lib/gitlab/google_code_import/importer_spec.rb
index 798ea0bac58..017facd0f5e 100644
--- a/spec/lib/gitlab/google_code_import/importer_spec.rb
+++ b/spec/lib/gitlab/google_code_import/importer_spec.rb
@@ -15,7 +15,7 @@ describe Gitlab::GoogleCodeImport::Importer do
subject { described_class.new(project) }
before do
- project.team << [project.creator, :master]
+ project.add_master(project.creator)
project.create_import_data(data: import_data)
end
diff --git a/spec/lib/gitlab/identifier_spec.rb b/spec/lib/gitlab/identifier_spec.rb
index cfaeb1f0d4f..0385dd762c2 100644
--- a/spec/lib/gitlab/identifier_spec.rb
+++ b/spec/lib/gitlab/identifier_spec.rb
@@ -70,6 +70,10 @@ describe Gitlab::Identifier do
expect(identifier.identify_using_commit(project, '123')).to eq(user)
end
end
+
+ it 'returns nil if the project & ref are not present' do
+ expect(identifier.identify_using_commit(nil, nil)).to be_nil
+ end
end
describe '#identify_using_user' do
diff --git a/spec/lib/gitlab/import_export/project.json b/spec/lib/gitlab/import_export/project.json
index f0752649121..7580b62cfc0 100644
--- a/spec/lib/gitlab/import_export/project.json
+++ b/spec/lib/gitlab/import_export/project.json
@@ -6465,78 +6465,100 @@
}
}
],
- "statuses": [
- {
- "id": 71,
- "project_id": 5,
- "status": "failed",
- "finished_at": "2016-03-29T06:28:12.630Z",
- "trace": null,
- "created_at": "2016-03-22T15:20:35.772Z",
- "updated_at": "2016-03-29T06:28:12.634Z",
- "started_at": null,
- "runner_id": null,
- "coverage": null,
- "commit_id": 36,
- "commands": "$ build command",
- "job_id": null,
- "name": "test build 1",
- "deploy": false,
- "options": null,
- "allow_failure": false,
- "stage": "test",
- "trigger_request_id": null,
- "stage_idx": 1,
- "tag": null,
- "ref": "master",
- "user_id": null,
- "target_url": null,
- "description": null,
- "artifacts_file": {
- "url": null
- },
- "artifacts_metadata": {
- "url": null
- },
- "erased_by_id": null,
- "erased_at": null,
- "type": "Ci::Build",
- "token": "abcd"
- },
- {
- "id": 72,
- "project_id": 5,
- "status": "success",
- "finished_at": null,
- "trace": "Porro ea qui ut dolores. Labore ab nemo explicabo aspernatur quis voluptates corporis. Et quasi delectus est sit aperiam perspiciatis asperiores. Repudiandae cum aut consectetur accusantium officia sunt.\n\nQuidem dolore iusto quaerat ut aut inventore et molestiae. Libero voluptates atque nemo qui. Nulla temporibus ipsa similique facere.\n\nAliquam ipsam perferendis qui fugit accusantium omnis id voluptatum. Dignissimos aliquid dicta eos voluptatem assumenda quia. Sed autem natus unde dolor et non nisi et. Consequuntur nihil consequatur rerum est.\n\nSimilique neque est iste ducimus qui fuga cupiditate. Libero autem est aut fuga. Consectetur natus quis non ducimus ut dolore. Magni voluptatibus eius et maxime aut.\n\nAd officiis tempore voluptate vitae corrupti explicabo labore est. Consequatur expedita et sunt nihil aut. Deleniti porro iusto molestiae et beatae.\n\nDeleniti modi nulla qui et labore sequi corrupti. Qui voluptatem assumenda eum cupiditate et. Nesciunt ipsam ut ea possimus eum. Consectetur quidem suscipit atque dolore itaque voluptatibus et cupiditate.",
- "created_at": "2016-03-22T15:20:35.777Z",
- "updated_at": "2016-03-22T15:20:35.777Z",
- "started_at": null,
- "runner_id": null,
- "coverage": null,
- "commit_id": 36,
- "commands": "$ build command",
- "job_id": null,
- "name": "test build 2",
- "deploy": false,
- "options": null,
- "allow_failure": false,
- "stage": "test",
- "trigger_request_id": null,
- "stage_idx": 1,
- "tag": null,
- "ref": "master",
- "user_id": null,
- "target_url": null,
- "description": null,
- "artifacts_file": {
- "url": "/Users/Test/Test/gitlab-development-kit/gitlab/shared/artifacts/2016_03/5/72/p5_build_artifacts.zip"
- },
- "artifacts_metadata": {
- "url": "/Users/Test/Test/gitlab-development-kit/gitlab/shared/artifacts/2016_03/5/72/p5_build_artifacts_metadata.gz"
- },
- "erased_by_id": null,
- "erased_at": null
+ "stages": [
+ {
+ "id": 11,
+ "project_id": 5,
+ "pipeline_id": 36,
+ "name": "test",
+ "status": 1,
+ "created_at": "2016-03-22T15:44:44.772Z",
+ "updated_at": "2016-03-29T06:44:44.634Z",
+ "statuses": [
+ {
+ "id": 71,
+ "project_id": 5,
+ "status": "failed",
+ "finished_at": "2016-03-29T06:28:12.630Z",
+ "trace": null,
+ "created_at": "2016-03-22T15:20:35.772Z",
+ "updated_at": "2016-03-29T06:28:12.634Z",
+ "started_at": null,
+ "runner_id": null,
+ "coverage": null,
+ "commit_id": 36,
+ "commands": "$ build command",
+ "job_id": null,
+ "name": "test build 1",
+ "deploy": false,
+ "options": null,
+ "allow_failure": false,
+ "stage": "test",
+ "trigger_request_id": null,
+ "stage_idx": 1,
+ "stage_id": 11,
+ "tag": null,
+ "ref": "master",
+ "user_id": null,
+ "target_url": null,
+ "description": null,
+ "artifacts_file": {
+ "url": null
+ },
+ "artifacts_metadata": {
+ "url": null
+ },
+ "erased_by_id": null,
+ "erased_at": null,
+ "type": "Ci::Build",
+ "token": "abcd"
+ },
+ {
+ "id": 72,
+ "project_id": 5,
+ "status": "success",
+ "finished_at": null,
+ "trace": "Porro ea qui ut dolores. Labore ab nemo explicabo aspernatur quis voluptates corporis. Et quasi delectus est sit aperiam perspiciatis asperiores. Repudiandae cum aut consectetur accusantium officia sunt.\n\nQuidem dolore iusto quaerat ut aut inventore et molestiae. Libero voluptates atque nemo qui. Nulla temporibus ipsa similique facere.\n\nAliquam ipsam perferendis qui fugit accusantium omnis id voluptatum. Dignissimos aliquid dicta eos voluptatem assumenda quia. Sed autem natus unde dolor et non nisi et. Consequuntur nihil consequatur rerum est.\n\nSimilique neque est iste ducimus qui fuga cupiditate. Libero autem est aut fuga. Consectetur natus quis non ducimus ut dolore. Magni voluptatibus eius et maxime aut.\n\nAd officiis tempore voluptate vitae corrupti explicabo labore est. Consequatur expedita et sunt nihil aut. Deleniti porro iusto molestiae et beatae.\n\nDeleniti modi nulla qui et labore sequi corrupti. Qui voluptatem assumenda eum cupiditate et. Nesciunt ipsam ut ea possimus eum. Consectetur quidem suscipit atque dolore itaque voluptatibus et cupiditate.",
+ "created_at": "2016-03-22T15:20:35.777Z",
+ "updated_at": "2016-03-22T15:20:35.777Z",
+ "started_at": null,
+ "runner_id": null,
+ "coverage": null,
+ "commit_id": 36,
+ "commands": "$ deploy command",
+ "job_id": null,
+ "name": "test build 2",
+ "deploy": false,
+ "options": null,
+ "allow_failure": false,
+ "stage": "deploy",
+ "trigger_request_id": null,
+ "stage_idx": 1,
+ "stage_id": 12,
+ "tag": null,
+ "ref": "master",
+ "user_id": null,
+ "target_url": null,
+ "description": null,
+ "artifacts_file": {
+ "url": "/Users/Test/Test/gitlab-development-kit/gitlab/shared/artifacts/2016_03/5/72/p5_build_artifacts.zip"
+ },
+ "artifacts_metadata": {
+ "url": "/Users/Test/Test/gitlab-development-kit/gitlab/shared/artifacts/2016_03/5/72/p5_build_artifacts_metadata.gz"
+ },
+ "erased_by_id": null,
+ "erased_at": null
+ }
+ ]
+ },
+ {
+ "id": 12,
+ "project_id": 5,
+ "pipeline_id": 36,
+ "name": "deploy",
+ "status": 2,
+ "created_at": "2016-03-22T15:45:45.772Z",
+ "updated_at": "2016-03-29T06:45:45.634Z"
}
]
},
@@ -6556,76 +6578,87 @@
"started_at": null,
"finished_at": null,
"duration": null,
- "statuses": [
- {
- "id": 74,
- "project_id": 5,
- "status": "success",
- "finished_at": null,
- "trace": "Ad ut quod repudiandae iste dolor doloribus. Adipisci consequuntur deserunt omnis quasi eveniet et sed fugit. Aut nemo omnis molestiae impedit ex consequatur ducimus. Voluptatum exercitationem quia aut est et hic dolorem.\n\nQuasi repellendus et eaque magni eum facilis. Dolorem aperiam nam nihil pariatur praesentium ad aliquam. Commodi enim et eos tenetur. Odio voluptatibus laboriosam mollitia rerum exercitationem magnam consequuntur. Tenetur ea vel eum corporis.\n\nVoluptatibus optio in aliquid est voluptates. Ad a ut ab placeat vero blanditiis. Earum aspernatur quia beatae expedita voluptatem dignissimos provident. Quis minima id nemo ut aut est veritatis provident.\n\nRerum voluptatem quidem eius maiores magnam veniam. Voluptatem aperiam aut voluptate et nulla deserunt voluptas. Quaerat aut accusantium laborum est dolorem architecto reiciendis. Aliquam asperiores doloribus omnis maxime enim nesciunt. Eum aut rerum repellendus debitis et ut eius.\n\nQuaerat assumenda ea sit consequatur autem in. Cum eligendi voluptatem quo sed. Ut fuga iusto cupiditate autem sint.\n\nOfficia totam officiis architecto corporis molestiae amet ut. Tempora sed dolorum rerum omnis voluptatem accusantium sit eum. Quia debitis ipsum quidem aliquam inventore sunt consequatur qui.",
- "created_at": "2016-03-22T15:20:35.846Z",
- "updated_at": "2016-03-22T15:20:35.846Z",
- "started_at": null,
- "runner_id": null,
- "coverage": null,
- "commit_id": 37,
- "commands": "$ build command",
- "job_id": null,
- "name": "test build 2",
- "deploy": false,
- "options": null,
- "allow_failure": false,
- "stage": "test",
- "trigger_request_id": null,
- "stage_idx": 1,
- "tag": null,
- "ref": "master",
- "user_id": null,
- "target_url": null,
- "description": null,
- "artifacts_file": {
- "url": "/Users/Test/Test/gitlab-development-kit/gitlab/shared/artifacts/2016_03/5/74/p5_build_artifacts.zip"
- },
- "artifacts_metadata": {
- "url": "/Users/Test/Test/gitlab-development-kit/gitlab/shared/artifacts/2016_03/5/74/p5_build_artifacts_metadata.gz"
- },
- "erased_by_id": null,
- "erased_at": null
- },
- {
- "id": 73,
- "project_id": 5,
- "status": "canceled",
- "finished_at": null,
- "trace": null,
- "created_at": "2016-03-22T15:20:35.842Z",
- "updated_at": "2016-03-22T15:20:35.842Z",
- "started_at": null,
- "runner_id": null,
- "coverage": null,
- "commit_id": 37,
- "commands": "$ build command",
- "job_id": null,
- "name": "test build 1",
- "deploy": false,
- "options": null,
- "allow_failure": false,
- "stage": "test",
- "trigger_request_id": null,
- "stage_idx": 1,
- "tag": null,
- "ref": "master",
- "user_id": null,
- "target_url": null,
- "description": null,
- "artifacts_file": {
- "url": null
- },
- "artifacts_metadata": {
- "url": null
- },
- "erased_by_id": null,
- "erased_at": null
+ "stages": [
+ {
+ "id": 21,
+ "project_id": 5,
+ "pipeline_id": 37,
+ "name": "test",
+ "status": 1,
+ "created_at": "2016-03-22T15:44:44.772Z",
+ "updated_at": "2016-03-29T06:44:44.634Z",
+ "statuses": [
+ {
+ "id": 74,
+ "project_id": 5,
+ "status": "success",
+ "finished_at": null,
+ "trace": "Ad ut quod repudiandae iste dolor doloribus. Adipisci consequuntur deserunt omnis quasi eveniet et sed fugit. Aut nemo omnis molestiae impedit ex consequatur ducimus. Voluptatum exercitationem quia aut est et hic dolorem.\n\nQuasi repellendus et eaque magni eum facilis. Dolorem aperiam nam nihil pariatur praesentium ad aliquam. Commodi enim et eos tenetur. Odio voluptatibus laboriosam mollitia rerum exercitationem magnam consequuntur. Tenetur ea vel eum corporis.\n\nVoluptatibus optio in aliquid est voluptates. Ad a ut ab placeat vero blanditiis. Earum aspernatur quia beatae expedita voluptatem dignissimos provident. Quis minima id nemo ut aut est veritatis provident.\n\nRerum voluptatem quidem eius maiores magnam veniam. Voluptatem aperiam aut voluptate et nulla deserunt voluptas. Quaerat aut accusantium laborum est dolorem architecto reiciendis. Aliquam asperiores doloribus omnis maxime enim nesciunt. Eum aut rerum repellendus debitis et ut eius.\n\nQuaerat assumenda ea sit consequatur autem in. Cum eligendi voluptatem quo sed. Ut fuga iusto cupiditate autem sint.\n\nOfficia totam officiis architecto corporis molestiae amet ut. Tempora sed dolorum rerum omnis voluptatem accusantium sit eum. Quia debitis ipsum quidem aliquam inventore sunt consequatur qui.",
+ "created_at": "2016-03-22T15:20:35.846Z",
+ "updated_at": "2016-03-22T15:20:35.846Z",
+ "started_at": null,
+ "runner_id": null,
+ "coverage": null,
+ "commit_id": 37,
+ "commands": "$ build command",
+ "job_id": null,
+ "name": "test build 2",
+ "deploy": false,
+ "options": null,
+ "allow_failure": false,
+ "stage": "test",
+ "trigger_request_id": null,
+ "stage_idx": 1,
+ "tag": null,
+ "ref": "master",
+ "user_id": null,
+ "target_url": null,
+ "description": null,
+ "artifacts_file": {
+ "url": "/Users/Test/Test/gitlab-development-kit/gitlab/shared/artifacts/2016_03/5/74/p5_build_artifacts.zip"
+ },
+ "artifacts_metadata": {
+ "url": "/Users/Test/Test/gitlab-development-kit/gitlab/shared/artifacts/2016_03/5/74/p5_build_artifacts_metadata.gz"
+ },
+ "erased_by_id": null,
+ "erased_at": null
+ },
+ {
+ "id": 73,
+ "project_id": 5,
+ "status": "canceled",
+ "finished_at": null,
+ "trace": null,
+ "created_at": "2016-03-22T15:20:35.842Z",
+ "updated_at": "2016-03-22T15:20:35.842Z",
+ "started_at": null,
+ "runner_id": null,
+ "coverage": null,
+ "commit_id": 37,
+ "commands": "$ build command",
+ "job_id": null,
+ "name": "test build 1",
+ "deploy": false,
+ "options": null,
+ "allow_failure": false,
+ "stage": "test",
+ "trigger_request_id": null,
+ "stage_idx": 1,
+ "tag": null,
+ "ref": "master",
+ "user_id": null,
+ "target_url": null,
+ "description": null,
+ "artifacts_file": {
+ "url": null
+ },
+ "artifacts_metadata": {
+ "url": null
+ },
+ "erased_by_id": null,
+ "erased_at": null
+ }
+ ]
}
]
},
@@ -6645,76 +6678,87 @@
"started_at": null,
"finished_at": null,
"duration": null,
- "statuses": [
- {
- "id": 76,
- "project_id": 5,
- "status": "success",
- "finished_at": null,
- "trace": "Et rerum quia ea cumque ut modi non. Libero eaque ipsam architecto maiores expedita deleniti. Ratione quia qui est id.\n\nQuod sit officiis sed unde inventore veniam quisquam velit. Ea harum cum quibusdam quisquam minima quo possimus non. Temporibus itaque aliquam aut rerum veritatis at.\n\nMagnam ipsum eius recusandae qui quis sit maiores eum. Et animi iusto aut itaque. Doloribus harum deleniti nobis accusantium et libero.\n\nRerum fuga perferendis magni commodi officiis id repudiandae. Consequatur ratione consequatur suscipit facilis sunt iure est dicta. Qui unde quasi facilis et quae nesciunt. Magnam iste et nobis officiis tenetur. Aspernatur quo et temporibus non in.\n\nNisi rerum velit est ad enim sint molestiae consequuntur. Quaerat nisi nesciunt quasi officiis. Possimus non blanditiis laborum quos.\n\nRerum laudantium facere animi qui. Ipsa est iusto magnam nihil. Enim omnis occaecati non dignissimos ut recusandae eum quasi. Qui maxime dolor et nemo voluptates incidunt quia.",
- "created_at": "2016-03-22T15:20:35.882Z",
- "updated_at": "2016-03-22T15:20:35.882Z",
- "started_at": null,
- "runner_id": null,
- "coverage": null,
- "commit_id": 38,
- "commands": "$ build command",
- "job_id": null,
- "name": "test build 2",
- "deploy": false,
- "options": null,
- "allow_failure": false,
- "stage": "test",
- "trigger_request_id": null,
- "stage_idx": 1,
- "tag": null,
- "ref": "master",
- "user_id": null,
- "target_url": null,
- "description": null,
- "artifacts_file": {
- "url": "/Users/Test/Test/gitlab-development-kit/gitlab/shared/artifacts/2016_03/5/76/p5_build_artifacts.zip"
- },
- "artifacts_metadata": {
- "url": "/Users/Test/Test/gitlab-development-kit/gitlab/shared/artifacts/2016_03/5/76/p5_build_artifacts_metadata.gz"
- },
- "erased_by_id": null,
- "erased_at": null
- },
- {
- "id": 75,
- "project_id": 5,
- "status": "failed",
- "finished_at": null,
- "trace": "Sed et iste recusandae dicta corporis. Sunt alias porro fugit sunt. Fugiat omnis nihil dignissimos aperiam explicabo doloremque sit aut. Harum fugit expedita quia rerum ut consequatur laboriosam aliquam.\n\nNatus libero ut ut tenetur earum. Tempora omnis autem omnis et libero dolores illum autem. Deleniti eos sunt mollitia ipsam. Cum dolor repellendus dolorum sequi officia. Ullam sunt in aut pariatur excepturi.\n\nDolor nihil debitis et est eos. Cumque eos eum saepe ducimus autem. Alias architecto consequatur aut pariatur possimus. Aut quos aut incidunt quam velit et. Quas voluptatum ad dolorum dignissimos.\n\nUt voluptates consectetur illo et. Est commodi accusantium vel quo. Eos qui fugiat soluta porro.\n\nRatione possimus alias vel maxime sint totam est repellat. Ipsum corporis eos sint voluptatem eos odit. Temporibus libero nulla harum eligendi labore similique ratione magnam. Suscipit sequi in omnis neque.\n\nLaudantium dolor amet omnis placeat mollitia aut molestiae. Aut rerum similique ipsum quod illo quas unde. Sunt aut veritatis eos omnis porro. Rem veritatis mollitia praesentium dolorem. Consequatur sequi ad cumque earum omnis quia necessitatibus.",
- "created_at": "2016-03-22T15:20:35.864Z",
- "updated_at": "2016-03-22T15:20:35.864Z",
- "started_at": null,
- "runner_id": null,
- "coverage": null,
- "commit_id": 38,
- "commands": "$ build command",
- "job_id": null,
- "name": "test build 1",
- "deploy": false,
- "options": null,
- "allow_failure": false,
- "stage": "test",
- "trigger_request_id": null,
- "stage_idx": 1,
- "tag": null,
- "ref": "master",
- "user_id": null,
- "target_url": null,
- "description": null,
- "artifacts_file": {
- "url": "/Users/Test/Test/gitlab-development-kit/gitlab/shared/artifacts/2016_03/5/75/p5_build_artifacts.zip"
- },
- "artifacts_metadata": {
- "url": "/Users/Test/Test/gitlab-development-kit/gitlab/shared/artifacts/2016_03/5/75/p5_build_artifacts_metadata.gz"
- },
- "erased_by_id": null,
- "erased_at": null
+ "stages": [
+ {
+ "id": 22,
+ "project_id": 5,
+ "pipeline_id": 38,
+ "name": "test",
+ "status": 1,
+ "created_at": "2016-03-22T15:44:44.772Z",
+ "updated_at": "2016-03-29T06:44:44.634Z",
+ "statuses": [
+ {
+ "id": 76,
+ "project_id": 5,
+ "status": "success",
+ "finished_at": null,
+ "trace": "Et rerum quia ea cumque ut modi non. Libero eaque ipsam architecto maiores expedita deleniti. Ratione quia qui est id.\n\nQuod sit officiis sed unde inventore veniam quisquam velit. Ea harum cum quibusdam quisquam minima quo possimus non. Temporibus itaque aliquam aut rerum veritatis at.\n\nMagnam ipsum eius recusandae qui quis sit maiores eum. Et animi iusto aut itaque. Doloribus harum deleniti nobis accusantium et libero.\n\nRerum fuga perferendis magni commodi officiis id repudiandae. Consequatur ratione consequatur suscipit facilis sunt iure est dicta. Qui unde quasi facilis et quae nesciunt. Magnam iste et nobis officiis tenetur. Aspernatur quo et temporibus non in.\n\nNisi rerum velit est ad enim sint molestiae consequuntur. Quaerat nisi nesciunt quasi officiis. Possimus non blanditiis laborum quos.\n\nRerum laudantium facere animi qui. Ipsa est iusto magnam nihil. Enim omnis occaecati non dignissimos ut recusandae eum quasi. Qui maxime dolor et nemo voluptates incidunt quia.",
+ "created_at": "2016-03-22T15:20:35.882Z",
+ "updated_at": "2016-03-22T15:20:35.882Z",
+ "started_at": null,
+ "runner_id": null,
+ "coverage": null,
+ "commit_id": 38,
+ "commands": "$ build command",
+ "job_id": null,
+ "name": "test build 2",
+ "deploy": false,
+ "options": null,
+ "allow_failure": false,
+ "stage": "test",
+ "trigger_request_id": null,
+ "stage_idx": 1,
+ "tag": null,
+ "ref": "master",
+ "user_id": null,
+ "target_url": null,
+ "description": null,
+ "artifacts_file": {
+ "url": "/Users/Test/Test/gitlab-development-kit/gitlab/shared/artifacts/2016_03/5/76/p5_build_artifacts.zip"
+ },
+ "artifacts_metadata": {
+ "url": "/Users/Test/Test/gitlab-development-kit/gitlab/shared/artifacts/2016_03/5/76/p5_build_artifacts_metadata.gz"
+ },
+ "erased_by_id": null,
+ "erased_at": null
+ },
+ {
+ "id": 75,
+ "project_id": 5,
+ "status": "failed",
+ "finished_at": null,
+ "trace": "Sed et iste recusandae dicta corporis. Sunt alias porro fugit sunt. Fugiat omnis nihil dignissimos aperiam explicabo doloremque sit aut. Harum fugit expedita quia rerum ut consequatur laboriosam aliquam.\n\nNatus libero ut ut tenetur earum. Tempora omnis autem omnis et libero dolores illum autem. Deleniti eos sunt mollitia ipsam. Cum dolor repellendus dolorum sequi officia. Ullam sunt in aut pariatur excepturi.\n\nDolor nihil debitis et est eos. Cumque eos eum saepe ducimus autem. Alias architecto consequatur aut pariatur possimus. Aut quos aut incidunt quam velit et. Quas voluptatum ad dolorum dignissimos.\n\nUt voluptates consectetur illo et. Est commodi accusantium vel quo. Eos qui fugiat soluta porro.\n\nRatione possimus alias vel maxime sint totam est repellat. Ipsum corporis eos sint voluptatem eos odit. Temporibus libero nulla harum eligendi labore similique ratione magnam. Suscipit sequi in omnis neque.\n\nLaudantium dolor amet omnis placeat mollitia aut molestiae. Aut rerum similique ipsum quod illo quas unde. Sunt aut veritatis eos omnis porro. Rem veritatis mollitia praesentium dolorem. Consequatur sequi ad cumque earum omnis quia necessitatibus.",
+ "created_at": "2016-03-22T15:20:35.864Z",
+ "updated_at": "2016-03-22T15:20:35.864Z",
+ "started_at": null,
+ "runner_id": null,
+ "coverage": null,
+ "commit_id": 38,
+ "commands": "$ build command",
+ "job_id": null,
+ "name": "test build 1",
+ "deploy": false,
+ "options": null,
+ "allow_failure": false,
+ "stage": "test",
+ "trigger_request_id": null,
+ "stage_idx": 1,
+ "tag": null,
+ "ref": "master",
+ "user_id": null,
+ "target_url": null,
+ "description": null,
+ "artifacts_file": {
+ "url": "/Users/Test/Test/gitlab-development-kit/gitlab/shared/artifacts/2016_03/5/75/p5_build_artifacts.zip"
+ },
+ "artifacts_metadata": {
+ "url": "/Users/Test/Test/gitlab-development-kit/gitlab/shared/artifacts/2016_03/5/75/p5_build_artifacts_metadata.gz"
+ },
+ "erased_by_id": null,
+ "erased_at": null
+ }
+ ]
}
]
},
@@ -6734,76 +6778,87 @@
"started_at": null,
"finished_at": null,
"duration": null,
- "statuses": [
- {
- "id": 78,
- "project_id": 5,
- "status": "success",
- "finished_at": null,
- "trace": "Dolorem deserunt quas quia error hic quo cum vel. Natus voluptatem cumque expedita numquam odit. Eos expedita nostrum corporis consequatur est recusandae.\n\nCulpa blanditiis rerum repudiandae alias voluptatem. Velit iusto est ullam consequatur doloribus porro. Corporis voluptas consectetur est veniam et quia quae.\n\nEt aut magni fuga nesciunt officiis molestias. Quaerat et nam necessitatibus qui rerum. Architecto quia officiis voluptatem laborum est recusandae. Quasi ducimus soluta odit necessitatibus labore numquam dignissimos. Quia facere sint temporibus inventore sunt nihil saepe dolorum.\n\nFacere dolores quis dolores a. Est minus nostrum nihil harum. Earum laborum et ipsum unde neque sit nemo. Corrupti est consequatur minima fugit. Illum voluptatem illo error ducimus officia qui debitis.\n\nDignissimos porro a autem harum aut. Aut id reprehenderit et exercitationem. Est et quisquam ipsa temporibus molestiae. Architecto natus dolore qui fugiat incidunt. Autem odit veniam excepturi et voluptatibus culpa ipsum eos.\n\nAmet quo quisquam dignissimos soluta modi dolores. Sint omnis eius optio corporis dolor. Eligendi animi porro quia placeat ut.",
- "created_at": "2016-03-22T15:20:35.927Z",
- "updated_at": "2016-03-22T15:20:35.927Z",
- "started_at": null,
- "runner_id": null,
- "coverage": null,
- "commit_id": 39,
- "commands": "$ build command",
- "job_id": null,
- "name": "test build 2",
- "deploy": false,
- "options": null,
- "allow_failure": false,
- "stage": "test",
- "trigger_request_id": null,
- "stage_idx": 1,
- "tag": null,
- "ref": "master",
- "user_id": null,
- "target_url": null,
- "description": null,
- "artifacts_file": {
- "url": "/Users/Test/Test/gitlab-development-kit/gitlab/shared/artifacts/2016_03/5/78/p5_build_artifacts.zip"
- },
- "artifacts_metadata": {
- "url": "/Users/Test/Test/gitlab-development-kit/gitlab/shared/artifacts/2016_03/5/78/p5_build_artifacts_metadata.gz"
- },
- "erased_by_id": null,
- "erased_at": null
- },
- {
- "id": 77,
- "project_id": 5,
- "status": "failed",
- "finished_at": null,
- "trace": "Rerum ut et suscipit est perspiciatis. Inventore debitis cum eius vitae. Ex incidunt id velit aut quo nisi. Laboriosam repellat deserunt eius reiciendis architecto et. Est harum quos nesciunt nisi consectetur.\n\nAlias esse omnis sint officia est consequatur in nobis. Dignissimos dolorum vel eligendi nesciunt dolores sit. Veniam mollitia ducimus et exercitationem molestiae libero sed. Atque omnis debitis laudantium voluptatibus qui. Repellendus tempore est commodi pariatur.\n\nExpedita voluptate illum est alias non. Modi nesciunt ab assumenda laborum nulla consequatur molestias doloremque. Magnam quod officia vel explicabo accusamus ut voluptatem incidunt. Rerum ut aliquid ullam saepe. Est eligendi debitis beatae blanditiis reiciendis.\n\nQui fuga sit dolores libero maiores et suscipit. Consectetur asperiores omnis minima impedit eos fugiat. Similique omnis nisi sed vero inventore ipsum aliquam exercitationem.\n\nBlanditiis magni iure dolorum omnis ratione delectus molestiae. Atque officia dolor voluptatem culpa quod. Incidunt suscipit quidem possimus veritatis non vel. Iusto aliquid et id quia quasi.\n\nVel facere velit blanditiis incidunt cupiditate sed maiores consequuntur. Quasi quia dicta consequuntur et quia voluptatem iste id. Incidunt et rerum fuga esse sint.",
- "created_at": "2016-03-22T15:20:35.905Z",
- "updated_at": "2016-03-22T15:20:35.905Z",
- "started_at": null,
- "runner_id": null,
- "coverage": null,
- "commit_id": 39,
- "commands": "$ build command",
- "job_id": null,
- "name": "test build 1",
- "deploy": false,
- "options": null,
- "allow_failure": false,
- "stage": "test",
- "trigger_request_id": null,
- "stage_idx": 1,
- "tag": null,
- "ref": "master",
- "user_id": null,
- "target_url": null,
- "description": null,
- "artifacts_file": {
- "url": "/Users/Test/Test/gitlab-development-kit/gitlab/shared/artifacts/2016_03/5/77/p5_build_artifacts.zip"
- },
- "artifacts_metadata": {
- "url": "/Users/Test/Test/gitlab-development-kit/gitlab/shared/artifacts/2016_03/5/77/p5_build_artifacts_metadata.gz"
- },
- "erased_by_id": null,
- "erased_at": null
+ "stages": [
+ {
+ "id": 23,
+ "project_id": 5,
+ "pipeline_id": 39,
+ "name": "test",
+ "status": 1,
+ "created_at": "2016-03-22T15:44:44.772Z",
+ "updated_at": "2016-03-29T06:44:44.634Z",
+ "statuses": [
+ {
+ "id": 78,
+ "project_id": 5,
+ "status": "success",
+ "finished_at": null,
+ "trace": "Dolorem deserunt quas quia error hic quo cum vel. Natus voluptatem cumque expedita numquam odit. Eos expedita nostrum corporis consequatur est recusandae.\n\nCulpa blanditiis rerum repudiandae alias voluptatem. Velit iusto est ullam consequatur doloribus porro. Corporis voluptas consectetur est veniam et quia quae.\n\nEt aut magni fuga nesciunt officiis molestias. Quaerat et nam necessitatibus qui rerum. Architecto quia officiis voluptatem laborum est recusandae. Quasi ducimus soluta odit necessitatibus labore numquam dignissimos. Quia facere sint temporibus inventore sunt nihil saepe dolorum.\n\nFacere dolores quis dolores a. Est minus nostrum nihil harum. Earum laborum et ipsum unde neque sit nemo. Corrupti est consequatur minima fugit. Illum voluptatem illo error ducimus officia qui debitis.\n\nDignissimos porro a autem harum aut. Aut id reprehenderit et exercitationem. Est et quisquam ipsa temporibus molestiae. Architecto natus dolore qui fugiat incidunt. Autem odit veniam excepturi et voluptatibus culpa ipsum eos.\n\nAmet quo quisquam dignissimos soluta modi dolores. Sint omnis eius optio corporis dolor. Eligendi animi porro quia placeat ut.",
+ "created_at": "2016-03-22T15:20:35.927Z",
+ "updated_at": "2016-03-22T15:20:35.927Z",
+ "started_at": null,
+ "runner_id": null,
+ "coverage": null,
+ "commit_id": 39,
+ "commands": "$ build command",
+ "job_id": null,
+ "name": "test build 2",
+ "deploy": false,
+ "options": null,
+ "allow_failure": false,
+ "stage": "test",
+ "trigger_request_id": null,
+ "stage_idx": 1,
+ "tag": null,
+ "ref": "master",
+ "user_id": null,
+ "target_url": null,
+ "description": null,
+ "artifacts_file": {
+ "url": "/Users/Test/Test/gitlab-development-kit/gitlab/shared/artifacts/2016_03/5/78/p5_build_artifacts.zip"
+ },
+ "artifacts_metadata": {
+ "url": "/Users/Test/Test/gitlab-development-kit/gitlab/shared/artifacts/2016_03/5/78/p5_build_artifacts_metadata.gz"
+ },
+ "erased_by_id": null,
+ "erased_at": null
+ },
+ {
+ "id": 77,
+ "project_id": 5,
+ "status": "failed",
+ "finished_at": null,
+ "trace": "Rerum ut et suscipit est perspiciatis. Inventore debitis cum eius vitae. Ex incidunt id velit aut quo nisi. Laboriosam repellat deserunt eius reiciendis architecto et. Est harum quos nesciunt nisi consectetur.\n\nAlias esse omnis sint officia est consequatur in nobis. Dignissimos dolorum vel eligendi nesciunt dolores sit. Veniam mollitia ducimus et exercitationem molestiae libero sed. Atque omnis debitis laudantium voluptatibus qui. Repellendus tempore est commodi pariatur.\n\nExpedita voluptate illum est alias non. Modi nesciunt ab assumenda laborum nulla consequatur molestias doloremque. Magnam quod officia vel explicabo accusamus ut voluptatem incidunt. Rerum ut aliquid ullam saepe. Est eligendi debitis beatae blanditiis reiciendis.\n\nQui fuga sit dolores libero maiores et suscipit. Consectetur asperiores omnis minima impedit eos fugiat. Similique omnis nisi sed vero inventore ipsum aliquam exercitationem.\n\nBlanditiis magni iure dolorum omnis ratione delectus molestiae. Atque officia dolor voluptatem culpa quod. Incidunt suscipit quidem possimus veritatis non vel. Iusto aliquid et id quia quasi.\n\nVel facere velit blanditiis incidunt cupiditate sed maiores consequuntur. Quasi quia dicta consequuntur et quia voluptatem iste id. Incidunt et rerum fuga esse sint.",
+ "created_at": "2016-03-22T15:20:35.905Z",
+ "updated_at": "2016-03-22T15:20:35.905Z",
+ "started_at": null,
+ "runner_id": null,
+ "coverage": null,
+ "commit_id": 39,
+ "commands": "$ build command",
+ "job_id": null,
+ "name": "test build 1",
+ "deploy": false,
+ "options": null,
+ "allow_failure": false,
+ "stage": "test",
+ "trigger_request_id": null,
+ "stage_idx": 1,
+ "tag": null,
+ "ref": "master",
+ "user_id": null,
+ "target_url": null,
+ "description": null,
+ "artifacts_file": {
+ "url": "/Users/Test/Test/gitlab-development-kit/gitlab/shared/artifacts/2016_03/5/77/p5_build_artifacts.zip"
+ },
+ "artifacts_metadata": {
+ "url": "/Users/Test/Test/gitlab-development-kit/gitlab/shared/artifacts/2016_03/5/77/p5_build_artifacts_metadata.gz"
+ },
+ "erased_by_id": null,
+ "erased_at": null
+ }
+ ]
}
]
},
@@ -6823,76 +6878,87 @@
"started_at": null,
"finished_at": null,
"duration": null,
- "statuses": [
- {
- "id": 79,
- "project_id": 5,
- "status": "failed",
- "finished_at": "2016-03-29T06:28:12.695Z",
- "trace": "Sed culpa est et facere saepe vel id ab. Quas temporibus aut similique dolorem consequatur corporis aut praesentium. Cum officia molestiae sit earum excepturi.\n\nSint possimus aut ratione quia. Quis nesciunt ratione itaque illo. Tenetur est dolor assumenda possimus voluptatem quia minima. Accusamus reprehenderit ut et itaque non reiciendis incidunt.\n\nRerum suscipit quibusdam dolore nam omnis. Consequatur ipsa nihil ut enim blanditiis delectus. Nulla quis hic occaecati mollitia qui placeat. Quo rerum sed perferendis a accusantium consequatur commodi ut. Sit quae et cumque vel eius tempora nostrum.\n\nUllam dolorem et itaque sint est. Ea molestias quia provident dolorem vitae error et et. Ea expedita officiis iste non. Qui vitae odit saepe illum. Dolores enim ratione deserunt tempore expedita amet non neque.\n\nEligendi asperiores voluptatibus omnis repudiandae expedita distinctio qui aliquid. Autem aut doloremque distinctio ab. Nostrum sapiente repudiandae aspernatur ea et quae voluptas. Officiis perspiciatis nisi laudantium asperiores error eligendi ab. Eius quia amet magni omnis exercitationem voluptatum et.\n\nVoluptatem ullam labore quas dicta est ex voluptas. Pariatur ea modi voluptas consequatur dolores perspiciatis similique. Numquam in distinctio perspiciatis ut qui earum. Quidem omnis mollitia facere aut beatae. Ea est iure et voluptatem.",
- "created_at": "2016-03-22T15:20:35.950Z",
- "updated_at": "2016-03-29T06:28:12.696Z",
- "started_at": null,
- "runner_id": null,
- "coverage": null,
- "commit_id": 40,
- "commands": "$ build command",
- "job_id": null,
- "name": "test build 1",
- "deploy": false,
- "options": null,
- "allow_failure": false,
- "stage": "test",
- "trigger_request_id": null,
- "stage_idx": 1,
- "tag": null,
- "ref": "master",
- "user_id": null,
- "target_url": null,
- "description": null,
- "artifacts_file": {
- "url": null
- },
- "artifacts_metadata": {
- "url": null
- },
- "erased_by_id": null,
- "erased_at": null
- },
- {
- "id": 80,
- "project_id": 5,
- "status": "success",
- "finished_at": null,
- "trace": "Impedit et optio nemo ipsa. Non ad non quis ut sequi laudantium omnis velit. Corporis a enim illo eos. Quia totam tempore inventore ad est.\n\nNihil recusandae cupiditate eaque voluptatem molestias sint. Consequatur id voluptatem cupiditate harum. Consequuntur iusto quaerat reiciendis aut autem libero est. Quisquam dolores veritatis rerum et sint maxime ullam libero. Id quas porro ut perspiciatis rem amet vitae.\n\nNemo inventore minus blanditiis magnam. Modi consequuntur nostrum aut voluptatem ex. Sunt rerum rem optio mollitia qui aliquam officiis officia. Aliquid eos et id aut minus beatae reiciendis.\n\nDolores non in temporibus dicta. Fugiat voluptatem est aspernatur expedita voluptatum nam qui. Quia et eligendi sit quae sint tempore exercitationem eos. Est sapiente corrupti quidem at. Qui magni odio repudiandae saepe tenetur optio dolore.\n\nEos placeat soluta at dolorem adipisci provident. Quo commodi id reprehenderit possimus quo tenetur. Ipsum et quae eligendi laborum. Et qui nesciunt at quasi quidem voluptatem cum rerum. Excepturi non facilis aut sunt vero sed.\n\nQui explicabo ratione ut eligendi recusandae. Quis quasi quas molestiae consequatur voluptatem et voluptatem. Ex repellat saepe occaecati aperiam ea eveniet dignissimos facilis.",
- "created_at": "2016-03-22T15:20:35.966Z",
- "updated_at": "2016-03-22T15:20:35.966Z",
- "started_at": null,
- "runner_id": null,
- "coverage": null,
- "commit_id": 40,
- "commands": "$ build command",
- "job_id": null,
- "name": "test build 2",
- "deploy": false,
- "options": null,
- "allow_failure": false,
- "stage": "test",
- "trigger_request_id": null,
- "stage_idx": 1,
- "tag": null,
- "ref": "master",
- "user_id": null,
- "target_url": null,
- "description": null,
- "artifacts_file": {
- "url": "/Users/Test/Test/gitlab-development-kit/gitlab/shared/artifacts/2016_03/5/80/p5_build_artifacts.zip"
- },
- "artifacts_metadata": {
- "url": "/Users/Test/Test/gitlab-development-kit/gitlab/shared/artifacts/2016_03/5/80/p5_build_artifacts_metadata.gz"
- },
- "erased_by_id": null,
- "erased_at": null
+ "stages": [
+ {
+ "id": 24,
+ "project_id": 5,
+ "pipeline_id": 40,
+ "name": "test",
+ "status": 1,
+ "created_at": "2016-03-22T15:44:44.772Z",
+ "updated_at": "2016-03-29T06:44:44.634Z",
+ "statuses": [
+ {
+ "id": 79,
+ "project_id": 5,
+ "status": "failed",
+ "finished_at": "2016-03-29T06:28:12.695Z",
+ "trace": "Sed culpa est et facere saepe vel id ab. Quas temporibus aut similique dolorem consequatur corporis aut praesentium. Cum officia molestiae sit earum excepturi.\n\nSint possimus aut ratione quia. Quis nesciunt ratione itaque illo. Tenetur est dolor assumenda possimus voluptatem quia minima. Accusamus reprehenderit ut et itaque non reiciendis incidunt.\n\nRerum suscipit quibusdam dolore nam omnis. Consequatur ipsa nihil ut enim blanditiis delectus. Nulla quis hic occaecati mollitia qui placeat. Quo rerum sed perferendis a accusantium consequatur commodi ut. Sit quae et cumque vel eius tempora nostrum.\n\nUllam dolorem et itaque sint est. Ea molestias quia provident dolorem vitae error et et. Ea expedita officiis iste non. Qui vitae odit saepe illum. Dolores enim ratione deserunt tempore expedita amet non neque.\n\nEligendi asperiores voluptatibus omnis repudiandae expedita distinctio qui aliquid. Autem aut doloremque distinctio ab. Nostrum sapiente repudiandae aspernatur ea et quae voluptas. Officiis perspiciatis nisi laudantium asperiores error eligendi ab. Eius quia amet magni omnis exercitationem voluptatum et.\n\nVoluptatem ullam labore quas dicta est ex voluptas. Pariatur ea modi voluptas consequatur dolores perspiciatis similique. Numquam in distinctio perspiciatis ut qui earum. Quidem omnis mollitia facere aut beatae. Ea est iure et voluptatem.",
+ "created_at": "2016-03-22T15:20:35.950Z",
+ "updated_at": "2016-03-29T06:28:12.696Z",
+ "started_at": null,
+ "runner_id": null,
+ "coverage": null,
+ "commit_id": 40,
+ "commands": "$ build command",
+ "job_id": null,
+ "name": "test build 1",
+ "deploy": false,
+ "options": null,
+ "allow_failure": false,
+ "stage": "test",
+ "trigger_request_id": null,
+ "stage_idx": 1,
+ "tag": null,
+ "ref": "master",
+ "user_id": null,
+ "target_url": null,
+ "description": null,
+ "artifacts_file": {
+ "url": null
+ },
+ "artifacts_metadata": {
+ "url": null
+ },
+ "erased_by_id": null,
+ "erased_at": null
+ },
+ {
+ "id": 80,
+ "project_id": 5,
+ "status": "success",
+ "finished_at": null,
+ "trace": "Impedit et optio nemo ipsa. Non ad non quis ut sequi laudantium omnis velit. Corporis a enim illo eos. Quia totam tempore inventore ad est.\n\nNihil recusandae cupiditate eaque voluptatem molestias sint. Consequatur id voluptatem cupiditate harum. Consequuntur iusto quaerat reiciendis aut autem libero est. Quisquam dolores veritatis rerum et sint maxime ullam libero. Id quas porro ut perspiciatis rem amet vitae.\n\nNemo inventore minus blanditiis magnam. Modi consequuntur nostrum aut voluptatem ex. Sunt rerum rem optio mollitia qui aliquam officiis officia. Aliquid eos et id aut minus beatae reiciendis.\n\nDolores non in temporibus dicta. Fugiat voluptatem est aspernatur expedita voluptatum nam qui. Quia et eligendi sit quae sint tempore exercitationem eos. Est sapiente corrupti quidem at. Qui magni odio repudiandae saepe tenetur optio dolore.\n\nEos placeat soluta at dolorem adipisci provident. Quo commodi id reprehenderit possimus quo tenetur. Ipsum et quae eligendi laborum. Et qui nesciunt at quasi quidem voluptatem cum rerum. Excepturi non facilis aut sunt vero sed.\n\nQui explicabo ratione ut eligendi recusandae. Quis quasi quas molestiae consequatur voluptatem et voluptatem. Ex repellat saepe occaecati aperiam ea eveniet dignissimos facilis.",
+ "created_at": "2016-03-22T15:20:35.966Z",
+ "updated_at": "2016-03-22T15:20:35.966Z",
+ "started_at": null,
+ "runner_id": null,
+ "coverage": null,
+ "commit_id": 40,
+ "commands": "$ build command",
+ "job_id": null,
+ "name": "test build 2",
+ "deploy": false,
+ "options": null,
+ "allow_failure": false,
+ "stage": "test",
+ "trigger_request_id": null,
+ "stage_idx": 1,
+ "tag": null,
+ "ref": "master",
+ "user_id": null,
+ "target_url": null,
+ "description": null,
+ "artifacts_file": {
+ "url": "/Users/Test/Test/gitlab-development-kit/gitlab/shared/artifacts/2016_03/5/80/p5_build_artifacts.zip"
+ },
+ "artifacts_metadata": {
+ "url": "/Users/Test/Test/gitlab-development-kit/gitlab/shared/artifacts/2016_03/5/80/p5_build_artifacts_metadata.gz"
+ },
+ "erased_by_id": null,
+ "erased_at": null
+ }
+ ]
}
]
}
diff --git a/spec/lib/gitlab/import_export/project_tree_restorer_spec.rb b/spec/lib/gitlab/import_export/project_tree_restorer_spec.rb
index 0ab3afd0074..9dfd879a1bc 100644
--- a/spec/lib/gitlab/import_export/project_tree_restorer_spec.rb
+++ b/spec/lib/gitlab/import_export/project_tree_restorer_spec.rb
@@ -179,6 +179,32 @@ describe Gitlab::ImportExport::ProjectTreeRestorer do
end
end
end
+
+ context 'when restoring hierarchy of pipeline, stages and jobs' do
+ it 'restores pipelines' do
+ expect(Ci::Pipeline.all.count).to be 5
+ end
+
+ it 'restores pipeline stages' do
+ expect(Ci::Stage.all.count).to be 6
+ end
+
+ it 'correctly restores association between stage and a pipeline' do
+ expect(Ci::Stage.all).to all(have_attributes(pipeline_id: a_value > 0))
+ end
+
+ it 'restores statuses' do
+ expect(CommitStatus.all.count).to be 10
+ end
+
+ it 'correctly restores association between a stage and a job' do
+ expect(CommitStatus.all).to all(have_attributes(stage_id: a_value > 0))
+ end
+
+ it 'correctly restores association between a pipeline and a job' do
+ expect(CommitStatus.all).to all(have_attributes(pipeline_id: a_value > 0))
+ end
+ end
end
end
diff --git a/spec/lib/gitlab/import_export/project_tree_saver_spec.rb b/spec/lib/gitlab/import_export/project_tree_saver_spec.rb
index 6243b6ac9f0..08e5bbbd400 100644
--- a/spec/lib/gitlab/import_export/project_tree_saver_spec.rb
+++ b/spec/lib/gitlab/import_export/project_tree_saver_spec.rb
@@ -9,7 +9,7 @@ describe Gitlab::ImportExport::ProjectTreeSaver do
let!(:project) { setup_project }
before do
- project.team << [user, :master]
+ project.add_master(user)
allow_any_instance_of(Gitlab::ImportExport).to receive(:storage_path).and_return(export_path)
allow_any_instance_of(MergeRequest).to receive(:source_branch_sha).and_return('ABCD')
allow_any_instance_of(MergeRequest).to receive(:target_branch_sha).and_return('DCBA')
@@ -109,12 +109,20 @@ describe Gitlab::ImportExport::ProjectTreeSaver do
expect(saved_project_json['merge_requests'].first['notes'].first['author']).not_to be_empty
end
+ it 'has pipeline stages' do
+ expect(saved_project_json.dig('pipelines', 0, 'stages')).not_to be_empty
+ end
+
it 'has pipeline statuses' do
- expect(saved_project_json['pipelines'].first['statuses']).not_to be_empty
+ expect(saved_project_json.dig('pipelines', 0, 'stages', 0, 'statuses')).not_to be_empty
end
it 'has pipeline builds' do
- expect(saved_project_json['pipelines'].first['statuses'].count { |hash| hash['type'] == 'Ci::Build' }).to eq(1)
+ builds_count = saved_project_json
+ .dig('pipelines', 0, 'stages', 0, 'statuses')
+ .count { |hash| hash['type'] == 'Ci::Build' }
+
+ expect(builds_count).to eq(1)
end
it 'has no when YML attributes but only the DB column' do
diff --git a/spec/lib/gitlab/import_export/repo_saver_spec.rb b/spec/lib/gitlab/import_export/repo_saver_spec.rb
index e6ad516deef..44f972fe530 100644
--- a/spec/lib/gitlab/import_export/repo_saver_spec.rb
+++ b/spec/lib/gitlab/import_export/repo_saver_spec.rb
@@ -9,7 +9,7 @@ describe Gitlab::ImportExport::RepoSaver do
let(:bundler) { described_class.new(project: project, shared: shared) }
before do
- project.team << [user, :master]
+ project.add_master(user)
allow_any_instance_of(Gitlab::ImportExport).to receive(:storage_path).and_return(export_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 1016a7ab591..1a554bd1a4e 100644
--- a/spec/lib/gitlab/import_export/safe_model_attributes.yml
+++ b/spec/lib/gitlab/import_export/safe_model_attributes.yml
@@ -461,6 +461,7 @@ Project:
- delete_error
- merge_requests_ff_only_enabled
- merge_requests_rebase_enabled
+- jobs_cache_index
Author:
- name
ProjectFeature:
diff --git a/spec/lib/gitlab/import_export/wiki_repo_saver_spec.rb b/spec/lib/gitlab/import_export/wiki_repo_saver_spec.rb
index 0e55993c8ef..1d1e7e7f89a 100644
--- a/spec/lib/gitlab/import_export/wiki_repo_saver_spec.rb
+++ b/spec/lib/gitlab/import_export/wiki_repo_saver_spec.rb
@@ -10,7 +10,7 @@ describe Gitlab::ImportExport::WikiRepoSaver do
let!(:project_wiki) { ProjectWiki.new(project, user) }
before do
- project.team << [user, :master]
+ project.add_master(user)
allow_any_instance_of(Gitlab::ImportExport).to receive(:storage_path).and_return(export_path)
project_wiki.wiki
project_wiki.create_page("index", "test content")
diff --git a/spec/lib/gitlab/kubernetes/helm_spec.rb b/spec/lib/gitlab/kubernetes/helm/api_spec.rb
index 15f99b0401f..69112fe90b1 100644
--- a/spec/lib/gitlab/kubernetes/helm_spec.rb
+++ b/spec/lib/gitlab/kubernetes/helm/api_spec.rb
@@ -1,22 +1,23 @@
require 'spec_helper'
-describe Gitlab::Kubernetes::Helm do
+describe Gitlab::Kubernetes::Helm::Api do
let(:client) { double('kubernetes client') }
let(:helm) { described_class.new(client) }
- let(:namespace) { Gitlab::Kubernetes::Namespace.new(described_class::NAMESPACE, client) }
+ let(:gitlab_namespace) { Gitlab::Kubernetes::Helm::NAMESPACE }
+ let(:namespace) { Gitlab::Kubernetes::Namespace.new(gitlab_namespace, client) }
let(:install_helm) { true }
let(:chart) { 'stable/a_chart' }
let(:application_name) { 'app_name' }
- let(:command) { Gitlab::Kubernetes::Helm::InstallCommand.new(application_name, install_helm, chart) }
+ let(:command) { Gitlab::Kubernetes::Helm::InstallCommand.new(application_name, install_helm: install_helm, chart: chart) }
subject { helm }
before do
- allow(Gitlab::Kubernetes::Namespace).to receive(:new).with(described_class::NAMESPACE, client).and_return(namespace)
+ allow(Gitlab::Kubernetes::Namespace).to receive(:new).with(gitlab_namespace, client).and_return(namespace)
end
describe '#initialize' do
it 'creates a namespace object' do
- expect(Gitlab::Kubernetes::Namespace).to receive(:new).with(described_class::NAMESPACE, client)
+ expect(Gitlab::Kubernetes::Namespace).to receive(:new).with(gitlab_namespace, client)
subject
end
@@ -41,7 +42,7 @@ describe Gitlab::Kubernetes::Helm do
let(:pod) { Kubeclient::Resource.new(status: { phase: phase }) } # partial representation
it 'fetches POD phase from kubernetes cluster' do
- expect(client).to receive(:get_pod).with(command.pod_name, described_class::NAMESPACE).once.and_return(pod)
+ expect(client).to receive(:get_pod).with(command.pod_name, gitlab_namespace).once.and_return(pod)
expect(subject.installation_status(command.pod_name)).to eq(phase)
end
@@ -52,7 +53,7 @@ describe Gitlab::Kubernetes::Helm do
let(:response) { RestClient::Response.new(log) }
it 'fetches POD phase from kubernetes cluster' do
- expect(client).to receive(:get_pod_log).with(command.pod_name, described_class::NAMESPACE).once.and_return(response)
+ expect(client).to receive(:get_pod_log).with(command.pod_name, gitlab_namespace).once.and_return(response)
expect(subject.installation_log(command.pod_name)).to eq(log)
end
@@ -60,41 +61,9 @@ describe Gitlab::Kubernetes::Helm do
describe '#delete_installation_pod!' do
it 'deletes the POD from kubernetes cluster' do
- expect(client).to receive(:delete_pod).with(command.pod_name, described_class::NAMESPACE).once
+ expect(client).to receive(:delete_pod).with(command.pod_name, gitlab_namespace).once
subject.delete_installation_pod!(command.pod_name)
end
end
-
- describe '#helm_init_command' do
- subject { helm.send(:helm_init_command, command) }
-
- context 'when command.install_helm is true' do
- let(:install_helm) { true }
-
- it { is_expected.to eq('helm init >/dev/null') }
- end
-
- context 'when command.install_helm is false' do
- let(:install_helm) { false }
-
- it { is_expected.to eq('helm init --client-only >/dev/null') }
- end
- end
-
- describe '#helm_install_command' do
- subject { helm.send(:helm_install_command, command) }
-
- context 'when command.chart is nil' do
- let(:chart) { nil }
-
- it { is_expected.to be_nil }
- end
-
- context 'when command.chart is set' do
- let(:chart) { 'stable/a_chart' }
-
- it { is_expected.to eq("helm install #{chart} --name #{application_name} --namespace #{namespace.name} >/dev/null")}
- end
- end
end
diff --git a/spec/lib/gitlab/kubernetes/helm/install_command_spec.rb b/spec/lib/gitlab/kubernetes/helm/install_command_spec.rb
new file mode 100644
index 00000000000..4afe48e72ad
--- /dev/null
+++ b/spec/lib/gitlab/kubernetes/helm/install_command_spec.rb
@@ -0,0 +1,111 @@
+require 'rails_helper'
+
+describe Gitlab::Kubernetes::Helm::InstallCommand do
+ let(:prometheus) { create(:clusters_applications_prometheus) }
+
+ describe "#initialize" do
+ context "With all the params" do
+ subject { described_class.new(prometheus.name, install_helm: true, chart: prometheus.chart, chart_values_file: prometheus.chart_values_file) }
+
+ it 'should assign all parameters' do
+ expect(subject.name).to eq(prometheus.name)
+ expect(subject.install_helm).to be_truthy
+ expect(subject.chart).to eq(prometheus.chart)
+ expect(subject.chart_values_file).to eq("#{Rails.root}/vendor/prometheus/values.yaml")
+ end
+ end
+
+ context 'when install_helm is not set' do
+ subject { described_class.new(prometheus.name, chart: prometheus.chart, chart_values_file: true) }
+
+ it 'should set install_helm as false' do
+ expect(subject.install_helm).to be_falsy
+ end
+ end
+
+ context 'when chart is not set' do
+ subject { described_class.new(prometheus.name, install_helm: true) }
+
+ it 'should set chart as nil' do
+ expect(subject.chart).to be_falsy
+ end
+ end
+
+ context 'when chart_values_file is not set' do
+ subject { described_class.new(prometheus.name, install_helm: true, chart: prometheus.chart) }
+
+ it 'should set chart_values_file as nil' do
+ expect(subject.chart_values_file).to be_falsy
+ end
+ end
+ end
+
+ describe "#generate_script" do
+ let(:install_command) { described_class.new(prometheus.name, install_helm: install_helm) }
+ let(:client) { double('kubernetes client') }
+ let(:namespace) { Gitlab::Kubernetes::Namespace.new(Gitlab::Kubernetes::Helm::NAMESPACE, client) }
+ subject { install_command.send(:generate_script, namespace.name) }
+
+ context 'when install helm is true' do
+ let(:install_helm) { true }
+ let(:command) do
+ <<~MSG
+ set -eo pipefail
+ apk add -U ca-certificates openssl >/dev/null
+ wget -q -O - https://kubernetes-helm.storage.googleapis.com/helm-v2.7.0-linux-amd64.tar.gz | tar zxC /tmp >/dev/null
+ mv /tmp/linux-amd64/helm /usr/bin/
+
+ helm init >/dev/null
+ MSG
+ end
+
+ it 'should return appropriate command' do
+ is_expected.to eq(command)
+ end
+ end
+
+ context 'when install helm is false' do
+ let(:install_helm) { false }
+ let(:command) do
+ <<~MSG
+ set -eo pipefail
+ apk add -U ca-certificates openssl >/dev/null
+ wget -q -O - https://kubernetes-helm.storage.googleapis.com/helm-v2.7.0-linux-amd64.tar.gz | tar zxC /tmp >/dev/null
+ mv /tmp/linux-amd64/helm /usr/bin/
+
+ helm init --client-only >/dev/null
+ MSG
+ end
+
+ it 'should return appropriate command' do
+ is_expected.to eq(command)
+ end
+ end
+
+ context 'when chart is present' do
+ let(:install_command) { described_class.new(prometheus.name, chart: prometheus.chart) }
+ let(:command) do
+ <<~MSG.chomp
+ set -eo pipefail
+ apk add -U ca-certificates openssl >/dev/null
+ wget -q -O - https://kubernetes-helm.storage.googleapis.com/helm-v2.7.0-linux-amd64.tar.gz | tar zxC /tmp >/dev/null
+ mv /tmp/linux-amd64/helm /usr/bin/
+
+ helm init --client-only >/dev/null
+ helm install #{prometheus.chart} --name #{prometheus.name} --namespace #{namespace.name} >/dev/null
+ MSG
+ end
+
+ it 'should return appropriate command' do
+ is_expected.to eq(command)
+ end
+ end
+ end
+
+ describe "#pod_name" do
+ let(:install_command) { described_class.new(prometheus.name, install_helm: true, chart: prometheus.chart, chart_values_file: true) }
+ subject { install_command.send(:pod_name) }
+
+ it { is_expected.to eq('install-prometheus') }
+ end
+end
diff --git a/spec/lib/gitlab/kubernetes/helm/pod_spec.rb b/spec/lib/gitlab/kubernetes/helm/pod_spec.rb
new file mode 100644
index 00000000000..906b10b96d4
--- /dev/null
+++ b/spec/lib/gitlab/kubernetes/helm/pod_spec.rb
@@ -0,0 +1,86 @@
+require 'rails_helper'
+
+describe Gitlab::Kubernetes::Helm::Pod do
+ describe '#generate' do
+ let(:cluster) { create(:cluster) }
+ let(:app) { create(:clusters_applications_prometheus, cluster: cluster) }
+ let(:command) { app.install_command }
+ let(:client) { double('kubernetes client') }
+ let(:namespace) { Gitlab::Kubernetes::Namespace.new(Gitlab::Kubernetes::Helm::NAMESPACE, client) }
+ subject { described_class.new(command, namespace.name, client) }
+
+ before do
+ allow(client).to receive(:create_config_map).and_return(nil)
+ end
+
+ shared_examples 'helm pod' do
+ it 'should generate a Kubeclient::Resource' do
+ expect(subject.generate).to be_a_kind_of(Kubeclient::Resource)
+ end
+
+ it 'should generate the appropriate metadata' do
+ metadata = subject.generate.metadata
+ expect(metadata.name).to eq("install-#{app.name}")
+ expect(metadata.namespace).to eq('gitlab-managed-apps')
+ expect(metadata.labels['gitlab.org/action']).to eq('install')
+ expect(metadata.labels['gitlab.org/application']).to eq(app.name)
+ end
+
+ it 'should generate a container spec' do
+ spec = subject.generate.spec
+ expect(spec.containers.count).to eq(1)
+ end
+
+ it 'should generate the appropriate specifications for the container' do
+ container = subject.generate.spec.containers.first
+ expect(container.name).to eq('helm')
+ expect(container.image).to eq('alpine:3.6')
+ expect(container.env.count).to eq(3)
+ expect(container.env.map(&:name)).to match_array([:HELM_VERSION, :TILLER_NAMESPACE, :COMMAND_SCRIPT])
+ expect(container.command).to match_array(["/bin/sh"])
+ expect(container.args).to match_array(["-c", "$(COMMAND_SCRIPT)"])
+ end
+
+ it 'should include a never restart policy' do
+ spec = subject.generate.spec
+ expect(spec.restartPolicy).to eq('Never')
+ end
+ end
+
+ context 'with a configuration file' do
+ it_behaves_like 'helm pod'
+
+ it 'should include volumes for the container' do
+ container = subject.generate.spec.containers.first
+ expect(container.volumeMounts.first['name']).to eq('config-volume')
+ expect(container.volumeMounts.first['mountPath']).to eq('/etc/config')
+ end
+
+ it 'should include a volume inside the specification' do
+ spec = subject.generate.spec
+ expect(spec.volumes.first['name']).to eq('config-volume')
+ end
+
+ it 'should mount configMap specification in the volume' do
+ spec = subject.generate.spec
+ expect(spec.volumes.first.configMap['name']).to eq('values-config')
+ end
+ end
+
+ context 'without a configuration file' do
+ let(:app) { create(:clusters_applications_ingress, cluster: cluster) }
+
+ it_behaves_like 'helm pod'
+
+ it 'should not include volumeMounts inside the container' do
+ container = subject.generate.spec.containers.first
+ expect(container.volumeMounts).to be_nil
+ end
+
+ it 'should not a volume inside the specification' do
+ spec = subject.generate.spec
+ expect(spec.volumes).to be_nil
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/ldap/adapter_spec.rb b/spec/lib/gitlab/ldap/adapter_spec.rb
index d9ddb4326be..6132abd9b35 100644
--- a/spec/lib/gitlab/ldap/adapter_spec.rb
+++ b/spec/lib/gitlab/ldap/adapter_spec.rb
@@ -16,7 +16,7 @@ describe Gitlab::LDAP::Adapter do
expect(adapter).to receive(:ldap_search) do |arg|
expect(arg[:filter].to_s).to eq('(uid=johndoe)')
expect(arg[:base]).to eq('dc=example,dc=com')
- expect(arg[:attributes]).to match(%w{dn uid cn mail email userPrincipalName})
+ expect(arg[:attributes]).to match(ldap_attributes)
end.and_return({})
adapter.users('uid', 'johndoe')
@@ -26,7 +26,7 @@ describe Gitlab::LDAP::Adapter do
expect(adapter).to receive(:ldap_search).with(
base: 'uid=johndoe,ou=users,dc=example,dc=com',
scope: Net::LDAP::SearchScope_BaseObject,
- attributes: %w{dn uid cn mail email userPrincipalName},
+ attributes: ldap_attributes,
filter: nil
).and_return({})
@@ -63,7 +63,7 @@ describe Gitlab::LDAP::Adapter do
it 'uses the right uid attribute when non-default' do
stub_ldap_config(uid: 'sAMAccountName')
expect(adapter).to receive(:ldap_search).with(
- hash_including(attributes: %w{dn sAMAccountName cn mail email userPrincipalName})
+ hash_including(attributes: ldap_attributes)
).and_return({})
adapter.users('sAMAccountName', 'johndoe')
@@ -137,4 +137,8 @@ describe Gitlab::LDAP::Adapter do
end
end
end
+
+ def ldap_attributes
+ Gitlab::LDAP::Person.ldap_attributes(Gitlab::LDAP::Config.new('ldapmain'))
+ end
end
diff --git a/spec/lib/gitlab/ldap/person_spec.rb b/spec/lib/gitlab/ldap/person_spec.rb
index d204050ef66..ff29d9aa5be 100644
--- a/spec/lib/gitlab/ldap/person_spec.rb
+++ b/spec/lib/gitlab/ldap/person_spec.rb
@@ -8,13 +8,16 @@ describe Gitlab::LDAP::Person do
before do
stub_ldap_config(
options: {
+ 'uid' => 'uid',
'attributes' => {
- 'name' => 'cn',
- 'email' => %w(mail email userPrincipalName)
+ 'name' => 'cn',
+ 'email' => %w(mail email userPrincipalName),
+ 'username' => username_attribute
}
}
)
end
+ let(:username_attribute) { %w(uid sAMAccountName userid) }
describe '.normalize_dn' do
subject { described_class.normalize_dn(given) }
@@ -44,6 +47,34 @@ describe Gitlab::LDAP::Person do
end
end
+ describe '.ldap_attributes' do
+ it 'returns a compact and unique array' do
+ stub_ldap_config(
+ options: {
+ 'uid' => nil,
+ 'attributes' => {
+ 'name' => 'cn',
+ 'email' => 'mail',
+ 'username' => %w(uid mail memberof)
+ }
+ }
+ )
+ config = Gitlab::LDAP::Config.new('ldapmain')
+ ldap_attributes = described_class.ldap_attributes(config)
+
+ expect(ldap_attributes).to match_array(%w(dn uid cn mail memberof))
+ end
+ end
+
+ describe '.validate_entry' do
+ it 'raises InvalidEntryError' do
+ entry['foo'] = 'bar'
+
+ expect { described_class.new(entry, 'ldapmain') }
+ .to raise_error(Gitlab::LDAP::Person::InvalidEntryError)
+ end
+ end
+
describe '#name' do
it 'uses the configured name attribute and handles values as an array' do
name = 'John Doe'
@@ -72,6 +103,44 @@ describe Gitlab::LDAP::Person do
end
end
+ describe '#username' do
+ context 'with default uid username attribute' do
+ let(:username_attribute) { 'uid' }
+
+ it 'returns the proper username value' do
+ attr_value = 'johndoe'
+ entry[username_attribute] = attr_value
+ person = described_class.new(entry, 'ldapmain')
+
+ expect(person.username).to eq(attr_value)
+ end
+ end
+
+ context 'with a different username attribute' do
+ let(:username_attribute) { 'sAMAccountName' }
+
+ it 'returns the proper username value' do
+ attr_value = 'johndoe'
+ entry[username_attribute] = attr_value
+ person = described_class.new(entry, 'ldapmain')
+
+ expect(person.username).to eq(attr_value)
+ end
+ end
+
+ context 'with a non-standard username attribute' do
+ let(:username_attribute) { 'mail' }
+
+ it 'returns the proper username value' do
+ attr_value = 'john.doe@example.com'
+ entry[username_attribute] = attr_value
+ person = described_class.new(entry, 'ldapmain')
+
+ expect(person.username).to eq(attr_value)
+ end
+ end
+ end
+
def assert_generic_test(test_description, got, expected)
test_failure_message = "Failed test description: '#{test_description}'\n\n expected: #{expected}\n got: #{got}"
expect(got).to eq(expected), test_failure_message
diff --git a/spec/lib/gitlab/ldap/user_spec.rb b/spec/lib/gitlab/ldap/user_spec.rb
index 260df6e4dae..048caa38fcf 100644
--- a/spec/lib/gitlab/ldap/user_spec.rb
+++ b/spec/lib/gitlab/ldap/user_spec.rb
@@ -38,7 +38,6 @@ describe Gitlab::LDAP::User do
it "does not mark existing ldap user as changed" do
create(:omniauth_user, email: 'john@example.com', extern_uid: 'uid=john smith,ou=people,dc=example,dc=com', provider: 'ldapmain')
- ldap_user.gl_user.user_synced_attributes_metadata(provider: 'ldapmain', email: true)
expect(ldap_user.changed?).to be_falsey
end
end
@@ -144,11 +143,15 @@ describe Gitlab::LDAP::User do
expect(ldap_user.gl_user.email).to eq(info[:email])
end
- it "has user_synced_attributes_metadata email set to true" do
+ it "has email set as synced" do
expect(ldap_user.gl_user.user_synced_attributes_metadata.email_synced).to be_truthy
end
- it "has synced_attribute_provider set to ldapmain" do
+ it "has email set as read-only" do
+ expect(ldap_user.gl_user.read_only_attribute?(:email)).to be_truthy
+ end
+
+ it "has synced attributes provider set to ldapmain" do
expect(ldap_user.gl_user.user_synced_attributes_metadata.provider).to eql 'ldapmain'
end
end
@@ -162,9 +165,13 @@ describe Gitlab::LDAP::User do
expect(ldap_user.gl_user.temp_oauth_email?).to be_truthy
end
- it "has synced attribute email set to false" do
+ it "has email set as not synced" do
expect(ldap_user.gl_user.user_synced_attributes_metadata.email_synced).to be_falsey
end
+
+ it "does not have email set as read-only" do
+ expect(ldap_user.gl_user.read_only_attribute?(:email)).to be_falsey
+ end
end
end
diff --git a/spec/lib/gitlab/metrics/method_call_spec.rb b/spec/lib/gitlab/metrics/method_call_spec.rb
index 5341addf911..41a9d1d9c90 100644
--- a/spec/lib/gitlab/metrics/method_call_spec.rb
+++ b/spec/lib/gitlab/metrics/method_call_spec.rb
@@ -20,9 +20,39 @@ describe Gitlab::Metrics::MethodCall do
context 'prometheus instrumentation is enabled' do
before do
+ allow(Feature.get(:prometheus_metrics_method_instrumentation)).to receive(:enabled?).and_call_original
+ described_class.measurement_enabled_cache_expires_at.value = Time.now.to_i - 1
Feature.get(:prometheus_metrics_method_instrumentation).enable
end
+ around do |example|
+ Timecop.freeze do
+ example.run
+ end
+ end
+
+ it 'caches subsequent invocations of feature check' do
+ 10.times do
+ method_call.measure { 'foo' }
+ end
+
+ expect(Feature.get(:prometheus_metrics_method_instrumentation)).to have_received(:enabled?).once
+ end
+
+ it 'expires feature check cache after 1 minute' do
+ method_call.measure { 'foo' }
+
+ Timecop.travel(1.minute.from_now) do
+ method_call.measure { 'foo' }
+ end
+
+ Timecop.travel(1.minute.from_now + 1.second) do
+ method_call.measure { 'foo' }
+ end
+
+ expect(Feature.get(:prometheus_metrics_method_instrumentation)).to have_received(:enabled?).twice
+ end
+
it 'observes the performance of the supplied block' do
expect(described_class.call_duration_histogram)
.to receive(:observe)
@@ -34,6 +64,8 @@ describe Gitlab::Metrics::MethodCall do
context 'prometheus instrumentation is disabled' do
before do
+ described_class.measurement_enabled_cache_expires_at.value = Time.now.to_i - 1
+
Feature.get(:prometheus_metrics_method_instrumentation).disable
end
@@ -64,14 +96,17 @@ describe Gitlab::Metrics::MethodCall do
describe '#to_metric' do
it 'returns a Metric instance' do
+ expect(method_call).to receive(:real_time).and_return(4.0001)
+ expect(method_call).to receive(:cpu_time).and_return(3.0001)
+
method_call.measure { 'foo' }
metric = method_call.to_metric
expect(metric).to be_an_instance_of(Gitlab::Metrics::Metric)
expect(metric.series).to eq('rails_method_calls')
- expect(metric.values[:duration]).to be_a_kind_of(Numeric)
- expect(metric.values[:cpu_duration]).to be_a_kind_of(Numeric)
+ expect(metric.values[:duration]).to eq(4000)
+ expect(metric.values[:cpu_duration]).to eq(3000)
expect(metric.values[:call_count]).to be_an(Integer)
expect(metric.tags).to eq({ method: 'Foo#bar' })
@@ -84,13 +119,13 @@ describe Gitlab::Metrics::MethodCall do
end
it 'returns false when the total call time is not above the threshold' do
- expect(method_call).to receive(:real_time).and_return(9)
+ expect(method_call).to receive(:real_time).and_return(0.009)
expect(method_call.above_threshold?).to eq(false)
end
it 'returns true when the total call time is above the threshold' do
- expect(method_call).to receive(:real_time).and_return(9000)
+ expect(method_call).to receive(:real_time).and_return(9)
expect(method_call.above_threshold?).to eq(true)
end
diff --git a/spec/lib/gitlab/metrics/samplers/influx_sampler_spec.rb b/spec/lib/gitlab/metrics/samplers/influx_sampler_spec.rb
index 667e4747897..f66451c5188 100644
--- a/spec/lib/gitlab/metrics/samplers/influx_sampler_spec.rb
+++ b/spec/lib/gitlab/metrics/samplers/influx_sampler_spec.rb
@@ -21,7 +21,6 @@ describe Gitlab::Metrics::Samplers::InfluxSampler do
it 'samples various statistics' do
expect(sampler).to receive(:sample_memory_usage)
expect(sampler).to receive(:sample_file_descriptors)
- expect(sampler).to receive(:sample_objects)
expect(sampler).to receive(:sample_gc)
expect(sampler).to receive(:flush)
@@ -72,28 +71,6 @@ describe Gitlab::Metrics::Samplers::InfluxSampler do
end
end
- if Gitlab::Metrics.mri?
- describe '#sample_objects' do
- it 'adds a metric containing the amount of allocated objects' do
- expect(sampler).to receive(:add_metric)
- .with(/object_counts/, an_instance_of(Hash), an_instance_of(Hash))
- .at_least(:once)
- .and_call_original
-
- sampler.sample_objects
- end
-
- it 'ignores classes without a name' do
- expect(Allocations).to receive(:to_hash).and_return({ Class.new => 4 })
-
- expect(sampler).not_to receive(:add_metric)
- .with('object_counts', an_instance_of(Hash), type: nil)
-
- sampler.sample_objects
- end
- end
- end
-
describe '#sample_gc' do
it 'adds a metric containing garbage collection statistics' do
expect(GC::Profiler).to receive(:total_time).and_return(0.24)
diff --git a/spec/lib/gitlab/metrics/samplers/ruby_sampler_spec.rb b/spec/lib/gitlab/metrics/samplers/ruby_sampler_spec.rb
index 53699327da1..375cbf8a9ca 100644
--- a/spec/lib/gitlab/metrics/samplers/ruby_sampler_spec.rb
+++ b/spec/lib/gitlab/metrics/samplers/ruby_sampler_spec.rb
@@ -11,7 +11,6 @@ describe Gitlab::Metrics::Samplers::RubySampler do
it 'samples various statistics' do
expect(Gitlab::Metrics::System).to receive(:memory_usage)
expect(Gitlab::Metrics::System).to receive(:file_descriptor_count)
- expect(sampler).to receive(:sample_objects)
expect(sampler).to receive(:sample_gc)
sampler.sample
@@ -65,26 +64,4 @@ describe Gitlab::Metrics::Samplers::RubySampler do
sampler.sample
end
end
-
- if Gitlab::Metrics.mri?
- describe '#sample_objects' do
- it 'adds a metric containing the amount of allocated objects' do
- expect(sampler.metrics[:objects_total]).to receive(:set)
- .with(include(class: anything), be > 0)
- .at_least(:once)
- .and_call_original
-
- sampler.sample
- end
-
- it 'ignores classes without a name' do
- expect(Allocations).to receive(:to_hash).and_return({ Class.new => 4 })
-
- expect(sampler.metrics[:objects_total]).not_to receive(:set)
- .with(include(class: 'object_counts'), anything)
-
- sampler.sample
- end
- end
- end
end
diff --git a/spec/lib/gitlab/metrics/system_spec.rb b/spec/lib/gitlab/metrics/system_spec.rb
index 4d94d8705fb..14afcdf5daa 100644
--- a/spec/lib/gitlab/metrics/system_spec.rb
+++ b/spec/lib/gitlab/metrics/system_spec.rb
@@ -29,19 +29,19 @@ describe Gitlab::Metrics::System do
describe '.cpu_time' do
it 'returns a Fixnum' do
- expect(described_class.cpu_time).to be_an(Integer)
+ expect(described_class.cpu_time).to be_an(Float)
end
end
describe '.real_time' do
it 'returns a Fixnum' do
- expect(described_class.real_time).to be_an(Integer)
+ expect(described_class.real_time).to be_an(Float)
end
end
describe '.monotonic_time' do
- it 'returns a Fixnum' do
- expect(described_class.monotonic_time).to be_an(Integer)
+ it 'returns a Float' do
+ expect(described_class.monotonic_time).to be_an(Float)
end
end
end
diff --git a/spec/lib/gitlab/o_auth/user_spec.rb b/spec/lib/gitlab/o_auth/user_spec.rb
index 2f19fb7312d..45fff4c5787 100644
--- a/spec/lib/gitlab/o_auth/user_spec.rb
+++ b/spec/lib/gitlab/o_auth/user_spec.rb
@@ -202,11 +202,13 @@ describe Gitlab::OAuth::User do
end
context "and no account for the LDAP user" do
- it "creates a user with dual LDAP and omniauth identities" do
+ before do
allow(Gitlab::LDAP::Person).to receive(:find_by_uid).and_return(ldap_user)
oauth_user.save
+ end
+ it "creates a user with dual LDAP and omniauth identities" do
expect(gl_user).to be_valid
expect(gl_user.username).to eql uid
expect(gl_user.email).to eql 'johndoe@example.com'
@@ -219,6 +221,18 @@ describe Gitlab::OAuth::User do
]
)
end
+
+ it "has email set as synced" do
+ expect(gl_user.user_synced_attributes_metadata.email_synced).to be_truthy
+ end
+
+ it "has email set as read-only" do
+ expect(gl_user.read_only_attribute?(:email)).to be_truthy
+ end
+
+ it "has synced attributes provider set to ldapmain" do
+ expect(gl_user.user_synced_attributes_metadata.provider).to eql 'ldapmain'
+ end
end
context "and LDAP user has an account already" do
@@ -261,6 +275,26 @@ describe Gitlab::OAuth::User do
end
end
+ context 'and a corresponding LDAP person with a non-default username' do
+ before do
+ allow(ldap_user).to receive(:uid) { uid }
+ allow(ldap_user).to receive(:username) { 'johndoe@example.com' }
+ allow(ldap_user).to receive(:email) { %w(johndoe@example.com john2@example.com) }
+ allow(ldap_user).to receive(:dn) { dn }
+ end
+
+ context 'and no account for the LDAP user' do
+ it 'creates a user favoring the LDAP username and strips email domain' do
+ allow(Gitlab::LDAP::Person).to receive(:find_by_uid).and_return(ldap_user)
+
+ oauth_user.save
+
+ expect(gl_user).to be_valid
+ expect(gl_user.username).to eql 'johndoe'
+ end
+ end
+ end
+
context "and no corresponding LDAP person" do
before do
allow(Gitlab::LDAP::Person).to receive(:find_by_uid).and_return(nil)
@@ -440,11 +474,15 @@ describe Gitlab::OAuth::User do
expect(gl_user.email).to eq(info_hash[:email])
end
- it "has external_attributes set to true" do
- expect(gl_user.user_synced_attributes_metadata).not_to be_nil
+ it "has email set as synced" do
+ expect(gl_user.user_synced_attributes_metadata.email_synced).to be_truthy
end
- it "has attributes_provider set to my-provider" do
+ it "has email set as read-only" do
+ expect(gl_user.read_only_attribute?(:email)).to be_truthy
+ end
+
+ it "has synced attributes provider set to my-provider" do
expect(gl_user.user_synced_attributes_metadata.provider).to eql 'my-provider'
end
end
@@ -458,10 +496,13 @@ describe Gitlab::OAuth::User do
expect(gl_user.email).not_to eq(info_hash[:email])
end
- it "has user_synced_attributes_metadata set to nil" do
- expect(gl_user.user_synced_attributes_metadata.provider).to eql 'my-provider'
+ it "has email set as not synced" do
expect(gl_user.user_synced_attributes_metadata.email_synced).to be_falsey
end
+
+ it "does not have email set as read-only" do
+ expect(gl_user.read_only_attribute?(:email)).to be_falsey
+ end
end
end
@@ -508,11 +549,15 @@ describe Gitlab::OAuth::User do
expect(gl_user.email).to eq(info_hash[:email])
end
- it "has email_synced_attribute set to true" do
+ it "has email set as synced" do
expect(gl_user.user_synced_attributes_metadata.email_synced).to be(true)
end
- it "has my-provider as attributes_provider" do
+ it "has email set as read-only" do
+ expect(gl_user.read_only_attribute?(:email)).to be_truthy
+ end
+
+ it "has synced attributes provider set to my-provider" do
expect(gl_user.user_synced_attributes_metadata.provider).to eql 'my-provider'
end
end
@@ -524,7 +569,14 @@ describe Gitlab::OAuth::User do
it "does not update the user email" do
expect(gl_user.email).not_to eq(info_hash[:email])
- expect(gl_user.user_synced_attributes_metadata.email_synced).to be(false)
+ end
+
+ it "has email set as not synced" do
+ expect(gl_user.user_synced_attributes_metadata.email_synced).to be_falsey
+ end
+
+ it "does not have email set as read-only" do
+ expect(gl_user.read_only_attribute?(:email)).to be_falsey
end
end
end
diff --git a/spec/lib/gitlab/project_authorizations_spec.rb b/spec/lib/gitlab/project_authorizations_spec.rb
index 953cfbb8b88..f3cd6961e94 100644
--- a/spec/lib/gitlab/project_authorizations_spec.rb
+++ b/spec/lib/gitlab/project_authorizations_spec.rb
@@ -15,7 +15,7 @@ describe Gitlab::ProjectAuthorizations do
end
before do
- other_project.team << [user, :reporter]
+ other_project.add_reporter(user)
group.add_developer(user)
end
diff --git a/spec/lib/gitlab/project_search_results_spec.rb b/spec/lib/gitlab/project_search_results_spec.rb
index a424f0f5cfe..17937726f2c 100644
--- a/spec/lib/gitlab/project_search_results_spec.rb
+++ b/spec/lib/gitlab/project_search_results_spec.rb
@@ -179,7 +179,7 @@ describe Gitlab::ProjectSearchResults do
end
it 'does not list project confidential issues for project members with guest role' do
- project.team << [member, :guest]
+ project.add_guest(member)
results = described_class.new(member, project, query)
issues = results.objects('issues')
@@ -211,7 +211,7 @@ describe Gitlab::ProjectSearchResults do
end
it 'lists project confidential issues for project members' do
- project.team << [member, :developer]
+ project.add_developer(member)
results = described_class.new(member, project, query)
issues = results.objects('issues')
@@ -290,12 +290,12 @@ describe Gitlab::ProjectSearchResults do
let!(:private_project) { create(:project, :private, :repository, creator: creator, namespace: creator.namespace) }
let(:team_master) do
user = create(:user, username: 'private-project-master')
- private_project.team << [user, :master]
+ private_project.add_master(user)
user
end
let(:team_reporter) do
user = create(:user, username: 'private-project-reporter')
- private_project.team << [user, :reporter]
+ private_project.add_reporter(user)
user
end
diff --git a/spec/lib/gitlab/reference_extractor_spec.rb b/spec/lib/gitlab/reference_extractor_spec.rb
index ef874368077..4139d1c650c 100644
--- a/spec/lib/gitlab/reference_extractor_spec.rb
+++ b/spec/lib/gitlab/reference_extractor_spec.rb
@@ -4,7 +4,7 @@ describe Gitlab::ReferenceExtractor do
let(:project) { create(:project) }
before do
- project.team << [project.creator, :developer]
+ project.add_developer(project.creator)
end
subject { described_class.new(project, project.creator) }
@@ -14,8 +14,8 @@ describe Gitlab::ReferenceExtractor do
@u_bar = create(:user, username: 'bar')
@u_offteam = create(:user, username: 'offteam')
- project.team << [@u_foo, :reporter]
- project.team << [@u_bar, :guest]
+ project.add_guest(@u_foo)
+ project.add_guest(@u_bar)
subject.analyze('@foo, @baduser, @bar, and @offteam')
expect(subject.users).to match_array([@u_foo, @u_bar, @u_offteam])
@@ -26,8 +26,8 @@ describe Gitlab::ReferenceExtractor do
@u_bar = create(:user, username: 'bar')
@u_offteam = create(:user, username: 'offteam')
- project.team << [@u_foo, :reporter]
- project.team << [@u_bar, :guest]
+ project.add_reporter(@u_foo)
+ project.add_reporter(@u_bar)
subject.analyze(%Q{
Inline code: `@foo`
@@ -115,6 +115,15 @@ describe Gitlab::ReferenceExtractor do
end
end
+ it 'does not include anchors from table of contents in issue references' do
+ issue1 = create(:issue, project: project)
+ issue2 = create(:issue, project: project)
+
+ subject.analyze("not real issue <h4>#{issue1.iid}</h4>, real issue #{issue2.to_reference}")
+
+ expect(subject.issues).to match_array([issue2])
+ end
+
it 'accesses valid issue objects' do
@i0 = create(:issue, project: project)
@i1 = create(:issue, project: project)
@@ -219,7 +228,7 @@ describe Gitlab::ReferenceExtractor do
let(:issue) { create(:issue, project: other_project) }
before do
- other_project.team << [project.creator, :developer]
+ other_project.add_developer(project.creator)
end
it 'handles project issue references' do
@@ -237,7 +246,7 @@ describe Gitlab::ReferenceExtractor do
let(:text) { "Ref. #{issue.to_reference} and #{label.to_reference}" }
before do
- project.team << [project.creator, :developer]
+ project.add_developer(project.creator)
subject.analyze(text)
end
diff --git a/spec/lib/gitlab/search_results_spec.rb b/spec/lib/gitlab/search_results_spec.rb
index e44a7c23452..b5a9ac570e6 100644
--- a/spec/lib/gitlab/search_results_spec.rb
+++ b/spec/lib/gitlab/search_results_spec.rb
@@ -16,7 +16,7 @@ describe Gitlab::SearchResults do
context 'as a user with access' do
before do
- project.team << [user, :developer]
+ project.add_developer(user)
end
describe '#projects_count' do
@@ -51,6 +51,38 @@ describe Gitlab::SearchResults do
expect(results.objects('merge_requests')).to include merge_request_2
end
+
+ describe '#merge_requests' do
+ it 'includes project filter by default' do
+ expect(results).to receive(:project_ids_relation).and_call_original
+
+ results.objects('merge_requests')
+ end
+
+ it 'it skips project filter if default project context is used' do
+ allow(results).to receive(:default_project_filter).and_return(true)
+
+ expect(results).not_to receive(:project_ids_relation)
+
+ results.objects('merge_requests')
+ end
+ end
+
+ describe '#issues' do
+ it 'includes project filter by default' do
+ expect(results).to receive(:project_ids_relation).and_call_original
+
+ results.objects('issues')
+ end
+
+ it 'it skips project filter if default project context is used' do
+ allow(results).to receive(:default_project_filter).and_return(true)
+
+ expect(results).not_to receive(:project_ids_relation)
+
+ results.objects('issues')
+ end
+ end
end
it 'does not list issues on private projects' do
@@ -93,8 +125,8 @@ describe Gitlab::SearchResults do
end
it 'does not list confidential issues for project members with guest role' do
- project_1.team << [member, :guest]
- project_2.team << [member, :guest]
+ project_1.add_guest(member)
+ project_2.add_guest(member)
results = described_class.new(member, limit_projects, query)
issues = results.objects('issues')
@@ -135,8 +167,8 @@ describe Gitlab::SearchResults do
end
it 'lists confidential issues for project members' do
- project_1.team << [member, :developer]
- project_2.team << [member, :developer]
+ project_1.add_developer(member)
+ project_2.add_developer(member)
results = described_class.new(member, limit_projects, query)
issues = results.objects('issues')
diff --git a/spec/lib/gitlab/shell_spec.rb b/spec/lib/gitlab/shell_spec.rb
index eec6858a5de..81d9e6a8f82 100644
--- a/spec/lib/gitlab/shell_spec.rb
+++ b/spec/lib/gitlab/shell_spec.rb
@@ -2,12 +2,19 @@ require 'spec_helper'
require 'stringio'
describe Gitlab::Shell do
- let(:project) { double('Project', id: 7, path: 'diaspora') }
+ set(:project) { create(:project, :repository) }
+
let(:gitlab_shell) { described_class.new }
let(:popen_vars) { { 'GIT_TERMINAL_PROMPT' => ENV['GIT_TERMINAL_PROMPT'] } }
+ let(:gitlab_projects) { double('gitlab_projects') }
+ let(:timeout) { Gitlab.config.gitlab_shell.git_timeout }
before do
allow(Project).to receive(:find).and_return(project)
+
+ allow(gitlab_shell).to receive(:gitlab_projects)
+ .with(project.repository_storage_path, project.disk_path + '.git')
+ .and_return(gitlab_projects)
end
it { is_expected.to respond_to :add_key }
@@ -44,38 +51,6 @@ describe Gitlab::Shell do
end
end
- describe 'projects commands' do
- let(:gitlab_shell_path) { File.expand_path('tmp/tests/gitlab-shell') }
- let(:projects_path) { File.join(gitlab_shell_path, 'bin/gitlab-projects') }
- let(:gitlab_shell_hooks_path) { File.join(gitlab_shell_path, 'hooks') }
-
- before do
- allow(Gitlab.config.gitlab_shell).to receive(:path).and_return(gitlab_shell_path)
- allow(Gitlab.config.gitlab_shell).to receive(:hooks_path).and_return(gitlab_shell_hooks_path)
- allow(Gitlab.config.gitlab_shell).to receive(:git_timeout).and_return(800)
- end
-
- describe '#mv_repository' do
- it 'executes the command' do
- expect(gitlab_shell).to receive(:gitlab_shell_fast_execute).with(
- [projects_path, 'mv-project', 'storage/path', 'project/path.git', 'new/path.git']
- )
- gitlab_shell.mv_repository('storage/path', 'project/path', 'new/path')
- end
- end
-
- describe '#add_key' do
- it 'removes trailing garbage' do
- allow(gitlab_shell).to receive(:gitlab_shell_keys_path).and_return(:gitlab_shell_keys_path)
- expect(gitlab_shell).to receive(:gitlab_shell_fast_execute).with(
- [:gitlab_shell_keys_path, 'add-key', 'key-123', 'ssh-rsa foobar']
- )
-
- gitlab_shell.add_key('key-123', 'ssh-rsa foobar trailing garbage')
- end
- end
- end
-
describe Gitlab::Shell::KeyAdder do
describe '#add_key' do
it 'removes trailing garbage' do
@@ -121,6 +96,17 @@ describe Gitlab::Shell do
allow(Gitlab.config.gitlab_shell).to receive(:git_timeout).and_return(800)
end
+ describe '#add_key' do
+ it 'removes trailing garbage' do
+ allow(gitlab_shell).to receive(:gitlab_shell_keys_path).and_return(:gitlab_shell_keys_path)
+ expect(gitlab_shell).to receive(:gitlab_shell_fast_execute).with(
+ [:gitlab_shell_keys_path, 'add-key', 'key-123', 'ssh-rsa foobar']
+ )
+
+ gitlab_shell.add_key('key-123', 'ssh-rsa foobar trailing garbage')
+ end
+ end
+
describe '#add_repository' do
shared_examples '#add_repository' do
let(:repository_storage) { 'default' }
@@ -162,83 +148,76 @@ describe Gitlab::Shell do
end
describe '#remove_repository' do
+ subject { gitlab_shell.remove_repository(project.repository_storage_path, project.disk_path) }
+
it 'returns true when the command succeeds' do
- expect(Gitlab::Popen).to receive(:popen)
- .with([projects_path, 'rm-project', 'current/storage', 'project/path.git'],
- nil, popen_vars).and_return([nil, 0])
+ expect(gitlab_projects).to receive(:rm_project) { true }
- expect(gitlab_shell.remove_repository('current/storage', 'project/path')).to be true
+ is_expected.to be_truthy
end
it 'returns false when the command fails' do
- expect(Gitlab::Popen).to receive(:popen)
- .with([projects_path, 'rm-project', 'current/storage', 'project/path.git'],
- nil, popen_vars).and_return(["error", 1])
+ expect(gitlab_projects).to receive(:rm_project) { false }
- expect(gitlab_shell.remove_repository('current/storage', 'project/path')).to be false
+ is_expected.to be_falsy
end
end
describe '#mv_repository' do
it 'returns true when the command succeeds' do
- expect(Gitlab::Popen).to receive(:popen)
- .with([projects_path, 'mv-project', 'current/storage', 'project/path.git', 'project/newpath.git'],
- nil, popen_vars).and_return([nil, 0])
+ expect(gitlab_projects).to receive(:mv_project).with('project/newpath.git') { true }
- expect(gitlab_shell.mv_repository('current/storage', 'project/path', 'project/newpath')).to be true
+ expect(gitlab_shell.mv_repository(project.repository_storage_path, project.disk_path, 'project/newpath')).to be_truthy
end
it 'returns false when the command fails' do
- expect(Gitlab::Popen).to receive(:popen)
- .with([projects_path, 'mv-project', 'current/storage', 'project/path.git', 'project/newpath.git'],
- nil, popen_vars).and_return(["error", 1])
+ expect(gitlab_projects).to receive(:mv_project).with('project/newpath.git') { false }
- expect(gitlab_shell.mv_repository('current/storage', 'project/path', 'project/newpath')).to be false
+ expect(gitlab_shell.mv_repository(project.repository_storage_path, project.disk_path, 'project/newpath')).to be_falsy
end
end
describe '#fork_repository' do
+ subject do
+ gitlab_shell.fork_repository(
+ project.repository_storage_path,
+ project.disk_path,
+ 'new/storage',
+ 'fork/path'
+ )
+ end
+
it 'returns true when the command succeeds' do
- expect(Gitlab::Popen).to receive(:popen)
- .with([projects_path, 'fork-repository', 'current/storage', 'project/path.git', 'new/storage', 'fork/path.git'],
- nil, popen_vars).and_return([nil, 0])
+ expect(gitlab_projects).to receive(:fork_repository).with('new/storage', 'fork/path.git') { true }
- expect(gitlab_shell.fork_repository('current/storage', 'project/path', 'new/storage', 'fork/path')).to be true
+ is_expected.to be_truthy
end
it 'return false when the command fails' do
- expect(Gitlab::Popen).to receive(:popen)
- .with([projects_path, 'fork-repository', 'current/storage', 'project/path.git', 'new/storage', 'fork/path.git'],
- nil, popen_vars).and_return(["error", 1])
+ expect(gitlab_projects).to receive(:fork_repository).with('new/storage', 'fork/path.git') { false }
- expect(gitlab_shell.fork_repository('current/storage', 'project/path', 'new/storage', 'fork/path')).to be false
+ is_expected.to be_falsy
end
end
shared_examples 'fetch_remote' do |gitaly_on|
- let(:project2) { create(:project, :repository) }
- let(:repository) { project2.repository }
+ let(:repository) { project.repository }
def fetch_remote(ssh_auth = nil)
- gitlab_shell.fetch_remote(repository.raw_repository, 'new/storage', ssh_auth: ssh_auth)
+ gitlab_shell.fetch_remote(repository.raw_repository, 'remote-name', ssh_auth: ssh_auth)
end
- def expect_popen(fail = false, vars = {})
- popen_args = [
- projects_path,
- 'fetch-remote',
- TestEnv.repos_path,
- repository.relative_path,
- 'new/storage',
- Gitlab.config.gitlab_shell.git_timeout.to_s
- ]
-
- return_value = fail ? ["error", 1] : [nil, 0]
+ def expect_gitlab_projects(fail = false, options = {})
+ expect(gitlab_projects).to receive(:fetch_remote).with(
+ 'remote-name',
+ timeout,
+ options
+ ).and_return(!fail)
- expect(Gitlab::Popen).to receive(:popen).with(popen_args, nil, popen_vars.merge(vars)).and_return(return_value)
+ allow(gitlab_projects).to receive(:output).and_return('error') if fail
end
- def expect_gitaly_call(fail, vars = {})
+ def expect_gitaly_call(fail, options = {})
receive_fetch_remote =
if fail
receive(:fetch_remote).and_raise(GRPC::NotFound)
@@ -250,12 +229,12 @@ describe Gitlab::Shell do
end
if gitaly_on
- def expect_call(fail, vars = {})
- expect_gitaly_call(fail, vars)
+ def expect_call(fail, options = {})
+ expect_gitaly_call(fail, options)
end
else
- def expect_call(fail, vars = {})
- expect_popen(fail, vars)
+ def expect_call(fail, options = {})
+ expect_gitlab_projects(fail, options)
end
end
@@ -271,20 +250,27 @@ describe Gitlab::Shell do
end
it 'returns true when the command succeeds' do
- expect_call(false)
+ expect_call(false, force: false, tags: true)
expect(fetch_remote).to be_truthy
end
it 'raises an exception when the command fails' do
- expect_call(true)
+ expect_call(true, force: false, tags: true)
expect { fetch_remote }.to raise_error(Gitlab::Shell::Error)
end
+ it 'allows forced and no_tags to be changed' do
+ expect_call(false, force: true, tags: false)
+
+ result = gitlab_shell.fetch_remote(repository.raw_repository, 'remote-name', forced: true, no_tags: true)
+ expect(result).to be_truthy
+ end
+
context 'SSH auth' do
it 'passes the SSH key if specified' do
- expect_call(false, 'GITLAB_SHELL_SSH_KEY' => 'foo')
+ expect_call(false, force: false, tags: true, ssh_key: 'foo')
ssh_auth = build_ssh_auth(ssh_key_auth?: true, ssh_private_key: 'foo')
@@ -292,7 +278,7 @@ describe Gitlab::Shell do
end
it 'does not pass an empty SSH key' do
- expect_call(false)
+ expect_call(false, force: false, tags: true)
ssh_auth = build_ssh_auth(ssh_key_auth: true, ssh_private_key: '')
@@ -300,7 +286,7 @@ describe Gitlab::Shell do
end
it 'does not pass the key unless SSH key auth is to be used' do
- expect_call(false)
+ expect_call(false, force: false, tags: true)
ssh_auth = build_ssh_auth(ssh_key_auth: false, ssh_private_key: 'foo')
@@ -308,7 +294,7 @@ describe Gitlab::Shell do
end
it 'passes the known_hosts data if specified' do
- expect_call(false, 'GITLAB_SHELL_KNOWN_HOSTS' => 'foo')
+ expect_call(false, force: false, tags: true, known_hosts: 'foo')
ssh_auth = build_ssh_auth(ssh_known_hosts: 'foo')
@@ -316,7 +302,7 @@ describe Gitlab::Shell do
end
it 'does not pass empty known_hosts data' do
- expect_call(false)
+ expect_call(false, force: false, tags: true)
ssh_auth = build_ssh_auth(ssh_known_hosts: '')
@@ -324,7 +310,7 @@ describe Gitlab::Shell do
end
it 'does not pass known_hosts data unless SSH is to be used' do
- expect_call(false, popen_vars)
+ expect_call(false, force: false, tags: true)
ssh_auth = build_ssh_auth(ssh_import?: false, ssh_known_hosts: 'foo')
@@ -342,20 +328,23 @@ describe Gitlab::Shell do
end
describe '#import_repository' do
+ let(:import_url) { 'https://gitlab.com/gitlab-org/gitlab-ce.git' }
+
it 'returns true when the command succeeds' do
- expect(Gitlab::Popen).to receive(:popen)
- .with([projects_path, 'import-project', 'current/storage', 'project/path.git', 'https://gitlab.com/gitlab-org/gitlab-ce.git', "800"],
- nil, popen_vars).and_return([nil, 0])
+ expect(gitlab_projects).to receive(:import_project).with(import_url, timeout) { true }
+
+ result = gitlab_shell.import_repository(project.repository_storage_path, project.disk_path, import_url)
- expect(gitlab_shell.import_repository('current/storage', 'project/path', 'https://gitlab.com/gitlab-org/gitlab-ce.git')).to be true
+ expect(result).to be_truthy
end
it 'raises an exception when the command fails' do
- expect(Gitlab::Popen).to receive(:popen)
- .with([projects_path, 'import-project', 'current/storage', 'project/path.git', 'https://gitlab.com/gitlab-org/gitlab-ce.git', "800"],
- nil, popen_vars).and_return(["error", 1])
+ allow(gitlab_projects).to receive(:output) { 'error' }
+ expect(gitlab_projects).to receive(:import_project) { false }
- expect { gitlab_shell.import_repository('current/storage', 'project/path', 'https://gitlab.com/gitlab-org/gitlab-ce.git') }.to raise_error(Gitlab::Shell::Error, "error")
+ expect do
+ gitlab_shell.import_repository(project.repository_storage_path, project.disk_path, import_url)
+ end.to raise_error(Gitlab::Shell::Error, "error")
end
end
end
diff --git a/spec/lib/gitlab/sidekiq_config_spec.rb b/spec/lib/gitlab/sidekiq_config_spec.rb
index 09f95be2213..0c66d764851 100644
--- a/spec/lib/gitlab/sidekiq_config_spec.rb
+++ b/spec/lib/gitlab/sidekiq_config_spec.rb
@@ -16,9 +16,30 @@ describe Gitlab::SidekiqConfig do
expect(queues).to include('post_receive')
expect(queues).to include('merge')
- expect(queues).to include('cronjob')
+ expect(queues).to include('cronjob:stuck_import_jobs')
expect(queues).to include('mailers')
expect(queues).to include('default')
end
end
+
+ describe '.expand_queues' do
+ it 'expands queue namespaces to concrete queue names' do
+ queues = described_class.expand_queues(%w[cronjob])
+
+ expect(queues).to include('cronjob:stuck_import_jobs')
+ expect(queues).to include('cronjob:stuck_merge_jobs')
+ end
+
+ it 'lets concrete queue names pass through' do
+ queues = described_class.expand_queues(%w[post_receive])
+
+ expect(queues).to include('post_receive')
+ end
+
+ it 'lets unknown queues pass through' do
+ queues = described_class.expand_queues(%w[unknown])
+
+ expect(queues).to include('unknown')
+ end
+ end
end
diff --git a/spec/lib/gitlab/sidekiq_versioning/manager_spec.rb b/spec/lib/gitlab/sidekiq_versioning/manager_spec.rb
new file mode 100644
index 00000000000..7debf70a16f
--- /dev/null
+++ b/spec/lib/gitlab/sidekiq_versioning/manager_spec.rb
@@ -0,0 +1,22 @@
+require 'spec_helper'
+
+describe Gitlab::SidekiqVersioning::Manager do
+ before do
+ Sidekiq::Manager.prepend described_class
+ end
+
+ describe '#initialize' do
+ it 'listens on all expanded queues' do
+ manager = Sidekiq::Manager.new(queues: %w[post_receive repository_fork cronjob unknown])
+
+ queues = manager.options[:queues]
+
+ expect(queues).to include('post_receive')
+ expect(queues).to include('repository_fork')
+ expect(queues).to include('cronjob')
+ expect(queues).to include('cronjob:stuck_import_jobs')
+ expect(queues).to include('cronjob:stuck_merge_jobs')
+ expect(queues).to include('unknown')
+ end
+ end
+end
diff --git a/spec/lib/gitlab/sidekiq_versioning_spec.rb b/spec/lib/gitlab/sidekiq_versioning_spec.rb
new file mode 100644
index 00000000000..fa6d42e730d
--- /dev/null
+++ b/spec/lib/gitlab/sidekiq_versioning_spec.rb
@@ -0,0 +1,44 @@
+require 'spec_helper'
+
+describe Gitlab::SidekiqVersioning, :sidekiq, :redis do
+ let(:foo_worker) do
+ Class.new do
+ def self.name
+ 'FooWorker'
+ end
+
+ include ApplicationWorker
+ end
+ end
+
+ let(:bar_worker) do
+ Class.new do
+ def self.name
+ 'BarWorker'
+ end
+
+ include ApplicationWorker
+ end
+ end
+
+ before do
+ allow(Gitlab::SidekiqConfig).to receive(:workers).and_return([foo_worker, bar_worker])
+ allow(Gitlab::SidekiqConfig).to receive(:worker_queues).and_return([foo_worker.queue, bar_worker.queue])
+ end
+
+ describe '.install!' do
+ it 'prepends SidekiqVersioning::Manager into Sidekiq::Manager' do
+ described_class.install!
+
+ expect(Sidekiq::Manager).to include(Gitlab::SidekiqVersioning::Manager)
+ end
+
+ it 'registers all versionless and versioned queues with Redis' do
+ described_class.install!
+
+ queues = Sidekiq::Queue.all.map(&:name)
+ expect(queues).to include('foo')
+ expect(queues).to include('bar')
+ end
+ end
+end
diff --git a/spec/lib/gitlab/slash_commands/issue_new_spec.rb b/spec/lib/gitlab/slash_commands/issue_new_spec.rb
index 75ae58d0582..3b077c58c50 100644
--- a/spec/lib/gitlab/slash_commands/issue_new_spec.rb
+++ b/spec/lib/gitlab/slash_commands/issue_new_spec.rb
@@ -7,7 +7,7 @@ describe Gitlab::SlashCommands::IssueNew do
let(:regex_match) { described_class.match("issue create bird is the word") }
before do
- project.team << [user, :master]
+ project.add_master(user)
end
subject do
diff --git a/spec/lib/gitlab/slash_commands/issue_search_spec.rb b/spec/lib/gitlab/slash_commands/issue_search_spec.rb
index 51f59216413..e41e5254dde 100644
--- a/spec/lib/gitlab/slash_commands/issue_search_spec.rb
+++ b/spec/lib/gitlab/slash_commands/issue_search_spec.rb
@@ -21,7 +21,7 @@ describe Gitlab::SlashCommands::IssueSearch do
context 'the user has access' do
before do
- project.team << [user, :master]
+ project.add_master(user)
end
it 'returns all results' do
diff --git a/spec/lib/gitlab/slash_commands/issue_show_spec.rb b/spec/lib/gitlab/slash_commands/issue_show_spec.rb
index 08c380ca8f1..e5834d5a2ee 100644
--- a/spec/lib/gitlab/slash_commands/issue_show_spec.rb
+++ b/spec/lib/gitlab/slash_commands/issue_show_spec.rb
@@ -8,7 +8,7 @@ describe Gitlab::SlashCommands::IssueShow do
let(:regex_match) { described_class.match("issue show #{issue.iid}") }
before do
- project.team << [user, :master]
+ project.add_master(user)
end
subject do
diff --git a/spec/lib/gitlab/tcp_checker_spec.rb b/spec/lib/gitlab/tcp_checker_spec.rb
new file mode 100644
index 00000000000..4acf0334496
--- /dev/null
+++ b/spec/lib/gitlab/tcp_checker_spec.rb
@@ -0,0 +1,32 @@
+require 'spec_helper'
+
+describe Gitlab::TcpChecker do
+ before do
+ @server = TCPServer.new('localhost', 0)
+ _, @port, _, @ip = @server.addr
+ end
+
+ after do
+ @server.close
+ end
+
+ subject(:checker) { described_class.new(@ip, @port) }
+
+ describe '#check' do
+ subject { checker.check }
+
+ it 'can connect to an open port' do
+ is_expected.to be_truthy
+
+ expect(checker.error).to be_nil
+ end
+
+ it 'fails to connect to a closed port' do
+ @server.close
+
+ is_expected.to be_falsy
+
+ expect(checker.error).to be_a(Errno::ECONNREFUSED)
+ end
+ end
+end
diff --git a/spec/lib/gitlab/user_access_spec.rb b/spec/lib/gitlab/user_access_spec.rb
index cd97416bcc9..7280acb6c82 100644
--- a/spec/lib/gitlab/user_access_spec.rb
+++ b/spec/lib/gitlab/user_access_spec.rb
@@ -8,19 +8,19 @@ describe Gitlab::UserAccess do
describe '#can_push_to_branch?' do
describe 'push to none protected branch' do
it 'returns true if user is a master' do
- project.team << [user, :master]
+ project.add_master(user)
expect(access.can_push_to_branch?('random_branch')).to be_truthy
end
it 'returns true if user is a developer' do
- project.team << [user, :developer]
+ project.add_developer(user)
expect(access.can_push_to_branch?('random_branch')).to be_truthy
end
it 'returns false if user is a reporter' do
- project.team << [user, :reporter]
+ project.add_reporter(user)
expect(access.can_push_to_branch?('random_branch')).to be_falsey
end
@@ -31,34 +31,34 @@ describe Gitlab::UserAccess do
let(:project_access) { described_class.new(user, project: empty_project) }
it 'returns true if user is master' do
- empty_project.team << [user, :master]
+ empty_project.add_master(user)
expect(project_access.can_push_to_branch?('master')).to be_truthy
end
it 'returns false if user is developer and project is fully protected' do
- empty_project.team << [user, :developer]
+ empty_project.add_developer(user)
stub_application_setting(default_branch_protection: Gitlab::Access::PROTECTION_FULL)
expect(project_access.can_push_to_branch?('master')).to be_falsey
end
it 'returns false if user is developer and it is not allowed to push new commits but can merge into branch' do
- empty_project.team << [user, :developer]
+ empty_project.add_developer(user)
stub_application_setting(default_branch_protection: Gitlab::Access::PROTECTION_DEV_CAN_MERGE)
expect(project_access.can_push_to_branch?('master')).to be_falsey
end
it 'returns true if user is developer and project is unprotected' do
- empty_project.team << [user, :developer]
+ empty_project.add_developer(user)
stub_application_setting(default_branch_protection: Gitlab::Access::PROTECTION_NONE)
expect(project_access.can_push_to_branch?('master')).to be_truthy
end
it 'returns true if user is developer and project grants developers permission' do
- empty_project.team << [user, :developer]
+ empty_project.add_developer(user)
stub_application_setting(default_branch_protection: Gitlab::Access::PROTECTION_DEV_CAN_PUSH)
expect(project_access.can_push_to_branch?('master')).to be_truthy
@@ -70,25 +70,25 @@ describe Gitlab::UserAccess do
let(:not_existing_branch) { create :protected_branch, :developers_can_merge, project: project }
it 'returns true if user is a master' do
- project.team << [user, :master]
+ project.add_master(user)
expect(access.can_push_to_branch?(branch.name)).to be_truthy
end
it 'returns false if user is a developer' do
- project.team << [user, :developer]
+ project.add_developer(user)
expect(access.can_push_to_branch?(branch.name)).to be_falsey
end
it 'returns false if user is a reporter' do
- project.team << [user, :reporter]
+ project.add_reporter(user)
expect(access.can_push_to_branch?(branch.name)).to be_falsey
end
it 'returns false if branch does not exist' do
- project.team << [user, :developer]
+ project.add_developer(user)
expect(access.can_push_to_branch?(not_existing_branch.name)).to be_falsey
end
@@ -100,19 +100,19 @@ describe Gitlab::UserAccess do
end
it 'returns true if user is a master' do
- project.team << [user, :master]
+ project.add_master(user)
expect(access.can_push_to_branch?(@branch.name)).to be_truthy
end
it 'returns true if user is a developer' do
- project.team << [user, :developer]
+ project.add_developer(user)
expect(access.can_push_to_branch?(@branch.name)).to be_truthy
end
it 'returns false if user is a reporter' do
- project.team << [user, :reporter]
+ project.add_reporter(user)
expect(access.can_push_to_branch?(@branch.name)).to be_falsey
end
@@ -124,19 +124,19 @@ describe Gitlab::UserAccess do
end
it 'returns true if user is a master' do
- project.team << [user, :master]
+ project.add_master(user)
expect(access.can_merge_to_branch?(@branch.name)).to be_truthy
end
it 'returns true if user is a developer' do
- project.team << [user, :developer]
+ project.add_developer(user)
expect(access.can_merge_to_branch?(@branch.name)).to be_truthy
end
it 'returns false if user is a reporter' do
- project.team << [user, :reporter]
+ project.add_reporter(user)
expect(access.can_merge_to_branch?(@branch.name)).to be_falsey
end
diff --git a/spec/lib/gitlab/utils/strong_memoize_spec.rb b/spec/lib/gitlab/utils/strong_memoize_spec.rb
index 4a104ab6d97..473f8100771 100644
--- a/spec/lib/gitlab/utils/strong_memoize_spec.rb
+++ b/spec/lib/gitlab/utils/strong_memoize_spec.rb
@@ -49,4 +49,16 @@ describe Gitlab::Utils::StrongMemoize do
end
end
end
+
+ describe '#clear_memoization' do
+ let(:value) { 'mepmep' }
+
+ it 'removes the instance variable' do
+ object.method_name
+
+ object.clear_memoization(:method_name)
+
+ expect(object.instance_variable_defined?(:@method_name)).to be(false)
+ end
+ end
end
diff --git a/spec/lib/gitlab/view/presenter/factory_spec.rb b/spec/lib/gitlab/view/presenter/factory_spec.rb
index 70d2e22b48f..6120bafb2e3 100644
--- a/spec/lib/gitlab/view/presenter/factory_spec.rb
+++ b/spec/lib/gitlab/view/presenter/factory_spec.rb
@@ -27,5 +27,13 @@ describe Gitlab::View::Presenter::Factory do
expect(presenter).to be_a(Ci::BuildPresenter)
end
+
+ it 'uses the presenter_class if given on #initialize' do
+ MyCustomPresenter = Class.new(described_class)
+
+ presenter = described_class.new(build, presenter_class: MyCustomPresenter).fabricate!
+
+ expect(presenter).to be_a(MyCustomPresenter)
+ end
end
end
diff --git a/spec/lib/gitlab/visibility_level_spec.rb b/spec/lib/gitlab/visibility_level_spec.rb
index 48a67773de9..d85dac630b4 100644
--- a/spec/lib/gitlab/visibility_level_spec.rb
+++ b/spec/lib/gitlab/visibility_level_spec.rb
@@ -49,4 +49,31 @@ describe Gitlab::VisibilityLevel do
.to eq([Gitlab::VisibilityLevel::PUBLIC])
end
end
+
+ describe '.allowed_levels' do
+ it 'only includes the levels that arent restricted' do
+ stub_application_setting(restricted_visibility_levels: [Gitlab::VisibilityLevel::INTERNAL])
+
+ expect(described_class.allowed_levels)
+ .to contain_exactly(described_class::PRIVATE, described_class::PUBLIC)
+ end
+ end
+
+ describe '.closest_allowed_level' do
+ it 'picks INTERNAL instead of PUBLIC if public is restricted' do
+ stub_application_setting(restricted_visibility_levels: [Gitlab::VisibilityLevel::PUBLIC])
+
+ expect(described_class.closest_allowed_level(described_class::PUBLIC))
+ .to eq(described_class::INTERNAL)
+ end
+
+ it 'picks PRIVATE if nothing is available' do
+ stub_application_setting(restricted_visibility_levels: [Gitlab::VisibilityLevel::PUBLIC,
+ Gitlab::VisibilityLevel::INTERNAL,
+ Gitlab::VisibilityLevel::PRIVATE])
+
+ expect(described_class.closest_allowed_level(described_class::PUBLIC))
+ .to eq(described_class::PRIVATE)
+ end
+ end
end
diff --git a/spec/lib/google_api/cloud_platform/client_spec.rb b/spec/lib/google_api/cloud_platform/client_spec.rb
index ecb4034ec8b..f65e41dfea3 100644
--- a/spec/lib/google_api/cloud_platform/client_spec.rb
+++ b/spec/lib/google_api/cloud_platform/client_spec.rb
@@ -50,6 +50,30 @@ describe GoogleApi::CloudPlatform::Client do
end
end
+ describe '#projects_list' do
+ subject { client.projects_list }
+ let(:projects) { double }
+
+ before do
+ allow_any_instance_of(Google::Apis::CloudresourcemanagerV1::CloudResourceManagerService)
+ .to receive(:fetch_all).and_return(projects)
+ end
+
+ it { is_expected.to eq(projects) }
+ end
+
+ describe '#projects_get_billing_info' do
+ subject { client.projects_get_billing_info('project') }
+ let(:billing_info) { double }
+
+ before do
+ allow_any_instance_of(Google::Apis::CloudbillingV1::CloudbillingService)
+ .to receive(:get_project_billing_info).and_return(billing_info)
+ end
+
+ it { is_expected.to eq(billing_info) }
+ end
+
describe '#projects_zones_clusters_get' do
subject { client.projects_zones_clusters_get(spy, spy, spy) }
let(:gke_cluster) { double }
diff --git a/spec/mailers/notify_spec.rb b/spec/mailers/notify_spec.rb
index e1d71a9573b..cbc8c67da61 100644
--- a/spec/mailers/notify_spec.rb
+++ b/spec/mailers/notify_spec.rb
@@ -342,6 +342,46 @@ describe Notify do
end
end
+ context 'for issue notes' do
+ let(:host) { Gitlab.config.gitlab.host }
+
+ context 'in discussion' do
+ set(:first_note) { create(:discussion_note_on_issue) }
+ set(:second_note) { create(:discussion_note_on_issue, in_reply_to: first_note) }
+ set(:third_note) { create(:discussion_note_on_issue, in_reply_to: second_note) }
+
+ subject { described_class.note_issue_email(recipient.id, third_note.id) }
+
+ it 'has In-Reply-To header pointing to previous note in discussion' do
+ expect(subject.header['In-Reply-To'].message_ids).to eq(["note_#{second_note.id}@#{host}"])
+ end
+
+ it 'has References header including the notes and issue of the discussion' do
+ expect(subject.header['References'].message_ids).to include("issue_#{first_note.noteable.id}@#{host}",
+ "note_#{first_note.id}@#{host}",
+ "note_#{second_note.id}@#{host}")
+ end
+
+ it 'has X-GitLab-Discussion-ID header' do
+ expect(subject.header['X-GitLab-Discussion-ID'].value).to eq(third_note.discussion.id)
+ end
+ end
+
+ context 'individual issue comments' do
+ set(:note) { create(:note_on_issue) }
+
+ subject { described_class.note_issue_email(recipient.id, note.id) }
+
+ it 'has In-Reply-To header pointing to the issue' do
+ expect(subject.header['In-Reply-To'].message_ids).to eq(["issue_#{note.noteable.id}@#{host}"])
+ end
+
+ it 'has References header including the notes and issue of the discussion' do
+ expect(subject.header['References'].message_ids).to include("issue_#{note.noteable.id}@#{host}")
+ end
+ end
+ end
+
context 'for snippet notes' do
let(:project_snippet) { create(:project_snippet, project: project) }
let(:project_snippet_note) { create(:note_on_project_snippet, project: project, noteable: project_snippet) }
@@ -377,7 +417,7 @@ describe Notify do
context 'for a project in a user namespace' do
let(:project) do
create(:project, :public, :access_requestable) do |project|
- project.team << [project.owner, :master, project.owner]
+ project.add_master(project.owner, current_user: project.owner)
end
end
@@ -480,7 +520,7 @@ describe Notify do
end
describe 'project invitation' do
- let(:master) { create(:user).tap { |u| project.team << [u, :master] } }
+ let(:master) { create(:user).tap { |u| project.add_master(u) } }
let(:project_member) { invite_to_project(project, inviter: master) }
subject { described_class.member_invited_email('project', project_member.id, project_member.invite_token) }
@@ -500,7 +540,7 @@ describe Notify do
describe 'project invitation accepted' do
let(:invited_user) { create(:user, name: 'invited user') }
- let(:master) { create(:user).tap { |u| project.team << [u, :master] } }
+ let(:master) { create(:user).tap { |u| project.add_master(u) } }
let(:project_member) do
invitee = invite_to_project(project, inviter: master)
invitee.accept_invite!(invited_user)
@@ -523,7 +563,7 @@ describe Notify do
end
describe 'project invitation declined' do
- let(:master) { create(:user).tap { |u| project.team << [u, :master] } }
+ let(:master) { create(:user).tap { |u| project.add_master(u) } }
let(:project_member) do
invitee = invite_to_project(project, inviter: master)
invitee.decline_invite!
diff --git a/spec/migrations/clean_up_for_members_spec.rb b/spec/migrations/clean_up_for_members_spec.rb
new file mode 100644
index 00000000000..0258860d169
--- /dev/null
+++ b/spec/migrations/clean_up_for_members_spec.rb
@@ -0,0 +1,78 @@
+require 'spec_helper'
+require Rails.root.join('db', 'migrate', '20171216111734_clean_up_for_members.rb')
+
+describe CleanUpForMembers, :migration do
+ let(:migration) { described_class.new }
+ let!(:group_member) { create_group_member }
+ let!(:unbinded_group_member) { create_group_member }
+ let!(:invited_group_member) { create_group_member(true) }
+ let!(:not_valid_group_member) { create_group_member }
+ let!(:project_member) { create_project_member }
+ let!(:invited_project_member) { create_project_member(true) }
+ let!(:unbinded_project_member) { create_project_member }
+ let!(:not_valid_project_member) { create_project_member }
+
+ it 'removes members without proper user_id' do
+ unbinded_group_member.update_column(:user_id, nil)
+ not_valid_group_member.update_column(:user_id, 9999)
+ unbinded_project_member.update_column(:user_id, nil)
+ not_valid_project_member.update_column(:user_id, 9999)
+
+ migrate!
+
+ expect(Member.all).not_to include(unbinded_group_member, not_valid_group_member, unbinded_project_member, not_valid_project_member)
+ expect(Member.all).to include(group_member, invited_group_member, project_member, invited_project_member)
+ end
+
+ def create_group_member(invited = false)
+ fill_member(GroupMember.new(group: create_group), invited)
+ end
+
+ def create_project_member(invited = false)
+ fill_member(ProjectMember.new(project: create_project), invited)
+ end
+
+ def fill_member(member_object, invited)
+ member_object.tap do |m|
+ m.access_level = 40
+ m.notification_level = 3
+
+ if invited
+ m.user_id = nil
+ m.invite_token = 'xxx'
+ m.invite_email = 'email@email.com'
+ else
+ m.user_id = create_user.id
+ end
+
+ m.save
+ end
+
+ member_object
+ end
+
+ def create_group
+ name = FFaker::Lorem.characters(10)
+
+ Group.create(name: name, path: name.downcase.gsub(/\s/, '_'))
+ end
+
+ def create_project
+ name = FFaker::Lorem.characters(10)
+ creator = create_user
+
+ Project.create(name: name,
+ path: name.downcase.gsub(/\s/, '_'),
+ namespace: creator.namespace,
+ creator: creator)
+ end
+
+ def create_user
+ User.create(email: FFaker::Internet.email,
+ password: '12345678',
+ name: FFaker::Name.name,
+ username: FFaker::Internet.user_name,
+ confirmed_at: Time.now,
+ confirmation_token: nil)
+ end
+end
diff --git a/spec/migrations/delete_conflicting_redirect_routes_spec.rb b/spec/migrations/delete_conflicting_redirect_routes_spec.rb
index 1df2477da51..8a191bd7139 100644
--- a/spec/migrations/delete_conflicting_redirect_routes_spec.rb
+++ b/spec/migrations/delete_conflicting_redirect_routes_spec.rb
@@ -10,9 +10,6 @@ describe DeleteConflictingRedirectRoutes, :migration, :sidekiq do
end
before do
- stub_const("DeleteConflictingRedirectRoutes::BATCH_SIZE", 2)
- stub_const("Gitlab::Database::MigrationHelpers::BACKGROUND_MIGRATION_JOB_BUFFER_SIZE", 2)
-
routes.create!(id: 1, source_id: 1, source_type: 'Namespace', path: 'foo1')
routes.create!(id: 2, source_id: 2, source_type: 'Namespace', path: 'foo2')
routes.create!(id: 3, source_id: 3, source_type: 'Namespace', path: 'foo3')
@@ -32,27 +29,14 @@ describe DeleteConflictingRedirectRoutes, :migration, :sidekiq do
redirect_routes.create!(source_id: 1, source_type: 'Namespace', path: 'foo5')
end
- it 'correctly schedules background migrations' do
+ # No-op. See https://gitlab.com/gitlab-com/infrastructure/issues/3460#note_53223252
+ it 'NO-OP: does not schedule any background migrations' do
Sidekiq::Testing.fake! do
Timecop.freeze do
migrate!
- expect(BackgroundMigrationWorker.jobs[0]['args']).to eq([described_class::MIGRATION, [1, 2]])
- expect(BackgroundMigrationWorker.jobs[0]['at']).to eq(12.seconds.from_now.to_f)
- expect(BackgroundMigrationWorker.jobs[1]['args']).to eq([described_class::MIGRATION, [3, 4]])
- expect(BackgroundMigrationWorker.jobs[1]['at']).to eq(24.seconds.from_now.to_f)
- expect(BackgroundMigrationWorker.jobs[2]['args']).to eq([described_class::MIGRATION, [5, 5]])
- expect(BackgroundMigrationWorker.jobs[2]['at']).to eq(36.seconds.from_now.to_f)
- expect(BackgroundMigrationWorker.jobs.size).to eq 3
+ expect(BackgroundMigrationWorker.jobs.size).to eq 0
end
end
end
-
- it 'schedules background migrations' do
- Sidekiq::Testing.inline! do
- expect do
- migrate!
- end.to change { redirect_routes.count }.from(8).to(3)
- end
- end
end
diff --git a/spec/migrations/issues_moved_to_id_foreign_key_spec.rb b/spec/migrations/issues_moved_to_id_foreign_key_spec.rb
new file mode 100644
index 00000000000..d2eef81f396
--- /dev/null
+++ b/spec/migrations/issues_moved_to_id_foreign_key_spec.rb
@@ -0,0 +1,25 @@
+require 'spec_helper'
+require Rails.root.join('db', 'migrate', '20171106151218_issues_moved_to_id_foreign_key.rb')
+
+# The schema version has to be far enough in advance to have the
+# only_mirror_protected_branches column in the projects table to create a
+# project via FactoryBot.
+describe IssuesMovedToIdForeignKey, :migration, schema: 20171114150259 do
+ let!(:issue_first) { create(:issue, moved_to_id: issue_second.id) }
+ let!(:issue_second) { create(:issue, moved_to_id: issue_third.id) }
+ let!(:issue_third) { create(:issue) }
+
+ subject { described_class.new }
+
+ it 'removes the orphaned moved_to_id' do
+ subject.down
+
+ issue_third.update_attributes(moved_to_id: 100000)
+
+ subject.up
+
+ expect(issue_first.reload.moved_to_id).to eq(issue_second.id)
+ expect(issue_second.reload.moved_to_id).to eq(issue_third.id)
+ expect(issue_third.reload.moved_to_id).to be_nil
+ end
+end
diff --git a/spec/migrations/migrate_gcp_clusters_to_new_clusters_architectures_spec.rb b/spec/migrations/migrate_gcp_clusters_to_new_clusters_architectures_spec.rb
index 05f281fffff..c81ec887ded 100644
--- a/spec/migrations/migrate_gcp_clusters_to_new_clusters_architectures_spec.rb
+++ b/spec/migrations/migrate_gcp_clusters_to_new_clusters_architectures_spec.rb
@@ -2,9 +2,10 @@ require 'spec_helper'
require Rails.root.join('db', 'post_migrate', '20171013104327_migrate_gcp_clusters_to_new_clusters_architectures.rb')
describe MigrateGcpClustersToNewClustersArchitectures, :migration do
- let(:project) { create(:project) }
+ let(:projects) { table(:projects) }
+ let(:project) { projects.create }
let(:user) { create(:user) }
- let(:service) { create(:kubernetes_service, project: project) }
+ let(:service) { create(:kubernetes_service, project_id: project.id) }
context 'when cluster is being created' do
let(:project_id) { project.id }
@@ -32,7 +33,7 @@ describe MigrateGcpClustersToNewClustersArchitectures, :migration do
let(:encrypted_gcp_token) { "'encrypted_gcp_token'" }
let(:encrypted_gcp_token_iv) { "'encrypted_gcp_token_iv'" }
- let(:cluster) { Clusters::Cluster.last }
+ let(:cluster) { described_class::Cluster.last }
let(:cluster_id) { cluster.id }
before do
@@ -45,21 +46,20 @@ describe MigrateGcpClustersToNewClustersArchitectures, :migration do
it 'correctly migrate to new clusters architectures' do
migrate!
- expect(Clusters::Cluster.count).to eq(1)
- expect(Clusters::Project.count).to eq(1)
- expect(Clusters::Providers::Gcp.count).to eq(1)
- expect(Clusters::Platforms::Kubernetes.count).to eq(1)
+ expect(described_class::Cluster.count).to eq(1)
+ expect(described_class::ClustersProject.count).to eq(1)
+ expect(described_class::ProvidersGcp.count).to eq(1)
+ expect(described_class::PlatformsKubernetes.count).to eq(1)
- expect(cluster.user).to eq(user)
+ expect(cluster.user_id).to eq(user.id)
expect(cluster.enabled).to be_truthy
expect(cluster.name).to eq(gcp_cluster_name.delete!("'"))
expect(cluster.provider_type).to eq('gcp')
expect(cluster.platform_type).to eq('kubernetes')
- expect(cluster.project).to eq(project)
- expect(project.clusters).to include(cluster)
+ expect(cluster.project_ids).to include(project.id)
- expect(cluster.provider_gcp.cluster).to eq(cluster)
+ expect(cluster.provider_gcp.cluster_id).to eq(cluster.id)
expect(cluster.provider_gcp.status).to eq(status)
expect(cluster.provider_gcp.status_reason).to eq(tr(status_reason))
expect(cluster.provider_gcp.gcp_project_id).to eq(tr(gcp_project_id))
@@ -71,7 +71,7 @@ describe MigrateGcpClustersToNewClustersArchitectures, :migration do
expect(cluster.provider_gcp.encrypted_access_token).to eq(tr(encrypted_gcp_token))
expect(cluster.provider_gcp.encrypted_access_token_iv).to eq(tr(encrypted_gcp_token_iv))
- expect(cluster.platform_kubernetes.cluster).to eq(cluster)
+ expect(cluster.platform_kubernetes.cluster_id).to eq(cluster.id)
expect(cluster.platform_kubernetes.api_url).to be_nil
expect(cluster.platform_kubernetes.ca_cert).to be_nil
expect(cluster.platform_kubernetes.namespace).to eq(tr(project_namespace))
@@ -109,7 +109,7 @@ describe MigrateGcpClustersToNewClustersArchitectures, :migration do
let(:encrypted_gcp_token) { "'encrypted_gcp_token'" }
let(:encrypted_gcp_token_iv) { "'encrypted_gcp_token_iv'" }
- let(:cluster) { Clusters::Cluster.last }
+ let(:cluster) { described_class::Cluster.last }
let(:cluster_id) { cluster.id }
before do
@@ -122,21 +122,20 @@ describe MigrateGcpClustersToNewClustersArchitectures, :migration do
it 'correctly migrate to new clusters architectures' do
migrate!
- expect(Clusters::Cluster.count).to eq(1)
- expect(Clusters::Project.count).to eq(1)
- expect(Clusters::Providers::Gcp.count).to eq(1)
- expect(Clusters::Platforms::Kubernetes.count).to eq(1)
+ expect(described_class::Cluster.count).to eq(1)
+ expect(described_class::ClustersProject.count).to eq(1)
+ expect(described_class::ProvidersGcp.count).to eq(1)
+ expect(described_class::PlatformsKubernetes.count).to eq(1)
- expect(cluster.user).to eq(user)
+ expect(cluster.user_id).to eq(user.id)
expect(cluster.enabled).to be_truthy
expect(cluster.name).to eq(tr(gcp_cluster_name))
expect(cluster.provider_type).to eq('gcp')
expect(cluster.platform_type).to eq('kubernetes')
- expect(cluster.project).to eq(project)
- expect(project.clusters).to include(cluster)
+ expect(cluster.project_ids).to include(project.id)
- expect(cluster.provider_gcp.cluster).to eq(cluster)
+ expect(cluster.provider_gcp.cluster_id).to eq(cluster.id)
expect(cluster.provider_gcp.status).to eq(status)
expect(cluster.provider_gcp.status_reason).to eq(tr(status_reason))
expect(cluster.provider_gcp.gcp_project_id).to eq(tr(gcp_project_id))
@@ -148,7 +147,7 @@ describe MigrateGcpClustersToNewClustersArchitectures, :migration do
expect(cluster.provider_gcp.encrypted_access_token).to eq(tr(encrypted_gcp_token))
expect(cluster.provider_gcp.encrypted_access_token_iv).to eq(tr(encrypted_gcp_token_iv))
- expect(cluster.platform_kubernetes.cluster).to eq(cluster)
+ expect(cluster.platform_kubernetes.cluster_id).to eq(cluster.id)
expect(cluster.platform_kubernetes.api_url).to eq('https://' + tr(endpoint))
expect(cluster.platform_kubernetes.ca_cert).to eq(tr(ca_cert))
expect(cluster.platform_kubernetes.namespace).to eq(tr(project_namespace))
diff --git a/spec/migrations/migrate_kubernetes_service_to_new_clusters_architectures_spec.rb b/spec/migrations/migrate_kubernetes_service_to_new_clusters_architectures_spec.rb
new file mode 100644
index 00000000000..df0015b6dd3
--- /dev/null
+++ b/spec/migrations/migrate_kubernetes_service_to_new_clusters_architectures_spec.rb
@@ -0,0 +1,312 @@
+require 'spec_helper'
+require Rails.root.join('db', 'post_migrate', '20171124104327_migrate_kubernetes_service_to_new_clusters_architectures.rb')
+
+describe MigrateKubernetesServiceToNewClustersArchitectures, :migration do
+ context 'when unique KubernetesService exists' do
+ shared_examples 'KubernetesService migration' do
+ let(:sample_num) { 2 }
+
+ let(:projects) do
+ (1..sample_num).each_with_object([]) do |n, array|
+ array << MigrateKubernetesServiceToNewClustersArchitectures::Project.create!
+ end
+ end
+
+ let!(:kubernetes_services) do
+ projects.map do |project|
+ MigrateKubernetesServiceToNewClustersArchitectures::Service.create!(
+ project: project,
+ active: active,
+ category: 'deployment',
+ type: 'KubernetesService',
+ properties: "{\"namespace\":\"prod\",\"api_url\":\"https://kubernetes#{project.id}.com\",\"ca_pem\":\"ca_pem#{project.id}\",\"token\":\"token#{project.id}\"}")
+ end
+ end
+
+ it 'migrates the KubernetesService to Platform::Kubernetes' do
+ expect { migrate! }.to change { MigrateKubernetesServiceToNewClustersArchitectures::Cluster.count }.by(sample_num)
+
+ projects.each do |project|
+ project.clusters.last.tap do |cluster|
+ expect(cluster.enabled).to eq(active)
+ expect(cluster.platform_kubernetes.api_url).to eq(project.kubernetes_service.api_url)
+ expect(cluster.platform_kubernetes.ca_cert).to eq(project.kubernetes_service.ca_pem)
+ expect(cluster.platform_kubernetes.token).to eq(project.kubernetes_service.token)
+ expect(project.kubernetes_service).not_to be_active
+ end
+ end
+ end
+ end
+
+ context 'when KubernetesService is active' do
+ let(:active) { true }
+
+ it_behaves_like 'KubernetesService migration'
+ end
+ end
+
+ context 'when unique KubernetesService spawned from Service Template' do
+ let(:sample_num) { 2 }
+
+ let(:projects) do
+ (1..sample_num).each_with_object([]) do |n, array|
+ array << MigrateKubernetesServiceToNewClustersArchitectures::Project.create!
+ end
+ end
+
+ let!(:kubernetes_service_template) do
+ MigrateKubernetesServiceToNewClustersArchitectures::Service.create!(
+ template: true,
+ category: 'deployment',
+ type: 'KubernetesService',
+ properties: "{\"namespace\":\"prod\",\"api_url\":\"https://sample.kubernetes.com\",\"ca_pem\":\"ca_pem-sample\",\"token\":\"token-sample\"}")
+ end
+
+ let!(:kubernetes_services) do
+ projects.map do |project|
+ MigrateKubernetesServiceToNewClustersArchitectures::Service.create!(
+ project: project,
+ category: 'deployment',
+ type: 'KubernetesService',
+ properties: "{\"namespace\":\"prod\",\"api_url\":\"#{kubernetes_service_template.api_url}\",\"ca_pem\":\"#{kubernetes_service_template.ca_pem}\",\"token\":\"#{kubernetes_service_template.token}\"}")
+ end
+ end
+
+ it 'migrates the KubernetesService to Platform::Kubernetes without template' do
+ expect { migrate! }.to change { MigrateKubernetesServiceToNewClustersArchitectures::Cluster.count }.by(sample_num)
+
+ projects.each do |project|
+ project.clusters.last.tap do |cluster|
+ expect(cluster.platform_kubernetes.api_url).to eq(project.kubernetes_service.api_url)
+ expect(cluster.platform_kubernetes.ca_cert).to eq(project.kubernetes_service.ca_pem)
+ expect(cluster.platform_kubernetes.token).to eq(project.kubernetes_service.token)
+ expect(project.kubernetes_service).not_to be_active
+ end
+ end
+ end
+ end
+
+ context 'when managed KubernetesService exists' do
+ let(:project) { MigrateKubernetesServiceToNewClustersArchitectures::Project.create! }
+
+ let(:cluster) do
+ MigrateKubernetesServiceToNewClustersArchitectures::Cluster.create!(
+ projects: [project],
+ name: 'sample-cluster',
+ platform_type: :kubernetes,
+ provider_type: :user,
+ platform_kubernetes_attributes: {
+ api_url: 'https://sample.kubernetes.com',
+ ca_cert: 'ca_pem-sample',
+ token: 'token-sample'
+ } )
+ end
+
+ let!(:kubernetes_service) do
+ MigrateKubernetesServiceToNewClustersArchitectures::Service.create!(
+ project: project,
+ active: cluster.enabled,
+ category: 'deployment',
+ type: 'KubernetesService',
+ properties: "{\"api_url\":\"#{cluster.platform_kubernetes.api_url}\"}")
+ end
+
+ it 'does not migrate the KubernetesService and disables the kubernetes_service' do # Because the corresponding Platform::Kubernetes already exists
+ expect { migrate! }.not_to change { MigrateKubernetesServiceToNewClustersArchitectures::Cluster.count }
+
+ kubernetes_service.reload
+ expect(kubernetes_service).not_to be_active
+ end
+ end
+
+ context 'when production cluster has already been existed' do # i.e. There are no environment_scope conflicts
+ let(:project) { MigrateKubernetesServiceToNewClustersArchitectures::Project.create! }
+
+ let(:cluster) do
+ MigrateKubernetesServiceToNewClustersArchitectures::Cluster.create!(
+ projects: [project],
+ name: 'sample-cluster',
+ platform_type: :kubernetes,
+ provider_type: :user,
+ environment_scope: 'production/*',
+ platform_kubernetes_attributes: {
+ api_url: 'https://sample.kubernetes.com',
+ ca_cert: 'ca_pem-sample',
+ token: 'token-sample'
+ } )
+ end
+
+ let!(:kubernetes_service) do
+ MigrateKubernetesServiceToNewClustersArchitectures::Service.create!(
+ project: project,
+ active: true,
+ category: 'deployment',
+ type: 'KubernetesService',
+ properties: "{\"api_url\":\"https://debug.kube.com\"}")
+ end
+
+ it 'migrates the KubernetesService to Platform::Kubernetes' do
+ expect { migrate! }.to change { MigrateKubernetesServiceToNewClustersArchitectures::Cluster.count }.by(1)
+
+ kubernetes_service.reload
+ project.clusters.last.tap do |cluster|
+ expect(cluster.environment_scope).to eq('*')
+ expect(cluster.platform_kubernetes.api_url).to eq(kubernetes_service.api_url)
+ expect(cluster.platform_kubernetes.ca_cert).to eq(kubernetes_service.ca_pem)
+ expect(cluster.platform_kubernetes.token).to eq(kubernetes_service.token)
+ expect(kubernetes_service).not_to be_active
+ end
+ end
+ end
+
+ context 'when default cluster has already been existed' do
+ let(:project) { MigrateKubernetesServiceToNewClustersArchitectures::Project.create! }
+
+ let!(:cluster) do
+ MigrateKubernetesServiceToNewClustersArchitectures::Cluster.create!(
+ projects: [project],
+ name: 'sample-cluster',
+ platform_type: :kubernetes,
+ provider_type: :user,
+ environment_scope: '*',
+ platform_kubernetes_attributes: {
+ api_url: 'https://sample.kubernetes.com',
+ ca_cert: 'ca_pem-sample',
+ token: 'token-sample'
+ } )
+ end
+
+ let!(:kubernetes_service) do
+ MigrateKubernetesServiceToNewClustersArchitectures::Service.create!(
+ project: project,
+ active: true,
+ category: 'deployment',
+ type: 'KubernetesService',
+ properties: "{\"api_url\":\"https://debug.kube.com\"}")
+ end
+
+ it 'migrates the KubernetesService to Platform::Kubernetes with dedicated environment_scope' do # Because environment_scope is duplicated
+ expect { migrate! }.to change { MigrateKubernetesServiceToNewClustersArchitectures::Cluster.count }.by(1)
+
+ kubernetes_service.reload
+ project.clusters.last.tap do |cluster|
+ expect(cluster.environment_scope).to eq('migrated/*')
+ expect(cluster.platform_kubernetes.api_url).to eq(kubernetes_service.api_url)
+ expect(cluster.platform_kubernetes.ca_cert).to eq(kubernetes_service.ca_pem)
+ expect(cluster.platform_kubernetes.token).to eq(kubernetes_service.token)
+ expect(kubernetes_service).not_to be_active
+ end
+ end
+ end
+
+ context 'when default cluster and migrated cluster has already been existed' do
+ let(:project) { MigrateKubernetesServiceToNewClustersArchitectures::Project.create! }
+
+ let!(:cluster) do
+ MigrateKubernetesServiceToNewClustersArchitectures::Cluster.create!(
+ projects: [project],
+ name: 'sample-cluster',
+ platform_type: :kubernetes,
+ provider_type: :user,
+ environment_scope: '*',
+ platform_kubernetes_attributes: {
+ api_url: 'https://sample.kubernetes.com',
+ ca_cert: 'ca_pem-sample',
+ token: 'token-sample'
+ } )
+ end
+
+ let!(:migrated_cluster) do
+ MigrateKubernetesServiceToNewClustersArchitectures::Cluster.create!(
+ projects: [project],
+ name: 'sample-cluster',
+ platform_type: :kubernetes,
+ provider_type: :user,
+ environment_scope: 'migrated/*',
+ platform_kubernetes_attributes: {
+ api_url: 'https://sample.kubernetes.com',
+ ca_cert: 'ca_pem-sample',
+ token: 'token-sample'
+ } )
+ end
+
+ let!(:kubernetes_service) do
+ MigrateKubernetesServiceToNewClustersArchitectures::Service.create!(
+ project: project,
+ active: true,
+ category: 'deployment',
+ type: 'KubernetesService',
+ properties: "{\"api_url\":\"https://debug.kube.com\"}")
+ end
+
+ it 'migrates the KubernetesService to Platform::Kubernetes with dedicated environment_scope' do # Because environment_scope is duplicated
+ expect { migrate! }.to change { MigrateKubernetesServiceToNewClustersArchitectures::Cluster.count }.by(1)
+
+ kubernetes_service.reload
+ project.clusters.last.tap do |cluster|
+ expect(cluster.environment_scope).to eq('migrated0/*')
+ expect(cluster.platform_kubernetes.api_url).to eq(kubernetes_service.api_url)
+ expect(cluster.platform_kubernetes.ca_cert).to eq(kubernetes_service.ca_pem)
+ expect(cluster.platform_kubernetes.token).to eq(kubernetes_service.token)
+ expect(kubernetes_service).not_to be_active
+ end
+ end
+ end
+
+ context 'when KubernetesService has nullified parameters' do
+ let(:project) { MigrateKubernetesServiceToNewClustersArchitectures::Project.create! }
+
+ before do
+ MigrateKubernetesServiceToNewClustersArchitectures::Service.create!(
+ project: project,
+ active: false,
+ category: 'deployment',
+ type: 'KubernetesService',
+ properties: "{}")
+ end
+
+ it 'does not migrate the KubernetesService and disables the kubernetes_service' do
+ expect { migrate! }.not_to change { MigrateKubernetesServiceToNewClustersArchitectures::Cluster.count }
+
+ expect(project.kubernetes_service).not_to be_active
+ end
+ end
+
+ # Platforms::Kubernetes validates `token` reagdless of the activeness,
+ # whereas KubernetesService validates `token` if only it's activated
+ # However, in this migration file, there are no validations because of the re-defined model class
+ # therefore, we should safely add this raw to Platform::Kubernetes
+ context 'when KubernetesService has empty token' do
+ let(:project) { MigrateKubernetesServiceToNewClustersArchitectures::Project.create! }
+
+ before do
+ MigrateKubernetesServiceToNewClustersArchitectures::Service.create!(
+ project: project,
+ active: false,
+ category: 'deployment',
+ type: 'KubernetesService',
+ properties: "{\"namespace\":\"prod\",\"api_url\":\"http://111.111.111.111\",\"ca_pem\":\"a\",\"token\":\"\"}")
+ end
+
+ it 'does not migrate the KubernetesService and disables the kubernetes_service' do
+ expect { migrate! }.to change { MigrateKubernetesServiceToNewClustersArchitectures::Cluster.count }.by(1)
+
+ project.clusters.last.tap do |cluster|
+ expect(cluster.environment_scope).to eq('*')
+ expect(cluster.platform_kubernetes.namespace).to eq('prod')
+ expect(cluster.platform_kubernetes.api_url).to eq('http://111.111.111.111')
+ expect(cluster.platform_kubernetes.ca_cert).to eq('a')
+ expect(cluster.platform_kubernetes.token).to be_empty
+ expect(project.kubernetes_service).not_to be_active
+ end
+ end
+ end
+
+ context 'when KubernetesService does not exist' do
+ let!(:project) { MigrateKubernetesServiceToNewClustersArchitectures::Project.create! }
+
+ it 'does not migrate the KubernetesService' do
+ expect { migrate! }.not_to change { MigrateKubernetesServiceToNewClustersArchitectures::Cluster.count }
+ end
+ end
+end
diff --git a/spec/migrations/migrate_stage_id_reference_in_background_spec.rb b/spec/migrations/migrate_stage_id_reference_in_background_spec.rb
index 9b92f4b70b0..a837498e1b1 100644
--- a/spec/migrations/migrate_stage_id_reference_in_background_spec.rb
+++ b/spec/migrations/migrate_stage_id_reference_in_background_spec.rb
@@ -35,9 +35,9 @@ describe MigrateStageIdReferenceInBackground, :migration, :sidekiq do
Timecop.freeze do
migrate!
- expect(described_class::MIGRATION).to be_scheduled_migration(2.minutes, 1, 2)
- expect(described_class::MIGRATION).to be_scheduled_migration(2.minutes, 3, 3)
- expect(described_class::MIGRATION).to be_scheduled_migration(4.minutes, 4, 5)
+ expect(described_class::MIGRATION).to be_scheduled_delayed_migration(2.minutes, 1, 2)
+ expect(described_class::MIGRATION).to be_scheduled_delayed_migration(2.minutes, 3, 3)
+ expect(described_class::MIGRATION).to be_scheduled_delayed_migration(4.minutes, 4, 5)
expect(BackgroundMigrationWorker.jobs.size).to eq 3
end
end
diff --git a/spec/migrations/migrate_stages_statuses_spec.rb b/spec/migrations/migrate_stages_statuses_spec.rb
index 094c9bc604e..79d2708f9ad 100644
--- a/spec/migrations/migrate_stages_statuses_spec.rb
+++ b/spec/migrations/migrate_stages_statuses_spec.rb
@@ -50,9 +50,9 @@ describe MigrateStagesStatuses, :migration do
Timecop.freeze do
migrate!
- expect(described_class::MIGRATION).to be_scheduled_migration(5.minutes, 1, 1)
- expect(described_class::MIGRATION).to be_scheduled_migration(5.minutes, 2, 2)
- expect(described_class::MIGRATION).to be_scheduled_migration(10.minutes, 3, 3)
+ expect(described_class::MIGRATION).to be_scheduled_delayed_migration(5.minutes, 1, 1)
+ expect(described_class::MIGRATION).to be_scheduled_delayed_migration(5.minutes, 2, 2)
+ expect(described_class::MIGRATION).to be_scheduled_delayed_migration(10.minutes, 3, 3)
expect(BackgroundMigrationWorker.jobs.size).to eq 3
end
end
diff --git a/spec/migrations/normalize_ldap_extern_uids_spec.rb b/spec/migrations/normalize_ldap_extern_uids_spec.rb
index 262d7742aaf..56a78f52802 100644
--- a/spec/migrations/normalize_ldap_extern_uids_spec.rb
+++ b/spec/migrations/normalize_ldap_extern_uids_spec.rb
@@ -27,11 +27,11 @@ describe NormalizeLdapExternUids, :migration, :sidekiq do
migrate!
expect(BackgroundMigrationWorker.jobs[0]['args']).to eq([described_class::MIGRATION, [1, 2]])
- expect(BackgroundMigrationWorker.jobs[0]['at']).to eq(10.seconds.from_now.to_f)
+ expect(BackgroundMigrationWorker.jobs[0]['at']).to eq(5.minutes.from_now.to_f)
expect(BackgroundMigrationWorker.jobs[1]['args']).to eq([described_class::MIGRATION, [3, 4]])
- expect(BackgroundMigrationWorker.jobs[1]['at']).to eq(20.seconds.from_now.to_f)
+ expect(BackgroundMigrationWorker.jobs[1]['at']).to eq(10.minutes.from_now.to_f)
expect(BackgroundMigrationWorker.jobs[2]['args']).to eq([described_class::MIGRATION, [5, 5]])
- expect(BackgroundMigrationWorker.jobs[2]['at']).to eq(30.seconds.from_now.to_f)
+ expect(BackgroundMigrationWorker.jobs[2]['at']).to eq(15.minutes.from_now.to_f)
expect(BackgroundMigrationWorker.jobs.size).to eq 3
end
end
diff --git a/spec/migrations/remove_assignee_id_from_issue_spec.rb b/spec/migrations/remove_assignee_id_from_issue_spec.rb
new file mode 100644
index 00000000000..2c6f992d3ae
--- /dev/null
+++ b/spec/migrations/remove_assignee_id_from_issue_spec.rb
@@ -0,0 +1,37 @@
+require 'spec_helper'
+require Rails.root.join('db', 'post_migrate', '20170523073948_remove_assignee_id_from_issue.rb')
+
+describe RemoveAssigneeIdFromIssue, :migration do
+ let(:issues) { table(:issues) }
+ let(:issue_assignees) { table(:issue_assignees) }
+ let(:users) { table(:users) }
+
+ let!(:user_1) { users.create(email: 'email1@example.com') }
+ let!(:user_2) { users.create(email: 'email2@example.com') }
+ let!(:user_3) { users.create(email: 'email3@example.com') }
+
+ def create_issue(assignees:)
+ issues.create.tap do |issue|
+ assignees.each do |assignee|
+ issue_assignees.create(issue_id: issue.id, user_id: assignee.id)
+ end
+ end
+ end
+
+ let!(:issue_single_assignee) { create_issue(assignees: [user_1]) }
+ let!(:issue_no_assignee) { create_issue(assignees: []) }
+ let!(:issue_multiple_assignees) { create_issue(assignees: [user_2, user_3]) }
+
+ describe '#down' do
+ it 'sets the assignee_id to a random matching assignee from the assignees table' do
+ migrate!
+ disable_migrations_output { described_class.new.down }
+
+ expect(issue_single_assignee.reload.assignee_id).to eq(user_1.id)
+ expect(issue_no_assignee.reload.assignee_id).to be_nil
+ expect(issue_multiple_assignees.reload.assignee_id).to eq(user_2.id).or(user_3.id)
+
+ disable_migrations_output { described_class.new.up }
+ end
+ end
+end
diff --git a/spec/migrations/schedule_create_gpg_key_subkeys_from_gpg_keys_spec.rb b/spec/migrations/schedule_create_gpg_key_subkeys_from_gpg_keys_spec.rb
index 0e884a7d910..65ec07da31c 100644
--- a/spec/migrations/schedule_create_gpg_key_subkeys_from_gpg_keys_spec.rb
+++ b/spec/migrations/schedule_create_gpg_key_subkeys_from_gpg_keys_spec.rb
@@ -2,18 +2,6 @@ require 'spec_helper'
require Rails.root.join('db', 'post_migrate', '20171005130944_schedule_create_gpg_key_subkeys_from_gpg_keys')
describe ScheduleCreateGpgKeySubkeysFromGpgKeys, :migration, :sidekiq do
- matcher :be_scheduled_migration do |*expected|
- match do |migration|
- BackgroundMigrationWorker.jobs.any? do |job|
- job['args'] == [migration, expected]
- end
- end
-
- failure_message do |migration|
- "Migration `#{migration}` with args `#{expected.inspect}` not scheduled!"
- end
- end
-
before do
create(:gpg_key, id: 1, key: GpgHelpers::User1.public_key)
create(:gpg_key, id: 2, key: GpgHelpers::User3.public_key)
diff --git a/spec/migrations/schedule_merge_request_diff_migrations_spec.rb b/spec/migrations/schedule_merge_request_diff_migrations_spec.rb
index 76afb6c19cf..d230f064444 100644
--- a/spec/migrations/schedule_merge_request_diff_migrations_spec.rb
+++ b/spec/migrations/schedule_merge_request_diff_migrations_spec.rb
@@ -24,9 +24,9 @@ describe ScheduleMergeRequestDiffMigrations, :migration, :sidekiq do
Timecop.freeze do
migrate!
- expect(described_class::MIGRATION).to be_scheduled_migration(5.minutes, 1, 1)
- expect(described_class::MIGRATION).to be_scheduled_migration(10.minutes, 2, 2)
- expect(described_class::MIGRATION).to be_scheduled_migration(15.minutes, 4, 4)
+ expect(described_class::MIGRATION).to be_scheduled_delayed_migration(5.minutes, 1, 1)
+ expect(described_class::MIGRATION).to be_scheduled_delayed_migration(10.minutes, 2, 2)
+ expect(described_class::MIGRATION).to be_scheduled_delayed_migration(15.minutes, 4, 4)
expect(BackgroundMigrationWorker.jobs.size).to eq 3
end
end
diff --git a/spec/migrations/schedule_merge_request_diff_migrations_take_two_spec.rb b/spec/migrations/schedule_merge_request_diff_migrations_take_two_spec.rb
index cf323973384..1aab4ae1650 100644
--- a/spec/migrations/schedule_merge_request_diff_migrations_take_two_spec.rb
+++ b/spec/migrations/schedule_merge_request_diff_migrations_take_two_spec.rb
@@ -24,9 +24,9 @@ describe ScheduleMergeRequestDiffMigrationsTakeTwo, :migration, :sidekiq do
Timecop.freeze do
migrate!
- expect(described_class::MIGRATION).to be_scheduled_migration(10.minutes, 1, 1)
- expect(described_class::MIGRATION).to be_scheduled_migration(20.minutes, 2, 2)
- expect(described_class::MIGRATION).to be_scheduled_migration(30.minutes, 4, 4)
+ expect(described_class::MIGRATION).to be_scheduled_delayed_migration(10.minutes, 1, 1)
+ expect(described_class::MIGRATION).to be_scheduled_delayed_migration(20.minutes, 2, 2)
+ expect(described_class::MIGRATION).to be_scheduled_delayed_migration(30.minutes, 4, 4)
expect(BackgroundMigrationWorker.jobs.size).to eq 3
end
end
diff --git a/spec/migrations/schedule_merge_request_latest_merge_request_diff_id_migrations_spec.rb b/spec/migrations/schedule_merge_request_latest_merge_request_diff_id_migrations_spec.rb
index 158d0bc02ed..c9fdbe95d13 100644
--- a/spec/migrations/schedule_merge_request_latest_merge_request_diff_id_migrations_spec.rb
+++ b/spec/migrations/schedule_merge_request_latest_merge_request_diff_id_migrations_spec.rb
@@ -44,9 +44,9 @@ describe ScheduleMergeRequestLatestMergeRequestDiffIdMigrations, :migration, :si
Timecop.freeze do
migrate!
- expect(described_class::MIGRATION).to be_scheduled_migration(5.minutes, merge_request_1.id, merge_request_1.id)
- expect(described_class::MIGRATION).to be_scheduled_migration(10.minutes, merge_request_2.id, merge_request_2.id)
- expect(described_class::MIGRATION).to be_scheduled_migration(15.minutes, merge_request_4.id, merge_request_4.id)
+ expect(described_class::MIGRATION).to be_scheduled_delayed_migration(5.minutes, merge_request_1.id, merge_request_1.id)
+ expect(described_class::MIGRATION).to be_scheduled_delayed_migration(10.minutes, merge_request_2.id, merge_request_2.id)
+ expect(described_class::MIGRATION).to be_scheduled_delayed_migration(15.minutes, merge_request_4.id, merge_request_4.id)
expect(BackgroundMigrationWorker.jobs.size).to eq 3
end
end
diff --git a/spec/migrations/schedule_populate_merge_request_metrics_with_events_data_spec.rb b/spec/migrations/schedule_populate_merge_request_metrics_with_events_data_spec.rb
new file mode 100644
index 00000000000..2e6b2cff0ab
--- /dev/null
+++ b/spec/migrations/schedule_populate_merge_request_metrics_with_events_data_spec.rb
@@ -0,0 +1,24 @@
+require 'spec_helper'
+require Rails.root.join('db', 'post_migrate', '20171128214150_schedule_populate_merge_request_metrics_with_events_data.rb')
+
+describe SchedulePopulateMergeRequestMetricsWithEventsData, :migration, :sidekiq do
+ let!(:mrs) { create_list(:merge_request, 3) }
+
+ it 'correctly schedules background migrations' do
+ stub_const("#{described_class.name}::BATCH_SIZE", 2)
+
+ Sidekiq::Testing.fake! do
+ Timecop.freeze do
+ migrate!
+
+ expect(described_class::MIGRATION)
+ .to be_scheduled_delayed_migration(10.minutes, mrs.first.id, mrs.second.id)
+
+ expect(described_class::MIGRATION)
+ .to be_scheduled_delayed_migration(20.minutes, mrs.third.id, mrs.third.id)
+
+ expect(BackgroundMigrationWorker.jobs.size).to eq(2)
+ end
+ end
+ end
+end
diff --git a/spec/migrations/track_untracked_uploads_spec.rb b/spec/migrations/track_untracked_uploads_spec.rb
index 7fe7a140e2f..fe4d5b8a279 100644
--- a/spec/migrations/track_untracked_uploads_spec.rb
+++ b/spec/migrations/track_untracked_uploads_spec.rb
@@ -4,18 +4,6 @@ require Rails.root.join('db', 'post_migrate', '20171103140253_track_untracked_up
describe TrackUntrackedUploads, :migration, :sidekiq do
include TrackUntrackedUploadsHelpers
- matcher :be_scheduled_migration do
- match do |migration|
- BackgroundMigrationWorker.jobs.any? do |job|
- job['args'] == [migration]
- end
- end
-
- failure_message do |migration|
- "Migration `#{migration}` with args `#{expected.inspect}` not scheduled!"
- end
- end
-
it 'correctly schedules the follow-up background migration' do
Sidekiq::Testing.fake! do
migrate!
diff --git a/spec/models/ability_spec.rb b/spec/models/ability_spec.rb
index 71aa51e1857..38fb98d4f50 100644
--- a/spec/models/ability_spec.rb
+++ b/spec/models/ability_spec.rb
@@ -34,19 +34,19 @@ describe Ability do
end
it 'returns false for a guest user' do
- project.team << [user, :guest]
+ project.add_guest(user)
expect(described_class.can_edit_note?(user, note)).to be_falsy
end
it 'returns false for a developer' do
- project.team << [user, :developer]
+ project.add_developer(user)
expect(described_class.can_edit_note?(user, note)).to be_falsy
end
it 'returns true for a master' do
- project.team << [user, :master]
+ project.add_master(user)
expect(described_class.can_edit_note?(user, note)).to be_truthy
end
diff --git a/spec/models/blob_viewer/package_json_spec.rb b/spec/models/blob_viewer/package_json_spec.rb
index 0f8330e91c1..5ed2f4400bc 100644
--- a/spec/models/blob_viewer/package_json_spec.rb
+++ b/spec/models/blob_viewer/package_json_spec.rb
@@ -22,4 +22,51 @@ describe BlobViewer::PackageJson do
expect(subject.package_name).to eq('module-name')
end
end
+
+ describe '#package_url' do
+ it 'returns the package URL' do
+ expect(subject).to receive(:prepare!)
+
+ expect(subject.package_url).to eq("https://www.npmjs.com/package/#{subject.package_name}")
+ end
+ end
+
+ describe '#package_type' do
+ it 'returns "package"' do
+ expect(subject).to receive(:prepare!)
+
+ expect(subject.package_type).to eq('package')
+ end
+ end
+
+ context 'when package.json has "private": true' do
+ let(:data) do
+ <<-SPEC.strip_heredoc
+ {
+ "name": "module-name",
+ "version": "10.3.1",
+ "private": true,
+ "homepage": "myawesomepackage.com"
+ }
+ SPEC
+ end
+ let(:blob) { fake_blob(path: 'package.json', data: data) }
+ subject { described_class.new(blob) }
+
+ describe '#package_url' do
+ it 'returns homepage if any' do
+ expect(subject).to receive(:prepare!)
+
+ expect(subject.package_url).to eq('myawesomepackage.com')
+ end
+ end
+
+ describe '#package_type' do
+ it 'returns "private package"' do
+ expect(subject).to receive(:prepare!)
+
+ expect(subject.package_type).to eq('private package')
+ end
+ end
+ end
end
diff --git a/spec/models/ci/build_spec.rb b/spec/models/ci/build_spec.rb
index 83352421d7d..3af27504967 100644
--- a/spec/models/ci/build_spec.rb
+++ b/spec/models/ci/build_spec.rb
@@ -132,11 +132,9 @@ describe Ci::Build do
end
describe '#artifacts?' do
- context 'when new artifacts are used' do
- let(:build) { create(:ci_build, :artifacts) }
-
- subject { build.artifacts? }
+ subject { build.artifacts? }
+ context 'when new artifacts are used' do
context 'artifacts archive does not exist' do
let(:build) { create(:ci_build) }
@@ -144,25 +142,19 @@ describe Ci::Build do
end
context 'artifacts archive exists' do
+ let(:build) { create(:ci_build, :artifacts) }
+
it { is_expected.to be_truthy }
context 'is expired' do
- let!(:build) { create(:ci_build, :artifacts, :expired) }
+ let(:build) { create(:ci_build, :artifacts, :expired) }
it { is_expected.to be_falsy }
end
-
- context 'is not expired' do
- it { is_expected.to be_truthy }
- end
end
end
context 'when legacy artifacts are used' do
- let(:build) { create(:ci_build, :legacy_artifacts) }
-
- subject { build.artifacts? }
-
context 'artifacts archive does not exist' do
let(:build) { create(:ci_build) }
@@ -184,17 +176,15 @@ describe Ci::Build do
end
context 'artifacts archive exists' do
+ let(:build) { create(:ci_build, :legacy_artifacts) }
+
it { is_expected.to be_truthy }
context 'is expired' do
- let!(:build) { create(:ci_build, :legacy_artifacts, :expired) }
+ let(:build) { create(:ci_build, :legacy_artifacts, :expired) }
it { is_expected.to be_falsy }
end
-
- context 'is not expired' do
- it { is_expected.to be_truthy }
- end
end
end
end
@@ -323,6 +313,42 @@ describe Ci::Build do
end
end
+ describe '#cache' do
+ let(:options) { { cache: { key: "key", paths: ["public"], policy: "pull-push" } } }
+
+ subject { build.cache }
+
+ context 'when build has cache' do
+ before do
+ allow(build).to receive(:options).and_return(options)
+ end
+
+ context 'when project has jobs_cache_index' do
+ before do
+ allow_any_instance_of(Project).to receive(:jobs_cache_index).and_return(1)
+ end
+
+ it { is_expected.to be_an(Array).and all(include(key: "key:1")) }
+ end
+
+ context 'when project does not have jobs_cache_index' do
+ before do
+ allow_any_instance_of(Project).to receive(:jobs_cache_index).and_return(nil)
+ end
+
+ it { is_expected.to eq([options[:cache]]) }
+ end
+ end
+
+ context 'when build does not have cache' do
+ before do
+ allow(build).to receive(:options).and_return({})
+ end
+
+ it { is_expected.to eq([nil]) }
+ end
+ end
+
describe '#depends_on_builds' do
let!(:build) { create(:ci_build, pipeline: pipeline, name: 'build', stage_idx: 0, stage: 'build') }
let!(:rspec_test) { create(:ci_build, pipeline: pipeline, name: 'rspec', stage_idx: 1, stage: 'test') }
@@ -1929,9 +1955,9 @@ describe Ci::Build do
describe 'state transition: any => [:running]' do
shared_examples 'validation is active' do
context 'when depended job has not been completed yet' do
- let!(:pre_stage_job) { create(:ci_build, :running, pipeline: pipeline, name: 'test', stage_idx: 0) }
+ let!(:pre_stage_job) { create(:ci_build, :manual, pipeline: pipeline, name: 'test', stage_idx: 0) }
- it { expect { job.run! }.to raise_error(Ci::Build::MissingDependenciesError) }
+ it { expect { job.run! }.not_to raise_error(Ci::Build::MissingDependenciesError) }
end
context 'when artifacts of depended job has been expired' do
@@ -1953,11 +1979,10 @@ describe Ci::Build do
shared_examples 'validation is not active' do
context 'when depended job has not been completed yet' do
- let!(:pre_stage_job) { create(:ci_build, :running, pipeline: pipeline, name: 'test', stage_idx: 0) }
+ let!(:pre_stage_job) { create(:ci_build, :manual, pipeline: pipeline, name: 'test', stage_idx: 0) }
it { expect { job.run! }.not_to raise_error }
end
-
context 'when artifacts of depended job has been expired' do
let!(:pre_stage_job) { create(:ci_build, :success, :expired, pipeline: pipeline, name: 'test', stage_idx: 0) }
diff --git a/spec/models/ci/pipeline_spec.rb b/spec/models/ci/pipeline_spec.rb
index bb89e093890..7bef798a782 100644
--- a/spec/models/ci/pipeline_spec.rb
+++ b/spec/models/ci/pipeline_spec.rb
@@ -116,7 +116,7 @@ describe Ci::Pipeline, :mailer do
end
it "calculates average when there is one build without coverage" do
- FactoryGirl.create(:ci_build, pipeline: pipeline)
+ FactoryBot.create(:ci_build, pipeline: pipeline)
expect(pipeline.coverage).to be_nil
end
end
@@ -435,7 +435,7 @@ describe Ci::Pipeline, :mailer do
describe 'merge request metrics' do
let(:project) { create(:project, :repository) }
- let(:pipeline) { FactoryGirl.create(:ci_empty_pipeline, status: 'created', project: project, ref: 'master', sha: project.repository.commit('master').id) }
+ let(:pipeline) { FactoryBot.create(:ci_empty_pipeline, status: 'created', project: project, ref: 'master', sha: project.repository.commit('master').id) }
let!(:merge_request) { create(:merge_request, source_project: project, source_branch: pipeline.ref) }
before do
@@ -1440,7 +1440,7 @@ describe Ci::Pipeline, :mailer do
end
before do
- project.team << [pipeline.user, Gitlab::Access::DEVELOPER]
+ project.add_developer(pipeline.user)
pipeline.user.global_notification_setting
.update(level: 'custom', failed_pipeline: true, success_pipeline: true)
@@ -1530,4 +1530,16 @@ describe Ci::Pipeline, :mailer do
expect(query_count).to eq(1)
end
end
+
+ describe '#total_size' do
+ let!(:build_job1) { create(:ci_build, pipeline: pipeline, stage_idx: 0) }
+ let!(:build_job2) { create(:ci_build, pipeline: pipeline, stage_idx: 0) }
+ let!(:test_job_failed_and_retried) { create(:ci_build, :failed, :retried, pipeline: pipeline, stage_idx: 1) }
+ let!(:second_test_job) { create(:ci_build, pipeline: pipeline, stage_idx: 1) }
+ let!(:deploy_job) { create(:ci_build, pipeline: pipeline, stage_idx: 2) }
+
+ it 'returns all jobs (including failed and retried)' do
+ expect(pipeline.total_size).to eq(5)
+ end
+ end
end
diff --git a/spec/models/ci/runner_spec.rb b/spec/models/ci/runner_spec.rb
index a93e7e233a8..b2b64e6ff48 100644
--- a/spec/models/ci/runner_spec.rb
+++ b/spec/models/ci/runner_spec.rb
@@ -51,24 +51,24 @@ describe Ci::Runner do
describe '#display_name' do
it 'returns the description if it has a value' do
- runner = FactoryGirl.build(:ci_runner, description: 'Linux/Ruby-1.9.3-p448')
+ runner = FactoryBot.build(:ci_runner, description: 'Linux/Ruby-1.9.3-p448')
expect(runner.display_name).to eq 'Linux/Ruby-1.9.3-p448'
end
it 'returns the token if it does not have a description' do
- runner = FactoryGirl.create(:ci_runner)
+ runner = FactoryBot.create(:ci_runner)
expect(runner.display_name).to eq runner.description
end
it 'returns the token if the description is an empty string' do
- runner = FactoryGirl.build(:ci_runner, description: '', token: 'token')
+ runner = FactoryBot.build(:ci_runner, description: '', token: 'token')
expect(runner.display_name).to eq runner.token
end
end
describe '#assign_to' do
- let!(:project) { FactoryGirl.create :project }
- let!(:shared_runner) { FactoryGirl.create(:ci_runner, :shared) }
+ let!(:project) { FactoryBot.create :project }
+ let!(:shared_runner) { FactoryBot.create(:ci_runner, :shared) }
before do
shared_runner.assign_to(project)
@@ -83,15 +83,15 @@ describe Ci::Runner do
subject { described_class.online }
before do
- @runner1 = FactoryGirl.create(:ci_runner, :shared, contacted_at: 1.year.ago)
- @runner2 = FactoryGirl.create(:ci_runner, :shared, contacted_at: 1.second.ago)
+ @runner1 = FactoryBot.create(:ci_runner, :shared, contacted_at: 1.year.ago)
+ @runner2 = FactoryBot.create(:ci_runner, :shared, contacted_at: 1.second.ago)
end
it { is_expected.to eq([@runner2])}
end
describe '#online?' do
- let(:runner) { FactoryGirl.create(:ci_runner, :shared) }
+ let(:runner) { FactoryBot.create(:ci_runner, :shared) }
subject { runner.online? }
@@ -268,7 +268,7 @@ describe Ci::Runner do
end
describe '#status' do
- let(:runner) { FactoryGirl.create(:ci_runner, :shared, contacted_at: 1.second.ago) }
+ let(:runner) { FactoryBot.create(:ci_runner, :shared, contacted_at: 1.second.ago) }
subject { runner.status }
@@ -442,9 +442,9 @@ describe Ci::Runner do
describe "belongs_to_one_project?" do
it "returns false if there are two projects runner assigned to" do
- runner = FactoryGirl.create(:ci_runner)
- project = FactoryGirl.create(:project)
- project1 = FactoryGirl.create(:project)
+ runner = FactoryBot.create(:ci_runner)
+ project = FactoryBot.create(:project)
+ project1 = FactoryBot.create(:project)
project.runners << runner
project1.runners << runner
@@ -452,8 +452,8 @@ describe Ci::Runner do
end
it "returns true" do
- runner = FactoryGirl.create(:ci_runner)
- project = FactoryGirl.create(:project)
+ runner = FactoryBot.create(:ci_runner)
+ project = FactoryBot.create(:project)
project.runners << runner
expect(runner.belongs_to_one_project?).to be_truthy
diff --git a/spec/models/ci/trigger_spec.rb b/spec/models/ci/trigger_spec.rb
index bd9c837402f..d4b72205203 100644
--- a/spec/models/ci/trigger_spec.rb
+++ b/spec/models/ci/trigger_spec.rb
@@ -69,7 +69,7 @@ describe Ci::Trigger do
context 'and is member of the project' do
before do
- project.team << [owner, :developer]
+ project.add_developer(owner)
end
it { is_expected.to eq(true) }
diff --git a/spec/models/clusters/applications/helm_spec.rb b/spec/models/clusters/applications/helm_spec.rb
index f8855079842..eb57abaf6ef 100644
--- a/spec/models/clusters/applications/helm_spec.rb
+++ b/spec/models/clusters/applications/helm_spec.rb
@@ -40,13 +40,13 @@ describe Clusters::Applications::Helm do
describe '#install_command' do
it 'has all the needed information' do
- expect(subject.install_command).to have_attributes(name: subject.name, install_helm: true, chart: nil)
+ expect(subject.install_command).to have_attributes(name: subject.name, install_helm: true)
end
end
describe 'status state machine' do
describe '#make_installing' do
- subject { create(:cluster_applications_helm, :scheduled) }
+ subject { create(:clusters_applications_helm, :scheduled) }
it 'is installing' do
subject.make_installing!
@@ -56,7 +56,7 @@ describe Clusters::Applications::Helm do
end
describe '#make_installed' do
- subject { create(:cluster_applications_helm, :installing) }
+ subject { create(:clusters_applications_helm, :installing) }
it 'is installed' do
subject.make_installed
@@ -66,7 +66,7 @@ describe Clusters::Applications::Helm do
end
describe '#make_errored' do
- subject { create(:cluster_applications_helm, :installing) }
+ subject { create(:clusters_applications_helm, :installing) }
let(:reason) { 'some errors' }
it 'is errored' do
@@ -78,7 +78,7 @@ describe Clusters::Applications::Helm do
end
describe '#make_scheduled' do
- subject { create(:cluster_applications_helm, :installable) }
+ subject { create(:clusters_applications_helm, :installable) }
it 'is scheduled' do
subject.make_scheduled
@@ -87,7 +87,7 @@ describe Clusters::Applications::Helm do
end
describe 'when was errored' do
- subject { create(:cluster_applications_helm, :errored) }
+ subject { create(:clusters_applications_helm, :errored) }
it 'clears #status_reason' do
expect(subject.status_reason).not_to be_nil
diff --git a/spec/models/clusters/applications/ingress_spec.rb b/spec/models/clusters/applications/ingress_spec.rb
index b83472e1944..619c088b0bf 100644
--- a/spec/models/clusters/applications/ingress_spec.rb
+++ b/spec/models/clusters/applications/ingress_spec.rb
@@ -4,105 +4,5 @@ describe Clusters::Applications::Ingress do
it { is_expected.to belong_to(:cluster) }
it { is_expected.to validate_presence_of(:cluster) }
- describe '#name' do
- it 'is .application_name' do
- expect(subject.name).to eq(described_class.application_name)
- end
-
- it 'is recorded in Clusters::Cluster::APPLICATIONS' do
- expect(Clusters::Cluster::APPLICATIONS[subject.name]).to eq(described_class)
- end
- end
-
- describe '#status' do
- let(:cluster) { create(:cluster, :provided_by_gcp) }
-
- subject { described_class.new(cluster: cluster) }
-
- it 'defaults to :not_installable' do
- expect(subject.status_name).to be(:not_installable)
- end
-
- context 'when application helm is scheduled' do
- before do
- create(:cluster_applications_helm, :scheduled, cluster: cluster)
- end
-
- it 'defaults to :not_installable' do
- expect(subject.status_name).to be(:not_installable)
- end
- end
-
- context 'when application helm is installed' do
- before do
- create(:cluster_applications_helm, :installed, cluster: cluster)
- end
-
- it 'defaults to :installable' do
- expect(subject.status_name).to be(:installable)
- end
- end
- end
-
- describe '#install_command' do
- it 'has all the needed information' do
- expect(subject.install_command).to have_attributes(name: subject.name, install_helm: false, chart: subject.chart)
- end
- end
-
- describe 'status state machine' do
- describe '#make_installing' do
- subject { create(:cluster_applications_ingress, :scheduled) }
-
- it 'is installing' do
- subject.make_installing!
-
- expect(subject).to be_installing
- end
- end
-
- describe '#make_installed' do
- subject { create(:cluster_applications_ingress, :installing) }
-
- it 'is installed' do
- subject.make_installed
-
- expect(subject).to be_installed
- end
- end
-
- describe '#make_errored' do
- subject { create(:cluster_applications_ingress, :installing) }
- let(:reason) { 'some errors' }
-
- it 'is errored' do
- subject.make_errored(reason)
-
- expect(subject).to be_errored
- expect(subject.status_reason).to eq(reason)
- end
- end
-
- describe '#make_scheduled' do
- subject { create(:cluster_applications_ingress, :installable) }
-
- it 'is scheduled' do
- subject.make_scheduled
-
- expect(subject).to be_scheduled
- end
-
- describe 'when was errored' do
- subject { create(:cluster_applications_ingress, :errored) }
-
- it 'clears #status_reason' do
- expect(subject.status_reason).not_to be_nil
-
- subject.make_scheduled!
-
- expect(subject.status_reason).to be_nil
- end
- end
- end
- end
+ include_examples 'cluster application specs', described_class
end
diff --git a/spec/models/clusters/applications/prometheus_spec.rb b/spec/models/clusters/applications/prometheus_spec.rb
new file mode 100644
index 00000000000..696099f7cf7
--- /dev/null
+++ b/spec/models/clusters/applications/prometheus_spec.rb
@@ -0,0 +1,16 @@
+require 'rails_helper'
+
+describe Clusters::Applications::Prometheus do
+ it { is_expected.to belong_to(:cluster) }
+ it { is_expected.to validate_presence_of(:cluster) }
+
+ include_examples 'cluster application specs', described_class
+
+ describe "#chart_values_file" do
+ subject { create(:clusters_applications_prometheus).chart_values_file }
+
+ it 'should return chart values file path' do
+ expect(subject).to eq("#{Rails.root}/vendor/prometheus/values.yaml")
+ end
+ end
+end
diff --git a/spec/models/clusters/cluster_spec.rb b/spec/models/clusters/cluster_spec.rb
index 2683d21ddbe..799d7ced116 100644
--- a/spec/models/clusters/cluster_spec.rb
+++ b/spec/models/clusters/cluster_spec.rb
@@ -5,6 +5,9 @@ describe Clusters::Cluster do
it { is_expected.to have_many(:projects) }
it { is_expected.to have_one(:provider_gcp) }
it { is_expected.to have_one(:platform_kubernetes) }
+ it { is_expected.to have_one(:application_helm) }
+ it { is_expected.to have_one(:application_ingress) }
+ it { is_expected.to have_one(:application_prometheus) }
it { is_expected.to delegate_method(:status).to(:provider) }
it { is_expected.to delegate_method(:status_reason).to(:provider) }
it { is_expected.to delegate_method(:status_name).to(:provider) }
@@ -190,11 +193,12 @@ describe Clusters::Cluster do
end
context 'when applications are created' do
- let!(:helm) { create(:cluster_applications_helm, cluster: cluster) }
- let!(:ingress) { create(:cluster_applications_ingress, cluster: cluster) }
+ let!(:helm) { create(:clusters_applications_helm, cluster: cluster) }
+ let!(:ingress) { create(:clusters_applications_ingress, cluster: cluster) }
+ let!(:prometheus) { create(:clusters_applications_prometheus, cluster: cluster) }
it 'returns a list of created applications' do
- is_expected.to contain_exactly(helm, ingress)
+ is_expected.to contain_exactly(helm, ingress, prometheus)
end
end
end
diff --git a/spec/models/commit_spec.rb b/spec/models/commit_spec.rb
index d18a5c9dfa6..817254c7d1e 100644
--- a/spec/models/commit_spec.rb
+++ b/spec/models/commit_spec.rb
@@ -13,6 +13,45 @@ describe Commit do
it { is_expected.to include_module(StaticModel) }
end
+ describe '.lazy' do
+ set(:project) { create(:project, :repository) }
+
+ context 'when the commits are found' do
+ let(:oids) do
+ %w(
+ 498214de67004b1da3d820901307bed2a68a8ef6
+ c642fe9b8b9f28f9225d7ea953fe14e74748d53b
+ 6f6d7e7ed97bb5f0054f2b1df789b39ca89b6ff9
+ 048721d90c449b244b7b4c53a9186b04330174ec
+ 281d3a76f31c812dbf48abce82ccf6860adedd81
+ )
+ end
+
+ subject { oids.map { |oid| described_class.lazy(project, oid) } }
+
+ it 'batches requests for commits' do
+ expect(project.repository).to receive(:commits_by).once.and_call_original
+
+ subject.first.title
+ subject.last.title
+ end
+
+ it 'maintains ordering' do
+ subject.each_with_index do |commit, i|
+ expect(commit.id).to eq(oids[i])
+ end
+ end
+ end
+
+ context 'when not found' do
+ it 'returns nil as commit' do
+ commit = described_class.lazy(project, 'deadbeef').__sync
+
+ expect(commit).to be_nil
+ end
+ end
+ end
+
describe '#author' do
it 'looks up the author in a case-insensitive way' do
user = create(:user, email: commit.author_email.upcase)
@@ -142,7 +181,6 @@ eos
it { is_expected.to respond_to(:parents) }
it { is_expected.to respond_to(:date) }
it { is_expected.to respond_to(:diffs) }
- it { is_expected.to respond_to(:tree) }
it { is_expected.to respond_to(:id) }
it { is_expected.to respond_to(:to_patch) }
end
@@ -154,8 +192,8 @@ eos
let(:commiter) { create :user }
before do
- project.team << [commiter, :developer]
- other_project.team << [commiter, :developer]
+ project.add_developer(commiter)
+ other_project.add_developer(commiter)
end
it 'detects issues that this commit is marked as closing' do
diff --git a/spec/models/concerns/blocks_json_serialization_spec.rb b/spec/models/concerns/blocks_json_serialization_spec.rb
new file mode 100644
index 00000000000..5906b588d0e
--- /dev/null
+++ b/spec/models/concerns/blocks_json_serialization_spec.rb
@@ -0,0 +1,17 @@
+require 'rails_helper'
+
+describe BlocksJsonSerialization do
+ DummyModel = Class.new do
+ include BlocksJsonSerialization
+ end
+
+ it 'blocks as_json' do
+ expect { DummyModel.new.as_json }
+ .to raise_error(described_class::JsonSerializationError, /DummyModel/)
+ end
+
+ it 'blocks to_json' do
+ expect { DummyModel.new.to_json }
+ .to raise_error(described_class::JsonSerializationError, /DummyModel/)
+ end
+end
diff --git a/spec/models/concerns/cache_markdown_field_spec.rb b/spec/models/concerns/cache_markdown_field_spec.rb
index 129dfa07f15..3c7f578975b 100644
--- a/spec/models/concerns/cache_markdown_field_spec.rb
+++ b/spec/models/concerns/cache_markdown_field_spec.rb
@@ -102,6 +102,26 @@ describe CacheMarkdownField do
it { expect(thing.cached_markdown_version).to eq(CacheMarkdownField::CACHE_VERSION) }
end
+ context 'when a markdown field is set repeatedly to an empty string' do
+ it do
+ expect(thing).to receive(:refresh_markdown_cache).once
+ thing.foo = ''
+ thing.save
+ thing.foo = ''
+ thing.save
+ end
+ end
+
+ context 'when a markdown field is set repeatedly to a string which renders as empty html' do
+ it do
+ expect(thing).to receive(:refresh_markdown_cache).once
+ thing.foo = '[//]: # (This is also a comment.)'
+ thing.save
+ thing.foo = '[//]: # (This is also a comment.)'
+ thing.save
+ end
+ end
+
context 'a non-markdown field changed' do
before do
thing.bar = 'OK'
diff --git a/spec/models/concerns/deployment_platform_spec.rb b/spec/models/concerns/deployment_platform_spec.rb
new file mode 100644
index 00000000000..7bb89fe41dc
--- /dev/null
+++ b/spec/models/concerns/deployment_platform_spec.rb
@@ -0,0 +1,73 @@
+require 'rails_helper'
+
+describe DeploymentPlatform do
+ let(:project) { create(:project) }
+
+ describe '#deployment_platform' do
+ subject { project.deployment_platform }
+
+ context 'with no Kubernetes configuration on CI/CD, no Kubernetes Service and a Kubernetes template configured' do
+ let!(:kubernetes_service) { create(:kubernetes_service, template: true) }
+
+ it 'returns a platform kubernetes' do
+ expect(subject).to be_a_kind_of(Clusters::Platforms::Kubernetes)
+ end
+
+ it 'creates a cluster and a platform kubernetes' do
+ expect { subject }
+ .to change { Clusters::Cluster.count }.by(1)
+ .and change { Clusters::Platforms::Kubernetes.count }.by(1)
+ end
+
+ it 'includes appropriate attributes for Cluster' do
+ cluster = subject.cluster
+ expect(cluster.name).to eq('kubernetes-template')
+ expect(cluster.project).to eq(project)
+ expect(cluster.provider_type).to eq('user')
+ expect(cluster.platform_type).to eq('kubernetes')
+ end
+
+ it 'creates a platform kubernetes' do
+ expect { subject }.to change { Clusters::Platforms::Kubernetes.count }.by(1)
+ end
+
+ it 'copies attributes from Clusters::Platform::Kubernetes template into the new Cluster::Platforms::Kubernetes' do
+ expect(subject.api_url).to eq(kubernetes_service.api_url)
+ expect(subject.ca_pem).to eq(kubernetes_service.ca_pem)
+ expect(subject.token).to eq(kubernetes_service.token)
+ expect(subject.namespace).to eq(kubernetes_service.namespace)
+ end
+ end
+
+ context 'with no Kubernetes configuration on CI/CD, no Kubernetes Service and no Kubernetes template configured' do
+ it { is_expected.to be_nil }
+ end
+
+ context 'when user configured kubernetes from CI/CD > Clusters' do
+ let!(:cluster) { create(:cluster, :provided_by_gcp, projects: [project]) }
+ let(:platform_kubernetes) { cluster.platform_kubernetes }
+
+ it 'returns the Kubernetes platform' do
+ expect(subject).to eq(platform_kubernetes)
+ end
+ end
+
+ context 'when user configured kubernetes integration from project services' do
+ let!(:kubernetes_service) { create(:kubernetes_service, project: project) }
+
+ it 'returns the Kubernetes service' do
+ expect(subject).to eq(kubernetes_service)
+ end
+ end
+
+ context 'when the cluster creation fails' do
+ let!(:kubernetes_service) { create(:kubernetes_service, template: true) }
+
+ before do
+ allow_any_instance_of(Clusters::Cluster).to receive(:persisted?).and_return(false)
+ end
+
+ it { is_expected.to be_nil }
+ end
+ end
+end
diff --git a/spec/models/concerns/issuable_spec.rb b/spec/models/concerns/issuable_spec.rb
index 9df26f06a11..4b217df2e8f 100644
--- a/spec/models/concerns/issuable_spec.rb
+++ b/spec/models/concerns/issuable_spec.rb
@@ -291,7 +291,7 @@ describe Issuable do
context 'total_time_spent is updated' do
before do
- issue.spend_time(duration: 2, user: user, spent_at: Time.now)
+ issue.spend_time(duration: 2, user_id: user.id, spent_at: Time.now)
issue.save
expect(Gitlab::HookData::IssuableBuilder)
.to receive(:new).with(issue).and_return(builder)
@@ -485,7 +485,7 @@ describe Issuable do
let(:issue) { create(:issue) }
def spend_time(seconds)
- issue.spend_time(duration: seconds, user: user)
+ issue.spend_time(duration: seconds, user_id: user.id)
issue.save!
end
diff --git a/spec/models/concerns/mentionable_spec.rb b/spec/models/concerns/mentionable_spec.rb
index 8b545aec7f5..c73ea6aa94c 100644
--- a/spec/models/concerns/mentionable_spec.rb
+++ b/spec/models/concerns/mentionable_spec.rb
@@ -62,7 +62,7 @@ describe Issue, "Mentionable" do
context 'when the current user can see the issue' do
before do
- private_project.team << [user, Gitlab::Access::DEVELOPER]
+ private_project.add_developer(user)
end
it 'includes the reference' do
@@ -107,7 +107,7 @@ describe Issue, "Mentionable" do
let(:issues) { create_list(:issue, 2, project: project, author: author) }
before do
- project.team << [author, Gitlab::Access::DEVELOPER]
+ project.add_developer(author)
end
context 'before changes are persisted' do
diff --git a/spec/models/concerns/milestoneish_spec.rb b/spec/models/concerns/milestoneish_spec.rb
index 9048da0c73d..87bf731340f 100644
--- a/spec/models/concerns/milestoneish_spec.rb
+++ b/spec/models/concerns/milestoneish_spec.rb
@@ -24,8 +24,8 @@ describe Milestone, 'Milestoneish' do
let(:label_3) { create(:label, title: 'label_3', project: project) }
before do
- project.team << [member, :developer]
- project.team << [guest, :guest]
+ project.add_developer(member)
+ project.add_guest(guest)
end
describe '#sorted_issues' do
@@ -189,9 +189,9 @@ describe Milestone, 'Milestoneish' do
describe '#total_issue_time_spent' do
it 'calculates total issue time spent' do
- closed_issue_1.spend_time(duration: 300, user: author)
+ closed_issue_1.spend_time(duration: 300, user_id: author.id)
closed_issue_1.save!
- closed_issue_2.spend_time(duration: 600, user: assignee)
+ closed_issue_2.spend_time(duration: 600, user_id: assignee.id)
closed_issue_2.save!
expect(milestone.total_issue_time_spent).to eq(900)
diff --git a/spec/models/concerns/resolvable_discussion_spec.rb b/spec/models/concerns/resolvable_discussion_spec.rb
index 1616c2ea985..2a2ef5a304d 100644
--- a/spec/models/concerns/resolvable_discussion_spec.rb
+++ b/spec/models/concerns/resolvable_discussion_spec.rb
@@ -190,7 +190,7 @@ describe Discussion, ResolvableDiscussion do
context "when the signed in user can push to the project" do
before do
- subject.project.team << [current_user, :master]
+ subject.project.add_master(current_user)
end
it "returns true" do
diff --git a/spec/models/deployment_spec.rb b/spec/models/deployment_spec.rb
index c5708e70ef9..ba8aa13d5ad 100644
--- a/spec/models/deployment_spec.rb
+++ b/spec/models/deployment_spec.rb
@@ -126,7 +126,7 @@ describe Deployment do
subject { deployment.stop_action }
context 'when no other actions' do
- let(:deployment) { FactoryGirl.build(:deployment, deployable: build) }
+ let(:deployment) { FactoryBot.build(:deployment, deployable: build) }
it { is_expected.to be_nil }
end
@@ -135,13 +135,13 @@ describe Deployment do
let!(:close_action) { create(:ci_build, :manual, pipeline: build.pipeline, name: 'close_app') }
context 'when matching action is defined' do
- let(:deployment) { FactoryGirl.build(:deployment, deployable: build, on_stop: 'close_other_app') }
+ let(:deployment) { FactoryBot.build(:deployment, deployable: build, on_stop: 'close_other_app') }
it { is_expected.to be_nil }
end
context 'when no matching action is defined' do
- let(:deployment) { FactoryGirl.build(:deployment, deployable: build, on_stop: 'close_app') }
+ let(:deployment) { FactoryBot.build(:deployment, deployable: build, on_stop: 'close_app') }
it { is_expected.to eq(close_action) }
end
@@ -159,7 +159,7 @@ describe Deployment do
context 'when matching action is defined' do
let(:build) { create(:ci_build) }
- let(:deployment) { FactoryGirl.build(:deployment, deployable: build, on_stop: 'close_app') }
+ let(:deployment) { FactoryBot.build(:deployment, deployable: build, on_stop: 'close_app') }
let!(:close_action) { create(:ci_build, :manual, pipeline: build.pipeline, name: 'close_app') }
it { is_expected.to be_truthy }
diff --git a/spec/models/diff_discussion_spec.rb b/spec/models/diff_discussion_spec.rb
index fa02434b0fd..50b19000799 100644
--- a/spec/models/diff_discussion_spec.rb
+++ b/spec/models/diff_discussion_spec.rb
@@ -47,8 +47,20 @@ describe DiffDiscussion do
diff_note.save!
end
- it 'returns the diff ID for the version to show' do
- expect(subject.merge_request_version_params).to eq(diff_id: merge_request_diff1.id)
+ context 'when commit_id is not present' do
+ it 'returns the diff ID for the version to show' do
+ expect(subject.merge_request_version_params).to eq(diff_id: merge_request_diff1.id)
+ end
+ end
+
+ context 'when commit_id is present' do
+ before do
+ diff_note.update_attribute(:commit_id, 'commit_123')
+ end
+
+ it 'includes the commit_id in the result' do
+ expect(subject.merge_request_version_params).to eq(diff_id: merge_request_diff1.id, commit_id: 'commit_123')
+ end
end
end
@@ -70,8 +82,20 @@ describe DiffDiscussion do
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)
+ context 'when commit_id is not present' do
+ 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
+ end
+
+ context 'when commit_id is present' do
+ before do
+ diff_note.update_attribute(:commit_id, 'commit_123')
+ end
+
+ it 'includes the commit_id in the result' do
+ expect(subject.merge_request_version_params).to eq(diff_id: merge_request_diff3.id, start_sha: merge_request_diff1.head_commit_sha, commit_id: 'commit_123')
+ end
end
end
@@ -83,8 +107,20 @@ describe DiffDiscussion do
diff_note.save!
end
- it 'returns nil' do
- expect(subject.merge_request_version_params).to be_nil
+ context 'when commit_id is not present' do
+ it 'returns empty hash' do
+ expect(subject.merge_request_version_params).to eq(nil)
+ end
+ end
+
+ context 'when commit_id is present' do
+ before do
+ diff_note.update_attribute(:commit_id, 'commit_123')
+ end
+
+ it 'returns the commit_id' do
+ expect(subject.merge_request_version_params).to eq(commit_id: 'commit_123')
+ end
end
end
end
diff --git a/spec/models/diff_note_spec.rb b/spec/models/diff_note_spec.rb
index 4d0b3245a13..2705421e540 100644
--- a/spec/models/diff_note_spec.rb
+++ b/spec/models/diff_note_spec.rb
@@ -112,22 +112,6 @@ describe DiffNote do
end
end
- describe "#for_line?" do
- context "when provided the correct diff line" do
- it "returns true" do
- expect(subject.for_line?(subject.diff_line)).to be true
- end
- end
-
- context "when provided a different diff line" do
- it "returns false" do
- some_line = subject.diff_file.diff_lines.first
-
- expect(subject.for_line?(some_line)).to be false
- end
- end
- end
-
describe "#active?" do
context "when noteable is a commit" do
subject { build(:diff_note_on_commit, project: project, position: position) }
diff --git a/spec/models/event_spec.rb b/spec/models/event_spec.rb
index aa7a8342a4c..67f49348acb 100644
--- a/spec/models/event_spec.rb
+++ b/spec/models/event_spec.rb
@@ -125,8 +125,8 @@ describe Event do
let(:event) { described_class.new(project: project, target: target, author_id: author.id) }
before do
- project.team << [member, :developer]
- project.team << [guest, :guest]
+ project.add_developer(member)
+ project.add_guest(guest)
end
context 'commit note event' do
@@ -347,6 +347,22 @@ describe Event do
end
end
+ describe '#target' do
+ it 'eager loads the author of an event target' do
+ create(:closed_issue_event)
+
+ events = described_class.preload(:target).all.to_a
+ count = ActiveRecord::QueryRecorder
+ .new { events.first.target.author }.count
+
+ # This expectation exists to make sure the test doesn't pass when the
+ # author is for some reason not loaded at all.
+ expect(events.first.target.author).to be_an_instance_of(User)
+
+ expect(count).to be_zero
+ end
+ end
+
def create_push_event(project, user)
event = create(:push_event, project: project, author: user)
diff --git a/spec/models/generic_commit_status_spec.rb b/spec/models/generic_commit_status_spec.rb
index 7f1909710d8..673049d1cc4 100644
--- a/spec/models/generic_commit_status_spec.rb
+++ b/spec/models/generic_commit_status_spec.rb
@@ -43,7 +43,7 @@ describe GenericCommitStatus do
context 'when user has ability to see datails' do
before do
- project.team << [user, :developer]
+ project.add_developer(user)
end
it 'details path points to an external URL' do
diff --git a/spec/models/hooks/system_hook_spec.rb b/spec/models/hooks/system_hook_spec.rb
index 431e3db9f00..0e965f541d8 100644
--- a/spec/models/hooks/system_hook_spec.rb
+++ b/spec/models/hooks/system_hook_spec.rb
@@ -62,7 +62,7 @@ describe SystemHook do
end
it "project_create hook" do
- project.team << [user, :master]
+ project.add_master(user)
expect(WebMock).to have_requested(:post, system_hook.url).with(
body: /user_add_to_team/,
@@ -71,7 +71,7 @@ describe SystemHook do
end
it "project_destroy hook" do
- project.team << [user, :master]
+ project.add_master(user)
project.project_members.destroy_all
expect(WebMock).to have_requested(:post, system_hook.url).with(
diff --git a/spec/models/identity_spec.rb b/spec/models/identity_spec.rb
index a45a6088831..7c66c98231b 100644
--- a/spec/models/identity_spec.rb
+++ b/spec/models/identity_spec.rb
@@ -44,4 +44,31 @@ describe Identity do
end
end
end
+
+ context 'callbacks' do
+ context 'before_save' do
+ describe 'normalizes extern uid' do
+ let!(:ldap_identity) { create(:identity, provider: 'ldapmain', extern_uid: 'uid=john smith,ou=people,dc=example,dc=com') }
+
+ it 'if extern_uid changes' do
+ expect(ldap_identity).not_to receive(:ensure_normalized_extern_uid)
+ ldap_identity.save
+ end
+
+ it 'if current_uid is nil' do
+ expect(ldap_identity).to receive(:ensure_normalized_extern_uid)
+
+ ldap_identity.update(extern_uid: nil)
+
+ expect(ldap_identity.extern_uid).to be_nil
+ end
+
+ it 'if extern_uid changed and not nil' do
+ ldap_identity.update(extern_uid: 'uid=john1,ou=PEOPLE,dc=example,dc=com')
+
+ expect(ldap_identity.extern_uid).to eq 'uid=john1,ou=people,dc=example,dc=com'
+ end
+ end
+ end
+ end
end
diff --git a/spec/models/issue_collection_spec.rb b/spec/models/issue_collection_spec.rb
index 34d98a3c975..580a98193af 100644
--- a/spec/models/issue_collection_spec.rb
+++ b/spec/models/issue_collection_spec.rb
@@ -42,7 +42,7 @@ describe IssueCollection do
context 'using a user that has reporter access to the project' do
it 'returns the issues of the project' do
- project.team << [user, :reporter]
+ project.add_reporter(user)
expect(collection.updatable_by_user(user)).to eq([issue1, issue2])
end
diff --git a/spec/models/issue_spec.rb b/spec/models/issue_spec.rb
index 0ea287d007a..5ced000cdb6 100644
--- a/spec/models/issue_spec.rb
+++ b/spec/models/issue_spec.rb
@@ -23,6 +23,32 @@ describe Issue do
it { is_expected.to have_db_index(:deleted_at) }
end
+ describe 'callbacks' do
+ describe '#ensure_metrics' do
+ it 'creates metrics after saving' do
+ issue = create(:issue)
+
+ expect(issue.metrics).to be_persisted
+ expect(Issue::Metrics.count).to eq(1)
+ end
+
+ it 'does not create duplicate metrics for an issue' do
+ issue = create(:issue)
+
+ issue.close!
+
+ expect(issue.metrics).to be_persisted
+ expect(Issue::Metrics.count).to eq(1)
+ end
+
+ it 'records current metrics' do
+ expect_any_instance_of(Issue::Metrics).to receive(:record!)
+
+ create(:issue)
+ end
+ end
+ end
+
describe '#order_by_position_and_priority' do
let(:project) { create :project }
let(:p1) { create(:label, title: 'P1', project: project, priority: 1) }
@@ -238,7 +264,7 @@ describe Issue do
let(:issue) { create(:issue, project: project) }
before do
- project.team << [user, :reporter]
+ project.add_reporter(user)
end
it { is_expected.to eq true }
@@ -254,7 +280,7 @@ describe Issue do
context 'destination project allowed' do
before do
- to_project.team << [user, :reporter]
+ to_project.add_reporter(user)
end
it { is_expected.to eq true }
@@ -262,7 +288,7 @@ describe Issue do
context 'destination project not allowed' do
before do
- to_project.team << [user, :guest]
+ to_project.add_guest(user)
end
it { is_expected.to eq false }
@@ -550,7 +576,7 @@ describe Issue do
context 'when the user is the project owner' do
before do
- project.team << [user, :master]
+ project.add_master(user)
end
it 'returns true for a regular issue' do
@@ -574,7 +600,7 @@ describe Issue do
context 'using a public project' do
before do
- project.team << [user, Gitlab::Access::DEVELOPER]
+ project.add_developer(user)
end
it 'returns true for a regular issue' do
@@ -594,7 +620,7 @@ describe Issue do
let(:project) { create(:project, :internal) }
before do
- project.team << [user, Gitlab::Access::DEVELOPER]
+ project.add_developer(user)
end
it 'returns true for a regular issue' do
@@ -614,7 +640,7 @@ describe Issue do
let(:project) { create(:project, :private) }
before do
- project.team << [user, Gitlab::Access::DEVELOPER]
+ project.add_developer(user)
end
it 'returns true for a regular issue' do
diff --git a/spec/models/member_spec.rb b/spec/models/member_spec.rb
index 0a017c068ad..6aa0e7f49c3 100644
--- a/spec/models/member_spec.rb
+++ b/spec/models/member_spec.rb
@@ -62,12 +62,12 @@ describe Member do
@owner_user = create(:user).tap { |u| group.add_owner(u) }
@owner = group.members.find_by(user_id: @owner_user.id)
- @master_user = create(:user).tap { |u| project.team << [u, :master] }
+ @master_user = create(:user).tap { |u| project.add_master(u) }
@master = project.members.find_by(user_id: @master_user.id)
@blocked_user = create(:user).tap do |u|
- project.team << [u, :master]
- project.team << [u, :developer]
+ project.add_master(u)
+ project.add_developer(u)
u.block!
end
@@ -527,7 +527,7 @@ describe Member do
it "refreshes user's authorized projects" do
project = create(:project, :private)
user = create(:user)
- member = project.team << [user, :reporter]
+ member = project.add_reporter(user)
member.destroy
diff --git a/spec/models/members/project_member_spec.rb b/spec/models/members/project_member_spec.rb
index fa3e80ba062..3e46fa36375 100644
--- a/spec/models/members/project_member_spec.rb
+++ b/spec/models/members/project_member_spec.rb
@@ -88,8 +88,8 @@ describe ProjectMember do
@user_1 = create :user
@user_2 = create :user
- @project_1.team << [@user_1, :developer]
- @project_2.team << [@user_2, :reporter]
+ @project_1.add_developer(@user_1)
+ @project_2.add_reporter(@user_2)
@status = @project_2.team.import(@project_1)
end
@@ -136,8 +136,8 @@ describe ProjectMember do
@user_1 = create :user
@user_2 = create :user
- @project_1.team << [@user_1, :developer]
- @project_2.team << [@user_2, :reporter]
+ @project_1.add_developer(@user_1)
+ @project_2.add_reporter(@user_2)
described_class.truncate_teams([@project_1.id, @project_2.id])
end
diff --git a/spec/models/merge_request/metrics_spec.rb b/spec/models/merge_request/metrics_spec.rb
index 9353d5c3c8a..02ff7839739 100644
--- a/spec/models/merge_request/metrics_spec.rb
+++ b/spec/models/merge_request/metrics_spec.rb
@@ -1,16 +1,11 @@
require 'spec_helper'
describe MergeRequest::Metrics do
- subject { create(:merge_request) }
+ subject { described_class.new }
- describe "when recording the default set of metrics on merge request save" do
- it "records the merge time" do
- time = Time.now
- Timecop.freeze(time) { subject.mark_as_merged }
- metrics = subject.metrics
-
- expect(metrics).to be_present
- expect(metrics.merged_at).to be_like_time(time)
- end
+ describe 'associations' do
+ it { is_expected.to belong_to(:merge_request) }
+ it { is_expected.to belong_to(:latest_closed_by).class_name('User') }
+ it { is_expected.to belong_to(:merged_by).class_name('User') }
end
end
diff --git a/spec/models/merge_request_spec.rb b/spec/models/merge_request_spec.rb
index 30a5a3bbff7..07b3e1c1758 100644
--- a/spec/models/merge_request_spec.rb
+++ b/spec/models/merge_request_spec.rb
@@ -65,6 +65,25 @@ describe MergeRequest do
end
end
+ describe 'callbacks' do
+ describe '#ensure_merge_request_metrics' do
+ it 'creates metrics after saving' do
+ merge_request = create(:merge_request)
+
+ expect(merge_request.metrics).to be_persisted
+ expect(MergeRequest::Metrics.count).to eq(1)
+ end
+
+ it 'does not duplicate metrics for a merge request' do
+ merge_request = create(:merge_request)
+
+ merge_request.mark_as_merged!
+
+ expect(MergeRequest::Metrics.count).to eq(1)
+ end
+ end
+ end
+
describe 'respond to' do
it { is_expected.to respond_to(:unchecked?) }
it { is_expected.to respond_to(:can_be_merged?) }
@@ -124,6 +143,7 @@ describe MergeRequest do
context 'when the target branch does not exist' do
before do
project.repository.rm_branch(subject.author, subject.target_branch)
+ subject.clear_memoized_shas
end
it 'returns nil' do
@@ -194,7 +214,7 @@ describe MergeRequest do
describe '#cache_merge_request_closes_issues!' do
before do
- subject.project.team << [subject.author, :developer]
+ subject.project.add_developer(subject.author)
subject.target_branch = subject.project.default_branch
end
@@ -480,7 +500,7 @@ describe MergeRequest do
let(:commit2) { double('commit2', safe_message: "Fixes #{issue1.to_reference}") }
before do
- subject.project.team << [subject.author, :developer]
+ subject.project.add_developer(subject.author)
allow(subject).to receive(:commits).and_return([commit0, commit1, commit2])
end
@@ -508,7 +528,7 @@ describe MergeRequest do
let(:commit) { double('commit', safe_message: "Fixes #{closing_issue.to_reference}") }
it 'detects issues mentioned in description but not closed' do
- subject.project.team << [subject.author, :developer]
+ subject.project.add_developer(subject.author)
subject.description = "Is related to #{mentioned_issue.to_reference} and #{closing_issue.to_reference}"
allow(subject).to receive(:commits).and_return([commit])
@@ -520,7 +540,7 @@ describe MergeRequest do
context 'when the project has an external issue tracker' do
before do
- subject.project.team << [subject.author, :developer]
+ subject.project.add_developer(subject.author)
commit = double(:commit, safe_message: 'Fixes TEST-3')
create(:jira_service, project: subject.project)
@@ -600,30 +620,30 @@ describe MergeRequest do
end
describe '#can_remove_source_branch?' do
- let(:user) { create(:user) }
- let(:user2) { create(:user) }
+ set(:user) { create(:user) }
+ set(:merge_request) { create(:merge_request, :simple) }
- before do
- subject.source_project.team << [user, :master]
+ subject { merge_request }
- subject.source_branch = "feature"
- subject.target_branch = "master"
- subject.save!
+ before do
+ subject.source_project.add_master(user)
end
it "can't be removed when its a protected branch" do
allow(ProtectedBranch).to receive(:protected?).and_return(true)
+
expect(subject.can_remove_source_branch?(user)).to be_falsey
end
it "can't remove a root ref" do
- subject.source_branch = "master"
- subject.target_branch = "feature"
+ subject.update(source_branch: 'master', target_branch: 'feature')
expect(subject.can_remove_source_branch?(user)).to be_falsey
end
it "is unable to remove the source branch for a project the user cannot push to" do
+ user2 = create(:user)
+
expect(subject.can_remove_source_branch?(user2)).to be_falsey
end
@@ -634,6 +654,7 @@ describe MergeRequest do
end
it "cannot be removed if the last commit is not also the head of the source branch" do
+ subject.clear_memoized_shas
subject.source_branch = "lfs"
expect(subject.can_remove_source_branch?(user)).to be_falsey
@@ -658,7 +679,7 @@ describe MergeRequest do
it 'includes its closed issues in the body' do
issue = create(:issue, project: subject.project)
- subject.project.team << [subject.author, :developer]
+ subject.project.add_developer(subject.author)
subject.description = "This issue Closes #{issue.to_reference}"
allow(subject.project).to receive(:default_branch)
@@ -733,7 +754,7 @@ describe MergeRequest do
before do
project.repository.raw_repository.delete_branch(subject.target_branch)
- subject.reload
+ subject.clear_memoized_shas
end
it 'does not crash' do
@@ -1404,6 +1425,16 @@ describe MergeRequest do
subject.reload_diff
end
+
+ context 'when using the after_update hook to update' do
+ context 'when the branches are updated' do
+ it 'uses the new heads to generate the diff' do
+ expect { subject.update!(source_branch: subject.target_branch, target_branch: subject.source_branch) }
+ .to change { subject.merge_request_diff.start_commit_sha }
+ .and change { subject.merge_request_diff.head_commit_sha }
+ end
+ end
+ end
end
describe '#update_diff_discussion_positions' do
@@ -1468,6 +1499,7 @@ describe MergeRequest do
context 'when the target branch does not exist' do
before do
subject.project.repository.rm_branch(subject.author, subject.target_branch)
+ subject.clear_memoized_shas
end
it 'returns nil' do
@@ -1675,7 +1707,7 @@ describe MergeRequest do
let(:mr_sha) { merge_request.diff_head_sha }
before do
- project.team << [developer, :developer]
+ project.add_developer(developer)
end
context 'when autocomplete_precheck is set to true' do
@@ -1855,4 +1887,66 @@ describe MergeRequest do
it_behaves_like 'throttled touch' do
subject { create(:merge_request, updated_at: 1.hour.ago) }
end
+
+ context 'state machine transitions' do
+ describe '#unlock_mr' do
+ subject { create(:merge_request, state: 'locked', merge_jid: 123) }
+
+ it 'updates merge request head pipeline and sets merge_jid to nil' do
+ pipeline = create(:ci_empty_pipeline, project: subject.project, ref: subject.source_branch, sha: subject.source_branch_sha)
+
+ subject.unlock_mr
+
+ subject.reload
+ expect(subject.head_pipeline).to eq(pipeline)
+ expect(subject.merge_jid).to be_nil
+ end
+ end
+ end
+
+ describe '#should_be_rebased?' do
+ let(:project) { create(:project, :repository) }
+
+ it 'returns false for the same source and target branches' do
+ merge_request = create(:merge_request, source_project: project, target_project: project)
+
+ expect(merge_request.should_be_rebased?).to be_falsey
+ end
+ end
+
+ describe '#rebase_in_progress?' do
+ # Create merge request and project before we stub file calls
+ before do
+ subject
+ end
+
+ it 'returns true when there is a current rebase directory' do
+ allow(File).to receive(:exist?).and_return(true)
+ allow(File).to receive(:mtime).and_return(Time.now)
+
+ expect(subject.rebase_in_progress?).to be_truthy
+ end
+
+ it 'returns false when there is no rebase directory' do
+ allow(File).to receive(:exist?).and_return(false)
+
+ expect(subject.rebase_in_progress?).to be_falsey
+ end
+
+ it 'returns false when the rebase directory has expired' do
+ allow(File).to receive(:exist?).and_return(true)
+ allow(File).to receive(:mtime).and_return(20.minutes.ago)
+
+ expect(subject.rebase_in_progress?).to be_falsey
+ end
+
+ it 'returns false when the source project has been removed' do
+ allow(subject).to receive(:source_project).and_return(nil)
+ allow(File).to receive(:exist?).and_return(true)
+ allow(File).to receive(:mtime).and_return(Time.now)
+
+ expect(File).not_to have_received(:exist?)
+ expect(subject.rebase_in_progress?).to be_falsey
+ end
+ end
end
diff --git a/spec/models/namespace_spec.rb b/spec/models/namespace_spec.rb
index 3817f20bfe7..b3f160f3119 100644
--- a/spec/models/namespace_spec.rb
+++ b/spec/models/namespace_spec.rb
@@ -203,7 +203,7 @@ describe Namespace do
context 'with subgroups' do
let(:parent) { create(:group, name: 'parent', path: 'parent') }
let(:child) { create(:group, name: 'child', path: 'child', parent: parent) }
- let!(:project) { create(:project_empty_repo, path: 'the-project', namespace: child) }
+ let!(:project) { create(:project_empty_repo, path: 'the-project', namespace: child, skip_disk_validation: true) }
let(:uploads_dir) { File.join(CarrierWave.root, FileUploader.base_dir) }
let(:pages_dir) { File.join(TestEnv.pages_path) }
@@ -240,6 +240,24 @@ describe Namespace do
end
end
end
+
+ it 'updates project full path in .git/config for each project inside namespace' do
+ parent = create(:group, name: 'mygroup', path: 'mygroup')
+ subgroup = create(:group, name: 'mysubgroup', path: 'mysubgroup', parent: parent)
+ project_in_parent_group = create(:project, :repository, namespace: parent, name: 'foo1')
+ hashed_project_in_subgroup = create(:project, :repository, :hashed, namespace: subgroup, name: 'foo2')
+ legacy_project_in_subgroup = create(:project, :repository, namespace: subgroup, name: 'foo3')
+
+ parent.update(path: 'mygroup_new')
+
+ expect(project_rugged(project_in_parent_group).config['gitlab.fullpath']).to eq "mygroup_new/#{project_in_parent_group.path}"
+ expect(project_rugged(hashed_project_in_subgroup).config['gitlab.fullpath']).to eq "mygroup_new/mysubgroup/#{hashed_project_in_subgroup.path}"
+ expect(project_rugged(legacy_project_in_subgroup).config['gitlab.fullpath']).to eq "mygroup_new/mysubgroup/#{legacy_project_in_subgroup.path}"
+ end
+
+ def project_rugged(project)
+ project.repository.rugged
+ end
end
describe '#rm_dir', 'callback' do
@@ -559,4 +577,34 @@ describe Namespace do
end
end
end
+
+ describe "#allowed_path_by_redirects" do
+ let(:namespace1) { create(:namespace, path: 'foo') }
+
+ context "when the path has been taken before" do
+ before do
+ namespace1.path = 'bar'
+ namespace1.save!
+ end
+
+ it 'should be invalid' do
+ namespace2 = build(:group, path: 'foo')
+ expect(namespace2).to be_invalid
+ end
+
+ it 'should return an error on path' do
+ namespace2 = build(:group, path: 'foo')
+ namespace2.valid?
+ expect(namespace2.errors.messages[:path].first).to eq('foo has been taken before. Please use another one')
+ end
+ end
+
+ context "when the path has not been taken before" do
+ it 'should be valid' do
+ expect(RedirectRoute.count).to eq(0)
+ namespace = build(:namespace)
+ expect(namespace).to be_valid
+ end
+ end
+ end
end
diff --git a/spec/models/note_spec.rb b/spec/models/note_spec.rb
index e1a0c55b6a6..3d030927036 100644
--- a/spec/models/note_spec.rb
+++ b/spec/models/note_spec.rb
@@ -195,7 +195,7 @@ describe Note do
describe "cross_reference_not_visible_for?" do
let(:private_user) { create(:user) }
- let(:private_project) { create(:project, namespace: private_user.namespace) { |p| p.team << [private_user, :master] } }
+ let(:private_project) { create(:project, namespace: private_user.namespace) { |p| p.add_master(private_user) } }
let(:private_issue) { create(:issue, project: private_project) }
let(:ext_proj) { create(:project, :public) }
@@ -756,6 +756,28 @@ describe Note do
end
end
+ describe '#references' do
+ context 'when part of a discussion' do
+ it 'references all earlier notes in the discussion' do
+ first_note = create(:discussion_note_on_issue)
+ second_note = create(:discussion_note_on_issue, in_reply_to: first_note)
+ third_note = create(:discussion_note_on_issue, in_reply_to: second_note)
+ create(:discussion_note_on_issue, in_reply_to: third_note)
+
+ expect(third_note.references).to eq([first_note.noteable, first_note, second_note])
+ end
+ end
+
+ context 'when not part of a discussion' do
+ subject { create(:note) }
+ let(:note) { create(:note, in_reply_to: subject) }
+
+ it 'returns the noteable' do
+ expect(note.references).to eq([note.noteable])
+ end
+ end
+ end
+
describe 'expiring ETag cache' do
let(:note) { build(:note_on_issue) }
diff --git a/spec/models/project_feature_spec.rb b/spec/models/project_feature_spec.rb
index de3e86b627f..63c6fbda3f2 100644
--- a/spec/models/project_feature_spec.rb
+++ b/spec/models/project_feature_spec.rb
@@ -37,7 +37,7 @@ describe ProjectFeature do
end
it "returns true when user is a team member" do
- project.team << [user, :developer]
+ project.add_developer(user)
features.each do |feature|
project.project_feature.update_attribute("#{feature}_access_level".to_sym, ProjectFeature::PRIVATE)
diff --git a/spec/models/project_services/jira_service_spec.rb b/spec/models/project_services/jira_service_spec.rb
index ad22fb2a386..c9b3c6cf602 100644
--- a/spec/models/project_services/jira_service_spec.rb
+++ b/spec/models/project_services/jira_service_spec.rb
@@ -395,6 +395,26 @@ describe JiraService do
end
end
+ describe 'additional cookies' do
+ let(:project) { create(:project) }
+
+ context 'provides additional cookies to allow basic auth with oracle webgate' do
+ before do
+ @service = project.create_jira_service(
+ active: true, properties: { url: 'http://jira.com' })
+ end
+
+ after do
+ @service.destroy!
+ end
+
+ it 'is initialized' do
+ expect(@service.options[:use_cookies]).to eq(true)
+ expect(@service.options[:additional_cookies]).to eq(["OBBasicAuth=fromDialog"])
+ end
+ end
+ end
+
describe 'project and issue urls' do
let(:project) { create(:project) }
diff --git a/spec/models/project_services/kubernetes_service_spec.rb b/spec/models/project_services/kubernetes_service_spec.rb
index f037ee77a94..6980ba335b8 100644
--- a/spec/models/project_services/kubernetes_service_spec.rb
+++ b/spec/models/project_services/kubernetes_service_spec.rb
@@ -52,12 +52,75 @@ describe KubernetesService, :use_clean_rails_memory_store_caching do
context 'when service is inactive' do
before do
+ subject.project = project
subject.active = false
end
it { is_expected.not_to validate_presence_of(:api_url) }
it { is_expected.not_to validate_presence_of(:token) }
end
+
+ context 'with a deprecated service' do
+ let(:kubernetes_service) { create(:kubernetes_service) }
+
+ before do
+ kubernetes_service.update_attribute(:active, false)
+ kubernetes_service.properties[:namespace] = "foo"
+ end
+
+ it 'should not update attributes' do
+ expect(kubernetes_service.save).to be_falsy
+ end
+
+ it 'should include an error with a deprecation message' do
+ kubernetes_service.valid?
+ expect(kubernetes_service.errors[:base].first).to match(/Kubernetes service integration has been deprecated/)
+ end
+ end
+
+ context 'with a non-deprecated service' do
+ let(:kubernetes_service) { create(:kubernetes_service) }
+
+ it 'should update attributes' do
+ kubernetes_service.properties[:namespace] = 'foo'
+ expect(kubernetes_service.save).to be_truthy
+ end
+ end
+
+ context 'with an active and deprecated service' do
+ let(:kubernetes_service) { create(:kubernetes_service) }
+
+ before do
+ kubernetes_service.active = false
+ kubernetes_service.properties[:namespace] = 'foo'
+ kubernetes_service.save
+ end
+
+ it 'should deactive the service' do
+ expect(kubernetes_service.active?).to be_falsy
+ end
+
+ it 'should not include a deprecation message as error' do
+ expect(kubernetes_service.errors.messages.count).to eq(0)
+ end
+
+ it 'should update attributes' do
+ expect(kubernetes_service.properties[:namespace]).to eq("foo")
+ end
+ end
+
+ context 'with a template service' do
+ let(:kubernetes_service) { create(:kubernetes_service, template: true, active: false) }
+
+ before do
+ kubernetes_service.properties[:namespace] = 'foo'
+ end
+
+ it 'should update attributes' do
+ expect(kubernetes_service.save).to be_truthy
+ expect(kubernetes_service.properties[:namespace]).to eq('foo')
+ end
+ end
end
describe '#initialize_properties' do
@@ -318,4 +381,42 @@ describe KubernetesService, :use_clean_rails_memory_store_caching do
it { is_expected.to eq(pods: []) }
end
end
+
+ describe "#deprecated?" do
+ let(:kubernetes_service) { create(:kubernetes_service) }
+
+ context 'with an active kubernetes service' do
+ it 'should return false' do
+ expect(kubernetes_service.deprecated?).to be_falsy
+ end
+ end
+
+ context 'with a inactive kubernetes service' do
+ it 'should return true' do
+ kubernetes_service.update_attribute(:active, false)
+ expect(kubernetes_service.deprecated?).to be_truthy
+ end
+ end
+ end
+
+ describe "#deprecation_message" do
+ let(:kubernetes_service) { create(:kubernetes_service) }
+
+ it 'should indicate the service is deprecated' do
+ expect(kubernetes_service.deprecation_message).to match(/Kubernetes service integration has been deprecated/)
+ end
+
+ context 'if the services is active' do
+ it 'should return a message' do
+ expect(kubernetes_service.deprecation_message).to match(/Your cluster information on this page is still editable/)
+ end
+ end
+
+ context 'if the service is not active' do
+ it 'should return a message' do
+ kubernetes_service.update_attribute(:active, false)
+ expect(kubernetes_service.deprecation_message).to match(/Fields on this page are now uneditable/)
+ end
+ end
+ end
end
diff --git a/spec/models/project_services/pipelines_email_service_spec.rb b/spec/models/project_services/pipelines_email_service_spec.rb
index be07ca2d945..75ae2207910 100644
--- a/spec/models/project_services/pipelines_email_service_spec.rb
+++ b/spec/models/project_services/pipelines_email_service_spec.rb
@@ -37,7 +37,7 @@ describe PipelinesEmailService, :mailer do
let(:user) { create(:user) }
before do
- project.team << [user, :developer]
+ project.add_developer(user)
end
it 'builds test data' do
diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb
index f4699fd243d..00afa09f1a3 100644
--- a/spec/models/project_spec.rb
+++ b/spec/models/project_spec.rb
@@ -93,7 +93,7 @@ describe Project do
let(:developer) { create(:user) }
before do
project.request_access(requester)
- project.team << [developer, :developer]
+ project.add_developer(developer)
end
it_behaves_like 'members and requesters associations' do
@@ -289,12 +289,12 @@ describe Project do
describe 'project token' do
it 'sets an random token if none provided' do
- project = FactoryGirl.create :project, runners_token: ''
+ project = FactoryBot.create :project, runners_token: ''
expect(project.runners_token).not_to eq('')
end
it 'does not set an random token if one provided' do
- project = FactoryGirl.create :project, runners_token: 'my-token'
+ project = FactoryBot.create :project, runners_token: 'my-token'
expect(project.runners_token).to eq('my-token')
end
end
@@ -418,14 +418,21 @@ describe Project do
end
describe '#merge_method' do
- it 'returns "ff" merge_method when ff is enabled' do
- project = build(:project, merge_requests_ff_only_enabled: true)
- expect(project.merge_method).to be :ff
+ using RSpec::Parameterized::TableSyntax
+
+ where(:ff, :rebase, :method) do
+ true | true | :ff
+ true | false | :ff
+ false | true | :rebase_merge
+ false | false | :merge
end
- it 'returns "merge" merge_method when ff is disabled' do
- project = build(:project, merge_requests_ff_only_enabled: false)
- expect(project.merge_method).to be :merge
+ with_them do
+ let(:project) { build(:project, merge_requests_rebase_enabled: rebase, merge_requests_ff_only_enabled: ff) }
+
+ subject { project.merge_method }
+
+ it { is_expected.to eq(method) }
end
end
@@ -520,7 +527,7 @@ describe Project do
let(:user) { create(:user) }
before do
- project.team << [user, :developer]
+ project.add_developer(user)
end
context 'with default issues tracker' do
@@ -1435,35 +1442,35 @@ describe Project do
let(:user) { create(:user) }
it 'returns false when default_branch_protection is in full protection and user is developer' do
- project.team << [user, :developer]
+ project.add_developer(user)
stub_application_setting(default_branch_protection: Gitlab::Access::PROTECTION_FULL)
expect(project.user_can_push_to_empty_repo?(user)).to be_falsey
end
it 'returns false when default_branch_protection only lets devs merge and user is dev' do
- project.team << [user, :developer]
+ project.add_developer(user)
stub_application_setting(default_branch_protection: Gitlab::Access::PROTECTION_DEV_CAN_MERGE)
expect(project.user_can_push_to_empty_repo?(user)).to be_falsey
end
it 'returns true when default_branch_protection lets devs push and user is developer' do
- project.team << [user, :developer]
+ project.add_developer(user)
stub_application_setting(default_branch_protection: Gitlab::Access::PROTECTION_DEV_CAN_PUSH)
expect(project.user_can_push_to_empty_repo?(user)).to be_truthy
end
it 'returns true when default_branch_protection is unprotected and user is developer' do
- project.team << [user, :developer]
+ project.add_developer(user)
stub_application_setting(default_branch_protection: Gitlab::Access::PROTECTION_NONE)
expect(project.user_can_push_to_empty_repo?(user)).to be_truthy
end
it 'returns true when user is master' do
- project.team << [user, :master]
+ project.add_master(user)
expect(project.user_can_push_to_empty_repo?(user)).to be_truthy
end
@@ -2626,6 +2633,14 @@ describe Project do
project.rename_repo
end
end
+
+ it 'updates project full path in .git/config' do
+ allow(project_storage).to receive(:rename_repo).and_return(true)
+
+ project.rename_repo
+
+ expect(project.repository.rugged.config['gitlab.fullpath']).to eq(project.full_path)
+ end
end
describe '#pages_path' do
@@ -2668,14 +2683,12 @@ describe Project do
end
context 'hashed storage' do
- let(:project) { create(:project, :repository) }
+ let(:project) { create(:project, :repository, skip_disk_validation: true) }
let(:gitlab_shell) { Gitlab::Shell.new }
- let(:hash) { '6b86b273ff34fce19d6b804eff5a3f5747ada4eaa22f1d49c01e52ddb7875b4b' }
+ let(:hash) { Digest::SHA2.hexdigest(project.id.to_s) }
before do
stub_application_setting(hashed_storage_enabled: true)
- allow(Digest::SHA2).to receive(:hexdigest) { hash }
- allow(project).to receive(:gitlab_shell).and_return(gitlab_shell)
end
describe '#legacy_storage?' do
@@ -2698,13 +2711,13 @@ describe Project do
describe '#base_dir' do
it 'returns base_dir based on hash of project id' do
- expect(project.base_dir).to eq('@hashed/6b/86')
+ expect(project.base_dir).to eq("@hashed/#{hash[0..1]}/#{hash[2..3]}")
end
end
describe '#disk_path' do
it 'returns disk_path based on hash of project id' do
- hashed_path = '@hashed/6b/86/6b86b273ff34fce19d6b804eff5a3f5747ada4eaa22f1d49c01e52ddb7875b4b'
+ hashed_path = "@hashed/#{hash[0..1]}/#{hash[2..3]}/#{hash}"
expect(project.disk_path).to eq(hashed_path)
end
@@ -2712,7 +2725,9 @@ describe Project do
describe '#ensure_storage_path_exists' do
it 'delegates to gitlab_shell to ensure namespace is created' do
- expect(gitlab_shell).to receive(:add_namespace).with(project.repository_storage_path, '@hashed/6b/86')
+ allow(project).to receive(:gitlab_shell).and_return(gitlab_shell)
+
+ expect(gitlab_shell).to receive(:add_namespace).with(project.repository_storage_path, "@hashed/#{hash[0..1]}/#{hash[2..3]}")
project.ensure_storage_path_exists
end
@@ -2772,7 +2787,7 @@ describe Project do
end
context 'when not rolled out' do
- let(:project) { create(:project, :repository, storage_version: 1) }
+ let(:project) { create(:project, :repository, storage_version: 1, skip_disk_validation: true) }
it 'moves pages folder to new location' do
expect_any_instance_of(Gitlab::UploadsTransfer).to receive(:rename_project)
@@ -2781,6 +2796,12 @@ describe Project do
end
end
end
+
+ it 'updates project full path in .git/config' do
+ project.rename_repo
+
+ expect(project.repository.rugged.config['gitlab.fullpath']).to eq(project.full_path)
+ end
end
describe '#pages_path' do
@@ -3058,9 +3079,51 @@ describe Project do
expect(project).to receive(:import_finish)
expect(project).to receive(:update_project_counter_caches)
expect(project).to receive(:remove_import_jid)
+ expect(project).to receive(:after_create_default_branch)
project.after_import
end
+
+ context 'branch protection' do
+ let(:project) { create(:project, :repository) }
+
+ it 'does not protect when branch protection is disabled' do
+ stub_application_setting(default_branch_protection: Gitlab::Access::PROTECTION_NONE)
+
+ project.after_import
+
+ expect(project.protected_branches).to be_empty
+ end
+
+ it "gives developer access to push when branch protection is set to 'developers can push'" do
+ stub_application_setting(default_branch_protection: Gitlab::Access::PROTECTION_DEV_CAN_PUSH)
+
+ project.after_import
+
+ expect(project.protected_branches).not_to be_empty
+ expect(project.default_branch).to eq(project.protected_branches.first.name)
+ expect(project.protected_branches.first.push_access_levels.map(&:access_level)).to eq([Gitlab::Access::DEVELOPER])
+ end
+
+ it "gives developer access to merge when branch protection is set to 'developers can merge'" do
+ stub_application_setting(default_branch_protection: Gitlab::Access::PROTECTION_DEV_CAN_MERGE)
+
+ project.after_import
+
+ expect(project.protected_branches).not_to be_empty
+ expect(project.default_branch).to eq(project.protected_branches.first.name)
+ expect(project.protected_branches.first.merge_access_levels.map(&:access_level)).to eq([Gitlab::Access::DEVELOPER])
+ end
+
+ it 'protects default branch' do
+ project.after_import
+
+ expect(project.protected_branches).not_to be_empty
+ expect(project.default_branch).to eq(project.protected_branches.first.name)
+ expect(project.protected_branches.first.push_access_levels.map(&:access_level)).to eq([Gitlab::Access::MASTER])
+ expect(project.protected_branches.first.merge_access_levels.map(&:access_level)).to eq([Gitlab::Access::MASTER])
+ end
+ end
end
describe '#update_project_counter_caches' do
@@ -3123,22 +3186,25 @@ describe Project do
end
end
- describe '#deployment_platform' do
- subject { project.deployment_platform }
+ describe '#write_repository_config' do
+ set(:project) { create(:project, :repository) }
- let(:project) { create(:project) }
+ it 'writes full path in .git/config when key is missing' do
+ project.write_repository_config
+
+ expect(project.repository.rugged.config['gitlab.fullpath']).to eq project.full_path
+ end
- context 'when user configured kubernetes from Integration > Kubernetes' do
- let!(:kubernetes_service) { create(:kubernetes_service, project: project) }
+ it 'updates full path in .git/config when key is present' do
+ project.write_repository_config(gl_full_path: 'old/path')
- it { is_expected.to eq(kubernetes_service) }
+ expect { project.write_repository_config }.to change { project.repository.rugged.config['gitlab.fullpath'] }.from('old/path').to(project.full_path)
end
- context 'when user configured kubernetes from CI/CD > Clusters' do
- let!(:cluster) { create(:cluster, :provided_by_gcp, projects: [project]) }
- let(:platform_kubernetes) { cluster.platform_kubernetes }
+ it 'does not raise an error with an empty repository' do
+ project = create(:project_empty_repo)
- it { is_expected.to eq(platform_kubernetes) }
+ expect { project.write_repository_config }.not_to raise_error
end
end
end
diff --git a/spec/models/project_team_spec.rb b/spec/models/project_team_spec.rb
index 314824b32da..e07c522800a 100644
--- a/spec/models/project_team_spec.rb
+++ b/spec/models/project_team_spec.rb
@@ -291,8 +291,8 @@ describe ProjectTeam do
group.add_master(master)
group.add_developer(developer)
- members_project.team << [developer, :developer]
- members_project.team << [master, :master]
+ members_project.add_developer(developer)
+ members_project.add_master(master)
create(:project_group_link, project: shared_project, group: group)
end
diff --git a/spec/models/repository_spec.rb b/spec/models/repository_spec.rb
index 358bc3dfb94..c0db2c1b386 100644
--- a/spec/models/repository_spec.rb
+++ b/spec/models/repository_spec.rb
@@ -59,12 +59,18 @@ describe Repository do
end
describe 'tags_sorted_by' do
- context 'name' do
- subject { repository.tags_sorted_by('name').map(&:name) }
+ context 'name_desc' do
+ subject { repository.tags_sorted_by('name_desc').map(&:name) }
it { is_expected.to eq(['v1.1.0', 'v1.0.0']) }
end
+ context 'name_asc' do
+ subject { repository.tags_sorted_by('name_asc').map(&:name) }
+
+ it { is_expected.to eq(['v1.0.0', 'v1.1.0']) }
+ end
+
context 'updated' do
let(:tag_a) { repository.find_tag('v1.0.0') }
let(:tag_b) { repository.find_tag('v1.1.0') }
@@ -233,6 +239,54 @@ describe Repository do
end
end
+ describe '#commits_by' do
+ set(:project) { create(:project, :repository) }
+
+ shared_examples 'batch commits fetching' do
+ let(:oids) { TestEnv::BRANCH_SHA.values }
+
+ subject { project.repository.commits_by(oids: oids) }
+
+ it 'finds each commit' do
+ expect(subject).not_to include(nil)
+ expect(subject.size).to eq(oids.size)
+ end
+
+ it 'returns only Commit instances' do
+ expect(subject).to all( be_a(Commit) )
+ end
+
+ context 'when some commits are not found ' do
+ let(:oids) do
+ ['deadbeef'] + TestEnv::BRANCH_SHA.values.first(10)
+ end
+
+ it 'returns only found commits' do
+ expect(subject).not_to include(nil)
+ expect(subject.size).to eq(10)
+ end
+ end
+
+ context 'when no oids are passed' do
+ let(:oids) { [] }
+
+ it 'does not call #batch_by_oid' do
+ expect(Gitlab::Git::Commit).not_to receive(:batch_by_oid)
+
+ subject
+ end
+ end
+ end
+
+ context 'when Gitaly list_commits_by_oid is enabled' do
+ it_behaves_like 'batch commits fetching'
+ end
+
+ context 'when Gitaly list_commits_by_oid is enabled', :disable_gitaly do
+ it_behaves_like 'batch commits fetching'
+ end
+ end
+
describe '#find_commits_by_message' do
shared_examples 'finding commits by message' do
it 'returns commits with messages containing a given string' do
@@ -528,38 +582,6 @@ describe Repository do
end
end
- describe '#get_committer_and_author' do
- it 'returns the committer and author data' do
- options = repository.get_committer_and_author(user)
- expect(options[:committer][:email]).to eq(user.email)
- expect(options[:author][:email]).to eq(user.email)
- end
-
- context 'when the email/name are given' do
- it 'returns an object containing the email/name' do
- options = repository.get_committer_and_author(user, email: author_email, name: author_name)
- expect(options[:author][:email]).to eq(author_email)
- expect(options[:author][:name]).to eq(author_name)
- end
- end
-
- context 'when the email is given but the name is not' do
- it 'returns the committer as the author' do
- options = repository.get_committer_and_author(user, email: author_email)
- expect(options[:author][:email]).to eq(user.email)
- expect(options[:author][:name]).to eq(user.name)
- end
- end
-
- context 'when the name is given but the email is not' do
- it 'returns nil' do
- options = repository.get_committer_and_author(user, name: author_name)
- expect(options[:author][:email]).to eq(user.email)
- expect(options[:author][:name]).to eq(user.name)
- end
- end
- end
-
describe "search_files_by_content" do
let(:results) { repository.search_files_by_content('feature', 'master') }
subject { results }
@@ -1007,7 +1029,7 @@ describe Repository do
it 'runs without errors' do
# old_rev is an ancestor of new_rev
- expect(repository.rugged.merge_base(old_rev, new_rev)).to eq(old_rev)
+ expect(repository.merge_base(old_rev, new_rev)).to eq(old_rev)
# old_rev is not a direct ancestor (parent) of new_rev
expect(repository.rugged.lookup(new_rev).parent_ids).not_to include(old_rev)
@@ -1029,7 +1051,7 @@ describe Repository do
it 'raises an exception' do
# The 'master' branch is NOT an ancestor of new_rev.
- expect(repository.rugged.merge_base(old_rev, new_rev)).not_to eq(old_rev)
+ expect(repository.merge_base(old_rev, new_rev)).not_to eq(old_rev)
# Updating 'master' to new_rev would lose the commits on 'master' that
# are not contained in new_rev. This should not be allowed.
@@ -1058,16 +1080,16 @@ describe Repository do
allow_any_instance_of(Gitlab::Git::Hook).to receive(:trigger).and_return([true, ''])
end
- it 'expires branch cache' do
- expect(repository).not_to receive(:expire_exists_cache)
- expect(repository).not_to receive(:expire_root_ref_cache)
- expect(repository).not_to receive(:expire_emptiness_caches)
- expect(repository).to receive(:expire_branches_cache)
-
- repository.with_branch(user, 'new-feature') do
+ subject do
+ Gitlab::Git::OperationService.new(git_user, repository.raw_repository).with_branch('new-feature') do
new_rev
end
end
+
+ it 'returns branch_created as true' do
+ expect(subject).not_to be_repo_created
+ expect(subject).to be_branch_created
+ end
end
context 'when repository is empty' do
@@ -1157,6 +1179,15 @@ describe Repository do
end
end
+ describe '#tag_exists?' do
+ it 'uses tag_names' do
+ allow(repository).to receive(:tag_names).and_return(['foobar'])
+
+ expect(repository.tag_exists?('foobar')).to eq(true)
+ expect(repository.tag_exists?('master')).to eq(false)
+ end
+ end
+
describe '#branch_names', :use_clean_rails_memory_store_caching do
let(:fake_branch_names) { ['foobar'] }
@@ -2152,6 +2183,15 @@ describe Repository do
end
end
+ describe '#diverging_commit_counts' do
+ it 'returns the commit counts behind and ahead of default branch' do
+ result = repository.diverging_commit_counts(
+ repository.find_branch('fix'))
+
+ expect(result).to eq(behind: 29, ahead: 2)
+ end
+ end
+
describe '#cache_method_output', :use_clean_rails_memory_store_caching do
let(:fallback) { 10 }
@@ -2343,4 +2383,111 @@ describe Repository do
end
end
end
+
+ describe '#contributors' do
+ let(:author_a) { build(:author, email: 'tiagonbotelho@hotmail.com', name: 'tiagonbotelho') }
+ let(:author_b) { build(:author, email: 'gitlab@winniehell.de', name: 'Winnie') }
+ let(:author_c) { build(:author, email: 'douwe@gitlab.com', name: 'Douwe Maan') }
+ let(:stubbed_commits) do
+ [build(:commit, author: author_a),
+ build(:commit, author: author_a),
+ build(:commit, author: author_b),
+ build(:commit, author: author_c),
+ build(:commit, author: author_c),
+ build(:commit, author: author_c)]
+ end
+ let(:order_by) { nil }
+ let(:sort) { nil }
+
+ before do
+ allow(repository).to receive(:commits).with(nil, limit: 2000, offset: 0, skip_merges: true).and_return(stubbed_commits)
+ end
+
+ subject { repository.contributors(order_by: order_by, sort: sort) }
+
+ def expect_contributors(*contributors)
+ expect(subject.map(&:email)).to eq(contributors.map(&:email))
+ end
+
+ it 'returns the array of Gitlab::Contributor for the repository' do
+ expect_contributors(author_a, author_b, author_c)
+ end
+
+ context 'order_by email' do
+ let(:order_by) { 'email' }
+
+ context 'asc' do
+ let(:sort) { 'asc' }
+
+ it 'returns all the contributors ordered by email asc case insensitive' do
+ expect_contributors(author_c, author_b, author_a)
+ end
+ end
+
+ context 'desc' do
+ let(:sort) { 'desc' }
+
+ it 'returns all the contributors ordered by email desc case insensitive' do
+ expect_contributors(author_a, author_b, author_c)
+ end
+ end
+ end
+
+ context 'order_by name' do
+ let(:order_by) { 'name' }
+
+ context 'asc' do
+ let(:sort) { 'asc' }
+
+ it 'returns all the contributors ordered by name asc case insensitive' do
+ expect_contributors(author_c, author_a, author_b)
+ end
+ end
+
+ context 'desc' do
+ let(:sort) { 'desc' }
+
+ it 'returns all the contributors ordered by name desc case insensitive' do
+ expect_contributors(author_b, author_a, author_c)
+ end
+ end
+ end
+
+ context 'order_by commits' do
+ let(:order_by) { 'commits' }
+
+ context 'asc' do
+ let(:sort) { 'asc' }
+
+ it 'returns all the contributors ordered by commits asc' do
+ expect_contributors(author_b, author_a, author_c)
+ end
+ end
+
+ context 'desc' do
+ let(:sort) { 'desc' }
+
+ it 'returns all the contributors ordered by commits desc' do
+ expect_contributors(author_c, author_a, author_b)
+ end
+ end
+ end
+
+ context 'invalid ordering' do
+ let(:order_by) { 'unknown' }
+
+ it 'returns the contributors unsorted' do
+ expect_contributors(author_a, author_b, author_c)
+ end
+ end
+
+ context 'invalid sorting' do
+ let(:order_by) { 'name' }
+ let(:sort) { 'unknown' }
+
+ it 'returns the contributors unsorted' do
+ expect_contributors(author_a, author_b, author_c)
+ end
+ end
+ end
end
diff --git a/spec/models/route_spec.rb b/spec/models/route_spec.rb
index fece370c03f..ddad6862a63 100644
--- a/spec/models/route_spec.rb
+++ b/spec/models/route_spec.rb
@@ -87,6 +87,7 @@ describe Route do
end
context 'when conflicting redirects exist' do
+ let(:route) { create(:project).route }
let!(:conflicting_redirect1) { route.create_redirect('bar/test') }
let!(:conflicting_redirect2) { route.create_redirect('bar/test/foo') }
let!(:conflicting_redirect3) { route.create_redirect('gitlab-org') }
@@ -141,11 +142,50 @@ describe Route do
expect(redirect_route.source).to eq(route.source)
expect(redirect_route.path).to eq('foo')
end
+
+ context 'when the source is a Project' do
+ it 'creates a temporal RedirectRoute' do
+ project = create(:project)
+ route = project.route
+ redirect_route = route.create_redirect('foo')
+ expect(redirect_route.permanent?).to be_falsy
+ end
+ end
+
+ context 'when the source is not a project' do
+ it 'creates a permanent RedirectRoute' do
+ redirect_route = route.create_redirect('foo', permanent: true)
+ expect(redirect_route.permanent?).to be_truthy
+ end
+ end
end
describe '#delete_conflicting_redirects' do
+ context 'with permanent redirect' do
+ it 'does not delete the redirect' do
+ route.create_redirect("#{route.path}/foo", permanent: true)
+
+ expect do
+ route.delete_conflicting_redirects
+ end.not_to change { RedirectRoute.count }
+ end
+ end
+
+ context 'with temporal redirect' do
+ let(:route) { create(:project).route }
+
+ it 'deletes the redirect' do
+ route.create_redirect("#{route.path}/foo")
+
+ expect do
+ route.delete_conflicting_redirects
+ end.to change { RedirectRoute.count }.by(-1)
+ end
+ end
+
context 'when a redirect route with the same path exists' do
context 'when the redirect route has matching case' do
+ let(:route) { create(:project).route }
let!(:redirect1) { route.create_redirect(route.path) }
it 'deletes the redirect' do
@@ -169,6 +209,7 @@ describe Route do
end
context 'when the redirect route is differently cased' do
+ let(:route) { create(:project).route }
let!(:redirect1) { route.create_redirect(route.path.upcase) }
it 'deletes the redirect' do
@@ -185,7 +226,32 @@ describe Route do
expect(route.conflicting_redirects).to be_an(ActiveRecord::Relation)
end
+ context 'with permanent redirects' do
+ it 'does not return anything' do
+ route.create_redirect("#{route.path}/foo", permanent: true)
+ route.create_redirect("#{route.path}/foo/bar", permanent: true)
+ route.create_redirect("#{route.path}/baz/quz", permanent: true)
+
+ expect(route.conflicting_redirects).to be_empty
+ end
+ end
+
+ context 'with temporal redirects' do
+ let(:route) { create(:project).route }
+
+ it 'returns the redirect routes' do
+ route = create(:project).route
+ redirect1 = route.create_redirect("#{route.path}/foo")
+ redirect2 = route.create_redirect("#{route.path}/foo/bar")
+ redirect3 = route.create_redirect("#{route.path}/baz/quz")
+
+ expect(route.conflicting_redirects).to match_array([redirect1, redirect2, redirect3])
+ end
+ end
+
context 'when a redirect route with the same path exists' do
+ let(:route) { create(:project).route }
+
context 'when the redirect route has matching case' do
let!(:redirect1) { route.create_redirect(route.path) }
@@ -214,4 +280,42 @@ describe Route do
end
end
end
+
+ describe "#conflicting_redirect_exists?" do
+ context 'when a conflicting redirect exists' do
+ let(:group1) { create(:group, path: 'foo') }
+ let(:group2) { create(:group, path: 'baz') }
+
+ it 'should not be saved' do
+ group1.path = 'bar'
+ group1.save
+
+ group2.path = 'foo'
+
+ expect(group2.save).to be_falsy
+ end
+
+ it 'should return an error on path' do
+ group1.path = 'bar'
+ group1.save
+
+ group2.path = 'foo'
+ group2.valid?
+ expect(group2.errors["route.path"].first).to eq('foo has been taken before. Please use another one')
+ end
+ end
+
+ context 'when a conflicting redirect does not exist' do
+ let(:project1) { create(:project, path: 'foo') }
+ let(:project2) { create(:project, path: 'baz') }
+
+ it 'should be saved' do
+ project1.path = 'bar'
+ project1.save
+
+ project2.path = 'foo'
+ expect(project2.save).to be_truthy
+ end
+ end
+ end
end
diff --git a/spec/models/service_spec.rb b/spec/models/service_spec.rb
index 0f2f906c667..ab6678cab38 100644
--- a/spec/models/service_spec.rb
+++ b/spec/models/service_spec.rb
@@ -254,4 +254,30 @@ describe Service do
end
end
end
+
+ describe "#deprecated?" do
+ let(:project) { create(:project, :repository) }
+
+ it 'should return false by default' do
+ service = create(:service, project: project)
+ expect(service.deprecated?).to be_falsy
+ end
+ end
+
+ describe "#deprecation_message" do
+ let(:project) { create(:project, :repository) }
+
+ it 'should be empty by default' do
+ service = create(:service, project: project)
+ expect(service.deprecation_message).to be_nil
+ end
+ end
+
+ describe '.find_by_template' do
+ let!(:kubernetes_service) { create(:kubernetes_service, template: true) }
+
+ it 'returns service template' do
+ expect(KubernetesService.find_by_template).to eq(kubernetes_service)
+ end
+ end
end
diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb
index 03c96a8f5aa..8d0eaf565a7 100644
--- a/spec/models/user_spec.rb
+++ b/spec/models/user_spec.rb
@@ -12,6 +12,7 @@ describe User do
it { is_expected.to include_module(Referable) }
it { is_expected.to include_module(Sortable) }
it { is_expected.to include_module(TokenAuthenticatable) }
+ it { is_expected.to include_module(BlocksJsonSerialization) }
end
describe 'delegations' do
@@ -21,7 +22,9 @@ describe User do
describe 'associations' do
it { is_expected.to have_one(:namespace) }
it { is_expected.to have_many(:snippets).dependent(:destroy) }
- it { is_expected.to have_many(:project_members).dependent(:destroy) }
+ it { is_expected.to have_many(:members) }
+ it { is_expected.to have_many(:project_members) }
+ it { is_expected.to have_many(:group_members) }
it { is_expected.to have_many(:groups) }
it { is_expected.to have_many(:keys).dependent(:destroy) }
it { is_expected.to have_many(:deploy_keys).dependent(:destroy) }
@@ -133,6 +136,16 @@ describe User do
end
end
+ it 'has a DB-level NOT NULL constraint on projects_limit' do
+ user = create(:user)
+
+ expect(user.persisted?).to eq(true)
+
+ expect do
+ user.update_columns(projects_limit: nil)
+ end.to raise_error(ActiveRecord::StatementInvalid)
+ end
+
it { is_expected.to validate_presence_of(:projects_limit) }
it { is_expected.to validate_numericality_of(:projects_limit) }
it { is_expected.to allow_value(0).for(:projects_limit) }
@@ -759,7 +772,7 @@ describe User do
before do
# add user to project
- project.team << [user, :master]
+ project.add_master(user)
# create invite to projet
create(:project_member, :developer, project: project, invite_token: '1234', invite_email: 'inviteduser1@example.com')
@@ -804,6 +817,13 @@ describe User do
expect(user.can_create_group).to be_falsey
expect(user.theme_id).to eq(1)
end
+
+ it 'does not undo projects_limit setting if it matches old DB default of 10' do
+ # If the real default project limit is 10 then this test is worthless
+ expect(Gitlab.config.gitlab.default_projects_limit).not_to eq(10)
+ user = described_class.new(projects_limit: 10)
+ expect(user.projects_limit).to eq(10)
+ end
end
context 'when current_application_settings.user_default_external is true' do
@@ -913,11 +933,11 @@ describe User do
describe 'email matching' do
it 'returns users with a matching Email' do
- expect(described_class.search(user.email)).to eq([user, user2])
+ expect(described_class.search(user.email)).to eq([user])
end
- it 'returns users with a partially matching Email' do
- expect(described_class.search(user.email[0..2])).to eq([user, user2])
+ it 'does not return users with a partially matching Email' do
+ expect(described_class.search(user.email[0..2])).not_to include(user, user2)
end
it 'returns users with a matching Email regardless of the casing' do
@@ -973,8 +993,8 @@ describe User do
expect(search_with_secondary_emails(user.email)).to eq([user])
end
- it 'returns users with a partially matching email' do
- expect(search_with_secondary_emails(user.email[0..2])).to eq([user])
+ it 'does not return users with a partially matching email' do
+ expect(search_with_secondary_emails(user.email[0..2])).not_to include([user])
end
it 'returns users with a matching email regardless of the casing' do
@@ -997,29 +1017,8 @@ describe User do
expect(search_with_secondary_emails(email.email)).to eq([email.user])
end
- it 'returns users with a matching part of secondary email' do
- expect(search_with_secondary_emails(email.email[1..4])).to eq([email.user])
- end
-
- it 'return users with a matching part of secondary email regardless of case' do
- expect(search_with_secondary_emails(email.email[1..4].upcase)).to eq([email.user])
- expect(search_with_secondary_emails(email.email[1..4].downcase)).to eq([email.user])
- expect(search_with_secondary_emails(email.email[1..4].capitalize)).to eq([email.user])
- end
-
- it 'returns multiple users with matching secondary emails' do
- email1 = create(:email, email: '1_testemail@example.com')
- email2 = create(:email, email: '2_testemail@example.com')
- email3 = create(:email, email: 'other@email.com')
- email3.user.update_attributes!(email: 'another@mail.com')
-
- expect(
- search_with_secondary_emails('testemail@example.com').map(&:id)
- ).to include(email1.user.id, email2.user.id)
-
- expect(
- search_with_secondary_emails('testemail@example.com').map(&:id)
- ).not_to include(email3.user.id)
+ it 'does not return users with a matching part of secondary email' do
+ expect(search_with_secondary_emails(email.email[1..4])).not_to include([email.user])
end
end
@@ -1468,8 +1467,8 @@ describe User do
let!(:merge_event) { create(:event, :created, project: project3, target: merge_request, author: subject) }
before do
- project1.team << [subject, :master]
- project2.team << [subject, :master]
+ project1.add_master(subject)
+ project2.add_master(subject)
end
it "includes IDs for projects the user has pushed to" do
@@ -1568,7 +1567,7 @@ describe User do
user = create(:user)
project = create(:project, :private)
- project.team << [user, Gitlab::Access::MASTER]
+ project.add_master(user)
expect(user.authorized_projects(Gitlab::Access::REPORTER))
.to contain_exactly(project)
@@ -1587,7 +1586,7 @@ describe User do
user2 = create(:user)
project = create(:project, :private, namespace: user1.namespace)
- project.team << [user2, Gitlab::Access::DEVELOPER]
+ project.add_developer(user2)
expect(user2.authorized_projects).to include(project)
end
@@ -1632,7 +1631,7 @@ describe User do
user2 = create(:user)
project = create(:project, :private, namespace: user1.namespace)
- project.team << [user2, Gitlab::Access::DEVELOPER]
+ project.add_developer(user2)
expect(user2.authorized_projects).to include(project)
@@ -1722,7 +1721,7 @@ describe User do
shared_examples :member do
context 'when the user is a master' do
before do
- add_user(Gitlab::Access::MASTER)
+ add_user(:master)
end
it 'loads' do
@@ -1732,7 +1731,7 @@ describe User do
context 'when the user is a developer' do
before do
- add_user(Gitlab::Access::DEVELOPER)
+ add_user(:developer)
end
it 'does not load' do
@@ -1756,7 +1755,7 @@ describe User do
let(:project) { create(:project) }
def add_user(access)
- project.team << [user, access]
+ project.add_role(user, access)
end
it_behaves_like :member
@@ -1769,8 +1768,8 @@ describe User do
let(:user) { create(:user) }
before do
- project1.team << [user, :reporter]
- project2.team << [user, :guest]
+ project1.add_reporter(user)
+ project2.add_guest(user)
end
it 'returns the projects when using a single project ID' do
@@ -1912,8 +1911,8 @@ describe User do
let(:user) { create(:user) }
before do
- project1.team << [user, :reporter]
- project2.team << [user, :guest]
+ project1.add_reporter(user)
+ project2.add_guest(user)
user.project_authorizations.delete_all
user.refresh_authorized_projects
@@ -2592,4 +2591,28 @@ describe User do
include_examples 'max member access for groups'
end
end
+
+ describe "#username_previously_taken?" do
+ let(:user1) { create(:user, username: 'foo') }
+
+ context 'when the username has been taken before' do
+ before do
+ user1.username = 'bar'
+ user1.save!
+ end
+
+ it 'should raise an ActiveRecord::RecordInvalid exception' do
+ user2 = build(:user, username: 'foo')
+ expect { user2.save! }.to raise_error(ActiveRecord::RecordInvalid, /Path foo has been taken before/)
+ end
+ end
+
+ context 'when the username has not been taken before' do
+ it 'should be valid' do
+ expect(RedirectRoute.count).to eq(0)
+ user2 = build(:user, username: 'baz')
+ expect(user2).to be_valid
+ end
+ end
+ end
end
diff --git a/spec/policies/ci/build_policy_spec.rb b/spec/policies/ci/build_policy_spec.rb
index 298a9d16425..41cf2ef7225 100644
--- a/spec/policies/ci/build_policy_spec.rb
+++ b/spec/policies/ci/build_policy_spec.rb
@@ -57,7 +57,7 @@ describe Ci::BuildPolicy do
context 'team member is a guest' do
before do
- project.team << [user, :guest]
+ project.add_guest(user)
end
context 'when public builds are enabled' do
@@ -77,7 +77,7 @@ describe Ci::BuildPolicy do
context 'team member is a reporter' do
before do
- project.team << [user, :reporter]
+ project.add_reporter(user)
end
context 'when public builds are enabled' do
diff --git a/spec/policies/ci/pipeline_schedule_policy_spec.rb b/spec/policies/ci/pipeline_schedule_policy_spec.rb
new file mode 100644
index 00000000000..1b0e9fac355
--- /dev/null
+++ b/spec/policies/ci/pipeline_schedule_policy_spec.rb
@@ -0,0 +1,92 @@
+require 'spec_helper'
+
+describe Ci::PipelineSchedulePolicy, :models do
+ set(:user) { create(:user) }
+ set(:project) { create(:project, :repository) }
+ set(:pipeline_schedule) { create(:ci_pipeline_schedule, :nightly, project: project) }
+
+ let(:policy) do
+ described_class.new(user, pipeline_schedule)
+ end
+
+ describe 'rules' do
+ describe 'rules for protected ref' do
+ before do
+ project.add_developer(user)
+ end
+
+ context 'when no one can push or merge to the branch' do
+ before do
+ create(:protected_branch, :no_one_can_push,
+ name: pipeline_schedule.ref, project: project)
+ end
+
+ it 'does not include ability to play pipeline schedule' do
+ expect(policy).to be_disallowed :play_pipeline_schedule
+ end
+ end
+
+ context 'when developers can push to the branch' do
+ before do
+ create(:protected_branch, :developers_can_merge,
+ name: pipeline_schedule.ref, project: project)
+ end
+
+ it 'includes ability to update pipeline' do
+ expect(policy).to be_allowed :play_pipeline_schedule
+ end
+ end
+
+ context 'when no one can create the tag' do
+ let(:tag) { 'v1.0.0' }
+
+ before do
+ pipeline_schedule.update(ref: tag)
+
+ create(:protected_tag, :no_one_can_create,
+ name: pipeline_schedule.ref, project: project)
+ end
+
+ it 'does not include ability to play pipeline schedule' do
+ expect(policy).to be_disallowed :play_pipeline_schedule
+ end
+ end
+
+ context 'when no one can create the tag but it is not a tag' do
+ before do
+ create(:protected_tag, :no_one_can_create,
+ name: pipeline_schedule.ref, project: project)
+ end
+
+ it 'includes ability to play pipeline schedule' do
+ expect(policy).to be_allowed :play_pipeline_schedule
+ end
+ end
+ end
+
+ describe 'rules for owner of schedule' do
+ before do
+ project.add_developer(user)
+ pipeline_schedule.update(owner: user)
+ end
+
+ it 'includes abilities to do do all operations on pipeline schedule' do
+ expect(policy).to be_allowed :play_pipeline_schedule
+ expect(policy).to be_allowed :update_pipeline_schedule
+ expect(policy).to be_allowed :admin_pipeline_schedule
+ end
+ end
+
+ describe 'rules for a master' do
+ before do
+ project.add_master(user)
+ end
+
+ it 'includes abilities to do do all operations on pipeline schedule' do
+ expect(policy).to be_allowed :play_pipeline_schedule
+ expect(policy).to be_allowed :update_pipeline_schedule
+ expect(policy).to be_allowed :admin_pipeline_schedule
+ end
+ end
+ end
+end
diff --git a/spec/policies/ci/trigger_policy_spec.rb b/spec/policies/ci/trigger_policy_spec.rb
index be40dbb2aa9..14630748c90 100644
--- a/spec/policies/ci/trigger_policy_spec.rb
+++ b/spec/policies/ci/trigger_policy_spec.rb
@@ -45,7 +45,7 @@ describe Ci::TriggerPolicy do
context 'when user is master of the project' do
before do
- project.team << [user, :master]
+ project.add_master(user)
end
it_behaves_like 'allows to admin and manage trigger'
@@ -53,7 +53,7 @@ describe Ci::TriggerPolicy do
context 'when user is developer of the project' do
before do
- project.team << [user, :developer]
+ project.add_developer(user)
end
it_behaves_like 'disallows to admin and manage trigger'
@@ -69,7 +69,7 @@ describe Ci::TriggerPolicy do
context 'when user is master of the project' do
before do
- project.team << [user, :master]
+ project.add_master(user)
end
it_behaves_like 'allows to admin and manage trigger'
@@ -81,7 +81,7 @@ describe Ci::TriggerPolicy do
context 'when user is master of the project' do
before do
- project.team << [user, :master]
+ project.add_master(user)
end
it_behaves_like 'allows to manage trigger'
@@ -89,7 +89,7 @@ describe Ci::TriggerPolicy do
context 'when user is developer of the project' do
before do
- project.team << [user, :developer]
+ project.add_developer(user)
end
it_behaves_like 'disallows to admin and manage trigger'
diff --git a/spec/policies/issue_policy_spec.rb b/spec/policies/issue_policy_spec.rb
index be4c24c727c..a4af9361ea6 100644
--- a/spec/policies/issue_policy_spec.rb
+++ b/spec/policies/issue_policy_spec.rb
@@ -19,10 +19,10 @@ describe IssuePolicy do
let(:issue_no_assignee) { create(:issue, project: project) }
before do
- project.team << [guest, :guest]
- project.team << [author, :guest]
- project.team << [assignee, :guest]
- project.team << [reporter, :reporter]
+ project.add_guest(guest)
+ project.add_guest(author)
+ project.add_guest(assignee)
+ project.add_reporter(reporter)
group.add_reporter(reporter_from_group_link)
@@ -114,8 +114,8 @@ describe IssuePolicy do
let(:issue_no_assignee) { create(:issue, project: project) }
before do
- project.team << [guest, :guest]
- project.team << [reporter, :reporter]
+ project.add_guest(guest)
+ project.add_reporter(reporter)
group.add_reporter(reporter_from_group_link)
diff --git a/spec/policies/project_snippet_policy_spec.rb b/spec/policies/project_snippet_policy_spec.rb
index f0bf46c480a..cdba1b09fc1 100644
--- a/spec/policies/project_snippet_policy_spec.rb
+++ b/spec/policies/project_snippet_policy_spec.rb
@@ -87,7 +87,7 @@ describe ProjectSnippetPolicy do
subject { abilities(external_user, :internal) }
before do
- project.team << [external_user, :developer]
+ project.add_developer(external_user)
end
it do
@@ -131,7 +131,7 @@ describe ProjectSnippetPolicy do
subject { abilities(regular_user, :private) }
before do
- project.team << [regular_user, :developer]
+ project.add_developer(regular_user)
end
it do
@@ -144,7 +144,7 @@ describe ProjectSnippetPolicy do
subject { abilities(external_user, :private) }
before do
- project.team << [external_user, :developer]
+ project.add_developer(external_user)
end
it do
diff --git a/spec/presenters/group_member_presenter_spec.rb b/spec/presenters/group_member_presenter_spec.rb
new file mode 100644
index 00000000000..c00e41725d9
--- /dev/null
+++ b/spec/presenters/group_member_presenter_spec.rb
@@ -0,0 +1,138 @@
+require 'spec_helper'
+
+describe GroupMemberPresenter do
+ let(:user) { double(:user) }
+ let(:group) { double(:group) }
+ let(:group_member) { double(:group_member, source: group) }
+ let(:presenter) { described_class.new(group_member, current_user: user) }
+
+ describe '#can_resend_invite?' do
+ context 'when group_member is invited' do
+ before do
+ expect(group_member).to receive(:invite?).and_return(true)
+ end
+
+ context 'and user can admin_group_member' do
+ before do
+ allow(presenter).to receive(:can?).with(user, :admin_group_member, group).and_return(true)
+ end
+
+ it { expect(presenter.can_resend_invite?).to eq(true) }
+ end
+
+ context 'and user cannot admin_group_member' do
+ before do
+ allow(presenter).to receive(:can?).with(user, :admin_group_member, group).and_return(false)
+ end
+
+ it { expect(presenter.can_resend_invite?).to eq(false) }
+ end
+ end
+
+ context 'when group_member is not invited' do
+ before do
+ expect(group_member).to receive(:invite?).and_return(false)
+ end
+
+ context 'and user can admin_group_member' do
+ before do
+ allow(presenter).to receive(:can?).with(user, :admin_group_member, group).and_return(true)
+ end
+
+ it { expect(presenter.can_resend_invite?).to eq(false) }
+ end
+
+ context 'and user cannot admin_group_member' do
+ before do
+ allow(presenter).to receive(:can?).with(user, :admin_group_member, group).and_return(false)
+ end
+
+ it { expect(presenter.can_resend_invite?).to eq(false) }
+ end
+ end
+ end
+
+ describe '#can_update?' do
+ context 'when user can update_group_member' do
+ before do
+ allow(presenter).to receive(:can?).with(user, :update_group_member, presenter).and_return(true)
+ end
+
+ it { expect(presenter.can_update?).to eq(true) }
+ end
+
+ context 'when user cannot update_group_member' do
+ before do
+ allow(presenter).to receive(:can?).with(user, :update_group_member, presenter).and_return(false)
+ allow(presenter).to receive(:can?).with(user, :override_group_member, presenter).and_return(false)
+ end
+
+ it { expect(presenter.can_update?).to eq(false) }
+ end
+ end
+
+ describe '#can_remove?' do
+ context 'when user can destroy_group_member' do
+ before do
+ allow(presenter).to receive(:can?).with(user, :destroy_group_member, presenter).and_return(true)
+ end
+
+ it { expect(presenter.can_remove?).to eq(true) }
+ end
+
+ context 'when user cannot destroy_group_member' do
+ before do
+ allow(presenter).to receive(:can?).with(user, :destroy_group_member, presenter).and_return(false)
+ end
+
+ it { expect(presenter.can_remove?).to eq(false) }
+ end
+ end
+
+ describe '#can_approve?' do
+ context 'when group_member has request an invite' do
+ before do
+ expect(group_member).to receive(:request?).and_return(true)
+ end
+
+ context 'when user can update_group_member' do
+ before do
+ allow(presenter).to receive(:can?).with(user, :update_group_member, presenter).and_return(true)
+ end
+
+ it { expect(presenter.can_approve?).to eq(true) }
+ end
+
+ context 'when user cannot update_group_member' do
+ before do
+ allow(presenter).to receive(:can?).with(user, :update_group_member, presenter).and_return(false)
+ allow(presenter).to receive(:can?).with(user, :override_group_member, presenter).and_return(false)
+ end
+
+ it { expect(presenter.can_approve?).to eq(false) }
+ end
+ end
+
+ context 'when group_member did not request an invite' do
+ before do
+ expect(group_member).to receive(:request?).and_return(false)
+ end
+
+ context 'when user can update_group_member' do
+ before do
+ allow(presenter).to receive(:can?).with(user, :update_group_member, presenter).and_return(true)
+ end
+
+ it { expect(presenter.can_approve?).to eq(false) }
+ end
+
+ context 'when user cannot update_group_member' do
+ before do
+ allow(presenter).to receive(:can?).with(user, :update_group_member, presenter).and_return(false)
+ end
+
+ it { expect(presenter.can_approve?).to eq(false) }
+ end
+ end
+ end
+end
diff --git a/spec/presenters/merge_request_presenter_spec.rb b/spec/presenters/merge_request_presenter_spec.rb
index f325d1776e4..e3b37739e8e 100644
--- a/spec/presenters/merge_request_presenter_spec.rb
+++ b/spec/presenters/merge_request_presenter_spec.rb
@@ -116,7 +116,7 @@ describe MergeRequestPresenter do
end
before do
- project.team << [user, :developer]
+ project.add_developer(user)
allow(resource.project).to receive(:default_branch)
.and_return(resource.target_branch)
@@ -270,7 +270,7 @@ describe MergeRequestPresenter do
context 'when can create issue and issues enabled' do
it 'returns path' do
allow(project).to receive(:issues_enabled?) { true }
- project.team << [user, :master]
+ project.add_master(user)
is_expected
.to eq("/#{resource.project.full_path}/issues/new?merge_request_to_resolve_discussions_of=#{resource.iid}")
@@ -288,7 +288,7 @@ describe MergeRequestPresenter do
context 'when issues disabled' do
it 'returns nil' do
allow(project).to receive(:issues_enabled?) { false }
- project.team << [user, :master]
+ project.add_master(user)
is_expected.to be_nil
end
@@ -307,7 +307,7 @@ describe MergeRequestPresenter do
context 'when merge request enabled and has permission' do
it 'has remove_wip_path' do
allow(project).to receive(:merge_requests_enabled?) { true }
- project.team << [user, :master]
+ project.add_master(user)
is_expected
.to eq("/#{resource.project.full_path}/merge_requests/#{resource.iid}/remove_wip")
@@ -404,4 +404,67 @@ describe MergeRequestPresenter do
.to eq("<a href=\"/#{resource.source_project.full_path}/tree/#{resource.source_branch}\">#{resource.source_branch}</a>")
end
end
+
+ describe '#rebase_path' do
+ before do
+ allow(resource).to receive(:rebase_in_progress?) { rebase_in_progress }
+ allow(resource).to receive(:should_be_rebased?) { should_be_rebased }
+
+ allow_any_instance_of(Gitlab::UserAccess::RequestCacheExtension)
+ .to receive(:can_push_to_branch?)
+ .with(resource.source_branch)
+ .and_return(can_push_to_branch)
+ end
+
+ subject do
+ described_class.new(resource, current_user: user).rebase_path
+ end
+
+ context 'when can rebase' do
+ let(:rebase_in_progress) { false }
+ let(:can_push_to_branch) { true }
+ let(:should_be_rebased) { true }
+
+ before do
+ allow(resource).to receive(:source_branch_exists?) { true }
+ end
+
+ it 'returns path' do
+ is_expected
+ .to eq("/#{project.full_path}/merge_requests/#{resource.iid}/rebase")
+ end
+ end
+
+ context 'when cannot rebase' do
+ context 'when rebase in progress' do
+ let(:rebase_in_progress) { true }
+ let(:can_push_to_branch) { true }
+ let(:should_be_rebased) { true }
+
+ it 'returns nil' do
+ is_expected.to be_nil
+ end
+ end
+
+ context 'when user cannot merge' do
+ let(:rebase_in_progress) { false }
+ let(:can_push_to_branch) { false }
+ let(:should_be_rebased) { true }
+
+ it 'returns nil' do
+ is_expected.to be_nil
+ end
+ end
+
+ context 'should not be rebased' do
+ let(:rebase_in_progress) { false }
+ let(:can_push_to_branch) { true }
+ let(:should_be_rebased) { false }
+
+ it 'returns nil' do
+ is_expected.to be_nil
+ end
+ end
+ end
+ end
end
diff --git a/spec/presenters/project_member_presenter_spec.rb b/spec/presenters/project_member_presenter_spec.rb
new file mode 100644
index 00000000000..83db5c56cdf
--- /dev/null
+++ b/spec/presenters/project_member_presenter_spec.rb
@@ -0,0 +1,138 @@
+require 'spec_helper'
+
+describe ProjectMemberPresenter do
+ let(:user) { double(:user) }
+ let(:project) { double(:project) }
+ let(:project_member) { double(:project_member, source: project) }
+ let(:presenter) { described_class.new(project_member, current_user: user) }
+
+ describe '#can_resend_invite?' do
+ context 'when project_member is invited' do
+ before do
+ expect(project_member).to receive(:invite?).and_return(true)
+ end
+
+ context 'and user can admin_project_member' do
+ before do
+ allow(presenter).to receive(:can?).with(user, :admin_project_member, project).and_return(true)
+ end
+
+ it { expect(presenter.can_resend_invite?).to eq(true) }
+ end
+
+ context 'and user cannot admin_project_member' do
+ before do
+ allow(presenter).to receive(:can?).with(user, :admin_project_member, project).and_return(false)
+ end
+
+ it { expect(presenter.can_resend_invite?).to eq(false) }
+ end
+ end
+
+ context 'when project_member is not invited' do
+ before do
+ expect(project_member).to receive(:invite?).and_return(false)
+ end
+
+ context 'and user can admin_project_member' do
+ before do
+ allow(presenter).to receive(:can?).with(user, :admin_project_member, project).and_return(true)
+ end
+
+ it { expect(presenter.can_resend_invite?).to eq(false) }
+ end
+
+ context 'and user cannot admin_project_member' do
+ before do
+ allow(presenter).to receive(:can?).with(user, :admin_project_member, project).and_return(false)
+ end
+
+ it { expect(presenter.can_resend_invite?).to eq(false) }
+ end
+ end
+ end
+
+ describe '#can_update?' do
+ context 'when user can update_project_member' do
+ before do
+ allow(presenter).to receive(:can?).with(user, :update_project_member, presenter).and_return(true)
+ end
+
+ it { expect(presenter.can_update?).to eq(true) }
+ end
+
+ context 'when user cannot update_project_member' do
+ before do
+ allow(presenter).to receive(:can?).with(user, :update_project_member, presenter).and_return(false)
+ allow(presenter).to receive(:can?).with(user, :override_project_member, presenter).and_return(false)
+ end
+
+ it { expect(presenter.can_update?).to eq(false) }
+ end
+ end
+
+ describe '#can_remove?' do
+ context 'when user can destroy_project_member' do
+ before do
+ allow(presenter).to receive(:can?).with(user, :destroy_project_member, presenter).and_return(true)
+ end
+
+ it { expect(presenter.can_remove?).to eq(true) }
+ end
+
+ context 'when user cannot destroy_project_member' do
+ before do
+ allow(presenter).to receive(:can?).with(user, :destroy_project_member, presenter).and_return(false)
+ end
+
+ it { expect(presenter.can_remove?).to eq(false) }
+ end
+ end
+
+ describe '#can_approve?' do
+ context 'when project_member has request an invite' do
+ before do
+ expect(project_member).to receive(:request?).and_return(true)
+ end
+
+ context 'and user can update_project_member' do
+ before do
+ allow(presenter).to receive(:can?).with(user, :update_project_member, presenter).and_return(true)
+ end
+
+ it { expect(presenter.can_approve?).to eq(true) }
+ end
+
+ context 'and user cannot update_project_member' do
+ before do
+ allow(presenter).to receive(:can?).with(user, :update_project_member, presenter).and_return(false)
+ allow(presenter).to receive(:can?).with(user, :override_project_member, presenter).and_return(false)
+ end
+
+ it { expect(presenter.can_approve?).to eq(false) }
+ end
+ end
+
+ context 'when project_member did not request an invite' do
+ before do
+ expect(project_member).to receive(:request?).and_return(false)
+ end
+
+ context 'and user can update_project_member' do
+ before do
+ allow(presenter).to receive(:can?).with(user, :update_project_member, presenter).and_return(true)
+ end
+
+ it { expect(presenter.can_approve?).to eq(false) }
+ end
+
+ context 'and user cannot update_project_member' do
+ before do
+ allow(presenter).to receive(:can?).with(user, :update_project_member, presenter).and_return(false)
+ end
+
+ it { expect(presenter.can_approve?).to eq(false) }
+ end
+ end
+ end
+end
diff --git a/spec/requests/api/access_requests_spec.rb b/spec/requests/api/access_requests_spec.rb
index 35ca3635a9d..24389f28b21 100644
--- a/spec/requests/api/access_requests_spec.rb
+++ b/spec/requests/api/access_requests_spec.rb
@@ -8,8 +8,8 @@ describe API::AccessRequests do
set(:project) do
create(:project, :public, :access_requestable, creator_id: master.id, namespace: master.namespace) do |project|
- project.team << [developer, :developer]
- project.team << [master, :master]
+ project.add_developer(developer)
+ project.add_master(master)
project.request_access(access_requester)
end
end
diff --git a/spec/requests/api/award_emoji_spec.rb b/spec/requests/api/award_emoji_spec.rb
index eaf12f71421..5adfb33677f 100644
--- a/spec/requests/api/award_emoji_spec.rb
+++ b/spec/requests/api/award_emoji_spec.rb
@@ -10,7 +10,7 @@ describe API::AwardEmoji do
set(:note) { create(:note, project: project, noteable: issue) }
before do
- project.team << [user, :master]
+ project.add_master(user)
end
describe "GET /projects/:id/awardable/:awardable_id/award_emoji" do
diff --git a/spec/requests/api/boards_spec.rb b/spec/requests/api/boards_spec.rb
index 546a1697e56..c6c10025f7f 100644
--- a/spec/requests/api/boards_spec.rb
+++ b/spec/requests/api/boards_spec.rb
@@ -6,18 +6,18 @@ describe API::Boards do
set(:non_member) { create(:user) }
set(:guest) { create(:user) }
set(:admin) { create(:user, :admin) }
- set(:project) { create(:project, :public, creator_id: user.id, namespace: user.namespace ) }
+ set(:board_parent) { create(:project, :public, creator_id: user.id, namespace: user.namespace ) }
set(:dev_label) do
- create(:label, title: 'Development', color: '#FFAABB', project: project)
+ create(:label, title: 'Development', color: '#FFAABB', project: board_parent)
end
set(:test_label) do
- create(:label, title: 'Testing', color: '#FFAACC', project: project)
+ create(:label, title: 'Testing', color: '#FFAACC', project: board_parent)
end
set(:ux_label) do
- create(:label, title: 'UX', color: '#FF0000', project: project)
+ create(:label, title: 'UX', color: '#FF0000', project: board_parent)
end
set(:dev_list) do
@@ -28,180 +28,25 @@ describe API::Boards do
create(:list, label: test_label, position: 2)
end
- set(:board) do
- create(:board, project: project, lists: [dev_list, test_list])
- end
-
- before do
- project.team << [user, :reporter]
- project.team << [guest, :guest]
- end
+ set(:milestone) { create(:milestone, project: board_parent) }
+ set(:board_label) { create(:label, project: board_parent) }
+ set(:board) { create(:board, project: board_parent, lists: [dev_list, test_list]) }
- describe "GET /projects/:id/boards" do
- let(:base_url) { "/projects/#{project.id}/boards" }
+ it_behaves_like 'group and project boards', "/projects/:id/boards"
- context "when unauthenticated" do
- it "returns authentication error" do
- get api(base_url)
-
- expect(response).to have_gitlab_http_status(401)
- end
- end
-
- context "when authenticated" do
- it "returns the project issue board" do
- get api(base_url, user)
-
- expect(response).to have_gitlab_http_status(200)
- expect(response).to include_pagination_headers
- expect(json_response).to be_an Array
- expect(json_response.length).to eq(1)
- expect(json_response.first['id']).to eq(board.id)
- expect(json_response.first['lists']).to be_an Array
- expect(json_response.first['lists'].length).to eq(2)
- expect(json_response.first['lists'].last).to have_key('position')
- end
- end
- end
-
- describe "GET /projects/:id/boards/:board_id/lists" do
- let(:base_url) { "/projects/#{project.id}/boards/#{board.id}/lists" }
-
- it 'returns issue board lists' do
- get api(base_url, user)
-
- expect(response).to have_gitlab_http_status(200)
- expect(response).to include_pagination_headers
- expect(json_response).to be_an Array
- expect(json_response.length).to eq(2)
- expect(json_response.first['label']['name']).to eq(dev_label.title)
- end
-
- it 'returns 404 if board not found' do
- get api("/projects/#{project.id}/boards/22343/lists", user)
-
- expect(response).to have_gitlab_http_status(404)
- end
- end
-
- describe "GET /projects/:id/boards/:board_id/lists/:list_id" do
- let(:base_url) { "/projects/#{project.id}/boards/#{board.id}/lists" }
-
- it 'returns a list' do
- get api("#{base_url}/#{dev_list.id}", user)
-
- expect(response).to have_gitlab_http_status(200)
- expect(json_response['id']).to eq(dev_list.id)
- expect(json_response['label']['name']).to eq(dev_label.title)
- expect(json_response['position']).to eq(1)
- end
-
- it 'returns 404 if list not found' do
- get api("#{base_url}/5324", user)
-
- expect(response).to have_gitlab_http_status(404)
- end
- end
-
- describe "POST /projects/:id/board/lists" do
- let(:base_url) { "/projects/#{project.id}/boards/#{board.id}/lists" }
+ describe "POST /projects/:id/boards/lists" do
+ let(:url) { "/projects/#{board_parent.id}/boards/#{board.id}/lists" }
it 'creates a new issue board list for group labels' do
group = create(:group)
group_label = create(:group_label, group: group)
- project.update(group: group)
+ board_parent.update(group: group)
- post api(base_url, user), label_id: group_label.id
+ post api(url, user), label_id: group_label.id
expect(response).to have_gitlab_http_status(201)
expect(json_response['label']['name']).to eq(group_label.title)
expect(json_response['position']).to eq(3)
end
-
- it 'creates a new issue board list for project labels' do
- post api(base_url, user), label_id: ux_label.id
-
- expect(response).to have_gitlab_http_status(201)
- expect(json_response['label']['name']).to eq(ux_label.title)
- expect(json_response['position']).to eq(3)
- end
-
- it 'returns 400 when creating a new list if label_id is invalid' do
- post api(base_url, user), label_id: 23423
-
- expect(response).to have_gitlab_http_status(400)
- end
-
- it 'returns 403 for project members with guest role' do
- put api("#{base_url}/#{test_list.id}", guest), position: 1
-
- expect(response).to have_gitlab_http_status(403)
- end
- end
-
- describe "PUT /projects/:id/boards/:board_id/lists/:list_id to update only position" do
- let(:base_url) { "/projects/#{project.id}/boards/#{board.id}/lists" }
-
- it "updates a list" do
- put api("#{base_url}/#{test_list.id}", user),
- position: 1
-
- expect(response).to have_gitlab_http_status(200)
- expect(json_response['position']).to eq(1)
- end
-
- it "returns 404 error if list id not found" do
- put api("#{base_url}/44444", user),
- position: 1
-
- expect(response).to have_gitlab_http_status(404)
- end
-
- it "returns 403 for project members with guest role" do
- put api("#{base_url}/#{test_list.id}", guest),
- position: 1
-
- expect(response).to have_gitlab_http_status(403)
- end
- end
-
- describe "DELETE /projects/:id/board/lists/:list_id" do
- let(:base_url) { "/projects/#{project.id}/boards/#{board.id}/lists" }
-
- it "rejects a non member from deleting a list" do
- delete api("#{base_url}/#{dev_list.id}", non_member)
-
- expect(response).to have_gitlab_http_status(403)
- end
-
- it "rejects a user with guest role from deleting a list" do
- delete api("#{base_url}/#{dev_list.id}", guest)
-
- expect(response).to have_gitlab_http_status(403)
- end
-
- it "returns 404 error if list id not found" do
- delete api("#{base_url}/44444", user)
-
- expect(response).to have_gitlab_http_status(404)
- end
-
- context "when the user is project owner" do
- set(:owner) { create(:user) }
-
- before do
- project.update(namespace: owner.namespace)
- end
-
- it "deletes the list if an admin requests it" do
- delete api("#{base_url}/#{dev_list.id}", owner)
-
- expect(response).to have_gitlab_http_status(204)
- end
-
- it_behaves_like '412 response' do
- let(:request) { api("#{base_url}/#{dev_list.id}", owner) }
- end
- end
end
end
diff --git a/spec/requests/api/deployments_spec.rb b/spec/requests/api/deployments_spec.rb
index c7977e624ff..6732c99e329 100644
--- a/spec/requests/api/deployments_spec.rb
+++ b/spec/requests/api/deployments_spec.rb
@@ -7,7 +7,7 @@ describe API::Deployments do
let!(:deployment) { create(:deployment) }
before do
- project.team << [user, :master]
+ project.add_master(user)
end
describe 'GET /projects/:id/deployments' do
diff --git a/spec/requests/api/environments_spec.rb b/spec/requests/api/environments_spec.rb
index 3665cfd7241..53d48a91007 100644
--- a/spec/requests/api/environments_spec.rb
+++ b/spec/requests/api/environments_spec.rb
@@ -7,7 +7,7 @@ describe API::Environments do
let!(:environment) { create(:environment, project: project) }
before do
- project.team << [user, :master]
+ project.add_master(user)
end
describe 'GET /projects/:id/environments' do
diff --git a/spec/requests/api/files_spec.rb b/spec/requests/api/files_spec.rb
index 5d8338a3fb7..d8fdfd6dee1 100644
--- a/spec/requests/api/files_spec.rb
+++ b/spec/requests/api/files_spec.rb
@@ -14,7 +14,7 @@ describe API::Files do
let(:author_name) { 'John Doe' }
before do
- project.team << [user, :developer]
+ project.add_developer(user)
end
def route(file_path = nil)
diff --git a/spec/requests/api/groups_spec.rb b/spec/requests/api/groups_spec.rb
index 554723d6b1e..3c0b4728dc2 100644
--- a/spec/requests/api/groups_spec.rb
+++ b/spec/requests/api/groups_spec.rb
@@ -173,6 +173,28 @@ describe API::Groups do
end
describe "GET /groups/:id" do
+ # Given a group, create one project for each visibility level
+ #
+ # group - Group to add projects to
+ # share_with - If provided, each project will be shared with this Group
+ #
+ # Returns a Hash of visibility_level => Project pairs
+ def add_projects_to_group(group, share_with: nil)
+ projects = {
+ public: create(:project, :public, namespace: group),
+ internal: create(:project, :internal, namespace: group),
+ private: create(:project, :private, namespace: group)
+ }
+
+ if share_with
+ create(:project_group_link, project: projects[:public], group: share_with)
+ create(:project_group_link, project: projects[:internal], group: share_with)
+ create(:project_group_link, project: projects[:private], group: share_with)
+ end
+
+ projects
+ end
+
context 'when unauthenticated' do
it 'returns 404 for a private group' do
get api("/groups/#{group2.id}")
@@ -183,6 +205,26 @@ describe API::Groups do
get api("/groups/#{group1.id}")
expect(response).to have_gitlab_http_status(200)
end
+
+ it 'returns only public projects in the group' do
+ public_group = create(:group, :public)
+ projects = add_projects_to_group(public_group)
+
+ get api("/groups/#{public_group.id}")
+
+ expect(json_response['projects'].map { |p| p['id'].to_i })
+ .to contain_exactly(projects[:public].id)
+ end
+
+ it 'returns only public projects shared with the group' do
+ public_group = create(:group, :public)
+ projects = add_projects_to_group(public_group, share_with: group1)
+
+ get api("/groups/#{group1.id}")
+
+ expect(json_response['shared_projects'].map { |p| p['id'].to_i })
+ .to contain_exactly(projects[:public].id)
+ end
end
context "when authenticated as user" do
@@ -222,6 +264,26 @@ describe API::Groups do
expect(response).to have_gitlab_http_status(404)
end
+
+ it 'returns only public and internal projects in the group' do
+ public_group = create(:group, :public)
+ projects = add_projects_to_group(public_group)
+
+ get api("/groups/#{public_group.id}", user2)
+
+ expect(json_response['projects'].map { |p| p['id'].to_i })
+ .to contain_exactly(projects[:public].id, projects[:internal].id)
+ end
+
+ it 'returns only public and internal projects shared with the group' do
+ public_group = create(:group, :public)
+ projects = add_projects_to_group(public_group, share_with: group1)
+
+ get api("/groups/#{group1.id}", user2)
+
+ expect(json_response['shared_projects'].map { |p| p['id'].to_i })
+ .to contain_exactly(projects[:public].id, projects[:internal].id)
+ end
end
context "when authenticated as admin" do
@@ -355,7 +417,7 @@ describe API::Groups do
end
it "only returns projects to which user has access" do
- project3.team << [user3, :developer]
+ project3.add_developer(user3)
get api("/groups/#{group1.id}/projects", user3)
diff --git a/spec/requests/api/internal_spec.rb b/spec/requests/api/internal_spec.rb
index 67e1539cbc3..7b25047ea8f 100644
--- a/spec/requests/api/internal_spec.rb
+++ b/spec/requests/api/internal_spec.rb
@@ -147,7 +147,7 @@ describe API::Internal do
describe "POST /internal/lfs_authenticate" do
before do
- project.team << [user, :developer]
+ project.add_developer(user)
end
context 'user key' do
@@ -199,7 +199,7 @@ describe API::Internal do
end
before do
- project.team << [user, :developer]
+ project.add_developer(user)
end
context 'with env passed as a JSON' do
@@ -359,7 +359,7 @@ describe API::Internal do
context "access denied" do
before do
- project.team << [user, :guest]
+ project.add_guest(user)
end
context "git pull" do
@@ -413,7 +413,7 @@ describe API::Internal do
context "archived project" do
before do
- project.team << [user, :developer]
+ project.add_developer(user)
project.archive!
end
@@ -527,7 +527,7 @@ describe API::Internal do
context 'web actions are always allowed' do
it 'allows WEB push' do
stub_application_setting(enabled_git_access_protocol: 'ssh')
- project.team << [user, :developer]
+ project.add_developer(user)
push(key, project, 'web')
expect(response.status).to eq(200)
@@ -537,37 +537,26 @@ describe API::Internal do
context 'the project path was changed' do
let!(:old_path_to_repo) { project.repository.path_to_repo }
- let!(:old_full_path) { project.full_path }
- let(:project_moved_message) do
- <<-MSG.strip_heredoc
- Project '#{old_full_path}' was moved to '#{project.full_path}'.
-
- Please update your Git remote and try again:
-
- git remote set-url origin #{project.ssh_url_to_repo}
- MSG
- end
+ let!(:repository) { project.repository }
before do
- project.team << [user, :developer]
+ project.add_developer(user)
project.path = 'new_path'
project.save!
end
it 'rejects the push' do
- push_with_path(key, old_path_to_repo)
+ push(key, project)
expect(response).to have_gitlab_http_status(200)
- expect(json_response['status']).to be_falsey
- expect(json_response['message']).to eq(project_moved_message)
+ expect(json_response['status']).to be_falsy
end
it 'rejects the SSH pull' do
- pull_with_path(key, old_path_to_repo)
+ pull(key, project)
expect(response).to have_gitlab_http_status(200)
- expect(json_response['status']).to be_falsey
- expect(json_response['message']).to eq(project_moved_message)
+ expect(json_response['status']).to be_falsy
end
end
end
@@ -577,7 +566,7 @@ describe API::Internal do
let(:changes) { URI.escape("#{Gitlab::Git::BLANK_SHA} 570e7b2abdd848b95f2f578043fc23bd6f6fd24d refs/heads/new_branch") }
before do
- project.team << [user, :developer]
+ project.add_developer(user)
end
it 'returns link to create new merge request' do
@@ -695,7 +684,7 @@ describe API::Internal do
# end
# end
- describe 'POST /internal/post_receive' do
+ describe 'POST /internal/post_receive', :clean_gitlab_redis_shared_state do
let(:identifier) { 'key-123' }
let(:valid_params) do
@@ -712,7 +701,9 @@ describe API::Internal do
end
before do
- project.team << [user, :developer]
+ project.add_developer(user)
+ allow(described_class).to receive(:identify).and_return(user)
+ allow_any_instance_of(Gitlab::Identifier).to receive(:identify).and_return(user)
end
it 'enqueues a PostReceive worker job' do
@@ -780,6 +771,29 @@ describe API::Internal do
expect(json_response['broadcast_message']).to eq(nil)
end
end
+
+ context 'with a redirected data' do
+ it 'returns redirected message on the response' do
+ project_moved = Gitlab::Checks::ProjectMoved.new(project, user, 'foo/baz', 'http')
+ project_moved.add_redirect_message
+
+ post api("/internal/post_receive"), valid_params
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(json_response["redirected_message"]).to be_present
+ expect(json_response["redirected_message"]).to eq(project_moved.redirect_message)
+ end
+ end
+
+ context 'with an orphaned write deploy key' do
+ it 'does not try to notify that project moved' do
+ allow_any_instance_of(Gitlab::Identifier).to receive(:identify).and_return(nil)
+
+ post api("/internal/post_receive"), valid_params
+
+ expect(response).to have_gitlab_http_status(200)
+ end
+ end
end
describe 'POST /internal/pre_receive' do
diff --git a/spec/requests/api/issues_spec.rb b/spec/requests/api/issues_spec.rb
index 99525cd0a6a..320217f2032 100644
--- a/spec/requests/api/issues_spec.rb
+++ b/spec/requests/api/issues_spec.rb
@@ -58,8 +58,8 @@ describe API::Issues, :mailer do
let(:no_milestone_title) { URI.escape(Milestone::None.title) }
before(:all) do
- project.team << [user, :reporter]
- project.team << [guest, :guest]
+ project.add_reporter(user)
+ project.add_guest(guest)
end
describe "GET /issues" do
@@ -344,7 +344,7 @@ describe API::Issues, :mailer do
let!(:group_note) { create(:note_on_issue, author: user, project: group_project, noteable: group_issue) }
before do
- group_project.team << [user, :reporter]
+ group_project.add_reporter(user)
end
let(:base_url) { "/groups/#{group.id}/issues" }
@@ -860,6 +860,20 @@ describe API::Issues, :mailer do
end
end
+ context 'user does not have permissions to create issue' do
+ let(:not_member) { create(:user) }
+
+ before do
+ project.project_feature.update(issues_access_level: ProjectFeature::PRIVATE)
+ end
+
+ it 'renders 403' do
+ post api("/projects/#{project.id}/issues", not_member), title: 'new issue'
+
+ expect(response).to have_gitlab_http_status(403)
+ end
+ end
+
it 'creates a new project issue' do
post api("/projects/#{project.id}/issues", user),
title: 'new issue', labels: 'label, label2', weight: 3,
@@ -953,7 +967,7 @@ describe API::Issues, :mailer do
let(:project) { merge_request.source_project }
before do
- project.team << [user, :master]
+ project.add_master(user)
end
context 'resolving all discussions in a merge request' do
@@ -1568,4 +1582,16 @@ describe API::Issues, :mailer do
expect(json_response).to be_an Array
expect(json_response.length).to eq(size) if size
end
+
+ describe 'GET projects/:id/issues/:issue_iid/participants' do
+ it_behaves_like 'issuable participants endpoint' do
+ let(:entity) { issue }
+ end
+
+ it 'returns 404 if the issue is confidential' do
+ post api("/projects/#{project.id}/issues/#{confidential_issue.iid}/participants", non_member)
+
+ expect(response).to have_gitlab_http_status(404)
+ end
+ end
end
diff --git a/spec/requests/api/jobs_spec.rb b/spec/requests/api/jobs_spec.rb
index 9ebf5bf7e97..79130f2d2a8 100644
--- a/spec/requests/api/jobs_spec.rb
+++ b/spec/requests/api/jobs_spec.rb
@@ -11,7 +11,7 @@ describe API::Jobs do
ref: project.default_branch)
end
- let(:job) { create(:ci_build, pipeline: pipeline) }
+ let!(:job) { create(:ci_build, :success, pipeline: pipeline) }
let(:user) { create(:user) }
let(:api_user) { user }
@@ -511,7 +511,7 @@ describe API::Jobs do
context 'user with :update_build persmission' do
it 'cancels running or pending job' do
expect(response).to have_gitlab_http_status(201)
- expect(project.builds.first.status).to eq('canceled')
+ expect(project.builds.first.status).to eq('success')
end
end
@@ -571,7 +571,7 @@ describe API::Jobs do
let(:role) { :master }
before do
- project.team << [user, role]
+ project.add_role(user, role)
post api("/projects/#{project.id}/jobs/#{job.id}/erase", user)
end
diff --git a/spec/requests/api/labels_spec.rb b/spec/requests/api/labels_spec.rb
index 3498e5bc8d9..34cbf75f4c1 100644
--- a/spec/requests/api/labels_spec.rb
+++ b/spec/requests/api/labels_spec.rb
@@ -7,7 +7,7 @@ describe API::Labels do
let!(:priority_label) { create(:label, title: 'bug', project: project, priority: 3) }
before do
- project.team << [user, :master]
+ project.add_master(user)
end
describe 'GET /projects/:id/labels' do
diff --git a/spec/requests/api/members_spec.rb b/spec/requests/api/members_spec.rb
index 3349e396ab8..5d4f81e07a6 100644
--- a/spec/requests/api/members_spec.rb
+++ b/spec/requests/api/members_spec.rb
@@ -8,8 +8,8 @@ describe API::Members do
let(:project) do
create(:project, :public, :access_requestable, creator_id: master.id, namespace: master.namespace) do |project|
- project.team << [developer, :developer]
- project.team << [master, :master]
+ project.add_developer(developer)
+ project.add_master(master)
project.request_access(access_requester)
end
end
diff --git a/spec/requests/api/merge_request_diffs_spec.rb b/spec/requests/api/merge_request_diffs_spec.rb
index bf4c8443b23..cb647aee70f 100644
--- a/spec/requests/api/merge_request_diffs_spec.rb
+++ b/spec/requests/api/merge_request_diffs_spec.rb
@@ -8,7 +8,7 @@ describe API::MergeRequestDiffs, 'MergeRequestDiffs' do
before do
merge_request.merge_request_diffs.create(head_commit_sha: '6f6d7e7ed97bb5f0054f2b1df789b39ca89b6ff9')
merge_request.merge_request_diffs.create(head_commit_sha: '5937ac0a7beb003549fc5fd26fc247adbce4a52e')
- project.team << [user, :master]
+ project.add_master(user)
end
describe 'GET /projects/:id/merge_requests/:merge_request_iid/versions' do
diff --git a/spec/requests/api/merge_requests_spec.rb b/spec/requests/api/merge_requests_spec.rb
index 91616da6d9a..0c9fbb1f187 100644
--- a/spec/requests/api/merge_requests_spec.rb
+++ b/spec/requests/api/merge_requests_spec.rb
@@ -25,7 +25,7 @@ describe API::MergeRequests do
let!(:upvote) { create(:award_emoji, :upvote, awardable: merge_request) }
before do
- project.team << [user, :reporter]
+ project.add_reporter(user)
end
describe 'GET /merge_requests' do
@@ -150,6 +150,26 @@ describe API::MergeRequests do
expect(json_response.length).to eq(1)
expect(json_response.first['id']).to eq(merge_request3.id)
end
+
+ context 'search params' do
+ before do
+ merge_request.update(title: 'Search title', description: 'Search description')
+ end
+
+ it 'returns merge requests matching given search string for title' do
+ get api("/merge_requests", user), search: merge_request.title
+
+ expect(json_response.length).to eq(1)
+ expect(json_response.first['id']).to eq(merge_request.id)
+ end
+
+ it 'returns merge requests for project matching given search string for description' do
+ get api("/merge_requests", user), project_id: project.id, search: merge_request.description
+
+ expect(json_response.length).to eq(1)
+ expect(json_response.first['id']).to eq(merge_request.id)
+ end
+ end
end
end
@@ -480,6 +500,12 @@ describe API::MergeRequests do
end
end
+ describe 'GET /projects/:id/merge_requests/:merge_request_iid/participants' do
+ it_behaves_like 'issuable participants endpoint' do
+ let(:entity) { merge_request }
+ end
+ end
+
describe 'GET /projects/:id/merge_requests/:merge_request_iid/commits' do
it 'returns a 200 when merge request is valid' do
get api("/projects/#{project.id}/merge_requests/#{merge_request.iid}/commits", user)
@@ -710,7 +736,7 @@ describe API::MergeRequests do
let(:developer) { create(:user) }
before do
- project.team << [developer, :developer]
+ project.add_developer(developer)
end
it "denies the deletion of the merge request" do
@@ -788,7 +814,7 @@ describe API::MergeRequests do
it "returns 401 if user has no permissions to merge" do
user2 = create(:user)
- project.team << [user2, :reporter]
+ project.add_reporter(user2)
put api("/projects/#{project.id}/merge_requests/#{merge_request.iid}/merge", user2)
expect(response).to have_gitlab_http_status(401)
expect(json_response['message']).to eq('401 Unauthorized')
@@ -977,7 +1003,7 @@ describe API::MergeRequests do
project = create(:project, :private)
merge_request = create(:merge_request, :simple, source_project: project)
guest = create(:user)
- project.team << [guest, :guest]
+ project.add_guest(guest)
get api("/projects/#{project.id}/merge_requests/#{merge_request.iid}/closes_issues", guest)
@@ -1025,7 +1051,7 @@ describe API::MergeRequests do
it 'returns 403 if user has no access to read code' do
guest = create(:user)
- project.team << [guest, :guest]
+ project.add_guest(guest)
post api("/projects/#{project.id}/merge_requests/#{merge_request.iid}/subscribe", guest)
@@ -1061,7 +1087,7 @@ describe API::MergeRequests do
it 'returns 403 if user has no access to read code' do
guest = create(:user)
- project.team << [guest, :guest]
+ project.add_guest(guest)
post api("/projects/#{project.id}/merge_requests/#{merge_request.iid}/unsubscribe", guest)
diff --git a/spec/requests/api/notes_spec.rb b/spec/requests/api/notes_spec.rb
index 3bfb4c5506f..981c9c27325 100644
--- a/spec/requests/api/notes_spec.rb
+++ b/spec/requests/api/notes_spec.rb
@@ -14,7 +14,7 @@ describe API::Notes do
let(:private_user) { create(:user) }
let(:private_project) do
create(:project, namespace: private_user.namespace)
- .tap { |p| p.team << [private_user, :master] }
+ .tap { |p| p.add_master(private_user) }
end
let(:private_issue) { create(:issue, project: private_project) }
@@ -29,7 +29,7 @@ describe API::Notes do
end
before do
- project.team << [user, :reporter]
+ project.add_reporter(user)
end
describe "GET /projects/:id/noteable/:noteable_id/notes" do
@@ -464,7 +464,7 @@ describe API::Notes do
describe "POST /projects/:id/noteable/:noteable_id/notes to test observer on create" do
it "creates an activity event when an issue note is created" do
- expect(Event).to receive(:create)
+ expect(Event).to receive(:create!)
post api("/projects/#{project.id}/issues/#{issue.iid}/notes", user), body: 'hi!'
end
diff --git a/spec/requests/api/pages_domains_spec.rb b/spec/requests/api/pages_domains_spec.rb
index d412b045e9f..5d01dc37f0e 100644
--- a/spec/requests/api/pages_domains_spec.rb
+++ b/spec/requests/api/pages_domains_spec.rb
@@ -46,6 +46,7 @@ describe API::PagesDomains do
expect(json_response).to be_an Array
expect(json_response.size).to eq(3)
expect(json_response.last).to have_key('domain')
+ expect(json_response.last).to have_key('project_id')
expect(json_response.last).to have_key('certificate_expiration')
expect(json_response.last['certificate_expiration']['expired']).to be true
expect(json_response.first).not_to have_key('certificate_expiration')
diff --git a/spec/requests/api/pipelines_spec.rb b/spec/requests/api/pipelines_spec.rb
index e4dcc9252fa..0736329f9fd 100644
--- a/spec/requests/api/pipelines_spec.rb
+++ b/spec/requests/api/pipelines_spec.rb
@@ -11,7 +11,7 @@ describe API::Pipelines do
end
before do
- project.team << [user, :master]
+ project.add_master(user)
end
describe 'GET /projects/:id/pipelines ' do
@@ -424,7 +424,7 @@ describe API::Pipelines do
let!(:reporter) { create(:user) }
before do
- project.team << [reporter, :reporter]
+ project.add_reporter(reporter)
end
it 'rejects the action' do
diff --git a/spec/requests/api/project_hooks_spec.rb b/spec/requests/api/project_hooks_spec.rb
index f31344a6238..1fd082ecc38 100644
--- a/spec/requests/api/project_hooks_spec.rb
+++ b/spec/requests/api/project_hooks_spec.rb
@@ -13,8 +13,8 @@ describe API::ProjectHooks, 'ProjectHooks' do
end
before do
- project.team << [user, :master]
- project.team << [user3, :developer]
+ project.add_master(user)
+ project.add_developer(user3)
end
describe "GET /projects/:id/hooks" do
@@ -206,7 +206,7 @@ describe API::ProjectHooks, 'ProjectHooks' do
it "returns a 404 if a user attempts to delete project hooks he/she does not own" do
test_user = create(:user)
other_project = create(:project)
- other_project.team << [test_user, :master]
+ other_project.add_master(test_user)
delete api("/projects/#{other_project.id}/hooks/#{hook.id}", test_user)
expect(response).to have_gitlab_http_status(404)
diff --git a/spec/requests/api/project_milestones_spec.rb b/spec/requests/api/project_milestones_spec.rb
index 72e1574b55f..08ea7314bb3 100644
--- a/spec/requests/api/project_milestones_spec.rb
+++ b/spec/requests/api/project_milestones_spec.rb
@@ -7,7 +7,7 @@ describe API::ProjectMilestones do
let!(:milestone) { create(:milestone, project: project, title: 'version2', description: 'open milestone') }
before do
- project.team << [user, :developer]
+ project.add_developer(user)
end
it_behaves_like 'group and project milestones', "/projects/:id/milestones" do
@@ -16,7 +16,7 @@ describe API::ProjectMilestones do
describe 'PUT /projects/:id/milestones/:milestone_id to test observer on close' do
it 'creates an activity event when an milestone is closed' do
- expect(Event).to receive(:create)
+ expect(Event).to receive(:create!)
put api("/projects/#{project.id}/milestones/#{milestone.id}", user),
state_event: 'close'
diff --git a/spec/requests/api/projects_spec.rb b/spec/requests/api/projects_spec.rb
index a41345da05b..de1763015fa 100644
--- a/spec/requests/api/projects_spec.rb
+++ b/spec/requests/api/projects_spec.rb
@@ -908,7 +908,7 @@ describe API::Projects do
describe 'permissions' do
context 'all projects' do
before do
- project.team << [user, :master]
+ project.add_master(user)
end
it 'contains permission information' do
@@ -923,7 +923,7 @@ describe API::Projects do
context 'personal project' do
it 'sets project access and returns 200' do
- project.team << [user, :master]
+ project.add_master(user)
get api("/projects/#{project.id}", user)
expect(response).to have_gitlab_http_status(200)
@@ -1539,7 +1539,7 @@ describe API::Projects do
context 'user without archiving rights to the project' do
before do
- project.team << [user3, :developer]
+ project.add_developer(user3)
end
it 'rejects the action' do
@@ -1575,7 +1575,7 @@ describe API::Projects do
context 'user without archiving rights to the project' do
before do
- project.team << [user3, :developer]
+ project.add_developer(user3)
end
it 'rejects the action' do
@@ -1650,7 +1650,7 @@ describe API::Projects do
it 'does not remove a project if not an owner' do
user3 = create(:user)
- project.team << [user3, :developer]
+ project.add_developer(user3)
delete api("/projects/#{project.id}", user3)
expect(response).to have_gitlab_http_status(403)
end
diff --git a/spec/requests/api/repositories_spec.rb b/spec/requests/api/repositories_spec.rb
index 9f2ff3b5af6..741800ff61d 100644
--- a/spec/requests/api/repositories_spec.rb
+++ b/spec/requests/api/repositories_spec.rb
@@ -378,6 +378,28 @@ describe API::Repositories do
expect(first_contributor['additions']).to eq(0)
expect(first_contributor['deletions']).to eq(0)
end
+
+ context 'using sorting' do
+ context 'by commits desc' do
+ it 'returns the repository contribuors sorted by commits desc' do
+ get api(route, current_user), { order_by: 'commits', sort: 'desc' }
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(response).to match_response_schema('contributors')
+ expect(json_response.first['commits']).to be > json_response.last['commits']
+ end
+ end
+
+ context 'by name desc' do
+ it 'returns the repository contribuors sorted by name asc case insensitive' do
+ get api(route, current_user), { order_by: 'name', sort: 'asc' }
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(response).to match_response_schema('contributors')
+ expect(json_response.first['name'].downcase).to be < json_response.last['name'].downcase
+ end
+ end
+ end
end
context 'when unauthenticated', 'and project is public' do
diff --git a/spec/requests/api/services_spec.rb b/spec/requests/api/services_spec.rb
index ba697e2b305..26d56c04862 100644
--- a/spec/requests/api/services_spec.rb
+++ b/spec/requests/api/services_spec.rb
@@ -53,6 +53,10 @@ describe API::Services do
describe "DELETE /projects/:id/services/#{service.dasherize}" do
include_context service
+ before do
+ initialize_service(service)
+ end
+
it "deletes #{service}" do
delete api("/projects/#{project.id}/services/#{dashed_service}", user)
@@ -67,9 +71,7 @@ describe API::Services do
# inject some properties into the service
before do
- service_object = project.find_or_initialize_service(service)
- service_object.properties = service_attrs
- service_object.save
+ initialize_service(service)
end
it 'returns authentication error when unauthenticated' do
@@ -92,7 +94,7 @@ describe API::Services do
end
it "returns error when authenticated but not a project owner" do
- project.team << [user2, :developer]
+ project.add_developer(user2)
get api("/projects/#{project.id}/services/#{dashed_service}", user2)
expect(response).to have_gitlab_http_status(403)
diff --git a/spec/requests/api/tags_spec.rb b/spec/requests/api/tags_spec.rb
index 0bf7863bdc8..e2b19ad59f9 100644
--- a/spec/requests/api/tags_spec.rb
+++ b/spec/requests/api/tags_spec.rb
@@ -16,6 +16,44 @@ describe API::Tags do
describe 'GET /projects/:id/repository/tags' do
let(:route) { "/projects/#{project_id}/repository/tags" }
+ context 'sorting' do
+ let(:current_user) { user }
+
+ it 'sorts by descending order by default' do
+ get api(route, current_user)
+
+ desc_order_tags = project.repository.tags.sort_by { |tag| tag.dereferenced_target.committed_date }
+ desc_order_tags.reverse!.map! { |tag| tag.dereferenced_target.id }
+
+ expect(json_response.map { |tag| tag['commit']['id'] }).to eq(desc_order_tags)
+ end
+
+ it 'sorts by ascending order if specified' do
+ get api("#{route}?sort=asc", current_user)
+
+ asc_order_tags = project.repository.tags.sort_by { |tag| tag.dereferenced_target.committed_date }
+ asc_order_tags.map! { |tag| tag.dereferenced_target.id }
+
+ expect(json_response.map { |tag| tag['commit']['id'] }).to eq(asc_order_tags)
+ end
+
+ it 'sorts by name in descending order when requested' do
+ get api("#{route}?order_by=name", current_user)
+
+ ordered_by_name = project.repository.tags.map { |tag| tag.name }.sort.reverse
+
+ expect(json_response.map { |tag| tag['name'] }).to eq(ordered_by_name)
+ end
+
+ it 'sorts by name in ascending order when requested' do
+ get api("#{route}?order_by=name&sort=asc", current_user)
+
+ ordered_by_name = project.repository.tags.map { |tag| tag.name }.sort
+
+ expect(json_response.map { |tag| tag['name'] }).to eq(ordered_by_name)
+ end
+ end
+
shared_examples_for 'repository tags' do
it 'returns the repository tags' do
get api(route, current_user)
diff --git a/spec/requests/api/todos_spec.rb b/spec/requests/api/todos_spec.rb
index c6063a2e089..fb3a33cadff 100644
--- a/spec/requests/api/todos_spec.rb
+++ b/spec/requests/api/todos_spec.rb
@@ -13,8 +13,8 @@ describe API::Todos do
let!(:done) { create(:todo, :done, project: project_1, author: author_1, user: john_doe) }
before do
- project_1.team << [john_doe, :developer]
- project_2.team << [john_doe, :developer]
+ project_1.add_developer(john_doe)
+ project_2.add_developer(john_doe)
end
describe 'GET /todos' do
@@ -191,7 +191,7 @@ describe API::Todos do
it 'returns an error if the issuable is not accessible' do
guest = create(:user)
- project_1.team << [guest, :guest]
+ project_1.add_guest(guest)
post api("/projects/#{project_1.id}/#{issuable_type}/#{issuable.iid}/todo", guest)
diff --git a/spec/requests/api/v3/award_emoji_spec.rb b/spec/requests/api/v3/award_emoji_spec.rb
index 0cd8b70007f..6dc430676b0 100644
--- a/spec/requests/api/v3/award_emoji_spec.rb
+++ b/spec/requests/api/v3/award_emoji_spec.rb
@@ -9,7 +9,7 @@ describe API::V3::AwardEmoji do
let!(:downvote) { create(:award_emoji, :downvote, awardable: merge_request, user: user) }
set(:note) { create(:note, project: project, noteable: issue) }
- before { project.team << [user, :master] }
+ before { project.add_master(user) }
describe "GET /projects/:id/awardable/:awardable_id/award_emoji" do
context 'on an issue' do
diff --git a/spec/requests/api/v3/boards_spec.rb b/spec/requests/api/v3/boards_spec.rb
index 14409d25544..dde4f096193 100644
--- a/spec/requests/api/v3/boards_spec.rb
+++ b/spec/requests/api/v3/boards_spec.rb
@@ -27,8 +27,8 @@ describe API::V3::Boards do
end
before do
- project.team << [user, :reporter]
- project.team << [guest, :guest]
+ project.add_reporter(user)
+ project.add_guest(guest)
end
describe "GET /projects/:id/boards" do
diff --git a/spec/requests/api/v3/commits_spec.rb b/spec/requests/api/v3/commits_spec.rb
index d31c94ddd2c..8b115e01f47 100644
--- a/spec/requests/api/v3/commits_spec.rb
+++ b/spec/requests/api/v3/commits_spec.rb
@@ -9,11 +9,11 @@ describe API::V3::Commits do
let!(:note) { create(:note_on_commit, author: user, project: project, commit_id: project.repository.commit.id, note: 'a comment on a commit') }
let!(:another_note) { create(:note_on_commit, author: user, project: project, commit_id: project.repository.commit.id, note: 'another comment on a commit') }
- before { project.team << [user, :reporter] }
+ before { project.add_reporter(user) }
describe "List repository commits" do
context "authorized user" do
- before { project.team << [user2, :reporter] }
+ before { project.add_reporter(user2) }
it "returns project commits" do
commit = project.repository.commit
@@ -415,7 +415,7 @@ describe API::V3::Commits do
describe "Get the diff of a commit" do
context "authorized user" do
- before { project.team << [user2, :reporter] }
+ before { project.add_reporter(user2) }
it "returns the diff of the selected commit" do
get v3_api("/projects/#{project.id}/repository/commits/#{project.repository.commit.id}/diff", user)
@@ -487,7 +487,7 @@ describe API::V3::Commits do
end
it 'returns 400 if you are not allowed to push to the target branch' do
- project.team << [user2, :developer]
+ project.add_developer(user2)
protected_branch = create(:protected_branch, project: project, name: 'feature')
post v3_api("/projects/#{project.id}/repository/commits/#{master_pickable_commit.id}/cherry_pick", user2), branch: protected_branch.name
diff --git a/spec/requests/api/v3/deployments_spec.rb b/spec/requests/api/v3/deployments_spec.rb
index 90eabda4dac..ac86fbea498 100644
--- a/spec/requests/api/v3/deployments_spec.rb
+++ b/spec/requests/api/v3/deployments_spec.rb
@@ -7,7 +7,7 @@ describe API::V3::Deployments do
let!(:deployment) { create(:deployment) }
before do
- project.team << [user, :master]
+ project.add_master(user)
end
shared_examples 'a paginated resources' do
diff --git a/spec/requests/api/v3/environments_spec.rb b/spec/requests/api/v3/environments_spec.rb
index 937250b5219..68be5256b64 100644
--- a/spec/requests/api/v3/environments_spec.rb
+++ b/spec/requests/api/v3/environments_spec.rb
@@ -7,7 +7,7 @@ describe API::V3::Environments do
let!(:environment) { create(:environment, project: project) }
before do
- project.team << [user, :master]
+ project.add_master(user)
end
shared_examples 'a paginated resources' do
diff --git a/spec/requests/api/v3/files_spec.rb b/spec/requests/api/v3/files_spec.rb
index 5500c1cf770..26a3d8870a0 100644
--- a/spec/requests/api/v3/files_spec.rb
+++ b/spec/requests/api/v3/files_spec.rb
@@ -27,7 +27,7 @@ describe API::V3::Files do
let(:author_email) { 'user@example.org' }
let(:author_name) { 'John Doe' }
- before { project.team << [user, :developer] }
+ before { project.add_developer(user) }
describe "GET /projects/:id/repository/files" do
let(:route) { "/projects/#{project.id}/repository/files" }
diff --git a/spec/requests/api/v3/groups_spec.rb b/spec/requests/api/v3/groups_spec.rb
index 498cb42fad1..a1cdf583de3 100644
--- a/spec/requests/api/v3/groups_spec.rb
+++ b/spec/requests/api/v3/groups_spec.rb
@@ -330,7 +330,7 @@ describe API::V3::Groups do
end
it "only returns projects to which user has access" do
- project3.team << [user3, :developer]
+ project3.add_developer(user3)
get v3_api("/groups/#{group1.id}/projects", user3)
diff --git a/spec/requests/api/v3/issues_spec.rb b/spec/requests/api/v3/issues_spec.rb
index 39a47a62f16..0dd6d673625 100644
--- a/spec/requests/api/v3/issues_spec.rb
+++ b/spec/requests/api/v3/issues_spec.rb
@@ -50,8 +50,8 @@ describe API::V3::Issues, :mailer do
let(:no_milestone_title) { URI.escape(Milestone::None.title) }
before do
- project.team << [user, :reporter]
- project.team << [guest, :guest]
+ project.add_reporter(user)
+ project.add_guest(guest)
end
describe "GET /issues" do
@@ -278,7 +278,7 @@ describe API::V3::Issues, :mailer do
let!(:group_note) { create(:note_on_issue, author: user, project: group_project, noteable: group_issue) }
before do
- group_project.team << [user, :reporter]
+ group_project.add_reporter(user)
end
let(:base_url) { "/groups/#{group.id}/issues" }
@@ -827,7 +827,7 @@ describe API::V3::Issues, :mailer do
let(:merge_request) { discussion.noteable }
let(:project) { merge_request.source_project }
before do
- project.team << [user, :master]
+ project.add_master(user)
post v3_api("/projects/#{project.id}/issues", user),
title: 'New Issue',
merge_request_for_resolving_discussions: merge_request.iid
diff --git a/spec/requests/api/v3/labels_spec.rb b/spec/requests/api/v3/labels_spec.rb
index 1d31213d5ca..cdab4d2bd73 100644
--- a/spec/requests/api/v3/labels_spec.rb
+++ b/spec/requests/api/v3/labels_spec.rb
@@ -7,7 +7,7 @@ describe API::V3::Labels do
let!(:priority_label) { create(:label, title: 'bug', project: project, priority: 3) }
before do
- project.team << [user, :master]
+ project.add_master(user)
end
describe 'GET /projects/:id/labels' do
diff --git a/spec/requests/api/v3/members_spec.rb b/spec/requests/api/v3/members_spec.rb
index 68be3d24c26..b91782ae511 100644
--- a/spec/requests/api/v3/members_spec.rb
+++ b/spec/requests/api/v3/members_spec.rb
@@ -8,8 +8,8 @@ describe API::V3::Members do
let(:project) do
create(:project, :public, :access_requestable, creator_id: master.id, namespace: master.namespace) do |project|
- project.team << [developer, :developer]
- project.team << [master, :master]
+ project.add_developer(developer)
+ project.add_master(master)
project.request_access(access_requester)
end
end
diff --git a/spec/requests/api/v3/merge_request_diffs_spec.rb b/spec/requests/api/v3/merge_request_diffs_spec.rb
index e613036a88d..547c066fadc 100644
--- a/spec/requests/api/v3/merge_request_diffs_spec.rb
+++ b/spec/requests/api/v3/merge_request_diffs_spec.rb
@@ -8,7 +8,7 @@ describe API::V3::MergeRequestDiffs, 'MergeRequestDiffs' do
before do
merge_request.merge_request_diffs.create(head_commit_sha: '6f6d7e7ed97bb5f0054f2b1df789b39ca89b6ff9')
merge_request.merge_request_diffs.create(head_commit_sha: '5937ac0a7beb003549fc5fd26fc247adbce4a52e')
- project.team << [user, :master]
+ project.add_master(user)
end
describe 'GET /projects/:id/merge_requests/:merge_request_id/versions' do
diff --git a/spec/requests/api/v3/merge_requests_spec.rb b/spec/requests/api/v3/merge_requests_spec.rb
index 2e2b9449429..b8b7d9d1c40 100644
--- a/spec/requests/api/v3/merge_requests_spec.rb
+++ b/spec/requests/api/v3/merge_requests_spec.rb
@@ -14,7 +14,7 @@ describe API::MergeRequests do
let(:milestone) { create(:milestone, title: '1.0.0', project: project) }
before do
- project.team << [user, :reporter]
+ project.add_reporter(user)
end
describe "GET /projects/:id/merge_requests" do
@@ -396,7 +396,7 @@ describe API::MergeRequests do
let(:developer) { create(:user) }
before do
- project.team << [developer, :developer]
+ project.add_developer(developer)
end
it "denies the deletion of the merge request" do
@@ -458,7 +458,7 @@ describe API::MergeRequests do
it "returns 401 if user has no permissions to merge" do
user2 = create(:user)
- project.team << [user2, :reporter]
+ project.add_reporter(user2)
put v3_api("/projects/#{project.id}/merge_requests/#{merge_request.id}/merge", user2)
expect(response).to have_gitlab_http_status(401)
expect(json_response['message']).to eq('401 Unauthorized')
@@ -645,7 +645,7 @@ describe API::MergeRequests do
project = create(:project, :private, :repository)
merge_request = create(:merge_request, :simple, source_project: project)
guest = create(:user)
- project.team << [guest, :guest]
+ project.add_guest(guest)
get v3_api("/projects/#{project.id}/merge_requests/#{merge_request.id}/closes_issues", guest)
@@ -675,7 +675,7 @@ describe API::MergeRequests do
it 'returns 403 if user has no access to read code' do
guest = create(:user)
- project.team << [guest, :guest]
+ project.add_guest(guest)
post v3_api("/projects/#{project.id}/merge_requests/#{merge_request.id}/subscription", guest)
@@ -705,7 +705,7 @@ describe API::MergeRequests do
it 'returns 403 if user has no access to read code' do
guest = create(:user)
- project.team << [guest, :guest]
+ project.add_guest(guest)
delete v3_api("/projects/#{project.id}/merge_requests/#{merge_request.id}/subscription", guest)
diff --git a/spec/requests/api/v3/milestones_spec.rb b/spec/requests/api/v3/milestones_spec.rb
index e82f35598a6..6021600e09c 100644
--- a/spec/requests/api/v3/milestones_spec.rb
+++ b/spec/requests/api/v3/milestones_spec.rb
@@ -6,7 +6,7 @@ describe API::V3::Milestones do
let!(:closed_milestone) { create(:closed_milestone, project: project) }
let!(:milestone) { create(:milestone, project: project) }
- before { project.team << [user, :developer] }
+ before { project.add_developer(user) }
describe 'GET /projects/:id/milestones' do
it 'returns project milestones' do
@@ -161,7 +161,7 @@ describe API::V3::Milestones do
describe 'PUT /projects/:id/milestones/:milestone_id to test observer on close' do
it 'creates an activity event when an milestone is closed' do
- expect(Event).to receive(:create)
+ expect(Event).to receive(:create!)
put v3_api("/projects/#{project.id}/milestones/#{milestone.id}", user),
state_event: 'close'
@@ -200,7 +200,7 @@ describe API::V3::Milestones do
let(:confidential_issue) { create(:issue, confidential: true, project: public_project) }
before do
- public_project.team << [user, :developer]
+ public_project.add_developer(user)
milestone.issues << issue << confidential_issue
end
@@ -215,7 +215,7 @@ describe API::V3::Milestones do
it 'does not return confidential issues to team members with guest role' do
member = create(:user)
- project.team << [member, :guest]
+ project.add_guest(member)
get v3_api("/projects/#{public_project.id}/milestones/#{milestone.id}/issues", member)
diff --git a/spec/requests/api/v3/notes_spec.rb b/spec/requests/api/v3/notes_spec.rb
index d3455a4bba4..5532795ab02 100644
--- a/spec/requests/api/v3/notes_spec.rb
+++ b/spec/requests/api/v3/notes_spec.rb
@@ -14,7 +14,7 @@ describe API::V3::Notes do
let(:private_user) { create(:user) }
let(:private_project) do
create(:project, namespace: private_user.namespace)
- .tap { |p| p.team << [private_user, :master] }
+ .tap { |p| p.add_master(private_user) }
end
let(:private_issue) { create(:issue, project: private_project) }
@@ -28,7 +28,7 @@ describe API::V3::Notes do
system: true
end
- before { project.team << [user, :reporter] }
+ before { project.add_reporter(user) }
describe "GET /projects/:id/noteable/:noteable_id/notes" do
context "when noteable is an Issue" do
@@ -302,7 +302,7 @@ describe API::V3::Notes do
describe "POST /projects/:id/noteable/:noteable_id/notes to test observer on create" do
it "creates an activity event when an issue note is created" do
- expect(Event).to receive(:create)
+ expect(Event).to receive(:create!)
post v3_api("/projects/#{project.id}/issues/#{issue.id}/notes", user), body: 'hi!'
end
diff --git a/spec/requests/api/v3/pipelines_spec.rb b/spec/requests/api/v3/pipelines_spec.rb
index 1c7d9fe32bb..ea943f22c41 100644
--- a/spec/requests/api/v3/pipelines_spec.rb
+++ b/spec/requests/api/v3/pipelines_spec.rb
@@ -10,7 +10,7 @@ describe API::V3::Pipelines do
ref: project.default_branch)
end
- before { project.team << [user, :master] }
+ before { project.add_master(user) }
shared_examples 'a paginated resources' do
before do
@@ -188,7 +188,7 @@ describe API::V3::Pipelines do
context 'user without proper access rights' do
let!(:reporter) { create(:user) }
- before { project.team << [reporter, :reporter] }
+ before { project.add_reporter(reporter) }
it 'rejects the action' do
post v3_api("/projects/#{project.id}/pipelines/#{pipeline.id}/cancel", reporter)
diff --git a/spec/requests/api/v3/project_hooks_spec.rb b/spec/requests/api/v3/project_hooks_spec.rb
index 00f59744a31..248ae97f875 100644
--- a/spec/requests/api/v3/project_hooks_spec.rb
+++ b/spec/requests/api/v3/project_hooks_spec.rb
@@ -13,8 +13,8 @@ describe API::ProjectHooks, 'ProjectHooks' do
end
before do
- project.team << [user, :master]
- project.team << [user3, :developer]
+ project.add_master(user)
+ project.add_developer(user3)
end
describe "GET /projects/:id/hooks" do
@@ -205,7 +205,7 @@ describe API::ProjectHooks, 'ProjectHooks' do
it "returns a 404 if a user attempts to delete project hooks he/she does not own" do
test_user = create(:user)
other_project = create(:project)
- other_project.team << [test_user, :master]
+ other_project.add_master(test_user)
delete v3_api("/projects/#{other_project.id}/hooks/#{hook.id}", test_user)
expect(response).to have_gitlab_http_status(404)
diff --git a/spec/requests/api/v3/projects_spec.rb b/spec/requests/api/v3/projects_spec.rb
index 27288b98d1c..13e465e0b2d 100644
--- a/spec/requests/api/v3/projects_spec.rb
+++ b/spec/requests/api/v3/projects_spec.rb
@@ -753,7 +753,7 @@ describe API::V3::Projects do
describe 'permissions' do
context 'all projects' do
- before { project.team << [user, :master] }
+ before { project.add_master(user) }
it 'contains permission information' do
get v3_api("/projects", user)
@@ -767,7 +767,7 @@ describe API::V3::Projects do
context 'personal project' do
it 'sets project access and returns 200' do
- project.team << [user, :master]
+ project.add_master(user)
get v3_api("/projects/#{project.id}", user)
expect(response).to have_gitlab_http_status(200)
@@ -1362,7 +1362,7 @@ describe API::V3::Projects do
context 'user without archiving rights to the project' do
before do
- project.team << [user3, :developer]
+ project.add_developer(user3)
end
it 'rejects the action' do
@@ -1398,7 +1398,7 @@ describe API::V3::Projects do
context 'user without archiving rights to the project' do
before do
- project.team << [user3, :developer]
+ project.add_developer(user3)
end
it 'rejects the action' do
@@ -1466,7 +1466,7 @@ describe API::V3::Projects do
it 'does not remove a project if not an owner' do
user3 = create(:user)
- project.team << [user3, :developer]
+ project.add_developer(user3)
delete v3_api("/projects/#{project.id}", user3)
expect(response).to have_gitlab_http_status(403)
end
diff --git a/spec/requests/api/v3/services_spec.rb b/spec/requests/api/v3/services_spec.rb
index 8f212ab6be6..c69a7d58ca6 100644
--- a/spec/requests/api/v3/services_spec.rb
+++ b/spec/requests/api/v3/services_spec.rb
@@ -10,6 +10,10 @@ describe API::V3::Services do
describe "DELETE /projects/:id/services/#{service.dasherize}" do
include_context service
+ before do
+ initialize_service(service)
+ end
+
it "deletes #{service}" do
delete v3_api("/projects/#{project.id}/services/#{dashed_service}", user)
diff --git a/spec/requests/api/v3/todos_spec.rb b/spec/requests/api/v3/todos_spec.rb
index 8f5c3fbf8dd..53fd962272a 100644
--- a/spec/requests/api/v3/todos_spec.rb
+++ b/spec/requests/api/v3/todos_spec.rb
@@ -12,8 +12,8 @@ describe API::V3::Todos do
let!(:done) { create(:todo, :done, project: project_1, author: author_1, user: john_doe) }
before do
- project_1.team << [john_doe, :developer]
- project_2.team << [john_doe, :developer]
+ project_1.add_developer(john_doe)
+ project_2.add_developer(john_doe)
end
describe 'DELETE /todos/:id' do
diff --git a/spec/requests/api/wikis_spec.rb b/spec/requests/api/wikis_spec.rb
index 65bd001e491..fb0806ff9f1 100644
--- a/spec/requests/api/wikis_spec.rb
+++ b/spec/requests/api/wikis_spec.rb
@@ -12,6 +12,8 @@ require 'spec_helper'
describe API::Wikis do
let(:user) { create(:user) }
+ let(:group) { create(:group).tap { |g| g.add_owner(user) } }
+ let(:project_wiki) { create(:project_wiki, project: project, user: user) }
let(:payload) { { content: 'content', format: 'rdoc', title: 'title' } }
let(:expected_keys_with_content) { %w(content format slug title) }
let(:expected_keys_without_content) { %w(format slug title) }
@@ -19,8 +21,8 @@ describe API::Wikis do
shared_examples_for 'returns list of wiki pages' do
context 'when wiki has pages' do
let!(:pages) do
- [create(:wiki_page, wiki: project.wiki, attrs: { title: 'page1', content: 'content of page1' }),
- create(:wiki_page, wiki: project.wiki, attrs: { title: 'page2', content: 'content of page2' })]
+ [create(:wiki_page, wiki: project_wiki, attrs: { title: 'page1', content: 'content of page1' }),
+ create(:wiki_page, wiki: project_wiki, attrs: { title: 'page2', content: 'content of page2' })]
end
it 'returns the list of wiki pages without content' do
@@ -445,7 +447,7 @@ describe API::Wikis do
end
describe 'PUT /projects/:id/wikis/:slug' do
- let(:page) { create(:wiki_page, wiki: project.wiki) }
+ let(:page) { create(:wiki_page, wiki: project_wiki) }
let(:payload) { { title: 'new title', content: 'new content' } }
let(:url) { "/projects/#{project.id}/wikis/#{page.slug}" }
@@ -568,10 +570,20 @@ describe API::Wikis do
end
end
end
+
+ context 'when wiki belongs to a group project' do
+ let(:project) { create(:project, namespace: group) }
+
+ before do
+ put(api(url, user), payload)
+ end
+
+ include_examples 'updates wiki page'
+ end
end
describe 'DELETE /projects/:id/wikis/:slug' do
- let(:page) { create(:wiki_page, wiki: project.wiki) }
+ let(:page) { create(:wiki_page, wiki: project_wiki) }
let(:url) { "/projects/#{project.id}/wikis/#{page.slug}" }
context 'when wiki is disabled' do
@@ -675,5 +687,15 @@ describe API::Wikis do
end
end
end
+
+ context 'when wiki belongs to a group project' do
+ let(:project) { create(:project, namespace: group) }
+
+ before do
+ delete(api(url, user))
+ end
+
+ include_examples '204 No Content'
+ end
end
end
diff --git a/spec/requests/git_http_spec.rb b/spec/requests/git_http_spec.rb
index a16f98bec36..27bd22d6bca 100644
--- a/spec/requests/git_http_spec.rb
+++ b/spec/requests/git_http_spec.rb
@@ -149,7 +149,7 @@ describe 'Git HTTP requests' do
context 'and as a developer on the team' do
before do
- project.team << [user, :developer]
+ project.add_developer(user)
end
context 'but the repo is disabled' do
@@ -182,7 +182,7 @@ describe 'Git HTTP requests' do
context 'when authenticated' do
context 'and as a developer on the team' do
before do
- project.team << [user, :developer]
+ project.add_developer(user)
end
context 'but the repo is disabled' do
@@ -240,7 +240,7 @@ describe 'Git HTTP requests' do
context 'as a developer on the team' do
before do
- project.team << [user, :developer]
+ project.add_developer(user)
end
it_behaves_like 'pulls are allowed'
@@ -324,9 +324,9 @@ describe 'Git HTTP requests' do
<<-MSG.strip_heredoc
Project '#{redirect.path}' was moved to '#{project.full_path}'.
- Please update your Git remote and try again:
+ Please update your Git remote:
- git remote set-url origin #{project.http_url_to_repo}
+ git remote set-url origin #{project.http_url_to_repo} and try again.
MSG
end
@@ -365,13 +365,13 @@ describe 'Git HTTP requests' do
context "when the user has access to the project" do
before do
- project.team << [user, :master]
+ project.add_master(user)
end
context "when the user is blocked" do
it "rejects pulls with 401 Unauthorized" do
user.block
- project.team << [user, :master]
+ project.add_master(user)
download(path, env) do |response|
expect(response).to have_gitlab_http_status(:unauthorized)
@@ -434,7 +434,7 @@ describe 'Git HTTP requests' do
let(:path) { "#{project.full_path}.git" }
before do
- project.team << [user, :master]
+ project.add_master(user)
end
context 'when username and password are provided' do
@@ -533,9 +533,9 @@ describe 'Git HTTP requests' do
<<-MSG.strip_heredoc
Project '#{redirect.path}' was moved to '#{project.full_path}'.
- Please update your Git remote and try again:
+ Please update your Git remote:
- git remote set-url origin #{project.http_url_to_repo}
+ git remote set-url origin #{project.http_url_to_repo} and try again.
MSG
end
@@ -612,7 +612,7 @@ describe 'Git HTTP requests' do
context 'and build created by' do
before do
build.update(user: user)
- project.team << [user, :reporter]
+ project.add_reporter(user)
end
shared_examples 'can download code only' do
@@ -795,7 +795,7 @@ describe 'Git HTTP requests' do
context 'and the user is on the team' do
before do
- project.team << [user, :master]
+ project.add_master(user)
end
it "responds with status 200" do
diff --git a/spec/requests/lfs_http_spec.rb b/spec/requests/lfs_http_spec.rb
index d7bdfde918c..8bfc8693981 100644
--- a/spec/requests/lfs_http_spec.rb
+++ b/spec/requests/lfs_http_spec.rb
@@ -63,7 +63,7 @@ describe 'Git LFS API and storage' do
context 'with LFS disabled globally' do
before do
- project.team << [user, :master]
+ project.add_master(user)
allow(Gitlab.config.lfs).to receive(:enabled).and_return(false)
end
@@ -106,7 +106,7 @@ describe 'Git LFS API and storage' do
context 'with LFS enabled globally' do
before do
- project.team << [user, :master]
+ project.add_master(user)
enable_lfs
end
@@ -236,7 +236,7 @@ describe 'Git LFS API and storage' do
context 'and does have project access' do
let(:update_permissions) do
- project.team << [user, :master]
+ project.add_master(user)
project.lfs_objects << lfs_object
end
@@ -276,7 +276,7 @@ describe 'Git LFS API and storage' do
context 'when user allowed' do
let(:update_permissions) do
- project.team << [user, :master]
+ project.add_master(user)
project.lfs_objects << lfs_object
end
@@ -312,7 +312,7 @@ describe 'Git LFS API and storage' do
let(:pipeline) { create(:ci_empty_pipeline, project: project) }
let(:update_permissions) do
- project.team << [user, :reporter]
+ project.add_reporter(user)
project.lfs_objects << lfs_object
end
@@ -534,7 +534,7 @@ describe 'Git LFS API and storage' do
let(:authorization) { authorize_user }
let(:update_user_permissions) do
- project.team << [user, role]
+ project.add_role(user, role)
end
it_behaves_like 'an authorized requests' do
@@ -570,7 +570,7 @@ describe 'Git LFS API and storage' do
let(:pipeline) { create(:ci_empty_pipeline, project: project) }
let(:update_user_permissions) do
- project.team << [user, :reporter]
+ project.add_reporter(user)
end
it_behaves_like 'an authorized requests'
@@ -690,7 +690,7 @@ describe 'Git LFS API and storage' do
let(:authorization) { authorize_user }
let(:update_user_permissions) do
- project.team << [user, :developer]
+ project.add_developer(user)
end
context 'when pushing an lfs object that already exists' do
@@ -812,7 +812,7 @@ describe 'Git LFS API and storage' do
context 'when user is not authenticated' do
context 'when user has push access' do
let(:update_user_permissions) do
- project.team << [user, :master]
+ project.add_master(user)
end
it 'responds with status 401' do
@@ -857,7 +857,7 @@ describe 'Git LFS API and storage' do
before do
allow(Gitlab::Database).to receive(:read_only?) { true }
- project.team << [user, :master]
+ project.add_master(user)
enable_lfs
end
@@ -952,7 +952,7 @@ describe 'Git LFS API and storage' do
describe 'when user has push access to the project' do
before do
- project.team << [user, :developer]
+ project.add_developer(user)
end
context 'and the request bypassed workhorse' do
@@ -1050,7 +1050,7 @@ describe 'Git LFS API and storage' do
describe 'and user does not have push access' do
before do
- project.team << [user, :reporter]
+ project.add_reporter(user)
end
it_behaves_like 'forbidden'
@@ -1067,7 +1067,7 @@ describe 'Git LFS API and storage' do
let(:build) { create(:ci_build, :running, pipeline: pipeline, user: user) }
before do
- project.team << [user, :developer]
+ project.add_developer(user)
put_authorize
end
@@ -1119,7 +1119,7 @@ describe 'Git LFS API and storage' do
describe 'when user has push access to the project' do
before do
- project.team << [user, :developer]
+ project.add_developer(user)
end
context 'and request is sent by gitlab-workhorse to authorize the request' do
@@ -1206,7 +1206,7 @@ describe 'Git LFS API and storage' do
let(:authorization) { authorize_user }
before do
- second_project.team << [user, :master]
+ second_project.add_master(user)
upstream_project.lfs_objects << lfs_object
end
diff --git a/spec/requests/projects/cycle_analytics_events_spec.rb b/spec/requests/projects/cycle_analytics_events_spec.rb
index 286d8a884a4..98f70e2101b 100644
--- a/spec/requests/projects/cycle_analytics_events_spec.rb
+++ b/spec/requests/projects/cycle_analytics_events_spec.rb
@@ -7,7 +7,7 @@ describe 'cycle analytics events' do
describe 'GET /:namespace/:project/cycle_analytics/events/issues' do
before do
- project.team << [user, :developer]
+ project.add_developer(user)
3.times do |count|
Timecop.freeze(Time.now + count.days) do
diff --git a/spec/rubocop/cop/active_record_dependent_spec.rb b/spec/rubocop/cop/active_record_dependent_spec.rb
deleted file mode 100644
index 599a032bfc5..00000000000
--- a/spec/rubocop/cop/active_record_dependent_spec.rb
+++ /dev/null
@@ -1,33 +0,0 @@
-require 'spec_helper'
-require 'rubocop'
-require 'rubocop/rspec/support'
-require_relative '../../../rubocop/cop/active_record_dependent'
-
-describe RuboCop::Cop::ActiveRecordDependent do
- include CopHelper
-
- subject(:cop) { described_class.new }
-
- context 'inside the app/models directory' do
- it 'registers an offense when dependent: is used' do
- allow(cop).to receive(:in_model?).and_return(true)
-
- inspect_source(cop, 'belongs_to :foo, dependent: :destroy')
-
- aggregate_failures do
- expect(cop.offenses.size).to eq(1)
- expect(cop.offenses.map(&:line)).to eq([1])
- end
- end
- end
-
- context 'outside the app/models directory' do
- it 'does nothing' do
- allow(cop).to receive(:in_model?).and_return(false)
-
- inspect_source(cop, 'belongs_to :foo, dependent: :destroy')
-
- expect(cop.offenses).to be_empty
- end
- end
-end
diff --git a/spec/rubocop/cop/active_record_serialize_spec.rb b/spec/rubocop/cop/active_record_serialize_spec.rb
deleted file mode 100644
index b94b25cecd0..00000000000
--- a/spec/rubocop/cop/active_record_serialize_spec.rb
+++ /dev/null
@@ -1,33 +0,0 @@
-require 'spec_helper'
-require 'rubocop'
-require 'rubocop/rspec/support'
-require_relative '../../../rubocop/cop/active_record_serialize'
-
-describe RuboCop::Cop::ActiveRecordSerialize do
- include CopHelper
-
- subject(:cop) { described_class.new }
-
- context 'inside the app/models directory' do
- it 'registers an offense when serialize is used' do
- allow(cop).to receive(:in_model?).and_return(true)
-
- inspect_source(cop, 'serialize :foo')
-
- aggregate_failures do
- expect(cop.offenses.size).to eq(1)
- expect(cop.offenses.map(&:line)).to eq([1])
- end
- end
- end
-
- context 'outside the app/models directory' do
- it 'does nothing' do
- allow(cop).to receive(:in_model?).and_return(false)
-
- inspect_source(cop, 'serialize :foo')
-
- expect(cop.offenses).to be_empty
- end
- end
-end
diff --git a/spec/rubocop/cop/custom_error_class_spec.rb b/spec/rubocop/cop/custom_error_class_spec.rb
deleted file mode 100644
index 381d7871a40..00000000000
--- a/spec/rubocop/cop/custom_error_class_spec.rb
+++ /dev/null
@@ -1,111 +0,0 @@
-require 'spec_helper'
-
-require 'rubocop'
-require 'rubocop/rspec/support'
-
-require_relative '../../../rubocop/cop/custom_error_class'
-
-describe RuboCop::Cop::CustomErrorClass do
- include CopHelper
-
- subject(:cop) { described_class.new }
-
- context 'when a class has a body' do
- it 'does nothing' do
- inspect_source(cop, 'class CustomError < StandardError; def foo; end; end')
-
- expect(cop.offenses).to be_empty
- end
- end
-
- context 'when a class has no explicit superclass' do
- it 'does nothing' do
- inspect_source(cop, 'class CustomError; end')
-
- expect(cop.offenses).to be_empty
- end
- end
-
- context 'when a class has a superclass that does not end in Error' do
- it 'does nothing' do
- inspect_source(cop, 'class CustomError < BasicObject; end')
-
- expect(cop.offenses).to be_empty
- end
- end
-
- context 'when a class is empty and inherits from a class ending in Error' do
- context 'when the class is on a single line' do
- let(:source) do
- <<-SOURCE
- module Foo
- class CustomError < Bar::Baz::BaseError; end
- end
- SOURCE
- end
-
- let(:expected) do
- <<-EXPECTED
- module Foo
- CustomError = Class.new(Bar::Baz::BaseError)
- end
- EXPECTED
- end
-
- it 'registers an offense' do
- expected_highlights = source.split("\n")[1].strip
-
- inspect_source(cop, source)
-
- aggregate_failures do
- expect(cop.offenses.size).to eq(1)
- expect(cop.offenses.map(&:line)).to eq([2])
- expect(cop.highlights).to contain_exactly(expected_highlights)
- end
- end
-
- it 'autocorrects to the right version' do
- autocorrected = autocorrect_source(cop, source, 'foo/custom_error.rb')
-
- expect(autocorrected).to eq(expected)
- end
- end
-
- context 'when the class is on multiple lines' do
- let(:source) do
- <<-SOURCE
- module Foo
- class CustomError < Bar::Baz::BaseError
- end
- end
- SOURCE
- end
-
- let(:expected) do
- <<-EXPECTED
- module Foo
- CustomError = Class.new(Bar::Baz::BaseError)
- end
- EXPECTED
- end
-
- it 'registers an offense' do
- expected_highlights = source.split("\n")[1..2].join("\n").strip
-
- inspect_source(cop, source)
-
- aggregate_failures do
- expect(cop.offenses.size).to eq(1)
- expect(cop.offenses.map(&:line)).to eq([2])
- expect(cop.highlights).to contain_exactly(expected_highlights)
- end
- end
-
- it 'autocorrects to the right version' do
- autocorrected = autocorrect_source(cop, source, 'foo/custom_error.rb')
-
- expect(autocorrected).to eq(expected)
- end
- end
- end
-end
diff --git a/spec/rubocop/cop/gem_fetcher_spec.rb b/spec/rubocop/cop/gem_fetcher_spec.rb
deleted file mode 100644
index c07f6a831dc..00000000000
--- a/spec/rubocop/cop/gem_fetcher_spec.rb
+++ /dev/null
@@ -1,46 +0,0 @@
-require 'spec_helper'
-
-require 'rubocop'
-require 'rubocop/rspec/support'
-
-require_relative '../../../rubocop/cop/gem_fetcher'
-
-describe RuboCop::Cop::GemFetcher do
- include CopHelper
-
- subject(:cop) { described_class.new }
-
- context 'in Gemfile' do
- before do
- allow(cop).to receive(:gemfile?).and_return(true)
- end
-
- it 'registers an offense when a gem uses `git`' do
- inspect_source(cop, 'gem "foo", git: "https://gitlab.com/foo/bar.git"')
-
- aggregate_failures do
- expect(cop.offenses.size).to eq(1)
- expect(cop.offenses.map(&:line)).to eq([1])
- expect(cop.highlights).to eq(['git: "https://gitlab.com/foo/bar.git"'])
- end
- end
-
- it 'registers an offense when a gem uses `github`' do
- inspect_source(cop, 'gem "foo", github: "foo/bar.git"')
-
- aggregate_failures do
- expect(cop.offenses.size).to eq(1)
- expect(cop.offenses.map(&:line)).to eq([1])
- expect(cop.highlights).to eq(['github: "foo/bar.git"'])
- end
- end
- end
-
- context 'outside of Gemfile' do
- it 'registers no offense' do
- inspect_source(cop, 'gem "foo", git: "https://gitlab.com/foo/bar.git"')
-
- expect(cop.offenses.size).to eq(0)
- end
- end
-end
diff --git a/spec/rubocop/cop/gitlab/module_with_instance_variables_spec.rb b/spec/rubocop/cop/gitlab/module_with_instance_variables_spec.rb
new file mode 100644
index 00000000000..8e2d5f70353
--- /dev/null
+++ b/spec/rubocop/cop/gitlab/module_with_instance_variables_spec.rb
@@ -0,0 +1,157 @@
+require 'spec_helper'
+require 'rubocop'
+require 'rubocop/rspec/support'
+require_relative '../../../../rubocop/cop/gitlab/module_with_instance_variables'
+
+describe RuboCop::Cop::Gitlab::ModuleWithInstanceVariables do
+ include CopHelper
+
+ subject(:cop) { described_class.new }
+
+ shared_examples('registering offense') do |options|
+ let(:offending_lines) { options[:offending_lines] }
+
+ it 'registers an offense when instance variable is used in a module' do
+ inspect_source(source)
+
+ aggregate_failures do
+ expect(cop.offenses.size).to eq(offending_lines.size)
+ expect(cop.offenses.map(&:line)).to eq(offending_lines)
+ end
+ end
+ end
+
+ shared_examples('not registering offense') do
+ it 'does not register offenses' do
+ inspect_source(source)
+
+ expect(cop.offenses).to be_empty
+ end
+ end
+
+ context 'when source is a regular module' do
+ it_behaves_like 'registering offense', offending_lines: [3] do
+ let(:source) do
+ <<~RUBY
+ module M
+ def f
+ @f = true
+ end
+ end
+ RUBY
+ end
+ end
+ end
+
+ context 'when source is a nested module' do
+ it_behaves_like 'registering offense', offending_lines: [4] do
+ let(:source) do
+ <<~RUBY
+ module N
+ module M
+ def f
+ @f = true
+ end
+ end
+ end
+ RUBY
+ end
+ end
+ end
+
+ context 'when source is a nested module with multiple offenses' do
+ it_behaves_like 'registering offense', offending_lines: [4, 12] do
+ let(:source) do
+ <<~RUBY
+ module N
+ module M
+ def f
+ @f = true
+ end
+
+ def g
+ true
+ end
+
+ def h
+ @h = true
+ end
+ end
+ end
+ RUBY
+ end
+ end
+ end
+
+ context 'when source is using simple or ivar assignment' do
+ it_behaves_like 'not registering offense' do
+ let(:source) do
+ <<~RUBY
+ module M
+ def f
+ @f ||= true
+ end
+ end
+ RUBY
+ end
+ end
+ end
+
+ context 'when source is using simple ivar' do
+ it_behaves_like 'not registering offense' do
+ let(:source) do
+ <<~RUBY
+ module M
+ def f?
+ @f
+ end
+ end
+ RUBY
+ end
+ end
+ end
+
+ context 'when source is defining initialize' do
+ it_behaves_like 'not registering offense' do
+ let(:source) do
+ <<~RUBY
+ module M
+ def initialize
+ @a = 1
+ @b = 2
+ end
+ end
+ RUBY
+ end
+ end
+ end
+
+ context 'when source is using simple or ivar assignment with other ivar' do
+ it_behaves_like 'registering offense', offending_lines: [3] do
+ let(:source) do
+ <<~RUBY
+ module M
+ def f
+ @f ||= g(@g)
+ end
+ end
+ RUBY
+ end
+ end
+ end
+
+ context 'when source is using or ivar assignment with something else' do
+ it_behaves_like 'registering offense', offending_lines: [3, 4] do
+ let(:source) do
+ <<~RUBY
+ module M
+ def f
+ @f ||= true
+ @f.to_s
+ end
+ end
+ RUBY
+ end
+ end
+ end
+end
diff --git a/spec/rubocop/cop/in_batches_spec.rb b/spec/rubocop/cop/in_batches_spec.rb
deleted file mode 100644
index 072481984c6..00000000000
--- a/spec/rubocop/cop/in_batches_spec.rb
+++ /dev/null
@@ -1,19 +0,0 @@
-require 'spec_helper'
-require 'rubocop'
-require 'rubocop/rspec/support'
-require_relative '../../../rubocop/cop/in_batches'
-
-describe RuboCop::Cop::InBatches do
- include CopHelper
-
- subject(:cop) { described_class.new }
-
- it 'registers an offense when in_batches is used' do
- inspect_source(cop, 'foo.in_batches do; end')
-
- aggregate_failures do
- expect(cop.offenses.size).to eq(1)
- expect(cop.offenses.map(&:line)).to eq([1])
- end
- end
-end
diff --git a/spec/rubocop/cop/include_sidekiq_worker_spec.rb b/spec/rubocop/cop/include_sidekiq_worker_spec.rb
new file mode 100644
index 00000000000..f5109287876
--- /dev/null
+++ b/spec/rubocop/cop/include_sidekiq_worker_spec.rb
@@ -0,0 +1,33 @@
+require 'spec_helper'
+
+require 'rubocop'
+require 'rubocop/rspec/support'
+
+require_relative '../../../rubocop/cop/include_sidekiq_worker'
+
+describe RuboCop::Cop::IncludeSidekiqWorker do
+ include CopHelper
+
+ subject(:cop) { described_class.new }
+
+ context 'when `Sidekiq::Worker` is included' do
+ let(:source) { 'include Sidekiq::Worker' }
+ let(:correct_source) { 'include ApplicationWorker' }
+
+ it 'registers an offense ' do
+ inspect_source(source)
+
+ aggregate_failures do
+ expect(cop.offenses.size).to eq(1)
+ expect(cop.offenses.map(&:line)).to eq([1])
+ expect(cop.highlights).to eq(['Sidekiq::Worker'])
+ end
+ end
+
+ it 'autocorrects to the right version' do
+ autocorrected = autocorrect_source(source)
+
+ expect(autocorrected).to eq(correct_source)
+ end
+ end
+end
diff --git a/spec/rubocop/cop/line_break_after_guard_clauses_spec.rb b/spec/rubocop/cop/line_break_after_guard_clauses_spec.rb
deleted file mode 100644
index 8899dc85384..00000000000
--- a/spec/rubocop/cop/line_break_after_guard_clauses_spec.rb
+++ /dev/null
@@ -1,160 +0,0 @@
-require 'spec_helper'
-require 'rubocop'
-require 'rubocop/rspec/support'
-require_relative '../../../rubocop/cop/line_break_after_guard_clauses'
-
-describe RuboCop::Cop::LineBreakAfterGuardClauses do
- include CopHelper
-
- subject(:cop) { described_class.new }
-
- shared_examples 'examples with guard clause' do |title|
- %w[if unless].each do |conditional|
- it "flags violation for #{title} #{conditional} without line breaks" do
- source = <<~RUBY
- #{title} #{conditional} condition
- do_stuff
- RUBY
- inspect_source(cop, source)
-
- expect(cop.offenses.size).to eq(1)
- offense = cop.offenses.first
-
- expect(offense.line).to eq(1)
- expect(cop.highlights).to eq(["#{title} #{conditional} condition"])
- expect(offense.message).to eq('Add a line break after guard clauses')
- end
-
- it "doesn't flag violation for #{title} #{conditional} with line break" do
- source = <<~RUBY
- #{title} #{conditional} condition
-
- do_stuff
- RUBY
- inspect_source(cop, source)
-
- expect(cop.offenses).to be_empty
- end
-
- it "doesn't flag violation for #{title} #{conditional} on multiple lines without line break" do
- source = <<~RUBY
- #{conditional} condition
- #{title}
- end
- do_stuff
- RUBY
- inspect_source(cop, source)
-
- expect(cop.offenses).to be_empty
- end
-
- it "doesn't flag violation for #{title} #{conditional} without line breaks when followed by end keyword" do
- source = <<~RUBY
- def test
- #{title} #{conditional} condition
- end
- RUBY
- inspect_source(cop, source)
-
- expect(cop.offenses).to be_empty
- end
-
- it "doesn't flag violation for #{title} #{conditional} without line breaks when followed by elsif keyword" do
- source = <<~RUBY
- if model
- #{title} #{conditional} condition
- elsif
- do_something
- end
- RUBY
- inspect_source(cop, source)
-
- expect(cop.offenses).to be_empty
- end
-
- it "doesn't flag violation for #{title} #{conditional} without line breaks when followed by else keyword" do
- source = <<~RUBY
- if model
- #{title} #{conditional} condition
- else
- do_something
- end
- RUBY
- inspect_source(cop, source)
-
- expect(cop.offenses).to be_empty
- end
-
- it "doesn't flag violation for #{title} #{conditional} without line breaks when followed by when keyword" do
- source = <<~RUBY
- case model
- when condition_a
- #{title} #{conditional} condition
- when condition_b
- do_something
- end
- RUBY
- inspect_source(cop, source)
-
- expect(cop.offenses).to be_empty
- end
-
- it "doesn't flag violation for #{title} #{conditional} without line breaks when followed by rescue keyword" do
- source = <<~RUBY
- begin
- #{title} #{conditional} condition
- rescue StandardError
- do_something
- end
- RUBY
- inspect_source(cop, source)
-
- expect(cop.offenses).to be_empty
- end
-
- it "doesn't flag violation for #{title} #{conditional} without line breaks when followed by ensure keyword" do
- source = <<~RUBY
- def foo
- #{title} #{conditional} condition
- ensure
- do_something
- end
- RUBY
- inspect_source(cop, source)
-
- expect(cop.offenses).to be_empty
- end
-
- it "doesn't flag violation for #{title} #{conditional} without line breaks when followed by another guard clause" do
- source = <<~RUBY
- #{title} #{conditional} condition
- #{title} #{conditional} condition
-
- do_stuff
- RUBY
- inspect_source(cop, source)
-
- expect(cop.offenses).to be_empty
- end
-
- it "autocorrects #{title} #{conditional} guard clauses without line break" do
- source = <<~RUBY
- #{title} #{conditional} condition
- do_stuff
- RUBY
- autocorrected = autocorrect_source(cop, source)
-
- expected_source = <<~RUBY
- #{title} #{conditional} condition
-
- do_stuff
- RUBY
- expect(autocorrected).to eql(expected_source)
- end
- end
- end
-
- %w[return fail raise next break throw].each do |example|
- it_behaves_like 'examples with guard clause', example
- end
-end
diff --git a/spec/rubocop/cop/migration/add_concurrent_foreign_key_spec.rb b/spec/rubocop/cop/migration/add_concurrent_foreign_key_spec.rb
index 7cb24dc5646..1df1fffb94e 100644
--- a/spec/rubocop/cop/migration/add_concurrent_foreign_key_spec.rb
+++ b/spec/rubocop/cop/migration/add_concurrent_foreign_key_spec.rb
@@ -1,6 +1,8 @@
require 'spec_helper'
+
require 'rubocop'
require 'rubocop/rspec/support'
+
require_relative '../../../../rubocop/cop/migration/add_concurrent_foreign_key'
describe RuboCop::Cop::Migration::AddConcurrentForeignKey do
@@ -10,7 +12,7 @@ describe RuboCop::Cop::Migration::AddConcurrentForeignKey do
context 'outside of a migration' do
it 'does not register any offenses' do
- inspect_source(cop, 'def up; add_foreign_key(:projects, :users, column: :user_id); end')
+ inspect_source('def up; add_foreign_key(:projects, :users, column: :user_id); end')
expect(cop.offenses).to be_empty
end
@@ -22,7 +24,7 @@ describe RuboCop::Cop::Migration::AddConcurrentForeignKey do
end
it 'registers an offense when using add_foreign_key' do
- inspect_source(cop, 'def up; add_foreign_key(:projects, :users, column: :user_id); end')
+ inspect_source('def up; add_foreign_key(:projects, :users, column: :user_id); end')
aggregate_failures do
expect(cop.offenses.size).to eq(1)
diff --git a/spec/rubocop/cop/migration/add_concurrent_index_spec.rb b/spec/rubocop/cop/migration/add_concurrent_index_spec.rb
index 19a5718b0b1..9c1ebcc0ced 100644
--- a/spec/rubocop/cop/migration/add_concurrent_index_spec.rb
+++ b/spec/rubocop/cop/migration/add_concurrent_index_spec.rb
@@ -16,7 +16,7 @@ describe RuboCop::Cop::Migration::AddConcurrentIndex do
end
it 'registers an offense when add_concurrent_index is used inside a change method' do
- inspect_source(cop, 'def change; add_concurrent_index :table, :column; end')
+ inspect_source('def change; add_concurrent_index :table, :column; end')
aggregate_failures do
expect(cop.offenses.size).to eq(1)
@@ -25,7 +25,7 @@ describe RuboCop::Cop::Migration::AddConcurrentIndex do
end
it 'registers no offense when add_concurrent_index is used inside an up method' do
- inspect_source(cop, 'def up; add_concurrent_index :table, :column; end')
+ inspect_source('def up; add_concurrent_index :table, :column; end')
expect(cop.offenses.size).to eq(0)
end
@@ -33,7 +33,7 @@ describe RuboCop::Cop::Migration::AddConcurrentIndex do
context 'outside of migration' do
it 'registers no offense' do
- inspect_source(cop, 'def change; add_concurrent_index :table, :column; end')
+ inspect_source('def change; add_concurrent_index :table, :column; end')
expect(cop.offenses.size).to eq(0)
end
diff --git a/spec/rubocop/cop/migration/add_timestamps_spec.rb b/spec/rubocop/cop/migration/add_timestamps_spec.rb
index 18df62dec3e..3a41c91add2 100644
--- a/spec/rubocop/cop/migration/add_timestamps_spec.rb
+++ b/spec/rubocop/cop/migration/add_timestamps_spec.rb
@@ -53,7 +53,7 @@ describe RuboCop::Cop::Migration::AddTimestamps do
end
it 'registers an offense when the "add_timestamps" method is used' do
- inspect_source(cop, migration_with_add_timestamps)
+ inspect_source(migration_with_add_timestamps)
aggregate_failures do
expect(cop.offenses.size).to eq(1)
@@ -62,7 +62,7 @@ describe RuboCop::Cop::Migration::AddTimestamps do
end
it 'does not register an offense when the "add_timestamps" method is not used' do
- inspect_source(cop, migration_without_add_timestamps)
+ inspect_source(migration_without_add_timestamps)
aggregate_failures do
expect(cop.offenses.size).to eq(0)
@@ -70,7 +70,7 @@ describe RuboCop::Cop::Migration::AddTimestamps do
end
it 'does not register an offense when the "add_timestamps_with_timezone" method is used' do
- inspect_source(cop, migration_with_add_timestamps_with_timezone)
+ inspect_source(migration_with_add_timestamps_with_timezone)
aggregate_failures do
expect(cop.offenses.size).to eq(0)
@@ -80,9 +80,9 @@ describe RuboCop::Cop::Migration::AddTimestamps do
context 'outside of migration' do
it 'registers no offense' do
- inspect_source(cop, migration_with_add_timestamps)
- inspect_source(cop, migration_without_add_timestamps)
- inspect_source(cop, migration_with_add_timestamps_with_timezone)
+ inspect_source(migration_with_add_timestamps)
+ inspect_source(migration_without_add_timestamps)
+ inspect_source(migration_with_add_timestamps_with_timezone)
expect(cop.offenses.size).to eq(0)
end
diff --git a/spec/rubocop/cop/migration/datetime_spec.rb b/spec/rubocop/cop/migration/datetime_spec.rb
index b1dfcf1b048..9e844325371 100644
--- a/spec/rubocop/cop/migration/datetime_spec.rb
+++ b/spec/rubocop/cop/migration/datetime_spec.rb
@@ -67,7 +67,7 @@ describe RuboCop::Cop::Migration::Datetime do
end
it 'registers an offense when the ":datetime" data type is used' do
- inspect_source(cop, migration_with_datetime)
+ inspect_source(migration_with_datetime)
aggregate_failures do
expect(cop.offenses.size).to eq(1)
@@ -77,7 +77,7 @@ describe RuboCop::Cop::Migration::Datetime do
end
it 'registers an offense when the ":timestamp" data type is used' do
- inspect_source(cop, migration_with_timestamp)
+ inspect_source(migration_with_timestamp)
aggregate_failures do
expect(cop.offenses.size).to eq(1)
@@ -87,7 +87,7 @@ describe RuboCop::Cop::Migration::Datetime do
end
it 'does not register an offense when the ":datetime" data type is not used' do
- inspect_source(cop, migration_without_datetime)
+ inspect_source(migration_without_datetime)
aggregate_failures do
expect(cop.offenses.size).to eq(0)
@@ -95,7 +95,7 @@ describe RuboCop::Cop::Migration::Datetime do
end
it 'does not register an offense when the ":datetime_with_timezone" data type is used' do
- inspect_source(cop, migration_with_datetime_with_timezone)
+ inspect_source(migration_with_datetime_with_timezone)
aggregate_failures do
expect(cop.offenses.size).to eq(0)
@@ -105,10 +105,10 @@ describe RuboCop::Cop::Migration::Datetime do
context 'outside of migration' do
it 'registers no offense' do
- inspect_source(cop, migration_with_datetime)
- inspect_source(cop, migration_with_timestamp)
- inspect_source(cop, migration_without_datetime)
- inspect_source(cop, migration_with_datetime_with_timezone)
+ inspect_source(migration_with_datetime)
+ inspect_source(migration_with_timestamp)
+ inspect_source(migration_without_datetime)
+ inspect_source(migration_with_datetime_with_timezone)
expect(cop.offenses.size).to eq(0)
end
diff --git a/spec/rubocop/cop/migration/hash_index_spec.rb b/spec/rubocop/cop/migration/hash_index_spec.rb
index 9a8576a19e5..5d53dde9a79 100644
--- a/spec/rubocop/cop/migration/hash_index_spec.rb
+++ b/spec/rubocop/cop/migration/hash_index_spec.rb
@@ -16,7 +16,7 @@ describe RuboCop::Cop::Migration::HashIndex do
end
it 'registers an offense when creating a hash index' do
- inspect_source(cop, 'def change; add_index :table, :column, using: :hash; end')
+ inspect_source('def change; add_index :table, :column, using: :hash; end')
aggregate_failures do
expect(cop.offenses.size).to eq(1)
@@ -25,7 +25,7 @@ describe RuboCop::Cop::Migration::HashIndex do
end
it 'registers an offense when creating a concurrent hash index' do
- inspect_source(cop, 'def change; add_concurrent_index :table, :column, using: :hash; end')
+ inspect_source('def change; add_concurrent_index :table, :column, using: :hash; end')
aggregate_failures do
expect(cop.offenses.size).to eq(1)
@@ -34,7 +34,7 @@ describe RuboCop::Cop::Migration::HashIndex do
end
it 'registers an offense when creating a hash index using t.index' do
- inspect_source(cop, 'def change; t.index :table, :column, using: :hash; end')
+ inspect_source('def change; t.index :table, :column, using: :hash; end')
aggregate_failures do
expect(cop.offenses.size).to eq(1)
@@ -45,7 +45,7 @@ describe RuboCop::Cop::Migration::HashIndex do
context 'outside of migration' do
it 'registers no offense' do
- inspect_source(cop, 'def change; index :table, :column, using: :hash; end')
+ inspect_source('def change; index :table, :column, using: :hash; end')
expect(cop.offenses.size).to eq(0)
end
diff --git a/spec/rubocop/cop/migration/remove_column_spec.rb b/spec/rubocop/cop/migration/remove_column_spec.rb
new file mode 100644
index 00000000000..f1a64f431bd
--- /dev/null
+++ b/spec/rubocop/cop/migration/remove_column_spec.rb
@@ -0,0 +1,68 @@
+require 'spec_helper'
+
+require 'rubocop'
+require 'rubocop/rspec/support'
+
+require_relative '../../../../rubocop/cop/migration/remove_column'
+
+describe RuboCop::Cop::Migration::RemoveColumn do
+ include CopHelper
+
+ subject(:cop) { described_class.new }
+
+ def source(meth = 'change')
+ "def #{meth}; remove_column :table, :column; end"
+ end
+
+ context 'in a regular migration' do
+ before do
+ allow(cop).to receive(:in_migration?).and_return(true)
+ allow(cop).to receive(:in_post_deployment_migration?).and_return(false)
+ end
+
+ it 'registers an offense when remove_column is used in the change method' do
+ inspect_source(source('change'))
+
+ aggregate_failures do
+ expect(cop.offenses.size).to eq(1)
+ expect(cop.offenses.map(&:line)).to eq([1])
+ end
+ end
+
+ it 'registers an offense when remove_column is used in the up method' do
+ inspect_source(source('up'))
+
+ aggregate_failures do
+ expect(cop.offenses.size).to eq(1)
+ expect(cop.offenses.map(&:line)).to eq([1])
+ end
+ end
+
+ it 'registers no offense when remove_column is used in the down method' do
+ inspect_source(source('down'))
+
+ expect(cop.offenses.size).to eq(0)
+ end
+ end
+
+ context 'in a post-deployment migration' do
+ before do
+ allow(cop).to receive(:in_migration?).and_return(true)
+ allow(cop).to receive(:in_post_deployment_migration?).and_return(true)
+ end
+
+ it 'registers no offense' do
+ inspect_source(source)
+
+ expect(cop.offenses.size).to eq(0)
+ end
+ end
+
+ context 'outside of a migration' do
+ it 'registers no offense' do
+ inspect_source(source)
+
+ expect(cop.offenses.size).to eq(0)
+ end
+ end
+end
diff --git a/spec/rubocop/cop/migration/remove_concurrent_index_spec.rb b/spec/rubocop/cop/migration/remove_concurrent_index_spec.rb
index a714bf4e5d5..a23d5d022e3 100644
--- a/spec/rubocop/cop/migration/remove_concurrent_index_spec.rb
+++ b/spec/rubocop/cop/migration/remove_concurrent_index_spec.rb
@@ -16,7 +16,7 @@ describe RuboCop::Cop::Migration::RemoveConcurrentIndex do
end
it 'registers an offense when remove_concurrent_index is used inside a change method' do
- inspect_source(cop, 'def change; remove_concurrent_index :table, :column; end')
+ inspect_source('def change; remove_concurrent_index :table, :column; end')
aggregate_failures do
expect(cop.offenses.size).to eq(1)
@@ -25,7 +25,7 @@ describe RuboCop::Cop::Migration::RemoveConcurrentIndex do
end
it 'registers no offense when remove_concurrent_index is used inside an up method' do
- inspect_source(cop, 'def up; remove_concurrent_index :table, :column; end')
+ inspect_source('def up; remove_concurrent_index :table, :column; end')
expect(cop.offenses.size).to eq(0)
end
@@ -33,7 +33,7 @@ describe RuboCop::Cop::Migration::RemoveConcurrentIndex do
context 'outside of migration' do
it 'registers no offense' do
- inspect_source(cop, 'def change; remove_concurrent_index :table, :column; end')
+ inspect_source('def change; remove_concurrent_index :table, :column; end')
expect(cop.offenses.size).to eq(0)
end
diff --git a/spec/rubocop/cop/migration/remove_index_spec.rb b/spec/rubocop/cop/migration/remove_index_spec.rb
index 31923cb7429..bbf2227e512 100644
--- a/spec/rubocop/cop/migration/remove_index_spec.rb
+++ b/spec/rubocop/cop/migration/remove_index_spec.rb
@@ -16,7 +16,7 @@ describe RuboCop::Cop::Migration::RemoveIndex do
end
it 'registers an offense when remove_index is used' do
- inspect_source(cop, 'def change; remove_index :table, :column; end')
+ inspect_source('def change; remove_index :table, :column; end')
aggregate_failures do
expect(cop.offenses.size).to eq(1)
@@ -27,7 +27,7 @@ describe RuboCop::Cop::Migration::RemoveIndex do
context 'outside of migration' do
it 'registers no offense' do
- inspect_source(cop, 'def change; remove_index :table, :column; end')
+ inspect_source('def change; remove_index :table, :column; end')
expect(cop.offenses.size).to eq(0)
end
diff --git a/spec/rubocop/cop/migration/reversible_add_column_with_default_spec.rb b/spec/rubocop/cop/migration/reversible_add_column_with_default_spec.rb
index 3723d635083..ba8cd2c6c4a 100644
--- a/spec/rubocop/cop/migration/reversible_add_column_with_default_spec.rb
+++ b/spec/rubocop/cop/migration/reversible_add_column_with_default_spec.rb
@@ -16,7 +16,7 @@ describe RuboCop::Cop::Migration::ReversibleAddColumnWithDefault do
end
it 'registers an offense when add_column_with_default is used inside a change method' do
- inspect_source(cop, 'def change; add_column_with_default :table, :column, default: false; end')
+ inspect_source('def change; add_column_with_default :table, :column, default: false; end')
aggregate_failures do
expect(cop.offenses.size).to eq(1)
@@ -25,7 +25,7 @@ describe RuboCop::Cop::Migration::ReversibleAddColumnWithDefault do
end
it 'registers no offense when add_column_with_default is used inside an up method' do
- inspect_source(cop, 'def up; add_column_with_default :table, :column, default: false; end')
+ inspect_source('def up; add_column_with_default :table, :column, default: false; end')
expect(cop.offenses.size).to eq(0)
end
@@ -33,7 +33,7 @@ describe RuboCop::Cop::Migration::ReversibleAddColumnWithDefault do
context 'outside of migration' do
it 'registers no offense' do
- inspect_source(cop, 'def change; add_column_with_default :table, :column, default: false; end')
+ inspect_source('def change; add_column_with_default :table, :column, default: false; end')
expect(cop.offenses.size).to eq(0)
end
diff --git a/spec/rubocop/cop/migration/safer_boolean_column_spec.rb b/spec/rubocop/cop/migration/safer_boolean_column_spec.rb
index 1006594499a..1c4f18fbcc3 100644
--- a/spec/rubocop/cop/migration/safer_boolean_column_spec.rb
+++ b/spec/rubocop/cop/migration/safer_boolean_column_spec.rb
@@ -32,7 +32,7 @@ describe RuboCop::Cop::Migration::SaferBooleanColumn do
sources_and_offense.each do |source, offense|
context "given the source \"#{source}\"" do
it "registers the offense matching \"#{offense}\"" do
- inspect_source(cop, source)
+ inspect_source(source)
aggregate_failures do
expect(cop.offenses.first.message).to match(offense)
@@ -49,7 +49,7 @@ describe RuboCop::Cop::Migration::SaferBooleanColumn do
inoffensive_sources.each do |source|
context "given the source \"#{source}\"" do
it "registers no offense" do
- inspect_source(cop, source)
+ inspect_source(source)
aggregate_failures do
expect(cop.offenses).to be_empty
@@ -61,14 +61,14 @@ describe RuboCop::Cop::Migration::SaferBooleanColumn do
end
it 'registers no offense for tables not listed in SMALL_TABLES' do
- inspect_source(cop, "add_column :large_table, :column, :boolean")
+ inspect_source("add_column :large_table, :column, :boolean")
expect(cop.offenses).to be_empty
end
it 'registers no offense for non-boolean columns' do
table = described_class::SMALL_TABLES.sample
- inspect_source(cop, "add_column :#{table}, :column, :string")
+ inspect_source("add_column :#{table}, :column, :string")
expect(cop.offenses).to be_empty
end
@@ -77,7 +77,7 @@ describe RuboCop::Cop::Migration::SaferBooleanColumn do
context 'outside of migration' do
it 'registers no offense' do
table = described_class::SMALL_TABLES.sample
- inspect_source(cop, "add_column :#{table}, :column, :boolean")
+ inspect_source("add_column :#{table}, :column, :boolean")
expect(cop.offenses).to be_empty
end
diff --git a/spec/rubocop/cop/migration/timestamps_spec.rb b/spec/rubocop/cop/migration/timestamps_spec.rb
index cdf1423d0c5..685bdb21803 100644
--- a/spec/rubocop/cop/migration/timestamps_spec.rb
+++ b/spec/rubocop/cop/migration/timestamps_spec.rb
@@ -62,7 +62,7 @@ describe RuboCop::Cop::Migration::Timestamps do
end
it 'registers an offense when the "timestamps" method is used' do
- inspect_source(cop, migration_with_timestamps)
+ inspect_source(migration_with_timestamps)
aggregate_failures do
expect(cop.offenses.size).to eq(1)
@@ -71,7 +71,7 @@ describe RuboCop::Cop::Migration::Timestamps do
end
it 'does not register an offense when the "timestamps" method is not used' do
- inspect_source(cop, migration_without_timestamps)
+ inspect_source(migration_without_timestamps)
aggregate_failures do
expect(cop.offenses.size).to eq(0)
@@ -79,7 +79,7 @@ describe RuboCop::Cop::Migration::Timestamps do
end
it 'does not register an offense when the "timestamps_with_timezone" method is used' do
- inspect_source(cop, migration_with_timestamps_with_timezone)
+ inspect_source(migration_with_timestamps_with_timezone)
aggregate_failures do
expect(cop.offenses.size).to eq(0)
@@ -89,9 +89,9 @@ describe RuboCop::Cop::Migration::Timestamps do
context 'outside of migration' do
it 'registers no offense' do
- inspect_source(cop, migration_with_timestamps)
- inspect_source(cop, migration_without_timestamps)
- inspect_source(cop, migration_with_timestamps_with_timezone)
+ inspect_source(migration_with_timestamps)
+ inspect_source(migration_without_timestamps)
+ inspect_source(migration_with_timestamps_with_timezone)
expect(cop.offenses.size).to eq(0)
end
diff --git a/spec/rubocop/cop/migration/update_column_in_batches_spec.rb b/spec/rubocop/cop/migration/update_column_in_batches_spec.rb
index 38b8f439a55..1c8ab0ad5d2 100644
--- a/spec/rubocop/cop/migration/update_column_in_batches_spec.rb
+++ b/spec/rubocop/cop/migration/update_column_in_batches_spec.rb
@@ -1,6 +1,8 @@
require 'spec_helper'
+
require 'rubocop'
require 'rubocop/rspec/support'
+
require_relative '../../../../rubocop/cop/migration/update_column_in_batches'
describe RuboCop::Cop::Migration::UpdateColumnInBatches do
@@ -25,7 +27,7 @@ describe RuboCop::Cop::Migration::UpdateColumnInBatches do
context 'outside of a migration' do
it 'does not register any offenses' do
- inspect_source(cop, migration_code)
+ inspect_source(migration_code)
expect(cop.offenses).to be_empty
end
@@ -49,7 +51,7 @@ describe RuboCop::Cop::Migration::UpdateColumnInBatches do
let(:relative_spec_filepath) { Pathname.new(spec_filepath).relative_path_from(tmp_rails_root) }
it 'registers an offense when using update_column_in_batches' do
- inspect_source(cop, migration_code, @migration_file)
+ inspect_source(migration_code, @migration_file)
aggregate_failures do
expect(cop.offenses.size).to eq(1)
@@ -72,7 +74,7 @@ describe RuboCop::Cop::Migration::UpdateColumnInBatches do
end
it 'does not register any offenses' do
- inspect_source(cop, migration_code, @migration_file)
+ inspect_source(migration_code, @migration_file)
expect(cop.offenses).to be_empty
end
diff --git a/spec/rubocop/cop/migration/update_large_table_spec.rb b/spec/rubocop/cop/migration/update_large_table_spec.rb
index 17b19e139e4..ef724fc8bad 100644
--- a/spec/rubocop/cop/migration/update_large_table_spec.rb
+++ b/spec/rubocop/cop/migration/update_large_table_spec.rb
@@ -18,7 +18,7 @@ describe RuboCop::Cop::Migration::UpdateLargeTable do
shared_examples 'large tables' do |update_method|
described_class::LARGE_TABLES.each do |table|
it "registers an offense for the #{table} table" do
- inspect_source(cop, "#{update_method} :#{table}, :column, default: true")
+ inspect_source("#{update_method} :#{table}, :column, default: true")
aggregate_failures do
expect(cop.offenses.size).to eq(1)
@@ -37,7 +37,7 @@ describe RuboCop::Cop::Migration::UpdateLargeTable do
end
it 'registers no offense for non-blacklisted tables' do
- inspect_source(cop, "add_column_with_default :table, :column, default: true")
+ inspect_source("add_column_with_default :table, :column, default: true")
expect(cop.offenses).to be_empty
end
@@ -45,7 +45,7 @@ describe RuboCop::Cop::Migration::UpdateLargeTable do
it 'registers no offense for non-blacklisted methods' do
table = described_class::LARGE_TABLES.sample
- inspect_source(cop, "some_other_method :#{table}, :column, default: true")
+ inspect_source("some_other_method :#{table}, :column, default: true")
expect(cop.offenses).to be_empty
end
@@ -55,13 +55,13 @@ describe RuboCop::Cop::Migration::UpdateLargeTable do
let(:table) { described_class::LARGE_TABLES.sample }
it 'registers no offense for add_column_with_default' do
- inspect_source(cop, "add_column_with_default :#{table}, :column, default: true")
+ inspect_source("add_column_with_default :#{table}, :column, default: true")
expect(cop.offenses).to be_empty
end
it 'registers no offense for update_column_in_batches' do
- inspect_source(cop, "add_column_with_default :#{table}, :column, default: true")
+ inspect_source("add_column_with_default :#{table}, :column, default: true")
expect(cop.offenses).to be_empty
end
diff --git a/spec/rubocop/cop/polymorphic_associations_spec.rb b/spec/rubocop/cop/polymorphic_associations_spec.rb
deleted file mode 100644
index 49959aa6419..00000000000
--- a/spec/rubocop/cop/polymorphic_associations_spec.rb
+++ /dev/null
@@ -1,33 +0,0 @@
-require 'spec_helper'
-require 'rubocop'
-require 'rubocop/rspec/support'
-require_relative '../../../rubocop/cop/polymorphic_associations'
-
-describe RuboCop::Cop::PolymorphicAssociations do
- include CopHelper
-
- subject(:cop) { described_class.new }
-
- context 'inside the app/models directory' do
- it 'registers an offense when polymorphic: true is used' do
- allow(cop).to receive(:in_model?).and_return(true)
-
- inspect_source(cop, 'belongs_to :foo, polymorphic: true')
-
- aggregate_failures do
- expect(cop.offenses.size).to eq(1)
- expect(cop.offenses.map(&:line)).to eq([1])
- end
- end
- end
-
- context 'outside the app/models directory' do
- it 'does nothing' do
- allow(cop).to receive(:in_model?).and_return(false)
-
- inspect_source(cop, 'belongs_to :foo, polymorphic: true')
-
- expect(cop.offenses).to be_empty
- end
- end
-end
diff --git a/spec/rubocop/cop/project_path_helper_spec.rb b/spec/rubocop/cop/project_path_helper_spec.rb
index bc47b45cad7..84e6eb7d87f 100644
--- a/spec/rubocop/cop/project_path_helper_spec.rb
+++ b/spec/rubocop/cop/project_path_helper_spec.rb
@@ -15,7 +15,7 @@ describe RuboCop::Cop::ProjectPathHelper do
let(:correct_source) { 'edit_project_issue_path(@issue.project, @issue)' }
it 'registers an offense' do
- inspect_source(cop, source)
+ inspect_source(source)
aggregate_failures do
expect(cop.offenses.size).to eq(1)
@@ -25,7 +25,7 @@ describe RuboCop::Cop::ProjectPathHelper do
end
it 'autocorrects to the right version' do
- autocorrected = autocorrect_source(cop, source)
+ autocorrected = autocorrect_source(source)
expect(autocorrected).to eq(correct_source)
end
@@ -33,7 +33,7 @@ describe RuboCop::Cop::ProjectPathHelper do
context 'when using namespace_project with a different namespace' do
it 'registers no offense' do
- inspect_source(cop, 'edit_namespace_project_issue_path(namespace, project)')
+ inspect_source('edit_namespace_project_issue_path(namespace, project)')
expect(cop.offenses.size).to eq(0)
end
diff --git a/spec/rubocop/cop/redirect_with_status_spec.rb b/spec/rubocop/cop/redirect_with_status_spec.rb
deleted file mode 100644
index 5ad63567f84..00000000000
--- a/spec/rubocop/cop/redirect_with_status_spec.rb
+++ /dev/null
@@ -1,86 +0,0 @@
-require 'spec_helper'
-
-require 'rubocop'
-require 'rubocop/rspec/support'
-
-require_relative '../../../rubocop/cop/redirect_with_status'
-
-describe RuboCop::Cop::RedirectWithStatus do
- include CopHelper
-
- subject(:cop) { described_class.new }
- let(:controller_fixture_without_status) do
- %q(
- class UserController < ApplicationController
- def show
- user = User.find(params[:id])
- redirect_to user_path if user.name == 'John Wick'
- end
-
- def destroy
- user = User.find(params[:id])
-
- if user.destroy
- redirect_to root_path
- else
- render :show
- end
- end
- end
- )
- end
-
- let(:controller_fixture_with_status) do
- %q(
- class UserController < ApplicationController
- def show
- user = User.find(params[:id])
- redirect_to user_path if user.name == 'John Wick'
- end
-
- def destroy
- user = User.find(params[:id])
-
- if user.destroy
- redirect_to root_path, status: 302
- else
- render :show
- end
- end
- end
- )
- end
-
- context 'in controller' do
- before do
- allow(cop).to receive(:in_controller?).and_return(true)
- end
-
- it 'registers an offense when a "destroy" action uses "redirect_to" without "status"' do
- inspect_source(cop, controller_fixture_without_status)
-
- aggregate_failures do
- expect(cop.offenses.size).to eq(1)
- expect(cop.offenses.map(&:line)).to eq([12]) # 'redirect_to' is located on 12th line in controller_fixture.
- expect(cop.highlights).to eq(['redirect_to'])
- end
- end
-
- it 'does not register an offense when a "destroy" action uses "redirect_to" with "status"' do
- inspect_source(cop, controller_fixture_with_status)
-
- aggregate_failures do
- expect(cop.offenses.size).to eq(0)
- end
- end
- end
-
- context 'outside of controller' do
- it 'registers no offense' do
- inspect_source(cop, controller_fixture_without_status)
- inspect_source(cop, controller_fixture_with_status)
-
- expect(cop.offenses.size).to eq(0)
- end
- end
-end
diff --git a/spec/rubocop/cop/rspec/env_assignment_spec.rb b/spec/rubocop/cop/rspec/env_assignment_spec.rb
index 4e859b6f6fa..659633f6467 100644
--- a/spec/rubocop/cop/rspec/env_assignment_spec.rb
+++ b/spec/rubocop/cop/rspec/env_assignment_spec.rb
@@ -17,7 +17,7 @@ describe RuboCop::Cop::RSpec::EnvAssignment do
shared_examples 'an offensive ENV#[]= call' do |content|
it "registers an offense for `#{content}`" do
- inspect_source(cop, content, source_file)
+ inspect_source(content, source_file)
expect(cop.offenses.size).to eq(1)
expect(cop.offenses.map(&:line)).to eq([1])
@@ -27,7 +27,7 @@ describe RuboCop::Cop::RSpec::EnvAssignment do
shared_examples 'an autocorrected ENV#[]= call' do |content, autocorrected_content|
it "registers an offense for `#{content}` and autocorrects it to `#{autocorrected_content}`" do
- autocorrected = autocorrect_source(cop, content, source_file)
+ autocorrected = autocorrect_source(content, source_file)
expect(autocorrected).to eql(autocorrected_content)
end
@@ -51,7 +51,7 @@ describe RuboCop::Cop::RSpec::EnvAssignment do
context 'outside of a spec file' do
it "does not register an offense for `#{OFFENSE_CALL_SINGLE_QUOTES_KEY}` in a non-spec file" do
- inspect_source(cop, OFFENSE_CALL_SINGLE_QUOTES_KEY)
+ inspect_source(OFFENSE_CALL_SINGLE_QUOTES_KEY)
expect(cop.offenses.size).to eq(0)
end
diff --git a/spec/rubocop/cop/rspec/single_line_hook_spec.rb b/spec/rubocop/cop/rspec/single_line_hook_spec.rb
deleted file mode 100644
index 6cf0831d3ad..00000000000
--- a/spec/rubocop/cop/rspec/single_line_hook_spec.rb
+++ /dev/null
@@ -1,66 +0,0 @@
-require 'spec_helper'
-
-require 'rubocop'
-require 'rubocop/rspec/support'
-
-require_relative '../../../../rubocop/cop/rspec/single_line_hook'
-
-describe RuboCop::Cop::RSpec::SingleLineHook do
- include CopHelper
-
- subject(:cop) { described_class.new }
-
- # Override `CopHelper#inspect_source` to always appear to be in a spec file,
- # so that our RSpec-only cop actually runs
- def inspect_source(*args)
- super(*args, 'foo_spec.rb')
- end
-
- it 'registers an offense for a single-line `before` block' do
- inspect_source(cop, 'before { do_something }')
-
- expect(cop.offenses.size).to eq(1)
- expect(cop.offenses.map(&:line)).to eq([1])
- expect(cop.highlights).to eq(['before { do_something }'])
- end
-
- it 'registers an offense for a single-line `after` block' do
- inspect_source(cop, 'after(:each) { undo_something }')
-
- expect(cop.offenses.size).to eq(1)
- expect(cop.offenses.map(&:line)).to eq([1])
- expect(cop.highlights).to eq(['after(:each) { undo_something }'])
- end
-
- it 'registers an offense for a single-line `around` block' do
- inspect_source(cop, 'around { |ex| do_something_else }')
-
- expect(cop.offenses.size).to eq(1)
- expect(cop.offenses.map(&:line)).to eq([1])
- expect(cop.highlights).to eq(['around { |ex| do_something_else }'])
- end
-
- it 'ignores a multi-line `before` block' do
- inspect_source(cop, ['before do',
- ' do_something',
- 'end'])
-
- expect(cop.offenses.size).to eq(0)
- end
-
- it 'ignores a multi-line `after` block' do
- inspect_source(cop, ['after(:each) do',
- ' undo_something',
- 'end'])
-
- expect(cop.offenses.size).to eq(0)
- end
-
- it 'ignores a multi-line `around` block' do
- inspect_source(cop, ['around do |ex|',
- ' do_something_else',
- 'end'])
-
- expect(cop.offenses.size).to eq(0)
- end
-end
diff --git a/spec/rubocop/cop/rspec/verbose_include_metadata_spec.rb b/spec/rubocop/cop/rspec/verbose_include_metadata_spec.rb
deleted file mode 100644
index 278662d32ea..00000000000
--- a/spec/rubocop/cop/rspec/verbose_include_metadata_spec.rb
+++ /dev/null
@@ -1,53 +0,0 @@
-require 'spec_helper'
-require 'rubocop'
-require 'rubocop/rspec/support'
-require_relative '../../../../rubocop/cop/rspec/verbose_include_metadata'
-
-describe RuboCop::Cop::RSpec::VerboseIncludeMetadata do
- include CopHelper
-
- subject(:cop) { described_class.new }
-
- let(:source_file) { 'foo_spec.rb' }
-
- # Override `CopHelper#inspect_source` to always appear to be in a spec file,
- # so that our RSpec-only cop actually runs
- def inspect_source(*args)
- super(*args, source_file)
- end
-
- shared_examples 'examples with include syntax' do |title|
- it "flags violation for #{title} examples that uses verbose include syntax" do
- inspect_source(cop, "#{title} 'Test', js: true do; end")
-
- expect(cop.offenses.size).to eq(1)
- offense = cop.offenses.first
-
- expect(offense.line).to eq(1)
- expect(cop.highlights).to eq(["#{title} 'Test', js: true"])
- expect(offense.message).to eq('Use `:js` instead of `js: true`.')
- end
-
- it "doesn't flag violation for #{title} examples that uses compact include syntax", :aggregate_failures do
- inspect_source(cop, "#{title} 'Test', :js do; end")
-
- expect(cop.offenses).to be_empty
- end
-
- it "doesn't flag violation for #{title} examples that uses flag: symbol" do
- inspect_source(cop, "#{title} 'Test', flag: :symbol do; end")
-
- expect(cop.offenses).to be_empty
- end
-
- it "autocorrects #{title} examples that uses verbose syntax into compact syntax" do
- autocorrected = autocorrect_source(cop, "#{title} 'Test', js: true do; end", source_file)
-
- expect(autocorrected).to eql("#{title} 'Test', :js do; end")
- end
- end
-
- %w(describe context feature example_group it specify example scenario its).each do |example|
- it_behaves_like 'examples with include syntax', example
- end
-end
diff --git a/spec/rubocop/cop/sidekiq_options_queue_spec.rb b/spec/rubocop/cop/sidekiq_options_queue_spec.rb
new file mode 100644
index 00000000000..7f237d5ffbb
--- /dev/null
+++ b/spec/rubocop/cop/sidekiq_options_queue_spec.rb
@@ -0,0 +1,28 @@
+require 'spec_helper'
+
+require 'rubocop'
+require 'rubocop/rspec/support'
+
+require_relative '../../../rubocop/cop/sidekiq_options_queue'
+
+describe RuboCop::Cop::SidekiqOptionsQueue do
+ include CopHelper
+
+ subject(:cop) { described_class.new }
+
+ it 'registers an offense when `sidekiq_options` is used with the `queue` option' do
+ inspect_source('sidekiq_options queue: "some_queue"')
+
+ aggregate_failures do
+ expect(cop.offenses.size).to eq(1)
+ expect(cop.offenses.map(&:line)).to eq([1])
+ expect(cop.highlights).to eq(['queue: "some_queue"'])
+ end
+ end
+
+ it 'does not register an offense when `sidekiq_options` is used with another option' do
+ inspect_source('sidekiq_options retry: false')
+
+ expect(cop.offenses).to be_empty
+ end
+end
diff --git a/spec/serializers/cluster_application_entity_spec.rb b/spec/serializers/cluster_application_entity_spec.rb
index 87c7b2ad36e..b5a55b4ef6e 100644
--- a/spec/serializers/cluster_application_entity_spec.rb
+++ b/spec/serializers/cluster_application_entity_spec.rb
@@ -2,7 +2,7 @@ require 'spec_helper'
describe ClusterApplicationEntity do
describe '#as_json' do
- let(:application) { build(:cluster_applications_helm) }
+ let(:application) { build(:clusters_applications_helm) }
subject { described_class.new(application).as_json }
it 'has name' do
@@ -18,7 +18,7 @@ describe ClusterApplicationEntity do
end
context 'when application is errored' do
- let(:application) { build(:cluster_applications_helm, :errored) }
+ let(:application) { build(:clusters_applications_helm, :errored) }
it 'has corresponded data' do
expect(subject[:status]).to eq(:errored)
diff --git a/spec/serializers/event_entity_spec.rb b/spec/serializers/event_entity_spec.rb
deleted file mode 100644
index bb54597c967..00000000000
--- a/spec/serializers/event_entity_spec.rb
+++ /dev/null
@@ -1,13 +0,0 @@
-require 'spec_helper'
-
-describe EventEntity do
- subject { described_class.represent(create(:event)).as_json }
-
- it 'exposes author' do
- expect(subject).to include(:author)
- end
-
- it 'exposes core elements of event' do
- expect(subject).to include(:updated_at)
- end
-end
diff --git a/spec/serializers/group_child_entity_spec.rb b/spec/serializers/group_child_entity_spec.rb
index 452754d7a79..505a9eaac5a 100644
--- a/spec/serializers/group_child_entity_spec.rb
+++ b/spec/serializers/group_child_entity_spec.rb
@@ -22,6 +22,7 @@ describe GroupChildEntity do
avatar_url
name
description
+ markdown_description
visibility
type
can_edit
@@ -60,9 +61,10 @@ describe GroupChildEntity do
end
describe 'for a group', :nested_groups do
+ let(:description) { 'Awesomeness' }
let(:object) do
create(:group, :nested, :with_avatar,
- description: 'Awesomeness')
+ description: description)
end
before do
@@ -96,6 +98,14 @@ describe GroupChildEntity do
expect(json[:edit_path]).to eq(edit_group_path(object))
end
+ context 'emoji in description' do
+ let(:description) { ':smile:' }
+
+ it 'has the correct markdown_description' do
+ expect(json[:markdown_description]).to eq('<p dir="auto"><gl-emoji title="smiling face with open mouth and smiling eyes" data-name="smile" data-unicode-version="6.0">😄</gl-emoji></p>')
+ end
+ end
+
it_behaves_like 'group child json'
end
end
diff --git a/spec/serializers/merge_request_serializer_spec.rb b/spec/serializers/merge_request_serializer_spec.rb
index e3abefa6d63..b259cb92962 100644
--- a/spec/serializers/merge_request_serializer_spec.rb
+++ b/spec/serializers/merge_request_serializer_spec.rb
@@ -1,37 +1,43 @@
require 'spec_helper'
describe MergeRequestSerializer do
- let(:user) { build_stubbed(:user) }
- let(:merge_request) { build_stubbed(:merge_request) }
-
- let(:serializer) do
+ let(:user) { create(:user) }
+ let(:resource) { create(:merge_request) }
+ let(:json_entity) do
described_class.new(current_user: user)
+ .represent(resource, serializer: serializer)
+ .with_indifferent_access
end
- describe '#represent' do
- let(:opts) { { serializer: serializer_entity } }
- subject { serializer.represent(merge_request, serializer: serializer_entity) }
+ context 'widget merge request serialization' do
+ let(:serializer) { 'widget' }
- context 'when passing basic serializer param' do
- let(:serializer_entity) { 'basic' }
+ it 'matches issue json schema' do
+ expect(json_entity).to match_schema('entities/merge_request_widget')
+ end
+ end
- it 'calls super class #represent with correct params' do
- expect_any_instance_of(BaseSerializer).to receive(:represent)
- .with(merge_request, opts, MergeRequestBasicEntity)
+ context 'sidebar merge request serialization' do
+ let(:serializer) { 'sidebar' }
- subject
- end
+ it 'matches basic merge request json schema' do
+ expect(json_entity).to match_schema('entities/merge_request_basic')
end
+ end
- context 'when serializer param is falsy' do
- let(:serializer_entity) { nil }
+ context 'basic merge request serialization' do
+ let(:serializer) { 'basic' }
+
+ it 'matches basic merge request json schema' do
+ expect(json_entity).to match_schema('entities/merge_request_basic')
+ end
+ end
- it 'calls super class #represent with correct params' do
- expect_any_instance_of(BaseSerializer).to receive(:represent)
- .with(merge_request, opts, MergeRequestEntity)
+ context 'no serializer' do
+ let(:serializer) { nil }
- subject
- end
+ it 'falls back to the widget entity' do
+ expect(json_entity).to match_schema('entities/merge_request_widget')
end
end
end
diff --git a/spec/serializers/merge_request_entity_spec.rb b/spec/serializers/merge_request_widget_entity_spec.rb
index 1ad672fd355..80a271ba7fb 100644
--- a/spec/serializers/merge_request_entity_spec.rb
+++ b/spec/serializers/merge_request_widget_entity_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe MergeRequestEntity do
+describe MergeRequestWidgetEntity do
let(:project) { create :project, :repository }
let(:resource) { create(:merge_request, source_project: project, target_project: project) }
let(:user) { create(:user) }
@@ -35,31 +35,79 @@ describe MergeRequestEntity do
end
end
- it 'includes issues_links' do
- issues_links = subject[:issues_links]
+ describe 'metrics' do
+ context 'when metrics record exists with merged data' do
+ before do
+ resource.mark_as_merged!
+ resource.metrics.update!(merged_by: user)
+ end
- expect(issues_links).to include(:closing, :mentioned_but_not_closing,
- :assign_to_closing)
- end
+ it 'matches merge request metrics schema' do
+ expect(subject[:metrics].with_indifferent_access)
+ .to match_schema('entities/merge_request_metrics')
+ end
- it 'has Issuable attributes' do
- expect(subject).to include(:id, :iid, :author_id, :description, :lock_version, :milestone_id,
- :title, :updated_by_id, :created_at, :updated_at, :milestone, :labels)
- end
+ it 'returns values from metrics record' do
+ expect(subject.dig(:metrics, :merged_by, :id))
+ .to eq(resource.metrics.merged_by_id)
+ end
+ end
- it 'has time estimation attributes' do
- expect(subject).to include(:time_estimate, :total_time_spent, :human_time_estimate, :human_total_time_spent)
- end
+ context 'when metrics record exists with closed data' do
+ before do
+ resource.close!
+ resource.metrics.update!(latest_closed_by: user)
+ end
+
+ it 'matches merge request metrics schema' do
+ expect(subject[:metrics].with_indifferent_access)
+ .to match_schema('entities/merge_request_metrics')
+ end
+
+ it 'returns values from metrics record' do
+ expect(subject.dig(:metrics, :closed_by, :id))
+ .to eq(resource.metrics.latest_closed_by_id)
+ end
+ end
+
+ context 'when metrics does not exists' do
+ before do
+ resource.mark_as_merged!
+ resource.metrics.destroy!
+ resource.reload
+ end
+
+ context 'when events exists' do
+ let!(:closed_event) { create(:event, :closed, project: project, target: resource) }
+ let!(:merge_event) { create(:event, :merged, project: project, target: resource) }
+
+ it 'matches merge request metrics schema' do
+ expect(subject[:metrics].with_indifferent_access)
+ .to match_schema('entities/merge_request_metrics')
+ end
+
+ it 'returns values from events record' do
+ expect(subject.dig(:metrics, :merged_by, :id))
+ .to eq(merge_event.author_id)
- it 'has important MergeRequest attributes' do
- expect(subject).to include(:state, :deleted_at, :diff_head_sha, :merge_commit_message,
- :has_conflicts, :has_ci, :merge_path,
- :conflict_resolution_path,
- :cancel_merge_when_pipeline_succeeds_path,
- :create_issue_to_resolve_discussions_path,
- :source_branch_path, :target_branch_commits_path,
- :target_branch_tree_path, :commits_count, :merge_ongoing,
- :ff_only_enabled)
+ expect(subject.dig(:metrics, :closed_by, :id))
+ .to eq(closed_event.author_id)
+
+ expect(subject.dig(:metrics, :merged_at).to_s)
+ .to eq(merge_event.updated_at.to_s)
+
+ expect(subject.dig(:metrics, :closed_at).to_s)
+ .to eq(closed_event.updated_at.to_s)
+ end
+ end
+
+ context 'when events does not exists' do
+ it 'matches merge request metrics schema' do
+ expect(subject[:metrics].with_indifferent_access)
+ .to match_schema('entities/merge_request_metrics')
+ end
+ end
+ end
end
it 'has email_patches_path' do
@@ -116,18 +164,6 @@ describe MergeRequestEntity do
end
end
- it 'includes merge_event' do
- create(:event, :merged, author: user, project: resource.project, target: resource)
-
- expect(subject[:merge_event]).to include(:author, :updated_at)
- end
-
- it 'includes closed_event' do
- create(:event, :closed, author: user, project: resource.project, target: resource)
-
- expect(subject[:closed_event]).to include(:author, :updated_at)
- end
-
describe 'diverged_commits_count' do
context 'when MR open and its diverging' do
it 'returns diverged commits count' do
@@ -154,4 +190,20 @@ describe MergeRequestEntity do
end
end
end
+
+ describe 'when source project is deleted' do
+ let(:project) { create(:project, :repository) }
+ let(:fork_project) { create(:project, :repository, forked_from_project: project) }
+ let(:merge_request) { create(:merge_request, source_project: fork_project, target_project: project) }
+
+ it 'returns a blank rebase_path' do
+ allow(merge_request).to receive(:should_be_rebased?).and_return(true)
+ fork_project.destroy
+ merge_request.reload
+
+ entity = described_class.new(merge_request, request: request).as_json
+
+ expect(entity[:rebase_path]).to be_nil
+ end
+ end
end
diff --git a/spec/serializers/pipeline_serializer_spec.rb b/spec/serializers/pipeline_serializer_spec.rb
index e40edbfb421..14c9f5cb58c 100644
--- a/spec/serializers/pipeline_serializer_spec.rb
+++ b/spec/serializers/pipeline_serializer_spec.rb
@@ -1,6 +1,7 @@
require 'spec_helper'
describe PipelineSerializer do
+ set(:project) { create(:project, :repository) }
set(:user) { create(:user) }
let(:serializer) do
@@ -16,7 +17,7 @@ describe PipelineSerializer do
end
context 'when a single object is being serialized' do
- let(:resource) { create(:ci_empty_pipeline) }
+ let(:resource) { create(:ci_empty_pipeline, project: project) }
it 'serializers the pipeline object' do
expect(subject[:id]).to eq resource.id
@@ -24,7 +25,7 @@ describe PipelineSerializer do
end
context 'when multiple objects are being serialized' do
- let(:resource) { create_list(:ci_pipeline, 2) }
+ let(:resource) { create_list(:ci_pipeline, 2, project: project) }
it 'serializers the array of pipelines' do
expect(subject).not_to be_empty
@@ -100,7 +101,6 @@ describe PipelineSerializer do
context 'number of queries' do
let(:resource) { Ci::Pipeline.all }
- let(:project) { create(:project) }
before do
# Since RequestStore.active? is true we have to allow the
diff --git a/spec/services/boards/issues/create_service_spec.rb b/spec/services/boards/issues/create_service_spec.rb
index 1a56164dba4..f0179e35652 100644
--- a/spec/services/boards/issues/create_service_spec.rb
+++ b/spec/services/boards/issues/create_service_spec.rb
@@ -11,7 +11,7 @@ describe Boards::Issues::CreateService do
subject(:service) { described_class.new(board.parent, project, user, board_id: board.id, list_id: list.id, title: 'New issue') }
before do
- project.team << [user, :developer]
+ project.add_developer(user)
end
it 'delegates the create proceedings to Issues::CreateService' do
diff --git a/spec/services/boards/issues/list_service_spec.rb b/spec/services/boards/issues/list_service_spec.rb
index 01ee3856c99..ff5733b7064 100644
--- a/spec/services/boards/issues/list_service_spec.rb
+++ b/spec/services/boards/issues/list_service_spec.rb
@@ -34,7 +34,7 @@ describe Boards::Issues::ListService do
let!(:closed_issue5) { create(:labeled_issue, :closed, project: project, labels: [development]) }
before do
- project.team << [user, :developer]
+ project.add_developer(user)
end
it 'delegates search to IssuesFinder' do
diff --git a/spec/services/boards/issues/move_service_spec.rb b/spec/services/boards/issues/move_service_spec.rb
index 464ff9f94b3..280e411683e 100644
--- a/spec/services/boards/issues/move_service_spec.rb
+++ b/spec/services/boards/issues/move_service_spec.rb
@@ -15,7 +15,7 @@ describe Boards::Issues::MoveService do
let!(:closed) { create(:closed_list, board: board1) }
before do
- project.team << [user, :developer]
+ project.add_developer(user)
end
context 'when moving an issue between lists' do
diff --git a/spec/services/boards/lists/create_service_spec.rb b/spec/services/boards/lists/create_service_spec.rb
index 7d0b396cd06..d5322e1bb21 100644
--- a/spec/services/boards/lists/create_service_spec.rb
+++ b/spec/services/boards/lists/create_service_spec.rb
@@ -10,7 +10,7 @@ describe Boards::Lists::CreateService do
subject(:service) { described_class.new(project, user, label_id: label.id) }
before do
- project.team << [user, :developer]
+ project.add_developer(user)
end
context 'when board lists is empty' do
diff --git a/spec/services/boards/lists/generate_service_spec.rb b/spec/services/boards/lists/generate_service_spec.rb
index 592f25059ac..82dbd1ee744 100644
--- a/spec/services/boards/lists/generate_service_spec.rb
+++ b/spec/services/boards/lists/generate_service_spec.rb
@@ -9,7 +9,7 @@ describe Boards::Lists::GenerateService do
subject(:service) { described_class.new(project, user) }
before do
- project.team << [user, :developer]
+ project.add_developer(user)
end
context 'when board lists is empty' do
diff --git a/spec/services/check_gcp_project_billing_service_spec.rb b/spec/services/check_gcp_project_billing_service_spec.rb
new file mode 100644
index 00000000000..f0e39ba6f49
--- /dev/null
+++ b/spec/services/check_gcp_project_billing_service_spec.rb
@@ -0,0 +1,31 @@
+require 'spec_helper'
+
+describe CheckGcpProjectBillingService do
+ let(:service) { described_class.new }
+ let(:projects) { [double(name: 'first_project'), double(name: 'second_project')] }
+
+ describe '#execute' do
+ before do
+ expect_any_instance_of(GoogleApi::CloudPlatform::Client)
+ .to receive(:projects_list).and_return(projects)
+
+ allow_any_instance_of(GoogleApi::CloudPlatform::Client)
+ .to receive_message_chain(:projects_get_billing_info, :billingEnabled)
+ .and_return(project_billing_enabled)
+ end
+
+ subject { service.execute('bogustoken') }
+
+ context 'google account has a billing enabled gcp project' do
+ let(:project_billing_enabled) { true }
+
+ it { is_expected.to eq(projects) }
+ end
+
+ context 'google account does not have a billing enabled gcp project' do
+ let(:project_billing_enabled) { false }
+
+ it { is_expected.to eq([]) }
+ end
+ end
+end
diff --git a/spec/services/ci/create_pipeline_service_spec.rb b/spec/services/ci/create_pipeline_service_spec.rb
index b0de8d447a2..267258b33a8 100644
--- a/spec/services/ci/create_pipeline_service_spec.rb
+++ b/spec/services/ci/create_pipeline_service_spec.rb
@@ -64,6 +64,18 @@ describe Ci::CreatePipelineService do
create(:merge_request, source_branch: 'master', target_branch: "branch_2", source_project: project)
end
+ context 'when related merge request is already merged' do
+ let!(:merged_merge_request) do
+ create(:merge_request, source_branch: 'master', target_branch: "branch_2", source_project: project, state: 'merged')
+ end
+
+ it 'does not schedule update head pipeline job' do
+ expect(UpdateHeadPipelineForMergeRequestWorker).not_to receive(:perform_async).with(merged_merge_request.id)
+
+ execute_service
+ end
+ end
+
context 'when the head pipeline sha equals merge request sha' do
it 'updates head pipeline of each merge request' do
merge_request_1
@@ -77,13 +89,13 @@ describe Ci::CreatePipelineService do
end
context 'when the head pipeline sha does not equal merge request sha' do
- it 'raises the ArgumentError error from worker and does not update the head piepeline of MRs' do
+ it 'does not update the head piepeline of MRs' do
merge_request_1
merge_request_2
allow_any_instance_of(Ci::Pipeline).to receive(:latest?).and_return(true)
- expect { execute_service(after: 'ae73cb07c9eeaf35924a10f713b364d32b2dd34f') }.to raise_error(ArgumentError)
+ expect { execute_service(after: 'ae73cb07c9eeaf35924a10f713b364d32b2dd34f') }.not_to raise_error
last_pipeline = Ci::Pipeline.last
@@ -518,5 +530,20 @@ describe Ci::CreatePipelineService do
end
end
end
+
+ context 'when pipeline is running for a tag' do
+ before do
+ config = YAML.dump(test: { script: 'test', only: ['branches'] },
+ deploy: { script: 'deploy', only: ['tags'] })
+
+ stub_ci_pipeline_yaml_file(config)
+ end
+
+ it 'creates a tagged pipeline' do
+ pipeline = execute_service(ref: 'v1.0.0')
+
+ expect(pipeline.tag?).to be true
+ end
+ end
end
end
diff --git a/spec/services/ci/register_job_service_spec.rb b/spec/services/ci/register_job_service_spec.rb
index 3ee59014b5b..97a563c1ce1 100644
--- a/spec/services/ci/register_job_service_spec.rb
+++ b/spec/services/ci/register_job_service_spec.rb
@@ -2,11 +2,11 @@ require 'spec_helper'
module Ci
describe RegisterJobService do
- let!(:project) { FactoryGirl.create :project, shared_runners_enabled: false }
- let!(:pipeline) { FactoryGirl.create :ci_pipeline, project: project }
- let!(:pending_job) { FactoryGirl.create :ci_build, pipeline: pipeline }
- let!(:shared_runner) { FactoryGirl.create(:ci_runner, is_shared: true) }
- let!(:specific_runner) { FactoryGirl.create(:ci_runner, is_shared: false) }
+ let!(:project) { FactoryBot.create :project, shared_runners_enabled: false }
+ let!(:pipeline) { FactoryBot.create :ci_pipeline, project: project }
+ let!(:pending_job) { FactoryBot.create :ci_build, pipeline: pipeline }
+ let!(:shared_runner) { FactoryBot.create(:ci_runner, is_shared: true) }
+ let!(:specific_runner) { FactoryBot.create(:ci_runner, is_shared: false) }
before do
specific_runner.assign_to(project)
@@ -74,11 +74,11 @@ module Ci
let!(:project3) { create :project, shared_runners_enabled: true }
let!(:pipeline3) { create :ci_pipeline, project: project3 }
let!(:build1_project1) { pending_job }
- let!(:build2_project1) { FactoryGirl.create :ci_build, pipeline: pipeline }
- let!(:build3_project1) { FactoryGirl.create :ci_build, pipeline: pipeline }
- let!(:build1_project2) { FactoryGirl.create :ci_build, pipeline: pipeline2 }
- let!(:build2_project2) { FactoryGirl.create :ci_build, pipeline: pipeline2 }
- let!(:build1_project3) { FactoryGirl.create :ci_build, pipeline: pipeline3 }
+ let!(:build2_project1) { FactoryBot.create :ci_build, pipeline: pipeline }
+ let!(:build3_project1) { FactoryBot.create :ci_build, pipeline: pipeline }
+ let!(:build1_project2) { FactoryBot.create :ci_build, pipeline: pipeline2 }
+ let!(:build2_project2) { FactoryBot.create :ci_build, pipeline: pipeline2 }
+ let!(:build1_project3) { FactoryBot.create :ci_build, pipeline: pipeline3 }
it 'prefers projects without builds first' do
# it gets for one build from each of the projects
@@ -287,9 +287,9 @@ module Ci
shared_examples 'validation is active' do
context 'when depended job has not been completed yet' do
- let!(:pre_stage_job) { create(:ci_build, :running, pipeline: pipeline, name: 'test', stage_idx: 0) }
+ let!(:pre_stage_job) { create(:ci_build, :manual, pipeline: pipeline, name: 'test', stage_idx: 0) }
- it_behaves_like 'not pick'
+ it { expect(subject).to eq(pending_job) }
end
context 'when artifacts of depended job has been expired' do
@@ -307,15 +307,27 @@ module Ci
it_behaves_like 'not pick'
end
+
+ context 'when job object is staled' do
+ let!(:pre_stage_job) { create(:ci_build, :success, :expired, pipeline: pipeline, name: 'test', stage_idx: 0) }
+
+ before do
+ allow_any_instance_of(Ci::Build).to receive(:drop!)
+ .and_raise(ActiveRecord::StaleObjectError.new(pending_job, :drop!))
+ end
+
+ it 'does not drop nor pick' do
+ expect(subject).to be_nil
+ end
+ end
end
shared_examples 'validation is not active' do
context 'when depended job has not been completed yet' do
- let!(:pre_stage_job) { create(:ci_build, :running, pipeline: pipeline, name: 'test', stage_idx: 0) }
+ let!(:pre_stage_job) { create(:ci_build, :manual, pipeline: pipeline, name: 'test', stage_idx: 0) }
it { expect(subject).to eq(pending_job) }
end
-
context 'when artifacts of depended job has been expired' do
let!(:pre_stage_job) { create(:ci_build, :success, :expired, pipeline: pipeline, name: 'test', stage_idx: 0) }
diff --git a/spec/services/ci/retry_build_service_spec.rb b/spec/services/ci/retry_build_service_spec.rb
index 9e02644ae0a..ea5d1f67b0c 100644
--- a/spec/services/ci/retry_build_service_spec.rb
+++ b/spec/services/ci/retry_build_service_spec.rb
@@ -41,7 +41,7 @@ describe Ci::RetryBuildService do
description: 'my-job', stage: 'test', pipeline: pipeline,
auto_canceled_by: create(:ci_empty_pipeline, project: project)) do |build|
##
- # TODO, workaround for FactoryGirl limitation when having both
+ # TODO, workaround for FactoryBot limitation when having both
# stage (text) and stage_id (integer) columns in the table.
build.stage_id = stage.id
end
diff --git a/spec/services/ci/stop_environments_service_spec.rb b/spec/services/ci/stop_environments_service_spec.rb
index e2a9ed27e87..3fc4e499b0c 100644
--- a/spec/services/ci/stop_environments_service_spec.rb
+++ b/spec/services/ci/stop_environments_service_spec.rb
@@ -15,7 +15,7 @@ describe Ci::StopEnvironmentsService do
context 'when user has permission to stop environment' do
before do
- project.team << [user, :developer]
+ project.add_developer(user)
end
context 'when environment is associated with removed branch' do
@@ -57,7 +57,7 @@ describe Ci::StopEnvironmentsService do
context 'when user does not have permission to stop environment' do
context 'when user has no access to manage deployments' do
before do
- project.team << [user, :guest]
+ project.add_guest(user)
end
it 'does not stop environment' do
@@ -86,7 +86,7 @@ describe Ci::StopEnvironmentsService do
context 'when user has permission to stop environments' do
before do
- project.team << [user, :master]
+ project.add_master(user)
end
it 'does not stop environment' do
diff --git a/spec/services/clusters/applications/check_installation_progress_service_spec.rb b/spec/services/clusters/applications/check_installation_progress_service_spec.rb
index 75fc05d36e9..6894c1797b0 100644
--- a/spec/services/clusters/applications/check_installation_progress_service_spec.rb
+++ b/spec/services/clusters/applications/check_installation_progress_service_spec.rb
@@ -3,7 +3,7 @@ require 'spec_helper'
describe Clusters::Applications::CheckInstallationProgressService do
RESCHEDULE_PHASES = Gitlab::Kubernetes::Pod::PHASES - [Gitlab::Kubernetes::Pod::SUCCEEDED, Gitlab::Kubernetes::Pod::FAILED].freeze
- let(:application) { create(:cluster_applications_helm, :installing) }
+ let(:application) { create(:clusters_applications_helm, :installing) }
let(:service) { described_class.new(application) }
let(:phase) { Gitlab::Kubernetes::Pod::UNKNOWN }
let(:errors) { nil }
@@ -33,7 +33,7 @@ describe Clusters::Applications::CheckInstallationProgressService do
end
context 'when timeouted' do
- let(:application) { create(:cluster_applications_helm, :timeouted) }
+ let(:application) { create(:clusters_applications_helm, :timeouted) }
it_behaves_like 'a terminated installation'
diff --git a/spec/services/clusters/applications/install_service_spec.rb b/spec/services/clusters/applications/install_service_spec.rb
index 054a49ffedf..ad175226e92 100644
--- a/spec/services/clusters/applications/install_service_spec.rb
+++ b/spec/services/clusters/applications/install_service_spec.rb
@@ -2,17 +2,19 @@ require 'spec_helper'
describe Clusters::Applications::InstallService do
describe '#execute' do
- let(:application) { create(:cluster_applications_helm, :scheduled) }
+ let(:application) { create(:clusters_applications_helm, :scheduled) }
+ let!(:install_command) { application.install_command }
let(:service) { described_class.new(application) }
- let(:helm_client) { instance_double(Gitlab::Kubernetes::Helm) }
+ let(:helm_client) { instance_double(Gitlab::Kubernetes::Helm::Api) }
before do
+ allow(service).to receive(:install_command).and_return(install_command)
allow(service).to receive(:helm_api).and_return(helm_client)
end
context 'when there are no errors' do
before do
- expect(helm_client).to receive(:install).with(application.install_command)
+ expect(helm_client).to receive(:install).with(install_command)
allow(ClusterWaitForAppInstallationWorker).to receive(:perform_in).and_return(nil)
end
@@ -33,7 +35,7 @@ describe Clusters::Applications::InstallService do
context 'when k8s cluster communication fails' do
before do
error = KubeException.new(500, 'system failure', nil)
- expect(helm_client).to receive(:install).with(application.install_command).and_raise(error)
+ expect(helm_client).to receive(:install).with(install_command).and_raise(error)
end
it 'make the application errored' do
@@ -45,7 +47,7 @@ describe Clusters::Applications::InstallService do
end
context 'when application cannot be persisted' do
- let(:application) { build(:cluster_applications_helm, :scheduled) }
+ let(:application) { build(:clusters_applications_helm, :scheduled) }
it 'make the application errored' do
expect(application).to receive(:make_installing!).once.and_raise(ActiveRecord::RecordInvalid)
diff --git a/spec/services/clusters/applications/schedule_installation_service_spec.rb b/spec/services/clusters/applications/schedule_installation_service_spec.rb
index 047a6e44dab..473dbcbb692 100644
--- a/spec/services/clusters/applications/schedule_installation_service_spec.rb
+++ b/spec/services/clusters/applications/schedule_installation_service_spec.rb
@@ -34,7 +34,7 @@ describe Clusters::Applications::ScheduleInstallationService do
end
context 'when installation is already in progress' do
- let(:application) { create(:cluster_applications_helm, :installing) }
+ let(:application) { create(:clusters_applications_helm, :installing) }
let(:cluster) { application.cluster }
it_behaves_like 'a failing service'
diff --git a/spec/services/create_deployment_service_spec.rb b/spec/services/create_deployment_service_spec.rb
index 08267d6e6a0..b9bfbb11511 100644
--- a/spec/services/create_deployment_service_spec.rb
+++ b/spec/services/create_deployment_service_spec.rb
@@ -266,7 +266,7 @@ describe CreateDeploymentService do
context "while updating the 'first_deployed_to_production_at' time" do
before do
- merge_request.mark_as_merged
+ merge_request.metrics.update!(merged_at: Time.now)
end
context "for merge requests merged before the current deploy" do
diff --git a/spec/services/delete_branch_service_spec.rb b/spec/services/delete_branch_service_spec.rb
index 19855c9bee2..9c9fba030e7 100644
--- a/spec/services/delete_branch_service_spec.rb
+++ b/spec/services/delete_branch_service_spec.rb
@@ -9,7 +9,7 @@ describe DeleteBranchService do
describe '#execute' do
context 'when user has access to push to repository' do
before do
- project.team << [user, :developer]
+ project.add_developer(user)
end
it 'removes the branch' do
diff --git a/spec/services/discussions/resolve_service_spec.rb b/spec/services/discussions/resolve_service_spec.rb
index ab8df7b74cd..3895a0b3aea 100644
--- a/spec/services/discussions/resolve_service_spec.rb
+++ b/spec/services/discussions/resolve_service_spec.rb
@@ -9,7 +9,7 @@ describe Discussions::ResolveService do
let(:service) { described_class.new(discussion.noteable.project, user, merge_request: merge_request) }
before do
- project.team << [user, :master]
+ project.add_master(user)
end
it "doesn't resolve discussions the user can't resolve" do
diff --git a/spec/services/files/delete_service_spec.rb b/spec/services/files/delete_service_spec.rb
new file mode 100644
index 00000000000..ace5f293097
--- /dev/null
+++ b/spec/services/files/delete_service_spec.rb
@@ -0,0 +1,64 @@
+require "spec_helper"
+
+describe Files::DeleteService do
+ subject { described_class.new(project, user, commit_params) }
+
+ let(:project) { create(:project, :repository) }
+ let(:user) { create(:user) }
+ let(:file_path) { 'files/ruby/popen.rb' }
+ let(:branch_name) { project.default_branch }
+ let(:last_commit_sha) { nil }
+
+ let(:commit_params) do
+ {
+ file_path: file_path,
+ commit_message: "Delete File",
+ last_commit_sha: last_commit_sha,
+ start_project: project,
+ start_branch: project.default_branch,
+ branch_name: branch_name
+ }
+ end
+
+ shared_examples 'successfully deletes the file' do
+ it 'returns a hash with the :success status' do
+ results = subject.execute
+
+ expect(results[:status]).to match(:success)
+ end
+
+ it 'deletes the file' do
+ subject.execute
+
+ blob = project.repository.blob_at_branch(project.default_branch, file_path)
+
+ expect(blob).to be_nil
+ end
+ end
+
+ before do
+ project.add_master(user)
+ end
+
+ describe "#execute" do
+ context "when the file's last commit sha does not match the supplied last_commit_sha" do
+ let(:last_commit_sha) { "foo" }
+
+ it "returns a hash with the correct error message and a :error status " do
+ expect { subject.execute }
+ .to raise_error(Files::UpdateService::FileChangedError,
+ "You are attempting to delete a file that has been previously updated.")
+ end
+ end
+
+ context "when the file's last commit sha does match the supplied last_commit_sha" do
+ let(:last_commit_sha) { Gitlab::Git::Commit.last_for_path(project.repository, project.default_branch, file_path).sha }
+
+ it_behaves_like 'successfully deletes the file'
+ end
+
+ context "when the last_commit_sha is not supplied" do
+ it_behaves_like 'successfully deletes the file'
+ end
+ end
+end
diff --git a/spec/services/files/multi_service_spec.rb b/spec/services/files/multi_service_spec.rb
new file mode 100644
index 00000000000..b9971776b33
--- /dev/null
+++ b/spec/services/files/multi_service_spec.rb
@@ -0,0 +1,144 @@
+require "spec_helper"
+
+describe Files::MultiService do
+ subject { described_class.new(project, user, commit_params) }
+
+ let(:project) { create(:project, :repository) }
+ let(:user) { create(:user) }
+ let(:branch_name) { project.default_branch }
+ let(:original_file_path) { 'files/ruby/popen.rb' }
+ let(:new_file_path) { 'files/ruby/popen.rb' }
+ let(:action) { 'update' }
+
+ let!(:original_commit_id) do
+ Gitlab::Git::Commit.last_for_path(project.repository, branch_name, original_file_path).sha
+ end
+
+ let(:actions) do
+ [
+ {
+ action: action,
+ file_path: new_file_path,
+ previous_path: original_file_path,
+ content: 'New content',
+ last_commit_id: original_commit_id
+ }
+ ]
+ end
+
+ let(:commit_params) do
+ {
+ commit_message: "Update File",
+ branch_name: branch_name,
+ start_branch: branch_name,
+ actions: actions
+ }
+ end
+
+ before do
+ project.add_master(user)
+ end
+
+ describe '#execute' do
+ context 'with a valid action' do
+ it 'returns a hash with the :success status' do
+ results = subject.execute
+
+ expect(results[:status]).to eq(:success)
+ end
+ end
+
+ context 'with an invalid action' do
+ let(:action) { 'rename' }
+
+ it 'returns a hash with the :error status' do
+ results = subject.execute
+
+ expect(results[:status]).to eq(:error)
+ expect(results[:message]).to match(/Unknown action/)
+ end
+ end
+
+ describe 'Updating files' do
+ context 'when the file has been previously updated' do
+ before do
+ update_file(original_file_path)
+ end
+
+ it 'rejects the commit' do
+ results = subject.execute
+
+ expect(results[:status]).to eq(:error)
+ expect(results[:message]).to match(new_file_path)
+ end
+ end
+
+ context 'when the file have not been modified' do
+ it 'accepts the commit' do
+ results = subject.execute
+
+ expect(results[:status]).to eq(:success)
+ end
+ end
+ end
+
+ context 'when moving a file' do
+ let(:action) { 'move' }
+ let(:new_file_path) { 'files/ruby/new_popen.rb' }
+
+ context 'when original file has been updated' do
+ before do
+ update_file(original_file_path)
+ end
+
+ it 'rejects the commit' do
+ results = subject.execute
+
+ expect(results[:status]).to eq(:error)
+ expect(results[:message]).to match(original_file_path)
+ end
+ end
+
+ context 'when original file have not been updated' do
+ it 'moves the file' do
+ results = subject.execute
+ blob = project.repository.blob_at_branch(branch_name, new_file_path)
+
+ expect(results[:status]).to eq(:success)
+ expect(blob).to be_present
+ end
+ end
+ end
+
+ context 'when file status validation is skipped' do
+ let(:action) { 'create' }
+ let(:new_file_path) { 'files/ruby/new_file.rb' }
+
+ it 'does not check the last commit' do
+ expect(Gitlab::Git::Commit).not_to receive(:last_for_path)
+
+ subject.execute
+ end
+
+ it 'creates the file' do
+ subject.execute
+
+ blob = project.repository.blob_at_branch(branch_name, new_file_path)
+
+ expect(blob).to be_present
+ end
+ end
+ end
+
+ def update_file(path)
+ params = {
+ file_path: path,
+ start_branch: branch_name,
+ branch_name: branch_name,
+ commit_message: 'Update file',
+ file_content: 'New content'
+ }
+
+ Files::UpdateService.new(project, user, params).execute
+ end
+end
diff --git a/spec/services/files/update_service_spec.rb b/spec/services/files/update_service_spec.rb
index 2b4f8cd42ee..43b0c9a63a9 100644
--- a/spec/services/files/update_service_spec.rb
+++ b/spec/services/files/update_service_spec.rb
@@ -24,7 +24,7 @@ describe Files::UpdateService do
end
before do
- project.team << [user, :master]
+ project.add_master(user)
end
describe "#execute" do
diff --git a/spec/services/git_push_service_spec.rb b/spec/services/git_push_service_spec.rb
index cc3d4e7da49..26fdf8d4b24 100644
--- a/spec/services/git_push_service_spec.rb
+++ b/spec/services/git_push_service_spec.rb
@@ -11,7 +11,7 @@ describe GitPushService, services: true do
let(:ref) { 'refs/heads/master' }
before do
- project.team << [user, :master]
+ project.add_master(user)
end
describe 'Push branches' do
@@ -266,8 +266,8 @@ describe GitPushService, services: true do
let(:commit) { project.commit }
before do
- project.team << [commit_author, :developer]
- project.team << [user, :developer]
+ project.add_developer(commit_author)
+ project.add_developer(user)
allow(commit).to receive_messages(
safe_message: "this commit \n mentions #{issue.to_reference}",
@@ -323,8 +323,8 @@ describe GitPushService, services: true do
let(:commit_time) { Time.now }
before do
- project.team << [commit_author, :developer]
- project.team << [user, :developer]
+ project.add_developer(commit_author)
+ project.add_developer(user)
allow(commit).to receive_messages(
safe_message: "this commit \n mentions #{issue.to_reference}",
@@ -376,7 +376,7 @@ describe GitPushService, services: true do
allow_any_instance_of(ProcessCommitWorker).to receive(:build_commit)
.and_return(closing_commit)
- project.team << [commit_author, :master]
+ project.add_master(commit_author)
end
context "to default branches" do
diff --git a/spec/services/git_tag_push_service_spec.rb b/spec/services/git_tag_push_service_spec.rb
index 05695aa8188..33405d7a7ec 100644
--- a/spec/services/git_tag_push_service_spec.rb
+++ b/spec/services/git_tag_push_service_spec.rb
@@ -35,7 +35,7 @@ describe GitTagPushService do
before do
stub_ci_pipeline_to_return_yaml_file
- project.team << [user, :developer]
+ project.add_developer(user)
end
it "creates a new pipeline" do
diff --git a/spec/services/issuable/bulk_update_service_spec.rb b/spec/services/issuable/bulk_update_service_spec.rb
index bdaab88d673..53c85f73cde 100644
--- a/spec/services/issuable/bulk_update_service_spec.rb
+++ b/spec/services/issuable/bulk_update_service_spec.rb
@@ -54,7 +54,7 @@ describe Issuable::BulkUpdateService do
context 'when the new assignee ID is a valid user' do
it 'succeeds' do
new_assignee = create(:user)
- project.team << [new_assignee, :developer]
+ project.add_developer(new_assignee)
result = bulk_update(merge_request, assignee_id: new_assignee.id)
@@ -64,7 +64,7 @@ describe Issuable::BulkUpdateService do
it 'updates the assignee to the user ID passed' do
assignee = create(:user)
- project.team << [assignee, :developer]
+ project.add_developer(assignee)
expect { bulk_update(merge_request, assignee_id: assignee.id) }
.to change { merge_request.reload.assignee }.from(user).to(assignee)
@@ -92,7 +92,7 @@ describe Issuable::BulkUpdateService do
context 'when the new assignee ID is a valid user' do
it 'succeeds' do
new_assignee = create(:user)
- project.team << [new_assignee, :developer]
+ project.add_developer(new_assignee)
result = bulk_update(issue, assignee_ids: [new_assignee.id])
@@ -102,7 +102,7 @@ describe Issuable::BulkUpdateService do
it 'updates the assignee to the user ID passed' do
assignee = create(:user)
- project.team << [assignee, :developer]
+ project.add_developer(assignee)
expect { bulk_update(issue, assignee_ids: [assignee.id]) }
.to change { issue.reload.assignees.first }.from(user).to(assignee)
end
diff --git a/spec/services/issuable/destroy_service_spec.rb b/spec/services/issuable/destroy_service_spec.rb
index d74d98c6079..0a3647a814f 100644
--- a/spec/services/issuable/destroy_service_spec.rb
+++ b/spec/services/issuable/destroy_service_spec.rb
@@ -2,7 +2,7 @@ require 'spec_helper'
describe Issuable::DestroyService do
let(:user) { create(:user) }
- let(:project) { create(:project) }
+ let(:project) { create(:project, :public) }
subject(:service) { described_class.new(project, user) }
@@ -19,6 +19,13 @@ describe Issuable::DestroyService do
service.execute(issue)
end
+
+ it 'updates the todo caches for users with todos on the issue' do
+ create(:todo, target: issue, user: user, author: user, project: project)
+
+ expect { service.execute(issue) }
+ .to change { user.todos_pending_count }.from(1).to(0)
+ end
end
context 'when issuable is a merge request' do
@@ -33,6 +40,13 @@ describe Issuable::DestroyService do
service.execute(merge_request)
end
+
+ it 'updates the todo caches for users with todos on the merge request' do
+ create(:todo, target: merge_request, user: user, author: user, project: project)
+
+ expect { service.execute(merge_request) }
+ .to change { user.todos_pending_count }.from(1).to(0)
+ end
end
end
end
diff --git a/spec/services/issues/build_service_spec.rb b/spec/services/issues/build_service_spec.rb
index 03f76bd428d..248e7d5a389 100644
--- a/spec/services/issues/build_service_spec.rb
+++ b/spec/services/issues/build_service_spec.rb
@@ -5,7 +5,7 @@ describe Issues::BuildService do
let(:user) { create(:user) }
before do
- project.team << [user, :developer]
+ project.add_developer(user)
end
context 'for a single discussion' do
diff --git a/spec/services/issues/close_service_spec.rb b/spec/services/issues/close_service_spec.rb
index 5c27e8fd561..8897a64a138 100644
--- a/spec/services/issues/close_service_spec.rb
+++ b/spec/services/issues/close_service_spec.rb
@@ -9,9 +9,9 @@ describe Issues::CloseService do
let!(:todo) { create(:todo, :assigned, user: user, project: project, target: issue, author: user2) }
before do
- project.team << [user, :master]
- project.team << [user2, :developer]
- project.team << [guest, :guest]
+ project.add_master(user)
+ project.add_developer(user2)
+ project.add_guest(guest)
end
describe '#execute' do
diff --git a/spec/services/issues/create_service_spec.rb b/spec/services/issues/create_service_spec.rb
index d86da244520..79bcdc41fb0 100644
--- a/spec/services/issues/create_service_spec.rb
+++ b/spec/services/issues/create_service_spec.rb
@@ -13,8 +13,8 @@ describe Issues::CreateService do
let(:labels) { create_pair(:label, project: project) }
before do
- project.team << [user, :master]
- project.team << [assignee, :master]
+ project.add_master(user)
+ project.add_master(assignee)
end
let(:opts) do
@@ -43,7 +43,7 @@ describe Issues::CreateService do
let(:guest) { create(:user) }
before do
- project.team << [guest, :guest]
+ project.add_guest(guest)
end
it 'filters out params that cannot be set without the :admin_issue permission' do
@@ -130,7 +130,7 @@ describe Issues::CreateService do
end
it 'invalidates open issues counter for assignees when issue is assigned' do
- project.team << [assignee, :master]
+ project.add_master(assignee)
described_class.new(project, user, opts).execute
@@ -160,7 +160,7 @@ describe Issues::CreateService do
context 'issue create service' do
context 'assignees' do
before do
- project.team << [user, :master]
+ project.add_master(user)
end
it 'removes assignee when user id is invalid' do
@@ -180,7 +180,7 @@ describe Issues::CreateService do
end
it 'saves assignee when user id is valid' do
- project.team << [assignee, :master]
+ project.add_master(assignee)
opts = { title: 'Title', description: 'Description', assignee_ids: [assignee.id] }
issue = described_class.new(project, user, opts).execute
@@ -224,8 +224,8 @@ describe Issues::CreateService do
end
before do
- project.team << [user, :master]
- project.team << [assignee, :master]
+ project.add_master(user)
+ project.add_master(assignee)
end
it 'assigns and sets milestone to issuable from command' do
@@ -242,7 +242,7 @@ describe Issues::CreateService do
let(:project) { merge_request.source_project }
before do
- project.team << [user, :master]
+ project.add_master(user)
end
describe 'for a single discussion' do
diff --git a/spec/services/issues/move_service_spec.rb b/spec/services/issues/move_service_spec.rb
index f2b35a8fadf..53ea88332fb 100644
--- a/spec/services/issues/move_service_spec.rb
+++ b/spec/services/issues/move_service_spec.rb
@@ -20,8 +20,8 @@ describe Issues::MoveService do
shared_context 'user can move issue' do
before do
- old_project.team << [user, :reporter]
- new_project.team << [user, :reporter]
+ old_project.add_reporter(user)
+ new_project.add_reporter(user)
labels = Array.new(2) { |x| "label%d" % (x + 1) }
@@ -289,6 +289,18 @@ describe Issues::MoveService do
.to raise_error(StandardError, /Cannot move issue/)
end
end
+
+ context 'project issue hooks' do
+ let(:hook) { create(:project_hook, project: old_project, issues_events: true) }
+
+ it 'executes project issue hooks' do
+ # Ideally, we'd test that `WebHookWorker.jobs.size` increased by 1,
+ # but since the entire spec run takes place in a transaction, we never
+ # actually get to the `after_commit` hook that queues these jobs.
+ expect { move_service.execute(old_issue, new_project) }
+ .not_to raise_error # Sidekiq::Worker::EnqueueFromTransactionError
+ end
+ end
end
describe 'move permissions' do
@@ -301,7 +313,7 @@ describe Issues::MoveService do
context 'user is reporter only in new project' do
before do
- new_project.team << [user, :reporter]
+ new_project.add_reporter(user)
end
it { expect { move }.to raise_error(StandardError, /permissions/) }
@@ -309,7 +321,7 @@ describe Issues::MoveService do
context 'user is reporter only in old project' do
before do
- old_project.team << [user, :reporter]
+ old_project.add_reporter(user)
end
it { expect { move }.to raise_error(StandardError, /permissions/) }
@@ -317,8 +329,8 @@ describe Issues::MoveService do
context 'user is reporter in one project and guest in another' do
before do
- new_project.team << [user, :guest]
- old_project.team << [user, :reporter]
+ new_project.add_guest(user)
+ old_project.add_reporter(user)
end
it { expect { move }.to raise_error(StandardError, /permissions/) }
@@ -346,8 +358,8 @@ describe Issues::MoveService do
context 'movable issue with no assigned labels' do
before do
- old_project.team << [user, :reporter]
- new_project.team << [user, :reporter]
+ old_project.add_reporter(user)
+ new_project.add_reporter(user)
labels = Array.new(2) { |x| "label%d" % (x + 1) }
diff --git a/spec/services/issues/reopen_service_spec.rb b/spec/services/issues/reopen_service_spec.rb
index 48fc98b3b2f..42e5d544f4c 100644
--- a/spec/services/issues/reopen_service_spec.rb
+++ b/spec/services/issues/reopen_service_spec.rb
@@ -8,7 +8,7 @@ describe Issues::ReopenService do
context 'when user is not authorized to reopen issue' do
before do
guest = create(:user)
- project.team << [guest, :guest]
+ project.add_guest(guest)
perform_enqueued_jobs do
described_class.new(project, guest).execute(issue)
@@ -24,7 +24,7 @@ describe Issues::ReopenService do
let(:user) { create(:user) }
before do
- project.team << [user, :master]
+ project.add_master(user)
end
it 'invalidates counter cache for assignees' do
diff --git a/spec/services/issues/resolve_discussions_spec.rb b/spec/services/issues/resolve_discussions_spec.rb
index 67a86c50fc1..13accc6ae1b 100644
--- a/spec/services/issues/resolve_discussions_spec.rb
+++ b/spec/services/issues/resolve_discussions_spec.rb
@@ -14,7 +14,7 @@ describe Issues::ResolveDiscussions do
let(:user) { create(:user) }
before do
- project.team << [user, :developer]
+ project.add_developer(user)
end
describe "for resolving discussions" do
diff --git a/spec/services/issues/update_service_spec.rb b/spec/services/issues/update_service_spec.rb
index f07b81e842a..1cb6f2e097f 100644
--- a/spec/services/issues/update_service_spec.rb
+++ b/spec/services/issues/update_service_spec.rb
@@ -17,9 +17,9 @@ describe Issues::UpdateService, :mailer do
end
before do
- project.team << [user, :master]
- project.team << [user2, :developer]
- project.team << [user3, :developer]
+ project.add_master(user)
+ project.add_developer(user2)
+ project.add_developer(user3)
end
describe 'execute' do
@@ -99,7 +99,7 @@ describe Issues::UpdateService, :mailer do
context 'when current user cannot admin issues in the project' do
let(:guest) { create(:user) }
before do
- project.team << [guest, :guest]
+ project.add_guest(guest)
end
it 'filters out params that cannot be set without the :admin_issue permission' do
@@ -318,7 +318,7 @@ describe Issues::UpdateService, :mailer do
let!(:subscriber) do
create(:user).tap do |u|
label.toggle_subscription(u, project)
- project.team << [u, :developer]
+ project.add_developer(u)
end
end
@@ -556,7 +556,7 @@ describe Issues::UpdateService, :mailer do
context 'valid project' do
before do
- target_project.team << [user, :master]
+ target_project.add_master(user)
end
it 'calls the move service with the proper issue and project' do
diff --git a/spec/services/labels/find_or_create_service_spec.rb b/spec/services/labels/find_or_create_service_spec.rb
index a781fbc7f7d..78aa5d442e7 100644
--- a/spec/services/labels/find_or_create_service_spec.rb
+++ b/spec/services/labels/find_or_create_service_spec.rb
@@ -17,7 +17,7 @@ describe Labels::FindOrCreateService do
let(:user) { create(:user) }
subject(:service) { described_class.new(user, project, params) }
before do
- project.team << [user, :developer]
+ project.add_developer(user)
end
context 'when label does not exist at group level' do
diff --git a/spec/services/members/approve_access_request_service_spec.rb b/spec/services/members/approve_access_request_service_spec.rb
index 302c488d6c6..b3018169a1c 100644
--- a/spec/services/members/approve_access_request_service_spec.rb
+++ b/spec/services/members/approve_access_request_service_spec.rb
@@ -123,7 +123,7 @@ describe Members::ApproveAccessRequestService do
context 'when current user can approve access request to the project' do
before do
- project.team << [user, :master]
+ project.add_master(user)
group.add_owner(user)
end
diff --git a/spec/services/members/authorized_destroy_service_spec.rb b/spec/services/members/authorized_destroy_service_spec.rb
index 2d04d824180..757c45708b9 100644
--- a/spec/services/members/authorized_destroy_service_spec.rb
+++ b/spec/services/members/authorized_destroy_service_spec.rb
@@ -13,7 +13,7 @@ describe Members::AuthorizedDestroyService do
context 'Invited users' do
# Regression spec for issue: https://gitlab.com/gitlab-org/gitlab-ce/issues/32504
it 'destroys invited project member' do
- project.team << [member_user, :developer]
+ project.add_developer(member_user)
member = create :project_member, :invited, project: project
@@ -45,14 +45,14 @@ describe Members::AuthorizedDestroyService do
expect { described_class.new(member, member_user).execute }
.to change { number_of_assigned_issuables(member_user) }.from(4).to(2)
- expect(issue.reload.assignee_id).to be_nil
+ expect(issue.reload.assignee_ids).to be_empty
expect(merge_request.reload.assignee_id).to be_nil
end
end
context 'Project member' do
it "unassigns issues and merge requests" do
- project.team << [member_user, :developer]
+ project.add_developer(member_user)
create :issue, project: project, assignees: [member_user]
create :merge_request, target_project: project, source_project: project, assignee: member_user
diff --git a/spec/services/members/create_service_spec.rb b/spec/services/members/create_service_spec.rb
index 2a793e0eb4d..6bd4718e780 100644
--- a/spec/services/members/create_service_spec.rb
+++ b/spec/services/members/create_service_spec.rb
@@ -6,7 +6,7 @@ describe Members::CreateService do
let(:project_user) { create(:user) }
before do
- project.team << [user, :master]
+ project.add_master(user)
end
it 'adds user to members' do
diff --git a/spec/services/members/destroy_service_spec.rb b/spec/services/members/destroy_service_spec.rb
index 72f5e27180d..91152df3ad9 100644
--- a/spec/services/members/destroy_service_spec.rb
+++ b/spec/services/members/destroy_service_spec.rb
@@ -71,7 +71,7 @@ describe Members::DestroyService do
context 'when a member is found' do
before do
- project.team << [member_user, :developer]
+ project.add_developer(member_user)
group.add_developer(member_user)
end
let(:params) { { user_id: member_user.id } }
@@ -88,7 +88,7 @@ describe Members::DestroyService do
context 'when current user can destroy the given member' do
before do
- project.team << [user, :master]
+ project.add_master(user)
group.add_owner(user)
end
diff --git a/spec/services/merge_requests/assign_issues_service_spec.rb b/spec/services/merge_requests/assign_issues_service_spec.rb
index fcbe0e5985f..bda6383a346 100644
--- a/spec/services/merge_requests/assign_issues_service_spec.rb
+++ b/spec/services/merge_requests/assign_issues_service_spec.rb
@@ -8,7 +8,7 @@ describe MergeRequests::AssignIssuesService do
let(:service) { described_class.new(project, user, merge_request: merge_request) }
before do
- project.team << [user, :developer]
+ project.add_developer(user)
end
it 'finds unassigned issues fixed in merge request' do
diff --git a/spec/services/merge_requests/build_service_spec.rb b/spec/services/merge_requests/build_service_spec.rb
index b5c92e681fb..a9605c6e4c6 100644
--- a/spec/services/merge_requests/build_service_spec.rb
+++ b/spec/services/merge_requests/build_service_spec.rb
@@ -28,7 +28,7 @@ describe MergeRequests::BuildService do
end
before do
- project.team << [user, :guest]
+ project.add_guest(user)
end
def stub_compare
diff --git a/spec/services/merge_requests/close_service_spec.rb b/spec/services/merge_requests/close_service_spec.rb
index b3886987316..4d12de3ecce 100644
--- a/spec/services/merge_requests/close_service_spec.rb
+++ b/spec/services/merge_requests/close_service_spec.rb
@@ -9,9 +9,9 @@ describe MergeRequests::CloseService do
let!(:todo) { create(:todo, :assigned, user: user, project: project, target: merge_request, author: user2) }
before do
- project.team << [user, :master]
- project.team << [user2, :developer]
- project.team << [guest, :guest]
+ project.add_master(user)
+ project.add_developer(user2)
+ project.add_guest(guest)
end
describe '#execute' do
@@ -52,6 +52,19 @@ describe MergeRequests::CloseService do
end
end
+ it 'updates metrics' do
+ metrics = merge_request.metrics
+ metrics_service = double(MergeRequestMetricsService)
+ allow(MergeRequestMetricsService)
+ .to receive(:new)
+ .with(metrics)
+ .and_return(metrics_service)
+
+ expect(metrics_service).to receive(:close)
+
+ described_class.new(project, user, {}).execute(merge_request)
+ end
+
it 'refreshes the number of open merge requests for a valid MR', :use_clean_rails_memory_store_caching do
service = described_class.new(project, user, {})
diff --git a/spec/services/merge_requests/conflicts/list_service_spec.rb b/spec/services/merge_requests/conflicts/list_service_spec.rb
index 0b32c51a16f..6cadcd438c3 100644
--- a/spec/services/merge_requests/conflicts/list_service_spec.rb
+++ b/spec/services/merge_requests/conflicts/list_service_spec.rb
@@ -32,14 +32,6 @@ describe MergeRequests::Conflicts::ListService do
expect(conflicts_service(merge_request).can_be_resolved_in_ui?).to be_falsey
end
- it 'returns a falsey value when the MR has a missing ref after a force push' do
- merge_request = create_merge_request('conflict-resolvable')
- service = conflicts_service(merge_request)
- allow_any_instance_of(Rugged::Repository).to receive(:merge_commits).and_raise(Rugged::OdbError)
-
- expect(service.can_be_resolved_in_ui?).to be_falsey
- end
-
it 'returns a falsey value when the MR does not support new diff notes' do
merge_request = create_merge_request('conflict-resolvable')
merge_request.merge_request_diff.update_attributes(start_commit_sha: nil)
@@ -76,5 +68,23 @@ describe MergeRequests::Conflicts::ListService do
expect(conflicts_service(merge_request).can_be_resolved_in_ui?).to be_truthy
end
+
+ it 'returns a falsey value when the MR has a missing ref after a force push' do
+ merge_request = create_merge_request('conflict-resolvable')
+ service = conflicts_service(merge_request)
+ allow_any_instance_of(Gitlab::GitalyClient::ConflictsService).to receive(:list_conflict_files).and_raise(GRPC::Unknown)
+
+ expect(service.can_be_resolved_in_ui?).to be_falsey
+ end
+
+ context 'with gitaly disabled', :skip_gitaly_mock do
+ it 'returns a falsey value when the MR has a missing ref after a force push' do
+ merge_request = create_merge_request('conflict-resolvable')
+ service = conflicts_service(merge_request)
+ allow_any_instance_of(Rugged::Repository).to receive(:merge_commits).and_raise(Rugged::OdbError)
+
+ expect(service.can_be_resolved_in_ui?).to be_falsey
+ end
+ end
end
end
diff --git a/spec/services/merge_requests/conflicts/resolve_service_spec.rb b/spec/services/merge_requests/conflicts/resolve_service_spec.rb
index 5376083e7f5..cff09237005 100644
--- a/spec/services/merge_requests/conflicts/resolve_service_spec.rb
+++ b/spec/services/merge_requests/conflicts/resolve_service_spec.rb
@@ -111,15 +111,6 @@ describe MergeRequests::Conflicts::ResolveService do
described_class.new(merge_request_from_fork).execute(user, params)
end
- it 'gets conflicts from the source project' do
- # REFACTOR NOTE: We used to test that `project.repository.rugged` wasn't
- # used in this case, but since the refactor, for simplification,
- # we always use that repository for read only operations.
- expect(forked_project.repository.rugged).to receive(:merge_commits).and_call_original
-
- subject
- end
-
it 'creates a commit with the message' do
subject
@@ -132,6 +123,17 @@ describe MergeRequests::Conflicts::ResolveService do
expect(merge_request_from_fork.source_branch_head.parents.map(&:id))
.to eq(['404fa3fc7c2c9b5dacff102f353bdf55b1be2813', target_head])
end
+
+ context 'when gitaly is disabled', :skip_gitaly_mock do
+ it 'gets conflicts from the source project' do
+ # REFACTOR NOTE: We used to test that `project.repository.rugged` wasn't
+ # used in this case, but since the refactor, for simplification,
+ # we always use that repository for read only operations.
+ expect(forked_project.repository.rugged).to receive(:merge_commits).and_call_original
+
+ subject
+ end
+ end
end
end
@@ -213,7 +215,7 @@ describe MergeRequests::Conflicts::ResolveService do
MergeRequests::Conflicts::ListService.new(merge_request).conflicts.resolver
end
let(:regex_conflict) do
- resolver.conflict_for_path('files/ruby/regex.rb', 'files/ruby/regex.rb')
+ resolver.conflict_for_path(resolver.conflicts, 'files/ruby/regex.rb', 'files/ruby/regex.rb')
end
let(:invalid_params) do
diff --git a/spec/services/merge_requests/create_from_issue_service_spec.rb b/spec/services/merge_requests/create_from_issue_service_spec.rb
index a7ab389b357..623b182b205 100644
--- a/spec/services/merge_requests/create_from_issue_service_spec.rb
+++ b/spec/services/merge_requests/create_from_issue_service_spec.rb
@@ -100,5 +100,17 @@ describe MergeRequests::CreateFromIssueService do
expect(result[:merge_request].target_branch).to eq(project.default_branch)
end
+
+ it 'executes quick actions if the build service sets them in the description' do
+ allow(service).to receive(:merge_request).and_wrap_original do |m, *args|
+ m.call(*args).tap do |merge_request|
+ merge_request.description = "/assign #{user.to_reference}"
+ end
+ end
+
+ result = service.execute
+
+ expect(result[:merge_request].assignee).to eq(user)
+ end
end
end
diff --git a/spec/services/merge_requests/create_service_spec.rb b/spec/services/merge_requests/create_service_spec.rb
index a047f891ab2..dd8c803a2f7 100644
--- a/spec/services/merge_requests/create_service_spec.rb
+++ b/spec/services/merge_requests/create_service_spec.rb
@@ -21,8 +21,8 @@ describe MergeRequests::CreateService do
let(:merge_request) { service.execute }
before do
- project.team << [user, :master]
- project.team << [assignee, :developer]
+ project.add_master(user)
+ project.add_developer(assignee)
allow(service).to receive(:execute_hooks)
end
@@ -148,8 +148,8 @@ describe MergeRequests::CreateService do
end
before do
- project.team << [user, :master]
- project.team << [assignee, :master]
+ project.add_master(user)
+ project.add_master(assignee)
end
it 'assigns and sets milestone to issuable from command' do
@@ -165,7 +165,7 @@ describe MergeRequests::CreateService do
let(:assignee) { create(:user) }
before do
- project.team << [user, :master]
+ project.add_master(user)
end
it 'removes assignee_id when user id is invalid' do
@@ -185,7 +185,7 @@ describe MergeRequests::CreateService do
end
it 'saves assignee when user id is valid' do
- project.team << [assignee, :master]
+ project.add_master(assignee)
opts = { title: 'Title', description: 'Description', assignee_id: assignee.id }
merge_request = described_class.new(project, user, opts).execute
@@ -205,7 +205,7 @@ describe MergeRequests::CreateService do
end
it 'invalidates open merge request counter for assignees when merge request is assigned' do
- project.team << [assignee, :master]
+ project.add_master(assignee)
described_class.new(project, user, opts).execute
@@ -249,8 +249,8 @@ describe MergeRequests::CreateService do
end
before do
- project.team << [user, :master]
- project.team << [assignee, :developer]
+ project.add_master(user)
+ project.add_developer(assignee)
end
it 'creates a `MergeRequestsClosingIssues` record for each issue' do
diff --git a/spec/services/merge_requests/ff_merge_service_spec.rb b/spec/services/merge_requests/ff_merge_service_spec.rb
index aaabf3ed2b0..aa90feeef89 100644
--- a/spec/services/merge_requests/ff_merge_service_spec.rb
+++ b/spec/services/merge_requests/ff_merge_service_spec.rb
@@ -12,8 +12,8 @@ describe MergeRequests::FfMergeService do
let(:project) { merge_request.project }
before do
- project.team << [user, :master]
- project.team << [user2, :developer]
+ project.add_master(user)
+ project.add_developer(user2)
end
describe '#execute' do
diff --git a/spec/services/merge_requests/merge_service_spec.rb b/spec/services/merge_requests/merge_service_spec.rb
index f86f1ac2443..c38ddf4612b 100644
--- a/spec/services/merge_requests/merge_service_spec.rb
+++ b/spec/services/merge_requests/merge_service_spec.rb
@@ -1,14 +1,14 @@
require 'spec_helper'
describe MergeRequests::MergeService do
- let(:user) { create(:user) }
- let(:user2) { create(:user) }
+ set(:user) { create(:user) }
+ set(:user2) { create(:user) }
let(:merge_request) { create(:merge_request, :simple, author: user2, assignee: user2) }
let(:project) { merge_request.project }
before do
- project.team << [user, :master]
- project.team << [user2, :developer]
+ project.add_master(user)
+ project.add_developer(user2)
end
describe '#execute' do
diff --git a/spec/services/merge_requests/post_merge_service_spec.rb b/spec/services/merge_requests/post_merge_service_spec.rb
index d2bd05d921f..70957431942 100644
--- a/spec/services/merge_requests/post_merge_service_spec.rb
+++ b/spec/services/merge_requests/post_merge_service_spec.rb
@@ -6,7 +6,7 @@ describe MergeRequests::PostMergeService do
let(:project) { merge_request.project }
before do
- project.team << [user, :master]
+ project.add_master(user)
end
describe '#execute' do
@@ -22,5 +22,18 @@ describe MergeRequests::PostMergeService do
expect { service.execute(merge_request) }
.to change { project.open_merge_requests_count }.from(1).to(0)
end
+
+ it 'updates metrics' do
+ metrics = merge_request.metrics
+ metrics_service = double(MergeRequestMetricsService)
+ allow(MergeRequestMetricsService)
+ .to receive(:new)
+ .with(metrics)
+ .and_return(metrics_service)
+
+ expect(metrics_service).to receive(:merge)
+
+ described_class.new(project, user, {}).execute(merge_request)
+ end
end
end
diff --git a/spec/services/merge_requests/rebase_service_spec.rb b/spec/services/merge_requests/rebase_service_spec.rb
new file mode 100644
index 00000000000..d1b37cdd073
--- /dev/null
+++ b/spec/services/merge_requests/rebase_service_spec.rb
@@ -0,0 +1,134 @@
+require 'spec_helper'
+
+describe MergeRequests::RebaseService do
+ include ProjectForksHelper
+
+ let(:user) { create(:user) }
+ let(:merge_request) do
+ create(:merge_request,
+ source_branch: 'feature_conflict',
+ target_branch: 'master')
+ end
+ let(:project) { merge_request.project }
+ let(:repository) { project.repository.raw }
+
+ subject(:service) { described_class.new(project, user, {}) }
+
+ before do
+ project.add_master(user)
+ end
+
+ describe '#execute' do
+ context 'when another rebase is already in progress' do
+ before do
+ allow(merge_request).to receive(:rebase_in_progress?).and_return(true)
+ end
+
+ it 'saves the error message' do
+ subject.execute(merge_request)
+
+ expect(merge_request.reload.merge_error).to eq 'Rebase task canceled: Another rebase is already in progress'
+ end
+
+ it 'returns an error' do
+ expect(service.execute(merge_request)).to match(status: :error,
+ message: 'Failed to rebase. Should be done manually')
+ end
+ end
+
+ context 'when unexpected error occurs' do
+ before do
+ allow(repository).to receive(:run_git!).and_raise('Something went wrong')
+ end
+
+ it 'saves the error message' do
+ subject.execute(merge_request)
+
+ expect(merge_request.reload.merge_error).to eq 'Something went wrong'
+ end
+
+ it 'returns an error' do
+ expect(service.execute(merge_request)).to match(status: :error,
+ message: 'Failed to rebase. Should be done manually')
+ end
+ end
+
+ context 'with git command failure' do
+ before do
+ allow(repository).to receive(:run_git!).and_raise(Gitlab::Git::Repository::GitError, 'Something went wrong')
+ end
+
+ it 'saves the error message' do
+ subject.execute(merge_request)
+
+ expect(merge_request.reload.merge_error).to eq 'Something went wrong'
+ end
+
+ it 'returns an error' do
+ expect(service.execute(merge_request)).to match(status: :error,
+ message: 'Failed to rebase. Should be done manually')
+ end
+ end
+
+ context 'valid params' do
+ before do
+ service.execute(merge_request)
+ end
+
+ it 'rebases source branch' do
+ parent_sha = merge_request.source_project.repository.commit(merge_request.source_branch).parents.first.sha
+ target_branch_sha = merge_request.target_project.repository.commit(merge_request.target_branch).sha
+ expect(parent_sha).to eq(target_branch_sha)
+ end
+
+ it 'records the new SHA on the merge request' do
+ head_sha = merge_request.source_project.repository.commit(merge_request.source_branch).sha
+ expect(merge_request.reload.rebase_commit_sha).to eq(head_sha)
+ end
+
+ it 'logs correct author and commiter' do
+ head_commit = merge_request.source_project.repository.commit(merge_request.source_branch)
+
+ expect(head_commit.author_email).to eq('dmitriy.zaporozhets@gmail.com')
+ expect(head_commit.author_name).to eq('Dmitriy Zaporozhets')
+ expect(head_commit.committer_email).to eq(user.email)
+ expect(head_commit.committer_name).to eq(user.name)
+ end
+
+ context 'git commands' do
+ it 'sets GL_REPOSITORY env variable when calling git commands' do
+ expect(repository).to receive(:popen).exactly(3)
+ .with(anything, anything, hash_including('GL_REPOSITORY'))
+ .and_return(['', 0])
+
+ service.execute(merge_request)
+ end
+ end
+
+ context 'fork' do
+ let(:forked_project) do
+ fork_project(project, user, repository: true)
+ end
+
+ let(:merge_request_from_fork) do
+ forked_project.repository.create_file(
+ user,
+ 'new-file-to-target',
+ '',
+ message: 'Add new file to target',
+ branch_name: 'master')
+
+ create(:merge_request,
+ source_branch: 'master', source_project: forked_project,
+ target_branch: 'master', target_project: project)
+ end
+
+ it 'rebases source branch' do
+ parent_sha = forked_project.repository.commit(merge_request_from_fork.source_branch).parents.first.sha
+ target_branch_sha = project.repository.commit(merge_request_from_fork.target_branch).sha
+ expect(parent_sha).to eq(target_branch_sha)
+ end
+ end
+ end
+ end
+end
diff --git a/spec/services/merge_requests/refresh_service_spec.rb b/spec/services/merge_requests/refresh_service_spec.rb
index 61ec4709c59..7a01d3dd698 100644
--- a/spec/services/merge_requests/refresh_service_spec.rb
+++ b/spec/services/merge_requests/refresh_service_spec.rb
@@ -300,8 +300,8 @@ describe MergeRequests::RefreshService do
let(:commit) { project.commit }
before do
- project.team << [commit_author, :developer]
- project.team << [user, :developer]
+ project.add_developer(commit_author)
+ project.add_developer(user)
allow(commit).to receive_messages(
safe_message: "Closes #{issue.to_reference}",
diff --git a/spec/services/merge_requests/reopen_service_spec.rb b/spec/services/merge_requests/reopen_service_spec.rb
index fa652611c6b..a44d63e5f9f 100644
--- a/spec/services/merge_requests/reopen_service_spec.rb
+++ b/spec/services/merge_requests/reopen_service_spec.rb
@@ -8,9 +8,9 @@ describe MergeRequests::ReopenService do
let(:project) { merge_request.project }
before do
- project.team << [user, :master]
- project.team << [user2, :developer]
- project.team << [guest, :guest]
+ project.add_master(user)
+ project.add_developer(user2)
+ project.add_guest(guest)
end
describe '#execute' do
@@ -47,6 +47,19 @@ describe MergeRequests::ReopenService do
end
end
+ it 'updates metrics' do
+ metrics = merge_request.metrics
+ service = double(MergeRequestMetricsService)
+ allow(MergeRequestMetricsService)
+ .to receive(:new)
+ .with(metrics)
+ .and_return(service)
+
+ expect(service).to receive(:reopen)
+
+ described_class.new(project, user, {}).execute(merge_request)
+ end
+
it 'refreshes the number of open merge requests for a valid MR' do
service = described_class.new(project, user, {})
diff --git a/spec/services/merge_requests/update_service_spec.rb b/spec/services/merge_requests/update_service_spec.rb
index 7a66b809550..2238da2d14d 100644
--- a/spec/services/merge_requests/update_service_spec.rb
+++ b/spec/services/merge_requests/update_service_spec.rb
@@ -16,9 +16,9 @@ describe MergeRequests::UpdateService, :mailer do
end
before do
- project.team << [user, :master]
- project.team << [user2, :developer]
- project.team << [user3, :developer]
+ project.add_master(user)
+ project.add_developer(user2)
+ project.add_developer(user3)
end
describe 'execute' do
@@ -356,8 +356,8 @@ describe MergeRequests::UpdateService, :mailer do
let!(:subscriber) { create(:user) { |u| label.toggle_subscription(u, project) } }
before do
- project.team << [non_subscriber, :developer]
- project.team << [subscriber, :developer]
+ project.add_developer(non_subscriber)
+ project.add_developer(subscriber)
end
it 'sends notifications for subscribers of newly added labels' do
diff --git a/spec/services/milestones/close_service_spec.rb b/spec/services/milestones/close_service_spec.rb
index 2bdf224804d..adad73f7e11 100644
--- a/spec/services/milestones/close_service_spec.rb
+++ b/spec/services/milestones/close_service_spec.rb
@@ -6,7 +6,7 @@ describe Milestones::CloseService do
let(:milestone) { create(:milestone, title: "Milestone v1.2", project: project) }
before do
- project.team << [user, :master]
+ project.add_master(user)
end
describe '#execute' do
diff --git a/spec/services/milestones/create_service_spec.rb b/spec/services/milestones/create_service_spec.rb
index 8837b91051d..f2a18c7295a 100644
--- a/spec/services/milestones/create_service_spec.rb
+++ b/spec/services/milestones/create_service_spec.rb
@@ -7,7 +7,7 @@ describe Milestones::CreateService do
describe '#execute' do
context "valid params" do
before do
- project.team << [user, :master]
+ project.add_master(user)
opts = {
title: 'v2.1.9',
diff --git a/spec/services/milestones/destroy_service_spec.rb b/spec/services/milestones/destroy_service_spec.rb
index af35e17bfa7..9703780b0e9 100644
--- a/spec/services/milestones/destroy_service_spec.rb
+++ b/spec/services/milestones/destroy_service_spec.rb
@@ -8,7 +8,7 @@ describe Milestones::DestroyService do
let!(:merge_request) { create(:merge_request, source_project: project, milestone: milestone) }
before do
- project.team << [user, :master]
+ project.add_master(user)
end
def service
diff --git a/spec/services/notes/create_service_spec.rb b/spec/services/notes/create_service_spec.rb
index 661d26946e7..0ae26e87154 100644
--- a/spec/services/notes/create_service_spec.rb
+++ b/spec/services/notes/create_service_spec.rb
@@ -10,7 +10,7 @@ describe Notes::CreateService do
describe '#execute' do
before do
- project.team << [user, :master]
+ project.add_master(user)
end
context "valid params" do
diff --git a/spec/services/notes/destroy_service_spec.rb b/spec/services/notes/destroy_service_spec.rb
index c9a99a43edb..64445be560e 100644
--- a/spec/services/notes/destroy_service_spec.rb
+++ b/spec/services/notes/destroy_service_spec.rb
@@ -1,15 +1,25 @@
require 'spec_helper'
describe Notes::DestroyService do
+ set(:project) { create(:project, :public) }
+ set(:issue) { create(:issue, project: project) }
+ let(:user) { issue.author }
+
describe '#execute' do
it 'deletes a note' do
- project = create(:project)
- issue = create(:issue, project: project)
note = create(:note, project: project, noteable: issue)
- described_class.new(project, note.author).execute(note)
+ described_class.new(project, user).execute(note)
expect(project.issues.find(issue.id).notes).not_to include(note)
end
+
+ it 'updates the todo counts for users with todos for the note' do
+ note = create(:note, project: project, noteable: issue)
+ create(:todo, note: note, target: issue, user: user, author: user, project: project)
+
+ expect { described_class.new(project, user).execute(note) }
+ .to change { user.todos_pending_count }.from(1).to(0)
+ end
end
end
diff --git a/spec/services/notes/post_process_service_spec.rb b/spec/services/notes/post_process_service_spec.rb
index a2b3638059f..6ef5e93cb20 100644
--- a/spec/services/notes/post_process_service_spec.rb
+++ b/spec/services/notes/post_process_service_spec.rb
@@ -7,7 +7,7 @@ describe Notes::PostProcessService do
describe '#execute' do
before do
- project.team << [user, :master]
+ project.add_master(user)
note_opts = {
note: 'Awesome comment',
noteable_type: 'Issue',
diff --git a/spec/services/notes/quick_actions_service_spec.rb b/spec/services/notes/quick_actions_service_spec.rb
index 0280a19098b..5eafe56c99d 100644
--- a/spec/services/notes/quick_actions_service_spec.rb
+++ b/spec/services/notes/quick_actions_service_spec.rb
@@ -3,11 +3,11 @@ require 'spec_helper'
describe Notes::QuickActionsService do
shared_context 'note on noteable' do
let(:project) { create(:project) }
- let(:master) { create(:user).tap { |u| project.team << [u, :master] } }
+ let(:master) { create(:user).tap { |u| project.add_master(u) } }
let(:assignee) { create(:user) }
before do
- project.team << [assignee, :master]
+ project.add_master(assignee)
end
end
@@ -226,7 +226,7 @@ describe Notes::QuickActionsService do
context 'CE restriction for issue assignees' do
describe '/assign' do
let(:project) { create(:project) }
- let(:master) { create(:user).tap { |u| project.team << [u, :master] } }
+ let(:master) { create(:user).tap { |u| project.add_master(u) } }
let(:assignee) { create(:user) }
let(:master) { create(:user) }
let(:service) { described_class.new(project, master) }
@@ -237,8 +237,8 @@ describe Notes::QuickActionsService do
end
before do
- project.team << [master, :master]
- project.team << [assignee, :master]
+ project.add_master(master)
+ project.add_master(assignee)
end
it 'adds only one assignee from the list' do
diff --git a/spec/services/notes/update_service_spec.rb b/spec/services/notes/update_service_spec.rb
index 3210539f3ee..65b1d613998 100644
--- a/spec/services/notes/update_service_spec.rb
+++ b/spec/services/notes/update_service_spec.rb
@@ -9,9 +9,9 @@ describe Notes::UpdateService do
let(:note) { create(:note, project: project, noteable: issue, author: user, note: "Old note #{user2.to_reference}") }
before do
- project.team << [user, :master]
- project.team << [user2, :developer]
- project.team << [user3, :developer]
+ project.add_master(user)
+ project.add_developer(user2)
+ project.add_developer(user3)
end
describe '#execute' do
diff --git a/spec/services/projects/autocomplete_service_spec.rb b/spec/services/projects/autocomplete_service_spec.rb
index 426593be428..7a8c54673f7 100644
--- a/spec/services/projects/autocomplete_service_spec.rb
+++ b/spec/services/projects/autocomplete_service_spec.rb
@@ -34,7 +34,7 @@ describe Projects::AutocompleteService do
end
it 'does not list project confidential issues for project members with guest role' do
- project.team << [member, :guest]
+ project.add_guest(member)
autocomplete = described_class.new(project, non_member)
issues = autocomplete.issues.map(&:iid)
@@ -66,7 +66,7 @@ describe Projects::AutocompleteService do
end
it 'lists project confidential issues for project members' do
- project.team << [member, :developer]
+ project.add_developer(member)
autocomplete = described_class.new(project, member)
issues = autocomplete.issues.map(&:iid)
diff --git a/spec/services/projects/create_service_spec.rb b/spec/services/projects/create_service_spec.rb
index dc89fdebce7..9a44dfde41b 100644
--- a/spec/services/projects/create_service_spec.rb
+++ b/spec/services/projects/create_service_spec.rb
@@ -252,6 +252,12 @@ describe Projects::CreateService, '#execute' do
end
end
+ it 'writes project full path to .git/config' do
+ project = create_project(user, opts)
+
+ expect(project.repository.rugged.config['gitlab.fullpath']).to eq project.full_path
+ end
+
def create_project(user, opts)
Projects::CreateService.new(user, opts).execute
end
diff --git a/spec/services/projects/fork_service_spec.rb b/spec/services/projects/fork_service_spec.rb
index 4057caca2ac..409d5de8d43 100644
--- a/spec/services/projects/fork_service_spec.rb
+++ b/spec/services/projects/fork_service_spec.rb
@@ -139,10 +139,10 @@ describe Projects::ForkService do
stub_application_setting(restricted_visibility_levels: [Gitlab::VisibilityLevel::INTERNAL])
end
- it "creates fork with highest allowed level" do
+ it "creates fork with lowest level" do
forked_project = fork_project(@from_project, @to_user)
- expect(forked_project.visibility_level).to eq(Gitlab::VisibilityLevel::PUBLIC)
+ expect(forked_project.visibility_level).to eq(Gitlab::VisibilityLevel::PRIVATE)
end
end
@@ -209,6 +209,19 @@ describe Projects::ForkService do
expect(to_project.errors[:path]).to eq(['has already been taken'])
end
end
+
+ context 'when the namespace has a lower visibility level than the project' do
+ it 'creates the project with the lower visibility level' do
+ public_project = create(:project, :public)
+ private_group = create(:group, :private)
+ group_owner = create(:user)
+ private_group.add_owner(group_owner)
+
+ forked_project = fork_project(public_project, group_owner, namespace: private_group)
+
+ expect(forked_project.visibility_level).to eq(Gitlab::VisibilityLevel::PRIVATE)
+ end
+ end
end
end
diff --git a/spec/services/projects/hashed_storage/migrate_repository_service_spec.rb b/spec/services/projects/hashed_storage/migrate_repository_service_spec.rb
index 3a3e47fd9c0..7b536cc05cb 100644
--- a/spec/services/projects/hashed_storage/migrate_repository_service_spec.rb
+++ b/spec/services/projects/hashed_storage/migrate_repository_service_spec.rb
@@ -2,7 +2,7 @@ require 'spec_helper'
describe Projects::HashedStorage::MigrateRepositoryService do
let(:gitlab_shell) { Gitlab::Shell.new }
- let(:project) { create(:project, :empty_repo, :wiki_repo) }
+ let(:project) { create(:project, :repository, :wiki_repo) }
let(:service) { described_class.new(project) }
let(:legacy_storage) { Storage::LegacyProject.new(project) }
let(:hashed_storage) { Storage::HashedProject.new(project) }
@@ -33,6 +33,12 @@ describe Projects::HashedStorage::MigrateRepositoryService do
service.execute
end
+
+ it 'writes project full path to .git/config' do
+ service.execute
+
+ expect(project.repository.rugged.config['gitlab.fullpath']).to eq project.full_path
+ end
end
context 'when one move fails' do
diff --git a/spec/services/projects/transfer_service_spec.rb b/spec/services/projects/transfer_service_spec.rb
index 2b1337bee7e..39f6388c25e 100644
--- a/spec/services/projects/transfer_service_spec.rb
+++ b/spec/services/projects/transfer_service_spec.rb
@@ -54,6 +54,12 @@ describe Projects::TransferService do
expect(project.disk_path).not_to eq(old_path)
expect(project.disk_path).to start_with(group.path)
end
+
+ it 'updates project full path in .git/config' do
+ transfer_project(project, user, group)
+
+ expect(project.repository.rugged.config['gitlab.fullpath']).to eq "#{group.full_path}/#{project.path}"
+ end
end
context 'when transfer fails' do
@@ -86,6 +92,12 @@ describe Projects::TransferService do
expect(original_path).to eq current_path
end
+ it 'rolls back project full path in .git/config' do
+ attempt_project_transfer
+
+ expect(project.repository.rugged.config['gitlab.fullpath']).to eq project.full_path
+ end
+
it "doesn't send move notifications" do
expect_any_instance_of(NotificationService).not_to receive(:project_was_moved)
diff --git a/spec/services/projects/unlink_fork_service_spec.rb b/spec/services/projects/unlink_fork_service_spec.rb
index 2bba71fef4f..3ec6139bfa6 100644
--- a/spec/services/projects/unlink_fork_service_spec.rb
+++ b/spec/services/projects/unlink_fork_service_spec.rb
@@ -62,6 +62,26 @@ describe Projects::UnlinkForkService do
expect(source.forks_count).to be_zero
end
+ context 'when the source has LFS objects' do
+ let(:lfs_object) { create(:lfs_object) }
+
+ before do
+ lfs_object.projects << project
+ end
+
+ it 'links the fork to the lfs object before unlinking' do
+ subject.execute
+
+ expect(lfs_object.projects).to include(forked_project)
+ end
+
+ it 'does not fail if the lfs objects were already linked' do
+ lfs_object.projects << forked_project
+
+ expect { subject.execute }.not_to raise_error
+ end
+ end
+
context 'when the original project was deleted' do
it 'does not fail when the original project is deleted' do
source = forked_project.forked_from_project
diff --git a/spec/services/projects/update_service_spec.rb b/spec/services/projects/update_service_spec.rb
index d887f70efae..fc6aa713d6f 100644
--- a/spec/services/projects/update_service_spec.rb
+++ b/spec/services/projects/update_service_spec.rb
@@ -61,7 +61,7 @@ describe Projects::UpdateService do
end
end
- context 'When project visibility is higher than parent group' do
+ context 'when project visibility is higher than parent group' do
let(:group) { create(:group, visibility_level: Gitlab::VisibilityLevel::INTERNAL) }
before do
diff --git a/spec/services/protected_branches/create_service_spec.rb b/spec/services/protected_branches/create_service_spec.rb
index 835e83d6dba..53b3e5e365d 100644
--- a/spec/services/protected_branches/create_service_spec.rb
+++ b/spec/services/protected_branches/create_service_spec.rb
@@ -19,5 +19,21 @@ describe ProtectedBranches::CreateService do
expect(project.protected_branches.last.push_access_levels.map(&:access_level)).to eq([Gitlab::Access::MASTER])
expect(project.protected_branches.last.merge_access_levels.map(&:access_level)).to eq([Gitlab::Access::MASTER])
end
+
+ context 'when user does not have permission' do
+ let(:user) { create(:user) }
+
+ before do
+ project.add_developer(user)
+ end
+
+ it 'creates a new protected branch if we skip authorization step' do
+ expect { service.execute(skip_authorization: true) }.to change(ProtectedBranch, :count).by(1)
+ end
+
+ it 'raises Gitlab::Access:AccessDeniedError' do
+ expect { service.execute }.to raise_error(Gitlab::Access::AccessDeniedError)
+ end
+ end
end
end
diff --git a/spec/services/quick_actions/interpret_service_spec.rb b/spec/services/quick_actions/interpret_service_spec.rb
index c35177f6ebc..ae160d104f1 100644
--- a/spec/services/quick_actions/interpret_service_spec.rb
+++ b/spec/services/quick_actions/interpret_service_spec.rb
@@ -12,7 +12,7 @@ describe QuickActions::InterpretService do
let(:service) { described_class.new(project, developer) }
before do
- project.team << [developer, :developer]
+ project.add_developer(developer)
end
describe '#execute' do
@@ -209,7 +209,7 @@ describe QuickActions::InterpretService do
expect(updates).to eq(spend_time: {
duration: 3600,
- user: developer,
+ user_id: developer.id,
spent_at: DateTime.now.to_date
})
end
@@ -221,7 +221,7 @@ describe QuickActions::InterpretService do
expect(updates).to eq(spend_time: {
duration: -1800,
- user: developer,
+ user_id: developer.id,
spent_at: DateTime.now.to_date
})
end
@@ -233,7 +233,7 @@ describe QuickActions::InterpretService do
expect(updates).to eq(spend_time: {
duration: 1800,
- user: developer,
+ user_id: developer.id,
spent_at: Date.parse(date)
})
end
@@ -267,7 +267,7 @@ describe QuickActions::InterpretService do
it 'populates spend_time: :reset if content contains /remove_time_spent' do
_, updates = service.execute(content, issuable)
- expect(updates).to eq(spend_time: { duration: :reset, user: developer })
+ expect(updates).to eq(spend_time: { duration: :reset, user_id: developer.id })
end
end
@@ -440,7 +440,7 @@ describe QuickActions::InterpretService do
let(:content) { "/assign @#{developer.username} @#{developer2.username}" }
before do
- project.team << [developer2, :developer]
+ project.add_developer(developer2)
end
context 'Issue' do
diff --git a/spec/services/reset_project_cache_service_spec.rb b/spec/services/reset_project_cache_service_spec.rb
new file mode 100644
index 00000000000..de475d16586
--- /dev/null
+++ b/spec/services/reset_project_cache_service_spec.rb
@@ -0,0 +1,28 @@
+require 'spec_helper'
+
+describe ResetProjectCacheService do
+ let(:project) { create(:project) }
+ let(:user) { create(:user) }
+
+ subject { described_class.new(project, user).execute }
+
+ context 'when project cache_index is nil' do
+ before do
+ project.jobs_cache_index = nil
+ end
+
+ it 'sets project cache_index to one' do
+ expect { subject }.to change { project.reload.jobs_cache_index }.from(nil).to(1)
+ end
+ end
+
+ context 'when project cache_index is a numeric value' do
+ before do
+ project.update_attributes(jobs_cache_index: 1)
+ end
+
+ it 'increments project cache index' do
+ expect { subject }.to change { project.reload.jobs_cache_index }.by(1)
+ end
+ end
+end
diff --git a/spec/services/search/snippet_service_spec.rb b/spec/services/search/snippet_service_spec.rb
index eae9bd4f5cf..bc7885b03d9 100644
--- a/spec/services/search/snippet_service_spec.rb
+++ b/spec/services/search/snippet_service_spec.rb
@@ -33,7 +33,7 @@ describe Search::SnippetService do
it 'returns public, internal snippets and project private snippets for project members' do
member = create(:user)
- project.team << [member, :developer]
+ project.add_developer(member)
search = described_class.new(member, search: 'password')
results = search.execute
diff --git a/spec/services/system_note_service_spec.rb b/spec/services/system_note_service_spec.rb
index 47412110b4b..4e640a82dfc 100644
--- a/spec/services/system_note_service_spec.rb
+++ b/spec/services/system_note_service_spec.rb
@@ -2,6 +2,7 @@ require 'spec_helper'
describe SystemNoteService do
include Gitlab::Routing
+ include RepoHelpers
set(:group) { create(:group) }
set(:project) { create(:project, :repository, group: group) }
@@ -927,7 +928,7 @@ describe SystemNoteService do
# We need a custom noteable in order to the shared examples to be green.
let(:noteable) do
mr = create(:merge_request, source_project: project)
- mr.spend_time(duration: 360000, user: author)
+ mr.spend_time(duration: 360000, user_id: author.id)
mr.save!
mr
end
@@ -965,7 +966,7 @@ describe SystemNoteService do
end
def spend_time!(seconds)
- noteable.spend_time(duration: seconds, user: author)
+ noteable.spend_time(duration: seconds, user_id: author.id)
noteable.save!
end
end
@@ -1070,17 +1071,32 @@ describe SystemNoteService do
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)
+ context 'when the change_position is valid for the discussion' do
+ 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_project_merge_request_url(project, merge_request, diff_id: diff_id, anchor: line_code))
+ end
end
- it 'links to the diff in the system note' do
- expect(subject.note).to include('version 1')
+ context 'when the change_position is invalid for the discussion' do
+ let(:change_position) { project.commit(sample_commit.id) }
- diff_id = merge_request.merge_request_diff.id
- line_code = change_position.line_code(project.repository)
- expect(subject.note).to include(diffs_project_merge_request_url(project, merge_request, diff_id: diff_id, anchor: line_code))
+ 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 'does not create a link' do
+ expect(subject.note).to eq('changed this line in version 1 of the diff')
+ end
end
end
diff --git a/spec/services/todo_service_spec.rb b/spec/services/todo_service_spec.rb
index dc2673abc73..5e6c24f5730 100644
--- a/spec/services/todo_service_spec.rb
+++ b/spec/services/todo_service_spec.rb
@@ -17,11 +17,11 @@ describe TodoService do
let(:service) { described_class.new }
before do
- project.team << [guest, :guest]
- project.team << [author, :developer]
- project.team << [member, :developer]
- project.team << [john_doe, :developer]
- project.team << [skipped, :developer]
+ project.add_guest(guest)
+ project.add_developer(author)
+ project.add_developer(member)
+ project.add_developer(john_doe)
+ project.add_developer(skipped)
end
describe 'Issues' do
@@ -248,11 +248,26 @@ describe TodoService do
end
end
- describe '#destroy_issuable' do
- it 'refresh the todos count cache for the user' do
- expect(john_doe).to receive(:update_todos_count_cache).and_call_original
+ describe '#destroy_target' do
+ it 'refreshes the todos count cache for users with todos on the target' do
+ create(:todo, target: issue, user: john_doe, author: john_doe, project: issue.project)
- service.destroy_issuable(issue, john_doe)
+ expect_any_instance_of(User).to receive(:update_todos_count_cache).and_call_original
+
+ service.destroy_target(issue) { }
+ end
+
+ it 'does not refresh the todos count cache for users with only done todos on the target' do
+ create(:todo, :done, target: issue, user: john_doe, author: john_doe, project: issue.project)
+
+ expect_any_instance_of(User).not_to receive(:update_todos_count_cache)
+
+ service.destroy_target(issue) { }
+ end
+
+ it 'yields the target to the caller' do
+ expect { |b| service.destroy_target(issue, &b) }
+ .to yield_with_args(issue)
end
end
diff --git a/spec/services/update_merge_request_metrics_service_spec.rb b/spec/services/update_merge_request_metrics_service_spec.rb
new file mode 100644
index 00000000000..b5fb999381d
--- /dev/null
+++ b/spec/services/update_merge_request_metrics_service_spec.rb
@@ -0,0 +1,42 @@
+require 'rails_helper'
+
+describe MergeRequestMetricsService do
+ let(:metrics) { create(:merge_request).metrics }
+
+ describe '#merge' do
+ it 'updates metrics' do
+ user = create(:user)
+ service = described_class.new(metrics)
+ event = double(Event, author_id: user.id, created_at: Time.now)
+
+ service.merge(event)
+
+ expect(metrics.merged_by).to eq(user)
+ expect(metrics.merged_at).to eq(event.created_at)
+ end
+ end
+
+ describe '#close' do
+ it 'updates metrics' do
+ user = create(:user)
+ service = described_class.new(metrics)
+ event = double(Event, author_id: user.id, created_at: Time.now)
+
+ service.close(event)
+
+ expect(metrics.latest_closed_by).to eq(user)
+ expect(metrics.latest_closed_at).to eq(event.created_at)
+ end
+ end
+
+ describe '#reopen' do
+ it 'updates metrics' do
+ service = described_class.new(metrics)
+
+ service.reopen
+
+ expect(metrics.latest_closed_by).to be_nil
+ expect(metrics.latest_closed_at).to be_nil
+ end
+ end
+end
diff --git a/spec/services/users/destroy_service_spec.rb b/spec/services/users/destroy_service_spec.rb
index 58a5bede3de..aeba9cd60bc 100644
--- a/spec/services/users/destroy_service_spec.rb
+++ b/spec/services/users/destroy_service_spec.rb
@@ -188,5 +188,22 @@ describe Users::DestroyService do
end
end
end
+
+ describe "calls the before/after callbacks" do
+ it 'of project_members' do
+ expect_any_instance_of(ProjectMember).to receive(:run_callbacks).with(:destroy).once
+
+ service.execute(user)
+ end
+
+ it 'of group_members' do
+ group_member = create(:group_member)
+ group_member.group.group_members.create(user: user, access_level: 40)
+
+ expect_any_instance_of(GroupMember).to receive(:run_callbacks).with(:destroy).once
+
+ service.execute(user)
+ end
+ end
end
end
diff --git a/spec/services/users/keys_count_service_spec.rb b/spec/services/users/keys_count_service_spec.rb
index a188cf86772..bee8380e8b7 100644
--- a/spec/services/users/keys_count_service_spec.rb
+++ b/spec/services/users/keys_count_service_spec.rb
@@ -15,14 +15,12 @@ describe Users::KeysCountService, :use_clean_rails_memory_store_caching do
expect(service.count).to eq(1)
end
- it 'caches the number of keys in Redis' do
+ it 'caches the number of keys in Redis', :request_store do
+ service.delete_cache
+ control_count = ActiveRecord::QueryRecorder.new { service.count }.count
service.delete_cache
- recorder = ActiveRecord::QueryRecorder.new do
- 2.times { service.count }
- end
-
- expect(recorder.count).to eq(1)
+ expect { 2.times { service.count } }.not_to exceed_query_limit(control_count)
end
end
diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb
index f94fb8733d5..f51bb44086b 100644
--- a/spec/spec_helper.rb
+++ b/spec/spec_helper.rb
@@ -183,7 +183,7 @@ RSpec::Matchers.define :match_asset_path do |expected|
end
end
-FactoryGirl::SyntaxRunner.class_eval do
+FactoryBot::SyntaxRunner.class_eval do
include RSpec::Mocks::ExampleMethods
end
diff --git a/spec/support/api/boards_shared_examples.rb b/spec/support/api/boards_shared_examples.rb
new file mode 100644
index 00000000000..943c1f6ffd7
--- /dev/null
+++ b/spec/support/api/boards_shared_examples.rb
@@ -0,0 +1,180 @@
+shared_examples_for 'group and project boards' do |route_definition, ee = false|
+ let(:root_url) { route_definition.gsub(":id", board_parent.id.to_s) }
+
+ before do
+ board_parent.add_reporter(user)
+ board_parent.add_guest(guest)
+ end
+
+ def expect_schema_match_for(response, schema_file, ee)
+ if ee
+ expect(response).to match_response_schema(schema_file, dir: "ee")
+ else
+ expect(response).to match_response_schema(schema_file)
+ end
+ end
+
+ describe "GET #{route_definition}" do
+ context "when unauthenticated" do
+ it "returns authentication error" do
+ get api(root_url)
+
+ expect(response).to have_gitlab_http_status(401)
+ end
+ end
+
+ context "when authenticated" do
+ it "returns the issue boards" do
+ get api(root_url, user)
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(response).to include_pagination_headers
+
+ expect_schema_match_for(response, 'public_api/v4/boards', ee)
+ end
+
+ describe "GET #{route_definition}/:board_id" do
+ let(:url) { "#{root_url}/#{board.id}" }
+
+ it 'get a single board by id' do
+ get api(url, user)
+
+ expect_schema_match_for(response, 'public_api/v4/board', ee)
+ end
+ end
+ end
+ end
+
+ describe "GET #{route_definition}/:board_id/lists" do
+ let(:url) { "#{root_url}/#{board.id}/lists" }
+
+ it 'returns issue board lists' do
+ get api(url, user)
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(response).to include_pagination_headers
+ expect(json_response).to be_an Array
+ expect(json_response.length).to eq(2)
+ expect(json_response.first['label']['name']).to eq(dev_label.title)
+ end
+
+ it 'returns 404 if board not found' do
+ get api("#{root_url}/22343/lists", user)
+
+ expect(response).to have_gitlab_http_status(404)
+ end
+ end
+
+ describe "GET #{route_definition}/:board_id/lists/:list_id" do
+ let(:url) { "#{root_url}/#{board.id}/lists" }
+
+ it 'returns a list' do
+ get api("#{url}/#{dev_list.id}", user)
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(json_response['id']).to eq(dev_list.id)
+ expect(json_response['label']['name']).to eq(dev_label.title)
+ expect(json_response['position']).to eq(1)
+ end
+
+ it 'returns 404 if list not found' do
+ get api("#{url}/5324", user)
+
+ expect(response).to have_gitlab_http_status(404)
+ end
+ end
+
+ describe "POST #{route_definition}/lists" do
+ let(:url) { "#{root_url}/#{board.id}/lists" }
+
+ it 'creates a new issue board list for labels' do
+ post api(url, user), label_id: ux_label.id
+
+ expect(response).to have_gitlab_http_status(201)
+ expect(json_response['label']['name']).to eq(ux_label.title)
+ expect(json_response['position']).to eq(3)
+ end
+
+ it 'returns 400 when creating a new list if label_id is invalid' do
+ post api(url, user), label_id: 23423
+
+ expect(response).to have_gitlab_http_status(400)
+ end
+
+ it 'returns 403 for members with guest role' do
+ put api("#{url}/#{test_list.id}", guest), position: 1
+
+ expect(response).to have_gitlab_http_status(403)
+ end
+ end
+
+ describe "PUT #{route_definition}/:board_id/lists/:list_id to update only position" do
+ let(:url) { "#{root_url}/#{board.id}/lists" }
+
+ it "updates a list" do
+ put api("#{url}/#{test_list.id}", user),
+ position: 1
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(json_response['position']).to eq(1)
+ end
+
+ it "returns 404 error if list id not found" do
+ put api("#{url}/44444", user),
+ position: 1
+
+ expect(response).to have_gitlab_http_status(404)
+ end
+
+ it "returns 403 for members with guest role" do
+ put api("#{url}/#{test_list.id}", guest),
+ position: 1
+
+ expect(response).to have_gitlab_http_status(403)
+ end
+ end
+
+ describe "DELETE #{route_definition}/lists/:list_id" do
+ let(:url) { "#{root_url}/#{board.id}/lists" }
+
+ it "rejects a non member from deleting a list" do
+ delete api("#{url}/#{dev_list.id}", non_member)
+
+ expect(response).to have_gitlab_http_status(403)
+ end
+
+ it "rejects a user with guest role from deleting a list" do
+ delete api("#{url}/#{dev_list.id}", guest)
+
+ expect(response).to have_gitlab_http_status(403)
+ end
+
+ it "returns 404 error if list id not found" do
+ delete api("#{url}/44444", user)
+
+ expect(response).to have_gitlab_http_status(404)
+ end
+
+ context "when the user is parent owner" do
+ set(:owner) { create(:user) }
+
+ before do
+ if board_parent.try(:namespace)
+ board_parent.update(namespace: owner.namespace)
+ else
+ board.parent.add_owner(owner)
+ end
+ end
+
+ it "deletes the list if an admin requests it" do
+ delete api("#{url}/#{dev_list.id}", owner)
+
+ expect(response).to have_gitlab_http_status(204)
+ end
+
+ it_behaves_like '412 response' do
+ let(:request) { api("#{url}/#{dev_list.id}", owner) }
+ end
+ end
+ end
+end
diff --git a/spec/support/api/milestones_shared_examples.rb b/spec/support/api/milestones_shared_examples.rb
index d9080b02541..0388f110d71 100644
--- a/spec/support/api/milestones_shared_examples.rb
+++ b/spec/support/api/milestones_shared_examples.rb
@@ -258,7 +258,7 @@ shared_examples_for 'group and project milestones' do |route_definition|
# Add public project to the group in context
setup_for_group if context_group
- public_project.team << [user, :developer]
+ public_project.add_developer(user)
milestone.issues << issue << confidential_issue
end
@@ -275,7 +275,7 @@ shared_examples_for 'group and project milestones' do |route_definition|
it 'does not return confidential issues to team members with guest role' do
member = create(:user)
- public_project.team << [member, :guest]
+ public_project.add_guest(member)
get api(issues_route, member)
diff --git a/spec/support/api/time_tracking_shared_examples.rb b/spec/support/api/time_tracking_shared_examples.rb
index af1083f4bfd..dd3089d22e5 100644
--- a/spec/support/api/time_tracking_shared_examples.rb
+++ b/spec/support/api/time_tracking_shared_examples.rb
@@ -79,7 +79,7 @@ shared_examples 'time tracking endpoints' do |issuable_name|
context 'when subtracting time' do
it 'subtracts time of the total spent time' do
- issuable.update_attributes!(spend_time: { duration: 7200, user: user })
+ issuable.update_attributes!(spend_time: { duration: 7200, user_id: user.id })
post api("/projects/#{project.id}/#{issuable_collection_name}/#{issuable.iid}/add_spent_time", user),
duration: '-1h'
@@ -91,7 +91,7 @@ shared_examples 'time tracking endpoints' do |issuable_name|
context 'when time to subtract is greater than the total spent time' do
it 'does not modify the total time spent' do
- issuable.update_attributes!(spend_time: { duration: 7200, user: user })
+ issuable.update_attributes!(spend_time: { duration: 7200, user_id: user.id })
post api("/projects/#{project.id}/#{issuable_collection_name}/#{issuable.iid}/add_spent_time", user),
duration: '-1w'
@@ -119,7 +119,7 @@ shared_examples 'time tracking endpoints' do |issuable_name|
describe "GET /projects/:id/#{issuable_collection_name}/:#{issuable_name}_id/time_stats" do
it "returns the time stats for #{issuable_name}" do
- issuable.update_attributes!(spend_time: { duration: 1800, user: user },
+ issuable.update_attributes!(spend_time: { duration: 1800, user_id: user.id },
time_estimate: 3600)
get api("/projects/#{project.id}/#{issuable_collection_name}/#{issuable.iid}/time_stats", user)
diff --git a/spec/support/api/v3/time_tracking_shared_examples.rb b/spec/support/api/v3/time_tracking_shared_examples.rb
index afe0f4cecda..f27a2d06c83 100644
--- a/spec/support/api/v3/time_tracking_shared_examples.rb
+++ b/spec/support/api/v3/time_tracking_shared_examples.rb
@@ -75,7 +75,7 @@ shared_examples 'V3 time tracking endpoints' do |issuable_name|
context 'when subtracting time' do
it 'subtracts time of the total spent time' do
- issuable.update_attributes!(spend_time: { duration: 7200, user: user })
+ issuable.update_attributes!(spend_time: { duration: 7200, user_id: user.id })
post v3_api("/projects/#{project.id}/#{issuable_collection_name}/#{issuable.id}/add_spent_time", user),
duration: '-1h'
@@ -87,7 +87,7 @@ shared_examples 'V3 time tracking endpoints' do |issuable_name|
context 'when time to subtract is greater than the total spent time' do
it 'does not modify the total time spent' do
- issuable.update_attributes!(spend_time: { duration: 7200, user: user })
+ issuable.update_attributes!(spend_time: { duration: 7200, user_id: user.id })
post v3_api("/projects/#{project.id}/#{issuable_collection_name}/#{issuable.id}/add_spent_time", user),
duration: '-1w'
@@ -115,7 +115,7 @@ shared_examples 'V3 time tracking endpoints' do |issuable_name|
describe "GET /projects/:id/#{issuable_collection_name}/:#{issuable_name}_id/time_stats" do
it "returns the time stats for #{issuable_name}" do
- issuable.update_attributes!(spend_time: { duration: 1800, user: user },
+ issuable.update_attributes!(spend_time: { duration: 1800, user_id: user.id },
time_estimate: 3600)
get v3_api("/projects/#{project.id}/#{issuable_collection_name}/#{issuable.id}/time_stats", user)
diff --git a/spec/support/background_migrations_matchers.rb b/spec/support/background_migrations_matchers.rb
index 423c0e4cefc..f4127efc6ae 100644
--- a/spec/support/background_migrations_matchers.rb
+++ b/spec/support/background_migrations_matchers.rb
@@ -1,4 +1,4 @@
-RSpec::Matchers.define :be_scheduled_migration do |delay, *expected|
+RSpec::Matchers.define :be_scheduled_delayed_migration do |delay, *expected|
match do |migration|
BackgroundMigrationWorker.jobs.any? do |job|
job['args'] == [migration, expected] &&
@@ -11,3 +11,16 @@ RSpec::Matchers.define :be_scheduled_migration do |delay, *expected|
'not scheduled in expected time!'
end
end
+
+RSpec::Matchers.define :be_scheduled_migration do |*expected|
+ match do |migration|
+ BackgroundMigrationWorker.jobs.any? do |job|
+ args = job['args'].size == 1 ? [BackgroundMigrationWorker.jobs[0]['args'][0], []] : job['args']
+ args == [migration, expected]
+ end
+ end
+
+ failure_message do |migration|
+ "Migration `#{migration}` with args `#{expected.inspect}` not scheduled!"
+ end
+end
diff --git a/spec/support/batch_loader.rb b/spec/support/batch_loader.rb
new file mode 100644
index 00000000000..bb790e660a6
--- /dev/null
+++ b/spec/support/batch_loader.rb
@@ -0,0 +1,5 @@
+RSpec.configure do |config|
+ config.after do
+ BatchLoader::Executor.clear_current
+ end
+end
diff --git a/spec/support/capybara.rb b/spec/support/capybara.rb
index 935b170a0f6..5189c57b7db 100644
--- a/spec/support/capybara.rb
+++ b/spec/support/capybara.rb
@@ -33,6 +33,9 @@ Capybara.register_driver :chrome do |app|
options.add_argument("disable-gpu")
end
+ # Disable /dev/shm use in CI. See https://gitlab.com/gitlab-org/gitlab-ee/issues/4252
+ options.add_argument("disable-dev-shm-usage") if ENV['CI'] || ENV['CI_SERVER']
+
Capybara::Selenium::Driver.new(
app,
browser: :chrome,
diff --git a/spec/support/cluster_application_spec.rb b/spec/support/cluster_application_spec.rb
new file mode 100644
index 00000000000..ab77910a050
--- /dev/null
+++ b/spec/support/cluster_application_spec.rb
@@ -0,0 +1,105 @@
+shared_examples 'cluster application specs' do
+ let(:factory_name) { described_class.to_s.downcase.gsub("::", "_") }
+
+ describe '#name' do
+ it 'is .application_name' do
+ expect(subject.name).to eq(described_class.application_name)
+ end
+
+ it 'is recorded in Clusters::Cluster::APPLICATIONS' do
+ expect(Clusters::Cluster::APPLICATIONS[subject.name]).to eq(described_class)
+ end
+ end
+
+ describe '#status' do
+ let(:cluster) { create(:cluster, :provided_by_gcp) }
+
+ subject { described_class.new(cluster: cluster) }
+
+ it 'defaults to :not_installable' do
+ expect(subject.status_name).to be(:not_installable)
+ end
+
+ context 'when application helm is scheduled' do
+ before do
+ create(factory_name, :scheduled, cluster: cluster)
+ end
+
+ it 'defaults to :not_installable' do
+ expect(subject.status_name).to be(:not_installable)
+ end
+ end
+
+ context 'when application helm is installed' do
+ before do
+ create(:clusters_applications_helm, :installed, cluster: cluster)
+ end
+
+ it 'defaults to :installable' do
+ expect(subject.status_name).to be(:installable)
+ end
+ end
+ end
+
+ describe '#install_command' do
+ it 'has all the needed information' do
+ expect(subject.install_command).to have_attributes(name: subject.name, install_helm: false)
+ end
+ end
+
+ describe 'status state machine' do
+ describe '#make_installing' do
+ subject { create(factory_name, :scheduled) }
+
+ it 'is installing' do
+ subject.make_installing!
+
+ expect(subject).to be_installing
+ end
+ end
+
+ describe '#make_installed' do
+ subject { create(factory_name, :installing) }
+
+ it 'is installed' do
+ subject.make_installed
+
+ expect(subject).to be_installed
+ end
+ end
+
+ describe '#make_errored' do
+ subject { create(factory_name, :installing) }
+ let(:reason) { 'some errors' }
+
+ it 'is errored' do
+ subject.make_errored(reason)
+
+ expect(subject).to be_errored
+ expect(subject.status_reason).to eq(reason)
+ end
+ end
+
+ describe '#make_scheduled' do
+ subject { create(factory_name, :installable) }
+
+ it 'is scheduled' do
+ subject.make_scheduled
+
+ expect(subject).to be_scheduled
+ end
+
+ describe 'when was errored' do
+ subject { create(factory_name, :errored) }
+
+ it 'clears #status_reason' do
+ expect(subject.status_reason).not_to be_nil
+
+ subject.make_scheduled!
+
+ expect(subject.status_reason).to be_nil
+ end
+ end
+ end
+ end
+end
diff --git a/spec/support/cookie_helper.rb b/spec/support/cookie_helper.rb
index 224619c899c..d72925e1838 100644
--- a/spec/support/cookie_helper.rb
+++ b/spec/support/cookie_helper.rb
@@ -8,6 +8,10 @@ module CookieHelper
page.driver.browser.manage.add_cookie(name: name, value: value, **options)
end
+ def get_cookie(name)
+ page.driver.browser.manage.cookie_named(name)
+ end
+
private
def on_a_page?
diff --git a/spec/support/factory_girl.rb b/spec/support/factory_girl.rb
index eec437fb3aa..c7890e49c66 100644
--- a/spec/support/factory_girl.rb
+++ b/spec/support/factory_girl.rb
@@ -1,3 +1,3 @@
RSpec.configure do |config|
- config.include FactoryGirl::Syntax::Methods
+ config.include FactoryBot::Syntax::Methods
end
diff --git a/spec/support/features/issuable_slash_commands_shared_examples.rb b/spec/support/features/issuable_slash_commands_shared_examples.rb
index 08e21ee2537..2c20821ac3f 100644
--- a/spec/support/features/issuable_slash_commands_shared_examples.rb
+++ b/spec/support/features/issuable_slash_commands_shared_examples.rb
@@ -19,7 +19,7 @@ shared_examples 'issuable record that supports quick actions in its description
let(:new_url_opts) { {} }
before do
- project.team << [master, :master]
+ project.add_master(master)
gitlab_sign_in(master)
end
diff --git a/spec/support/google_api/cloud_platform_helpers.rb b/spec/support/google_api/cloud_platform_helpers.rb
index 8a073e58db8..99752ed396e 100644
--- a/spec/support/google_api/cloud_platform_helpers.rb
+++ b/spec/support/google_api/cloud_platform_helpers.rb
@@ -10,6 +10,12 @@ module GoogleApi
request.session[GoogleApi::CloudPlatform::Client.session_key_for_expires_at] = 1.hour.ago.to_i.to_s
end
+ def stub_google_project_billing_status
+ redis_double = double
+ allow(Gitlab::Redis::SharedState).to receive(:with).and_yield(redis_double)
+ allow(redis_double).to receive(:get).with(CheckGcpProjectBillingWorker.redis_shared_state_key_for('token')).and_return('true')
+ end
+
def stub_cloud_platform_get_zone_cluster(project_id, zone, cluster_id, **options)
WebMock.stub_request(:get, cloud_platform_get_zone_cluster_url(project_id, zone, cluster_id))
.to_return(cloud_platform_response(cloud_platform_cluster_body(options)))
diff --git a/spec/support/markdown_feature.rb b/spec/support/markdown_feature.rb
index c90359d7cfa..39e94ad53de 100644
--- a/spec/support/markdown_feature.rb
+++ b/spec/support/markdown_feature.rb
@@ -8,7 +8,7 @@
# The class renders `spec/fixtures/markdown.md.erb` using ERB, allowing for
# reference to the factory-created objects.
class MarkdownFeature
- include FactoryGirl::Syntax::Methods
+ include FactoryBot::Syntax::Methods
def user
@user ||= create(:user)
@@ -24,7 +24,7 @@ class MarkdownFeature
def project
@project ||= create(:project, :repository, group: group).tap do |project|
- project.team << [user, :master]
+ project.add_master(user)
end
end
@@ -85,7 +85,7 @@ class MarkdownFeature
@xproject ||= begin
group = create(:group, :nested)
create(:project, :repository, namespace: group) do |project|
- project.team << [user, :developer]
+ project.add_developer(user)
end
end
end
diff --git a/spec/support/mentionable_shared_examples.rb b/spec/support/mentionable_shared_examples.rb
index 3ac201f1fb1..1685decbe94 100644
--- a/spec/support/mentionable_shared_examples.rb
+++ b/spec/support/mentionable_shared_examples.rb
@@ -53,7 +53,7 @@ shared_context 'mentionable context' do
set_mentionable_text.call(ref_string)
- project.team << [author, :developer]
+ project.add_developer(author)
end
end
diff --git a/spec/support/reference_parser_shared_examples.rb b/spec/support/reference_parser_shared_examples.rb
index bd83cb88058..baf8bcc04b8 100644
--- a/spec/support/reference_parser_shared_examples.rb
+++ b/spec/support/reference_parser_shared_examples.rb
@@ -26,7 +26,7 @@ RSpec.shared_examples "referenced feature visibility" do |*related_features|
end
it "creates reference for member" do
- project.team << [user, :developer]
+ project.add_developer(user)
expect(subject.nodes_visible_to_user(user, [link])).to eq([link])
end
diff --git a/spec/support/services/issuable_create_service_slash_commands_shared_examples.rb b/spec/support/services/issuable_create_service_slash_commands_shared_examples.rb
index 9399745f900..7b064162726 100644
--- a/spec/support/services/issuable_create_service_slash_commands_shared_examples.rb
+++ b/spec/support/services/issuable_create_service_slash_commands_shared_examples.rb
@@ -3,7 +3,7 @@
shared_examples 'new issuable record that supports quick actions' do
let!(:project) { create(:project, :repository) }
- let(:user) { create(:user).tap { |u| project.team << [u, :master] } }
+ let(:user) { create(:user).tap { |u| project.add_master(u) } }
let(:assignee) { create(:user) }
let!(:milestone) { create(:milestone, project: project) }
let!(:labels) { create_list(:label, 3, project: project) }
@@ -12,7 +12,7 @@ shared_examples 'new issuable record that supports quick actions' do
let(:issuable) { described_class.new(project, user, params).execute }
before do
- project.team << [assignee, :master]
+ project.add_master(assignee)
end
context 'with labels in command only' do
diff --git a/spec/support/services_shared_context.rb b/spec/support/services_shared_context.rb
index 7457484a932..3f1fd169b72 100644
--- a/spec/support/services_shared_context.rb
+++ b/spec/support/services_shared_context.rb
@@ -29,5 +29,13 @@ Service.available_services_names.each do |service|
end
end
end
+
+ def initialize_service(service)
+ service_item = project.find_or_initialize_service(service)
+ service_item.properties = service_attrs
+ service_item.active = true if service == "kubernetes"
+ service_item.save
+ service_item
+ end
end
end
diff --git a/spec/support/shared_examples/requests/api/issuable_participants_examples.rb b/spec/support/shared_examples/requests/api/issuable_participants_examples.rb
new file mode 100644
index 00000000000..96d59e0c472
--- /dev/null
+++ b/spec/support/shared_examples/requests/api/issuable_participants_examples.rb
@@ -0,0 +1,29 @@
+shared_examples 'issuable participants endpoint' do
+ let(:area) { entity.class.name.underscore.pluralize }
+
+ it 'returns participants' do
+ get api("/projects/#{project.id}/#{area}/#{entity.iid}/participants", user)
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(response).to include_pagination_headers
+ expect(json_response).to be_an Array
+ expect(json_response.size).to eq(entity.participants.size)
+
+ last_participant = entity.participants.last
+ expect(json_response.last['id']).to eq(last_participant.id)
+ expect(json_response.last['name']).to eq(last_participant.name)
+ expect(json_response.last['username']).to eq(last_participant.username)
+ end
+
+ it 'returns a 404 when iid does not exist' do
+ get api("/projects/#{project.id}/#{area}/999/participants", user)
+
+ expect(response).to have_gitlab_http_status(404)
+ end
+
+ it 'returns a 404 when id is used instead of iid' do
+ get api("/projects/#{project.id}/#{area}/#{entity.id}/participants", user)
+
+ expect(response).to have_gitlab_http_status(404)
+ end
+end
diff --git a/spec/support/stub_configuration.rb b/spec/support/stub_configuration.rb
index b36cf3c544c..9f08c139322 100644
--- a/spec/support/stub_configuration.rb
+++ b/spec/support/stub_configuration.rb
@@ -7,6 +7,9 @@ module StubConfiguration
allow_any_instance_of(ApplicationSetting).to receive_messages(to_settings(messages))
allow(Gitlab::CurrentSettings.current_application_settings)
.to receive_messages(to_settings(messages))
+
+ # Ensure that we don't use the Markdown cache when stubbing these values
+ allow_any_instance_of(ApplicationSetting).to receive(:cached_html_up_to_date?).and_return(false)
end
def stub_not_protect_default_branch
diff --git a/spec/support/stub_env.rb b/spec/support/stub_env.rb
index 19fbe572930..f621463e621 100644
--- a/spec/support/stub_env.rb
+++ b/spec/support/stub_env.rb
@@ -17,6 +17,7 @@ module StubENV
def add_stubbed_value(key, value)
allow(ENV).to receive(:[]).with(key).and_return(value)
+ allow(ENV).to receive(:key?).with(key).and_return(true)
allow(ENV).to receive(:fetch).with(key).and_return(value)
allow(ENV).to receive(:fetch).with(key, anything()) do |_, default_val|
value || default_val
@@ -29,6 +30,7 @@ module StubENV
def init_stub
allow(ENV).to receive(:[]).and_call_original
+ allow(ENV).to receive(:key?).and_call_original
allow(ENV).to receive(:fetch).and_call_original
add_stubbed_value(STUBBED_KEY, true)
end
diff --git a/spec/support/test_env.rb b/spec/support/test_env.rb
index b300b493f86..664698fcbaf 100644
--- a/spec/support/test_env.rb
+++ b/spec/support/test_env.rb
@@ -1,4 +1,5 @@
require 'rspec/mocks'
+require 'toml'
module TestEnv
extend self
@@ -82,10 +83,10 @@ module TestEnv
setup_gitaly
- # Create repository for FactoryGirl.create(:project)
+ # Create repository for FactoryBot.create(:project)
setup_factory_repo
- # Create repository for FactoryGirl.create(:forked_project_with_submodules)
+ # Create repository for FactoryBot.create(:forked_project_with_submodules)
setup_forked_repo
end
@@ -147,6 +148,9 @@ module TestEnv
version: Gitlab::GitalyClient.expected_server_version,
task: "gitlab:gitaly:install[#{gitaly_dir}]") do
+ # Always re-create config, in case it's outdated. This is fast anyway.
+ Gitlab::SetupHelper.create_gitaly_configuration(gitaly_dir, force: true)
+
start_gitaly(gitaly_dir)
end
end
@@ -215,7 +219,7 @@ module TestEnv
end
def copy_repo(project, bare_repo:, refs:)
- target_repo_path = File.expand_path(project.repository_storage_path + "/#{project.full_path}.git")
+ target_repo_path = File.expand_path(project.repository_storage_path + "/#{project.disk_path}.git")
FileUtils.mkdir_p(target_repo_path)
FileUtils.cp_r("#{File.expand_path(bare_repo)}/.", target_repo_path)
FileUtils.chmod_R 0755, target_repo_path
@@ -347,6 +351,9 @@ module TestEnv
end
def component_needs_update?(component_folder, expected_version)
+ # Allow local overrides of the component for tests during development
+ return false if Rails.env.test? && File.symlink?(component_folder)
+
version = File.read(File.join(component_folder, 'VERSION')).strip
# Notice that this will always yield true when using branch versions
diff --git a/spec/support/updating_mentions_shared_examples.rb b/spec/support/updating_mentions_shared_examples.rb
index 565d3203e4f..5e3f19ba19e 100644
--- a/spec/support/updating_mentions_shared_examples.rb
+++ b/spec/support/updating_mentions_shared_examples.rb
@@ -3,7 +3,7 @@ RSpec.shared_examples 'updating mentions' do |service_class|
let(:service_class) { service_class }
before do
- project.team << [mentioned_user, :developer]
+ project.add_developer(mentioned_user)
end
def update_mentionable(opts)
diff --git a/spec/tasks/gitlab/git_rake_spec.rb b/spec/tasks/gitlab/git_rake_spec.rb
new file mode 100644
index 00000000000..dacc5dc5ae7
--- /dev/null
+++ b/spec/tasks/gitlab/git_rake_spec.rb
@@ -0,0 +1,38 @@
+require 'rake_helper'
+
+describe 'gitlab:git rake tasks' do
+ before do
+ Rake.application.rake_require 'tasks/gitlab/git'
+
+ storages = { 'default' => { 'path' => Settings.absolute('tmp/tests/default_storage') } }
+
+ FileUtils.mkdir_p(Settings.absolute('tmp/tests/default_storage/@hashed/1/2/test.git'))
+ allow(Gitlab.config.repositories).to receive(:storages).and_return(storages)
+ allow_any_instance_of(String).to receive(:color) { |string, _color| string }
+
+ stub_warn_user_is_not_gitlab
+ end
+
+ after do
+ FileUtils.rm_rf(Settings.absolute('tmp/tests/default_storage'))
+ end
+
+ describe 'fsck' do
+ it 'outputs the integrity check for a repo' do
+ expect { run_rake_task('gitlab:git:fsck') }.to output(/Performed Checking integrity at .*@hashed\/1\/2\/test.git/).to_stdout
+ end
+
+ it 'errors out about config.lock issues' do
+ FileUtils.touch(Settings.absolute('tmp/tests/default_storage/@hashed/1/2/test.git/config.lock'))
+
+ expect { run_rake_task('gitlab:git:fsck') }.to output(/file exists\? ... yes/).to_stdout
+ end
+
+ it 'errors out about ref lock issues' do
+ FileUtils.mkdir_p(Settings.absolute('tmp/tests/default_storage/@hashed/1/2/test.git/refs/heads'))
+ FileUtils.touch(Settings.absolute('tmp/tests/default_storage/@hashed/1/2/test.git/refs/heads/blah.lock'))
+
+ expect { run_rake_task('gitlab:git:fsck') }.to output(/Ref lock files exist:/).to_stdout
+ end
+ end
+end
diff --git a/spec/uploaders/records_uploads_spec.rb b/spec/uploaders/records_uploads_spec.rb
index bb32ee62ccb..7ef7fb7d758 100644
--- a/spec/uploaders/records_uploads_spec.rb
+++ b/spec/uploaders/records_uploads_spec.rb
@@ -8,7 +8,7 @@ describe RecordsUploads do
storage :file
def model
- FactoryGirl.build_stubbed(:user)
+ FactoryBot.build_stubbed(:user)
end
end
diff --git a/spec/views/events/event/_push.html.haml_spec.rb b/spec/views/events/event/_push.html.haml_spec.rb
new file mode 100644
index 00000000000..f5634de4916
--- /dev/null
+++ b/spec/views/events/event/_push.html.haml_spec.rb
@@ -0,0 +1,55 @@
+require 'spec_helper'
+
+describe 'events/event/_push.html.haml' do
+ let(:event) { build_stubbed(:push_event) }
+
+ context 'with a branch' do
+ let(:payload) { build_stubbed(:push_event_payload, event: event) }
+
+ before do
+ allow(event).to receive(:push_event_payload).and_return(payload)
+ end
+
+ it 'links to the branch' do
+ allow(event.project.repository).to receive(:branch_exists?).with(event.ref_name).and_return(true)
+ link = project_commits_path(event.project, event.ref_name)
+
+ render partial: 'events/event/push', locals: { event: event }
+
+ expect(rendered).to have_link(event.ref_name, href: link)
+ end
+
+ context 'that has been deleted' do
+ it 'does not link to the branch' do
+ render partial: 'events/event/push', locals: { event: event }
+
+ expect(rendered).not_to have_link(event.ref_name)
+ end
+ end
+ end
+
+ context 'with a tag' do
+ let(:payload) { build_stubbed(:push_event_payload, event: event, ref_type: :tag, ref: 'v0.1.0') }
+
+ before do
+ allow(event).to receive(:push_event_payload).and_return(payload)
+ end
+
+ it 'links to the tag' do
+ allow(event.project.repository).to receive(:tag_exists?).with(event.ref_name).and_return(true)
+ link = project_commits_path(event.project, event.ref_name)
+
+ render partial: 'events/event/push', locals: { event: event }
+
+ expect(rendered).to have_link(event.ref_name, href: link)
+ end
+
+ context 'that has been deleted' do
+ it 'does not link to the tag' do
+ render partial: 'events/event/push', locals: { event: event }
+
+ expect(rendered).not_to have_link(event.ref_name)
+ end
+ end
+ end
+end
diff --git a/spec/views/projects/imports/new.html.haml_spec.rb b/spec/views/projects/imports/new.html.haml_spec.rb
index 9b293065797..ec435ec3b32 100644
--- a/spec/views/projects/imports/new.html.haml_spec.rb
+++ b/spec/views/projects/imports/new.html.haml_spec.rb
@@ -8,7 +8,7 @@ describe "projects/imports/new.html.haml" do
before do
sign_in(user)
- project.team << [user, :master]
+ project.add_master(user)
end
it "escapes HTML in import errors" do
diff --git a/spec/views/projects/jobs/show.html.haml_spec.rb b/spec/views/projects/jobs/show.html.haml_spec.rb
index 6139529013f..6a67da79ec5 100644
--- a/spec/views/projects/jobs/show.html.haml_spec.rb
+++ b/spec/views/projects/jobs/show.html.haml_spec.rb
@@ -187,7 +187,7 @@ describe 'projects/jobs/show' do
context 'when incomplete trigger_request is used' do
before do
- build.trigger_request = FactoryGirl.build(:ci_trigger_request, trigger: nil)
+ build.trigger_request = FactoryBot.build(:ci_trigger_request, trigger: nil)
end
it 'test should not render token block' do
@@ -199,7 +199,7 @@ describe 'projects/jobs/show' do
context 'when complete trigger_request is used' do
before do
- build.trigger_request = FactoryGirl.build(:ci_trigger_request)
+ build.trigger_request = FactoryBot.build(:ci_trigger_request)
end
it 'should render token' do
diff --git a/spec/views/projects/merge_requests/show.html.haml_spec.rb b/spec/views/projects/merge_requests/show.html.haml_spec.rb
index 28d54c2fb77..264e0ce0b40 100644
--- a/spec/views/projects/merge_requests/show.html.haml_spec.rb
+++ b/spec/views/projects/merge_requests/show.html.haml_spec.rb
@@ -54,6 +54,8 @@ describe 'projects/merge_requests/show.html.haml' do
it 'closes the merge request if the source project does not exist' do
closed_merge_request.update_attributes(state: 'open')
forked_project.destroy
+ # Reload merge request so MergeRequest#source_project turns to `nil`
+ closed_merge_request.reload
render
diff --git a/spec/views/projects/tree/_blob_item.html.haml_spec.rb b/spec/views/projects/tree/_blob_item.html.haml_spec.rb
new file mode 100644
index 00000000000..6a477c712ff
--- /dev/null
+++ b/spec/views/projects/tree/_blob_item.html.haml_spec.rb
@@ -0,0 +1,40 @@
+require 'spec_helper'
+
+describe 'projects/tree/_blob_item' do
+ let(:project) { create(:project, :repository) }
+ let(:repository) { project.repository }
+ let(:blob_item) { Gitlab::Git::Tree.where(repository, SeedRepo::Commit::ID, 'files/ruby').first }
+
+ before do
+ assign(:project, project)
+ assign(:repository, repository)
+ assign(:id, File.join('master', ''))
+ assign(:lfs_blob_ids, [])
+ end
+
+ it 'renders blob item' do
+ render_partial(blob_item)
+
+ expect(rendered).to have_content(blob_item.name)
+ expect(rendered).not_to have_selector('.label-lfs', text: 'LFS')
+ end
+
+ describe 'LFS blob' do
+ before do
+ assign(:lfs_blob_ids, [blob_item].map(&:id))
+
+ render_partial(blob_item)
+ end
+
+ it 'renders LFS badge' do
+ expect(rendered).to have_selector('.label-lfs', text: 'LFS')
+ end
+ end
+
+ def render_partial(blob_item)
+ render partial: 'projects/tree/blob_item', locals: {
+ blob_item: blob_item,
+ type: 'blob'
+ }
+ end
+end
diff --git a/spec/views/projects/tree/show.html.haml_spec.rb b/spec/views/projects/tree/show.html.haml_spec.rb
index 3c25e341b39..44b32df0395 100644
--- a/spec/views/projects/tree/show.html.haml_spec.rb
+++ b/spec/views/projects/tree/show.html.haml_spec.rb
@@ -9,6 +9,7 @@ describe 'projects/tree/show' do
before do
assign(:project, project)
assign(:repository, repository)
+ assign(:lfs_blob_ids, [])
allow(view).to receive(:can?).and_return(true)
allow(view).to receive(:can_collaborate_with_project?).and_return(true)
diff --git a/spec/views/shared/notes/_form.html.haml_spec.rb b/spec/views/shared/notes/_form.html.haml_spec.rb
index cae6bee2776..50980718e66 100644
--- a/spec/views/shared/notes/_form.html.haml_spec.rb
+++ b/spec/views/shared/notes/_form.html.haml_spec.rb
@@ -7,7 +7,7 @@ describe 'shared/notes/_form' do
let(:project) { create(:project, :repository) }
before do
- project.team << [user, :master]
+ project.add_master(user)
assign(:project, project)
assign(:note, note)
diff --git a/spec/workers/background_migration_worker_spec.rb b/spec/workers/background_migration_worker_spec.rb
index 1c54cf55fa0..d67e7698635 100644
--- a/spec/workers/background_migration_worker_spec.rb
+++ b/spec/workers/background_migration_worker_spec.rb
@@ -1,13 +1,32 @@
require 'spec_helper'
-describe BackgroundMigrationWorker, :sidekiq do
+describe BackgroundMigrationWorker, :sidekiq, :clean_gitlab_redis_shared_state do
+ let(:worker) { described_class.new }
+
describe '.perform' do
it 'performs a background migration' do
expect(Gitlab::BackgroundMigration)
.to receive(:perform)
.with('Foo', [10, 20])
- described_class.new.perform('Foo', [10, 20])
+ worker.perform('Foo', [10, 20])
+ end
+
+ it 'reschedules a migration if it was performed recently' do
+ expect(worker)
+ .to receive(:always_perform?)
+ .and_return(false)
+
+ worker.lease_for('Foo').try_obtain
+
+ expect(Gitlab::BackgroundMigration)
+ .not_to receive(:perform)
+
+ expect(described_class)
+ .to receive(:perform_in)
+ .with(a_kind_of(Numeric), 'Foo', [10, 20])
+
+ worker.perform('Foo', [10, 20])
end
end
end
diff --git a/spec/workers/check_gcp_project_billing_worker_spec.rb b/spec/workers/check_gcp_project_billing_worker_spec.rb
new file mode 100644
index 00000000000..f52a903327c
--- /dev/null
+++ b/spec/workers/check_gcp_project_billing_worker_spec.rb
@@ -0,0 +1,61 @@
+require 'spec_helper'
+
+describe CheckGcpProjectBillingWorker do
+ describe '.perform' do
+ let(:token) { 'bogustoken' }
+
+ subject { described_class.new.perform('token_key') }
+
+ context 'when there is a token in redis' do
+ before do
+ allow_any_instance_of(described_class).to receive(:get_session_token).and_return(token)
+ end
+
+ context 'when there is no lease' do
+ before do
+ allow_any_instance_of(described_class).to receive(:try_obtain_lease_for).and_return('randomuuid')
+ end
+
+ it 'calls the service' do
+ expect(CheckGcpProjectBillingService).to receive_message_chain(:new, :execute).and_return([double])
+
+ subject
+ end
+
+ it 'stores billing status in redis' do
+ redis_double = double
+
+ expect(CheckGcpProjectBillingService).to receive_message_chain(:new, :execute).and_return([double])
+ expect(Gitlab::Redis::SharedState).to receive(:with).and_yield(redis_double)
+ expect(redis_double).to receive(:set).with(described_class.redis_shared_state_key_for(token), anything, anything)
+
+ subject
+ end
+ end
+
+ context 'when there is a lease' do
+ before do
+ allow_any_instance_of(described_class).to receive(:try_obtain_lease_for).and_return(false)
+ end
+
+ it 'does not call the service' do
+ expect(CheckGcpProjectBillingService).not_to receive(:new)
+
+ subject
+ end
+ end
+ end
+
+ context 'when there is no token in redis' do
+ before do
+ allow_any_instance_of(described_class).to receive(:get_session_token).and_return(nil)
+ end
+
+ it 'does not call the service' do
+ expect(CheckGcpProjectBillingService).not_to receive(:new)
+
+ subject
+ end
+ end
+ end
+end
diff --git a/spec/workers/concerns/application_worker_spec.rb b/spec/workers/concerns/application_worker_spec.rb
index 0145563e0ed..901d77178bc 100644
--- a/spec/workers/concerns/application_worker_spec.rb
+++ b/spec/workers/concerns/application_worker_spec.rb
@@ -17,6 +17,14 @@ describe ApplicationWorker do
end
end
+ describe '.queue_namespace' do
+ it 'sets the queue name based on the class name' do
+ worker.queue_namespace :some_namespace
+
+ expect(worker.queue).to eq('some_namespace:foo_bar_dummy')
+ end
+ end
+
describe '.queue' do
it 'returns the queue name' do
worker.sidekiq_options queue: :some_queue
diff --git a/spec/workers/concerns/cluster_queue_spec.rb b/spec/workers/concerns/cluster_queue_spec.rb
index 5049886b55c..4118b9aa194 100644
--- a/spec/workers/concerns/cluster_queue_spec.rb
+++ b/spec/workers/concerns/cluster_queue_spec.rb
@@ -14,6 +14,6 @@ describe ClusterQueue do
it 'sets a default pipelines queue automatically' do
expect(worker.sidekiq_options['queue'])
- .to eq :gcp_cluster
+ .to eq 'gcp_cluster:dummy'
end
end
diff --git a/spec/workers/concerns/cronjob_queue_spec.rb b/spec/workers/concerns/cronjob_queue_spec.rb
index 3ae1c5f54d8..c042a52f41f 100644
--- a/spec/workers/concerns/cronjob_queue_spec.rb
+++ b/spec/workers/concerns/cronjob_queue_spec.rb
@@ -13,7 +13,7 @@ describe CronjobQueue do
end
it 'sets the queue name of a worker' do
- expect(worker.sidekiq_options['queue'].to_s).to eq('cronjob')
+ expect(worker.sidekiq_options['queue'].to_s).to eq('cronjob:dummy')
end
it 'disables retrying of failed jobs' do
diff --git a/spec/workers/concerns/gitlab/github_import/queue_spec.rb b/spec/workers/concerns/gitlab/github_import/queue_spec.rb
index 9c69ee32da1..a96f583aff7 100644
--- a/spec/workers/concerns/gitlab/github_import/queue_spec.rb
+++ b/spec/workers/concerns/gitlab/github_import/queue_spec.rb
@@ -11,6 +11,6 @@ describe Gitlab::GithubImport::Queue do
include Gitlab::GithubImport::Queue
end
- expect(worker.sidekiq_options['queue']).to eq('github_importer')
+ expect(worker.sidekiq_options['queue']).to eq('github_importer:dummy')
end
end
diff --git a/spec/workers/concerns/pipeline_queue_spec.rb b/spec/workers/concerns/pipeline_queue_spec.rb
index dd911760948..a312b307fce 100644
--- a/spec/workers/concerns/pipeline_queue_spec.rb
+++ b/spec/workers/concerns/pipeline_queue_spec.rb
@@ -14,15 +14,6 @@ describe PipelineQueue do
it 'sets a default pipelines queue automatically' do
expect(worker.sidekiq_options['queue'])
- .to eq 'pipeline_default'
- end
-
- describe '.enqueue_in' do
- it 'sets a custom sidekiq queue with prefix and group' do
- worker.enqueue_in(group: :processing)
-
- expect(worker.sidekiq_options['queue'])
- .to eq 'pipeline_processing'
- end
+ .to eq 'pipeline_default:dummy'
end
end
diff --git a/spec/workers/concerns/project_import_options_spec.rb b/spec/workers/concerns/project_import_options_spec.rb
new file mode 100644
index 00000000000..b6c111df8b9
--- /dev/null
+++ b/spec/workers/concerns/project_import_options_spec.rb
@@ -0,0 +1,40 @@
+require 'spec_helper'
+
+describe ProjectImportOptions do
+ let(:project) { create(:project, :import_started) }
+ let(:job) { { 'args' => [project.id, nil, nil], 'jid' => '123' } }
+ let(:worker_class) do
+ Class.new do
+ include Sidekiq::Worker
+ include ProjectImportOptions
+ end
+ end
+
+ it 'sets default retry limit' do
+ expect(worker_class.sidekiq_options['retry']).to eq(ProjectImportOptions::IMPORT_RETRY_COUNT)
+ end
+
+ it 'sets default status expiration' do
+ expect(worker_class.sidekiq_options['status_expiration']).to eq(StuckImportJobsWorker::IMPORT_JOBS_EXPIRATION)
+ end
+
+ describe '.sidekiq_retries_exhausted' do
+ it 'marks fork as failed' do
+ expect { worker_class.sidekiq_retries_exhausted_block.call(job) }.to change { project.reload.import_status }.from("started").to("failed")
+ end
+
+ it 'logs the appropriate error message for forked projects' do
+ allow_any_instance_of(Project).to receive(:forked?).and_return(true)
+
+ worker_class.sidekiq_retries_exhausted_block.call(job)
+
+ expect(project.reload.import_error).to include("fork")
+ end
+
+ it 'logs the appropriate error message for forked projects' do
+ worker_class.sidekiq_retries_exhausted_block.call(job)
+
+ expect(project.reload.import_error).to include("import")
+ end
+ end
+end
diff --git a/spec/workers/concerns/repository_check_queue_spec.rb b/spec/workers/concerns/repository_check_queue_spec.rb
index fdbbfcc90a5..d2eeecfc9a8 100644
--- a/spec/workers/concerns/repository_check_queue_spec.rb
+++ b/spec/workers/concerns/repository_check_queue_spec.rb
@@ -13,7 +13,7 @@ describe RepositoryCheckQueue do
end
it 'sets the queue name of a worker' do
- expect(worker.sidekiq_options['queue'].to_s).to eq('repository_check')
+ expect(worker.sidekiq_options['queue'].to_s).to eq('repository_check:dummy')
end
it 'disables retrying of failed jobs' do
diff --git a/spec/workers/every_sidekiq_worker_spec.rb b/spec/workers/every_sidekiq_worker_spec.rb
index 7ee0a51a263..9e3b99b3502 100644
--- a/spec/workers/every_sidekiq_worker_spec.rb
+++ b/spec/workers/every_sidekiq_worker_spec.rb
@@ -1,21 +1,36 @@
require 'spec_helper'
describe 'Every Sidekiq worker' do
- it 'includes ApplicationWorker' do
- expect(Gitlab::SidekiqConfig.workers).to all(include(ApplicationWorker))
- end
-
it 'does not use the default queue' do
expect(Gitlab::SidekiqConfig.workers.map(&:queue)).not_to include('default')
end
it 'uses the cronjob queue when the worker runs as a cronjob' do
- expect(Gitlab::SidekiqConfig.cron_workers.map(&:queue)).to all(eq('cronjob'))
+ expect(Gitlab::SidekiqConfig.cron_workers.map(&:queue)).to all(start_with('cronjob:'))
+ end
+
+ it 'has its queue in app/workers/all_queues.yml', :aggregate_failures do
+ file_worker_queues = Gitlab::SidekiqConfig.worker_queues.to_set
+
+ worker_queues = Gitlab::SidekiqConfig.workers.map(&:queue).to_set
+ worker_queues << ActionMailer::DeliveryJob.queue_name
+ worker_queues << 'default'
+
+ missing_from_file = worker_queues - file_worker_queues
+ expect(missing_from_file).to be_empty, "expected #{missing_from_file.to_a.inspect} to be in app/workers/all_queues.yml"
+
+ unncessarily_in_file = file_worker_queues - worker_queues
+ expect(unncessarily_in_file).to be_empty, "expected #{unncessarily_in_file.to_a.inspect} not to be in app/workers/all_queues.yml"
end
- it 'defines the queue in the Sidekiq configuration file' do
- config_queue_names = Gitlab::SidekiqConfig.config_queues.to_set
+ it 'has its queue or namespace in config/sidekiq_queues.yml', :aggregate_failures do
+ config_queues = Gitlab::SidekiqConfig.config_queues.to_set
+
+ Gitlab::SidekiqConfig.workers.each do |worker|
+ queue = worker.queue
+ queue_namespace = queue.split(':').first
- expect(Gitlab::SidekiqConfig.worker_queues).to all(be_in(config_queue_names))
+ expect(config_queues).to include(queue).or(include(queue_namespace))
+ end
end
end
diff --git a/spec/workers/merge_worker_spec.rb b/spec/workers/merge_worker_spec.rb
index 303193bab9b..c861a56497e 100644
--- a/spec/workers/merge_worker_spec.rb
+++ b/spec/workers/merge_worker_spec.rb
@@ -8,7 +8,7 @@ describe MergeWorker do
let!(:author) { merge_request.author }
before do
- source_project.team << [author, :master]
+ source_project.add_master(author)
source_project.repository.expire_branches_cache
end
diff --git a/spec/workers/rebase_worker_spec.rb b/spec/workers/rebase_worker_spec.rb
new file mode 100644
index 00000000000..20aff020dbb
--- /dev/null
+++ b/spec/workers/rebase_worker_spec.rb
@@ -0,0 +1,27 @@
+require 'spec_helper'
+
+describe RebaseWorker, '#perform' do
+ context 'when rebasing an MR from a fork where upstream has protected branches' do
+ let(:upstream_project) { create(:project, :repository) }
+ let(:fork_project) { create(:project, :repository) }
+
+ let(:merge_request) do
+ create(:merge_request,
+ source_project: fork_project,
+ source_branch: 'feature_conflict',
+ target_project: upstream_project,
+ target_branch: 'master')
+ end
+
+ before do
+ create(:forked_project_link, forked_to_project: fork_project, forked_from_project: upstream_project)
+ end
+
+ it 'sets the correct project for running hooks' do
+ expect(MergeRequests::RebaseService)
+ .to receive(:new).with(fork_project, merge_request.author).and_call_original
+
+ subject.perform(merge_request, merge_request.author)
+ end
+ end
+end
diff --git a/spec/workers/repository_fork_worker_spec.rb b/spec/workers/repository_fork_worker_spec.rb
index 74c85848b7e..31598586f59 100644
--- a/spec/workers/repository_fork_worker_spec.rb
+++ b/spec/workers/repository_fork_worker_spec.rb
@@ -1,17 +1,21 @@
require 'spec_helper'
describe RepositoryForkWorker do
- let(:project) { create(:project, :repository) }
- let(:fork_project) { create(:project, :repository, :import_scheduled, forked_from_project: project) }
- let(:shell) { Gitlab::Shell.new }
-
- subject { described_class.new }
-
- before do
- allow(subject).to receive(:gitlab_shell).and_return(shell)
+ describe 'modules' do
+ it 'includes ProjectImportOptions' do
+ expect(described_class).to include_module(ProjectImportOptions)
+ end
end
describe "#perform" do
+ let(:project) { create(:project, :repository) }
+ let(:fork_project) { create(:project, :repository, :import_scheduled, forked_from_project: project) }
+ let(:shell) { Gitlab::Shell.new }
+
+ before do
+ allow(subject).to receive(:gitlab_shell).and_return(shell)
+ end
+
def perform!
subject.perform(fork_project.id, '/test/path', project.disk_path)
end
@@ -60,14 +64,7 @@ describe RepositoryForkWorker do
expect_fork_repository.and_return(false)
- expect { perform! }.to raise_error(RepositoryForkWorker::ForkError, error_message)
- end
-
- it 'handles unexpected error' do
- expect_fork_repository.and_raise(RuntimeError)
-
- expect { perform! }.to raise_error(RepositoryForkWorker::ForkError)
- expect(fork_project.reload.import_status).to eq('failed')
+ expect { perform! }.to raise_error(StandardError, error_message)
end
end
end
diff --git a/spec/workers/repository_import_worker_spec.rb b/spec/workers/repository_import_worker_spec.rb
index 0af537647ad..7274a9f00f9 100644
--- a/spec/workers/repository_import_worker_spec.rb
+++ b/spec/workers/repository_import_worker_spec.rb
@@ -1,11 +1,15 @@
require 'spec_helper'
describe RepositoryImportWorker do
- let(:project) { create(:project, :import_scheduled) }
-
- subject { described_class.new }
+ describe 'modules' do
+ it 'includes ProjectImportOptions' do
+ expect(described_class).to include_module(ProjectImportOptions)
+ end
+ end
describe '#perform' do
+ let(:project) { create(:project, :import_scheduled) }
+
context 'when worker was reset without cleanup' do
let(:jid) { '12345678' }
let(:started_project) { create(:project, :import_started, import_jid: jid) }
@@ -28,6 +32,7 @@ describe RepositoryImportWorker do
expect_any_instance_of(Projects::ImportService).to receive(:execute)
.and_return({ status: :ok })
+ expect_any_instance_of(Project).to receive(:after_import).and_call_original
expect_any_instance_of(Repository).to receive(:expire_emptiness_caches)
expect_any_instance_of(Project).to receive(:import_finish)
@@ -44,22 +49,11 @@ describe RepositoryImportWorker do
expect do
subject.perform(project.id)
- end.to raise_error(RepositoryImportWorker::ImportError, error)
+ end.to raise_error(StandardError, error)
expect(project.reload.import_jid).not_to be_nil
end
end
- context 'with unexpected error' do
- it 'marks import as failed' do
- allow_any_instance_of(Projects::ImportService).to receive(:execute).and_raise(RuntimeError)
-
- expect do
- subject.perform(project.id)
- end.to raise_error(RepositoryImportWorker::ImportError)
- expect(project.reload.import_status).to eq('failed')
- end
- end
-
context 'when using an asynchronous importer' do
it 'does not mark the import process as finished' do
service = double(:service)
diff --git a/spec/workers/run_pipeline_schedule_worker_spec.rb b/spec/workers/run_pipeline_schedule_worker_spec.rb
new file mode 100644
index 00000000000..481a84837f9
--- /dev/null
+++ b/spec/workers/run_pipeline_schedule_worker_spec.rb
@@ -0,0 +1,39 @@
+require 'spec_helper'
+
+describe RunPipelineScheduleWorker do
+ describe '#perform' do
+ set(:project) { create(:project) }
+ set(:user) { create(:user) }
+ set(:pipeline_schedule) { create(:ci_pipeline_schedule, :nightly, project: project ) }
+ let(:worker) { described_class.new }
+
+ context 'when a project not found' do
+ it 'does not call the Service' do
+ expect(Ci::CreatePipelineService).not_to receive(:new)
+ expect(worker).not_to receive(:run_pipeline_schedule)
+
+ worker.perform(100000, user.id)
+ end
+ end
+
+ context 'when a user not found' do
+ it 'does not call the Service' do
+ expect(Ci::CreatePipelineService).not_to receive(:new)
+ expect(worker).not_to receive(:run_pipeline_schedule)
+
+ worker.perform(pipeline_schedule.id, 10000)
+ end
+ end
+
+ context 'when everything is ok' do
+ let(:create_pipeline_service) { instance_double(Ci::CreatePipelineService) }
+
+ it 'calls the Service' do
+ expect(Ci::CreatePipelineService).to receive(:new).with(project, user, ref: pipeline_schedule.ref).and_return(create_pipeline_service)
+ expect(create_pipeline_service).to receive(:execute).with(:schedule, ignore_skip_ci: true, save_on_errors: false, schedule: pipeline_schedule)
+
+ worker.perform(pipeline_schedule.id, user.id)
+ end
+ end
+ end
+end
diff --git a/spec/workers/stuck_merge_jobs_worker_spec.rb b/spec/workers/stuck_merge_jobs_worker_spec.rb
index f8b55e873df..c2c2a5f9121 100644
--- a/spec/workers/stuck_merge_jobs_worker_spec.rb
+++ b/spec/workers/stuck_merge_jobs_worker_spec.rb
@@ -14,7 +14,6 @@ describe StuckMergeJobsWorker do
mr_with_sha.reload
mr_without_sha.reload
-
expect(mr_with_sha).to be_merged
expect(mr_without_sha).to be_opened
expect(mr_with_sha.merge_jid).to be_present
@@ -24,10 +23,13 @@ describe StuckMergeJobsWorker do
it 'updates merge request to opened when locked but has not been merged' do
allow(Gitlab::SidekiqStatus).to receive(:completed_jids).and_return(%w(123))
merge_request = create(:merge_request, :locked, merge_jid: '123', state: :locked)
+ pipeline = create(:ci_empty_pipeline, project: merge_request.project, ref: merge_request.source_branch, sha: merge_request.source_branch_sha)
worker.perform
- expect(merge_request.reload).to be_opened
+ merge_request.reload
+ expect(merge_request).to be_opened
+ expect(merge_request.head_pipeline).to eq(pipeline)
end
it 'logs updated stuck merge job ids' do
diff --git a/spec/workers/update_head_pipeline_for_merge_request_worker_spec.rb b/spec/workers/update_head_pipeline_for_merge_request_worker_spec.rb
index 522e1566271..9adde5fc21a 100644
--- a/spec/workers/update_head_pipeline_for_merge_request_worker_spec.rb
+++ b/spec/workers/update_head_pipeline_for_merge_request_worker_spec.rb
@@ -22,7 +22,7 @@ describe UpdateHeadPipelineForMergeRequestWorker do
end
it 'does not update head_pipeline_id' do
- expect { subject.perform(merge_request.id) }.to raise_error(ArgumentError)
+ expect { subject.perform(merge_request.id) }.not_to raise_error
expect(merge_request.reload.head_pipeline_id).to eq(nil)
end