summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.codeclimate.yml15
-rw-r--r--.gitlab-ci.yml19
-rw-r--r--.rubocop.yml7
-rw-r--r--CHANGELOG.md15
-rw-r--r--app/assets/javascripts/api.js4
-rw-r--r--app/assets/javascripts/boards/filtered_search_boards.js1
-rw-r--r--app/assets/javascripts/commit/pipelines/pipelines_table.js2
-rw-r--r--app/assets/javascripts/dispatcher.js29
-rw-r--r--app/assets/javascripts/dropzone_input.js4
-rw-r--r--app/assets/javascripts/environments/components/environment_item.vue6
-rw-r--r--app/assets/javascripts/environments/stores/environments_store.js1
-rw-r--r--app/assets/javascripts/filtered_search/dropdown_hint.js2
-rw-r--r--app/assets/javascripts/gfm_auto_complete.js6
-rw-r--r--app/assets/javascripts/groups/components/group_item.vue6
-rw-r--r--app/assets/javascripts/groups/stores/groups_store.js1
-rw-r--r--app/assets/javascripts/issue_show/components/fields/description.vue2
-rw-r--r--app/assets/javascripts/lib/utils/datetime_utility.js2
-rw-r--r--app/assets/javascripts/locale/en/app.js2
-rw-r--r--app/assets/javascripts/locale/es/app.js2
-rw-r--r--app/assets/javascripts/locale/fr/app.js1
-rw-r--r--app/assets/javascripts/locale/zh_HK/app.js2
-rw-r--r--app/assets/javascripts/milestone.js159
-rw-r--r--app/assets/javascripts/notes.js74
-rw-r--r--app/assets/javascripts/pipeline_schedules/components/interval_pattern_input.js15
-rw-r--r--app/assets/javascripts/pipeline_schedules/components/pipeline_schedules_callout.js14
-rw-r--r--app/assets/javascripts/pipelines/components/pipeline_url.vue44
-rw-r--r--app/assets/javascripts/pipelines/components/pipelines.vue289
-rw-r--r--app/assets/javascripts/pipelines/components/pipelines_actions.js91
-rw-r--r--app/assets/javascripts/pipelines/components/pipelines_actions.vue88
-rw-r--r--app/assets/javascripts/pipelines/components/pipelines_artifacts.js33
-rw-r--r--app/assets/javascripts/pipelines/components/pipelines_artifacts.vue51
-rw-r--r--app/assets/javascripts/pipelines/components/time_ago.js98
-rw-r--r--app/assets/javascripts/pipelines/components/time_ago.vue93
-rw-r--r--app/assets/javascripts/pipelines/index.js22
-rw-r--r--app/assets/javascripts/pipelines/pipelines.js293
-rw-r--r--app/assets/javascripts/pipelines/pipelines_bundle.js24
-rw-r--r--app/assets/javascripts/preview_markdown.js2
-rw-r--r--app/assets/javascripts/sidebar/components/time_tracking/help_state.js4
-rw-r--r--app/assets/javascripts/sidebar/components/time_tracking/sidebar_time_tracking.js8
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/mr_widget_memory_usage.js4
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/mr_widget_related_links.js41
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_merged.js10
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/mr_widget_options.js11
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/stores/mr_widget_store.js13
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/stores/state_maps.js13
-rw-r--r--app/assets/javascripts/vue_shared/components/commit.js159
-rw-r--r--app/assets/javascripts/vue_shared/components/commit.vue166
-rw-r--r--app/assets/javascripts/vue_shared/components/pipelines_table.js55
-rw-r--r--app/assets/javascripts/vue_shared/components/pipelines_table.vue64
-rw-r--r--app/assets/javascripts/vue_shared/components/pipelines_table_row.vue (renamed from app/assets/javascripts/vue_shared/components/pipelines_table_row.js)158
-rw-r--r--app/assets/javascripts/vue_shared/components/time_ago_tooltip.vue16
-rw-r--r--app/assets/stylesheets/framework/files.scss51
-rw-r--r--app/assets/stylesheets/framework/filters.scss2
-rw-r--r--app/assets/stylesheets/framework/forms.scss14
-rw-r--r--app/assets/stylesheets/framework/layout.scss4
-rw-r--r--app/assets/stylesheets/framework/markdown_area.scss11
-rw-r--r--app/assets/stylesheets/framework/page-header.scss4
-rw-r--r--app/assets/stylesheets/framework/panels.scss90
-rw-r--r--app/assets/stylesheets/framework/responsive-tables.scss48
-rw-r--r--app/assets/stylesheets/framework/selects.scss35
-rw-r--r--app/assets/stylesheets/framework/tw_bootstrap_variables.scss6
-rw-r--r--app/assets/stylesheets/framework/variables.scss8
-rw-r--r--app/assets/stylesheets/mailers/devise.scss140
-rw-r--r--app/assets/stylesheets/pages/boards.scss28
-rw-r--r--app/assets/stylesheets/pages/builds.scss3
-rw-r--r--app/assets/stylesheets/pages/environments.scss37
-rw-r--r--app/assets/stylesheets/pages/events.scss1
-rw-r--r--app/assets/stylesheets/pages/issues.scss2
-rw-r--r--app/assets/stylesheets/pages/issues/issue_count_badge.scss29
-rw-r--r--app/assets/stylesheets/pages/merge_requests.scss4
-rw-r--r--app/assets/stylesheets/pages/milestone.scss6
-rw-r--r--app/assets/stylesheets/pages/pipeline_schedules.scss2
-rw-r--r--app/assets/stylesheets/pages/pipelines.scss181
-rw-r--r--app/assets/stylesheets/pages/status.scss182
-rw-r--r--app/assets/stylesheets/pages/wiki.scss9
-rw-r--r--app/controllers/admin/application_settings_controller.rb2
-rw-r--r--app/controllers/concerns/milestone_actions.rb2
-rw-r--r--app/controllers/projects/application_controller.rb4
-rw-r--r--app/controllers/projects/git_http_client_controller.rb46
-rw-r--r--app/controllers/projects/git_http_controller.rb2
-rw-r--r--app/controllers/projects/graphs_controller.rb1
-rw-r--r--app/controllers/projects/issues_controller.rb15
-rw-r--r--app/controllers/projects/milestones_controller.rb18
-rw-r--r--app/controllers/projects/pipelines_controller.rb1
-rw-r--r--app/finders/groups_finder.rb18
-rw-r--r--app/finders/issuable_finder.rb13
-rw-r--r--app/helpers/application_helper.rb8
-rw-r--r--app/helpers/blame_helper.rb21
-rw-r--r--app/helpers/broadcast_messages_helper.rb2
-rw-r--r--app/helpers/diff_helper.rb28
-rw-r--r--app/helpers/emails_helper.rb13
-rw-r--r--app/helpers/notes_helper.rb4
-rw-r--r--app/helpers/projects_helper.rb30
-rw-r--r--app/mailers/devise_mailer.rb4
-rw-r--r--app/models/application_setting.rb14
-rw-r--r--app/models/blob_viewer/server_side.rb14
-rw-r--r--app/models/broadcast_message.rb2
-rw-r--r--app/models/ci/build.rb4
-rw-r--r--app/models/commit_status.rb4
-rw-r--r--app/models/concerns/issuable.rb2
-rw-r--r--app/models/concerns/milestoneish.rb10
-rw-r--r--app/models/diff_viewer/added.rb8
-rw-r--r--app/models/diff_viewer/base.rb87
-rw-r--r--app/models/diff_viewer/client_side.rb10
-rw-r--r--app/models/diff_viewer/deleted.rb8
-rw-r--r--app/models/diff_viewer/image.rb12
-rw-r--r--app/models/diff_viewer/mode_changed.rb8
-rw-r--r--app/models/diff_viewer/no_preview.rb9
-rw-r--r--app/models/diff_viewer/not_diffable.rb9
-rw-r--r--app/models/diff_viewer/renamed.rb8
-rw-r--r--app/models/diff_viewer/rich.rb11
-rw-r--r--app/models/diff_viewer/server_side.rb26
-rw-r--r--app/models/diff_viewer/simple.rb11
-rw-r--r--app/models/diff_viewer/static.rb10
-rw-r--r--app/models/diff_viewer/text.rb15
-rw-r--r--app/models/environment.rb3
-rw-r--r--app/models/generic_commit_status.rb1
-rw-r--r--app/models/issue.rb5
-rw-r--r--app/models/legacy_diff_note.rb2
-rw-r--r--app/models/merge_request.rb5
-rw-r--r--app/models/merge_request_diff.rb54
-rw-r--r--app/models/merge_request_diff_file.rb11
-rw-r--r--app/models/milestone.rb32
-rw-r--r--app/models/note.rb2
-rw-r--r--app/models/notification_setting.rb45
-rw-r--r--app/models/project_services/kubernetes_service.rb37
-rw-r--r--app/models/project_services/mattermost_slash_commands_service.rb2
-rw-r--r--app/models/project_services/slack_slash_commands_service.rb2
-rw-r--r--app/models/project_services/slash_commands_service.rb (renamed from app/models/project_services/chat_slash_commands_service.rb)6
-rw-r--r--app/models/user.rb29
-rw-r--r--app/policies/global_policy.rb2
-rw-r--r--app/policies/project_policy.rb2
-rw-r--r--app/serializers/build_details_entity.rb11
-rw-r--r--app/serializers/build_serializer.rb2
-rw-r--r--app/serializers/deployment_entity.rb4
-rw-r--r--app/serializers/group_entity.rb6
-rw-r--r--app/serializers/issuable_entity.rb1
-rw-r--r--app/serializers/job_entity.rb (renamed from app/serializers/build_entity.rb)4
-rw-r--r--app/serializers/job_group_entity.rb2
-rw-r--r--app/services/git_push_service.rb6
-rw-r--r--app/services/git_tag_push_service.rb4
-rw-r--r--app/services/issuable_base_service.rb11
-rw-r--r--app/services/merge_requests/update_service.rb6
-rw-r--r--app/services/notes/create_service.rb8
-rw-r--r--app/services/notes/quick_actions_service.rb (renamed from app/services/notes/slash_commands_service.rb)4
-rw-r--r--app/services/notification_recipient_service.rb8
-rw-r--r--app/services/notification_service.rb2
-rw-r--r--app/services/preview_markdown_service.rb12
-rw-r--r--app/services/projects/autocomplete_service.rb2
-rw-r--r--app/services/quick_actions/interpret_service.rb (renamed from app/services/slash_commands/interpret_service.rb)14
-rw-r--r--app/services/users/refresh_authorized_projects_service.rb2
-rw-r--r--app/views/admin/application_settings/_form.html.haml19
-rw-r--r--app/views/admin/broadcast_messages/_form.html.haml4
-rw-r--r--app/views/devise/mailer/confirmation_instructions.html.haml31
-rw-r--r--app/views/devise/mailer/password_change.html.haml18
-rw-r--r--app/views/devise/mailer/reset_password_instructions.html.haml22
-rw-r--r--app/views/devise/mailer/unlock_instructions.html.haml17
-rw-r--r--app/views/discussions/_diff_with_notes.html.haml5
-rw-r--r--app/views/help/index.html.haml20
-rw-r--r--app/views/help/show.html.haml2
-rw-r--r--app/views/layouts/_broadcast.html.haml3
-rw-r--r--app/views/layouts/_mailer.html.haml74
-rw-r--r--app/views/layouts/devise_mailer.html.haml34
-rw-r--r--app/views/layouts/mailer.html.haml73
-rw-r--r--app/views/layouts/mailer/devise.html.haml21
-rw-r--r--app/views/profiles/show.html.haml15
-rw-r--r--app/views/projects/_visibility_select.html.haml4
-rw-r--r--app/views/projects/_zen.html.haml9
-rw-r--r--app/views/projects/blame/_age_map_legend.html.haml12
-rw-r--r--app/views/projects/blame/show.html.haml8
-rw-r--r--app/views/projects/blob/_upload.html.haml8
-rw-r--r--app/views/projects/boards/components/_board.html.haml6
-rw-r--r--app/views/projects/buttons/_download.html.haml2
-rw-r--r--app/views/projects/buttons/_dropdown.html.haml2
-rw-r--r--app/views/projects/buttons/_fork.html.haml6
-rw-r--r--app/views/projects/commit/_change.html.haml23
-rw-r--r--app/views/projects/commit/_commit_box.html.haml28
-rw-r--r--app/views/projects/commits/_commit.html.haml8
-rw-r--r--app/views/projects/deployments/_deployment.html.haml4
-rw-r--r--app/views/projects/diffs/_collapsed.html.haml5
-rw-r--r--app/views/projects/diffs/_content.html.haml27
-rw-r--r--app/views/projects/diffs/_render_error.html.haml6
-rw-r--r--app/views/projects/diffs/_viewer.html.haml16
-rw-r--r--app/views/projects/diffs/viewers/_added.html.haml2
-rw-r--r--app/views/projects/diffs/viewers/_deleted.html.haml2
-rw-r--r--app/views/projects/diffs/viewers/_image.html.haml1
-rw-r--r--app/views/projects/diffs/viewers/_mode_changed.html.haml3
-rw-r--r--app/views/projects/diffs/viewers/_no_preview.html.haml2
-rw-r--r--app/views/projects/diffs/viewers/_not_diffable.html.haml2
-rw-r--r--app/views/projects/diffs/viewers/_renamed.html.haml2
-rw-r--r--app/views/projects/diffs/viewers/_text.html.haml2
-rw-r--r--app/views/projects/edit.html.haml6
-rw-r--r--app/views/projects/issues/_issue_by_email.html.haml2
-rw-r--r--app/views/projects/jobs/_sidebar.html.haml1
-rw-r--r--app/views/projects/merge_requests/conflicts/_submit_form.html.haml2
-rw-r--r--app/views/projects/pipeline_schedules/_form.html.haml10
-rw-r--r--app/views/projects/pipeline_schedules/_pipeline_schedule.html.haml4
-rw-r--r--app/views/projects/pipeline_schedules/index.html.haml2
-rw-r--r--app/views/projects/project_members/_new_project_member.html.haml4
-rw-r--r--app/views/projects/project_members/_new_shared_group.html.haml2
-rw-r--r--app/views/projects/wikis/_form.html.haml12
-rw-r--r--app/views/projects/wikis/_new.html.haml39
-rw-r--r--app/views/projects/wikis/edit.html.haml51
-rw-r--r--app/views/projects/wikis/git_access.html.haml67
-rw-r--r--app/views/projects/wikis/history.html.haml69
-rw-r--r--app/views/projects/wikis/show.html.haml45
-rw-r--r--app/views/shared/_clone_panel.html.haml2
-rw-r--r--app/views/shared/_commit_message_container.html.haml2
-rw-r--r--app/views/shared/_new_commit_form.html.haml8
-rw-r--r--app/views/shared/_new_merge_request_checkbox.html.haml8
-rw-r--r--app/views/shared/form_elements/_description.html.haml10
-rw-r--r--app/views/shared/issuable/form/_merge_params.html.haml3
-rw-r--r--app/views/shared/milestones/_issuables.html.haml6
-rw-r--r--app/views/shared/milestones/_tabs.html.haml6
-rw-r--r--app/views/shared/notes/_form.html.haml12
-rw-r--r--app/views/shared/notes/_hints.html.haml6
-rw-r--r--app/views/shared/notes/_notes_with_form.html.haml2
-rw-r--r--changelogs/unreleased/12151-add-since-and-until-params-to-issuables.yml4
-rw-r--r--changelogs/unreleased/12200-add-french-translation.yml4
-rw-r--r--changelogs/unreleased/13336-multiple-broadcast-messages.yml4
-rw-r--r--changelogs/unreleased/23998-blame-age-map.yml4
-rw-r--r--changelogs/unreleased/25164-disable-fork-on-project-limit.yml4
-rw-r--r--changelogs/unreleased/26212-upload-user-avatar-trough-api.yml4
-rw-r--r--changelogs/unreleased/27070-rename-slash-commands-to-quick-actions.yml5
-rw-r--r--changelogs/unreleased/27586-center-dropdown.yml4
-rw-r--r--changelogs/unreleased/27697-make-arrow-icons-consistent-in-dropdown.yml4
-rw-r--r--changelogs/unreleased/28139-use-color-input-broadcast-messages.yml4
-rw-r--r--changelogs/unreleased/30725-reset-user-limits-when-unchecking-external-user.yml4
-rw-r--r--changelogs/unreleased/31415-responsive-pipelines-table-2.yml4
-rw-r--r--changelogs/unreleased/31556-ci-coverage-paralel-rspec.yml4
-rw-r--r--changelogs/unreleased/32054-rails-should-use-timestamptz-database-type-for-postgresql.yml4
-rw-r--r--changelogs/unreleased/32470-pag-links.yml4
-rw-r--r--changelogs/unreleased/32790-pipeline_schedules-pages-throwing-error-500.yml4
-rw-r--r--changelogs/unreleased/32995-issue-contents-dynamically-replaced-with-stale-version-after-saving-or-refreshing-relative-external_url-only.yml4
-rw-r--r--changelogs/unreleased/33048-markdown-rendering-of-md-files-has-ceased-to-display-latex-equations.yml4
-rw-r--r--changelogs/unreleased/33445-document-delete-merge-branches-won-t-touch-protected-branches-docs.yml4
-rw-r--r--changelogs/unreleased/33461-display-user-id.yml4
-rw-r--r--changelogs/unreleased/counters_cache_invalidation.yml4
-rw-r--r--changelogs/unreleased/disable-blocked-manual-actions.yml4
-rw-r--r--changelogs/unreleased/dm-diff-viewers.yml4
-rw-r--r--changelogs/unreleased/dm-fix-parser-cache.yml4
-rw-r--r--changelogs/unreleased/dm-target-branch-slash-command-desc.yml4
-rw-r--r--changelogs/unreleased/feature-add-support-for-services-configuration.yml4
-rw-r--r--changelogs/unreleased/feature-unify-email-layouts.yml4
-rw-r--r--changelogs/unreleased/fix-backup-restore-resume.yml4
-rw-r--r--changelogs/unreleased/fix-gb-use-merge-ability-for-protected-manual-actions.yml4
-rw-r--r--changelogs/unreleased/fix-github-clone-wiki.yml4
-rw-r--r--changelogs/unreleased/fix-missing-function-dropzone-input.yml4
-rw-r--r--changelogs/unreleased/fix-overflow-slash-commands.yml4
-rw-r--r--changelogs/unreleased/fix-support-for-external-ci-services.yml4
-rw-r--r--changelogs/unreleased/fix-terminals-support-for-kubernetes-service.yml4
-rw-r--r--changelogs/unreleased/help-landing-page-customizations.yml4
-rw-r--r--changelogs/unreleased/instrument-merge-request-diff-load-commits.yml4
-rw-r--r--changelogs/unreleased/issue_20900.yml4
-rw-r--r--changelogs/unreleased/issue_33205.yml4
-rw-r--r--changelogs/unreleased/karma-headless-chrome.yml4
-rw-r--r--changelogs/unreleased/moved-submodules.yml4
-rw-r--r--changelogs/unreleased/mr-widget-memory-usage-tech-debt-fix.yml4
-rw-r--r--changelogs/unreleased/reduce-sidekiq-wait-timings.yml4
-rw-r--r--changelogs/unreleased/sh-fix-lfs-from-moving-across-filesystems.yml4
-rw-r--r--changelogs/unreleased/sh-fix-submodules-trailing-spaces.yml4
-rw-r--r--changelogs/unreleased/sh-recaptcha-fix-try2.yml4
-rw-r--r--changelogs/unreleased/speed-up-graphs.yml4
-rw-r--r--changelogs/unreleased/zj-commit-status-sortable-name.yml4
-rw-r--r--changelogs/unreleased/zj-drop-fk-if-exists.yml4
-rw-r--r--changelogs/unreleased/zj-raise-etag-route-regex-miss.yml4
-rw-r--r--config.ru3
-rw-r--r--config/boot.rb3
-rw-r--r--config/environments/production.rb2
-rw-r--r--config/initializers/8_metrics.rb3
-rw-r--r--config/initializers/active_record_data_types.rb24
-rw-r--r--config/initializers/active_record_table_definition.rb24
-rw-r--r--config/initializers/rugged_use_gitlab_git_attributes.rb25
-rw-r--r--config/karma.config.js23
-rw-r--r--config/locales/en.yml215
-rw-r--r--config/locales/es.yml1
-rw-r--r--config/webpack.config.js25
-rw-r--r--db/migrate/20160314114439_add_requested_at_to_members.rb1
-rw-r--r--db/migrate/20160415062917_create_personal_access_tokens.rb2
-rw-r--r--db/migrate/20160610204157_add_deployments.rb1
-rw-r--r--db/migrate/20160610204158_add_environments.rb1
-rw-r--r--db/migrate/20160705054938_add_protected_branches_push_access.rb1
-rw-r--r--db/migrate/20160705054952_add_protected_branches_merge_access.rb1
-rw-r--r--db/migrate/20160724205507_add_resolved_to_notes.rb1
-rw-r--r--db/migrate/20160727163552_create_user_agent_details.rb1
-rw-r--r--db/migrate/20160727191041_create_boards.rb1
-rw-r--r--db/migrate/20160727193336_create_lists.rb1
-rw-r--r--db/migrate/20160805041956_add_deleted_at_to_namespaces.rb1
-rw-r--r--db/migrate/20160824124900_add_table_issue_metrics.rb2
-rw-r--r--db/migrate/20160825052008_add_table_merge_request_metrics.rb2
-rw-r--r--db/migrate/20160831214002_create_project_features.rb1
-rw-r--r--db/migrate/20160915042921_create_merge_requests_closing_issues.rb1
-rw-r--r--db/migrate/20161014173530_create_label_priorities.rb1
-rw-r--r--db/migrate/20161113184239_create_user_chat_names_table.rb2
-rw-r--r--db/migrate/20161124111402_add_routes_table.rb1
-rw-r--r--db/migrate/20161221152132_add_last_used_at_to_key.rb1
-rw-r--r--db/migrate/20161223034646_create_timelogs_ce.rb1
-rw-r--r--db/migrate/20161228124936_change_expires_at_to_date_in_personal_access_tokens.rb1
-rw-r--r--db/migrate/20170120131253_create_chat_teams.rb1
-rw-r--r--db/migrate/20170130221926_create_uploads.rb1
-rw-r--r--db/migrate/20170222143317_drop_ci_projects.rb1
-rw-r--r--db/migrate/20170301205639_remove_unused_ci_tables_and_columns.rb1
-rw-r--r--db/migrate/20170309173138_create_protected_tags.rb1
-rw-r--r--db/migrate/20170314082049_create_system_note_metadata.rb1
-rw-r--r--db/migrate/20170315194013_add_closed_at_to_issues.rb1
-rw-r--r--db/migrate/20170322013926_create_container_repository.rb1
-rw-r--r--db/migrate/20170329095907_create_ci_trigger_schedules.rb1
-rw-r--r--db/migrate/20170425112128_create_pipeline_schedules_table.rb2
-rw-r--r--db/migrate/20170427215854_create_redirect_routes.rb1
-rw-r--r--db/migrate/20170503004125_add_last_repository_updated_at_to_projects.rb1
-rw-r--r--db/migrate/20170523121229_create_conversational_development_index_metrics.rb1
-rw-r--r--db/migrate/20170525132202_create_pipeline_stages.rb1
-rw-r--r--db/migrate/20170602154736_add_help_page_hide_commercial_content_to_application_settings.rb9
-rw-r--r--db/migrate/20170602154813_add_help_page_support_url_to_application_settings.rb9
-rw-r--r--db/migrate/20170606154216_add_notification_setting_columns.rb26
-rw-r--r--db/migrate/20170608171156_create_merge_request_diff_files.rb22
-rw-r--r--db/migrate/20170614115405_merge_request_diff_file_limits_to_mysql.rb1
-rw-r--r--db/migrate/merge_request_diff_file_limits_to_mysql.rb12
-rw-r--r--db/post_migrate/20170425130047_drop_ci_trigger_schedules_table.rb1
-rw-r--r--db/post_migrate/20170607121233_convert_custom_notification_settings_to_columns.rb55
-rw-r--r--db/post_migrate/20170609183112_remove_position_from_issuables.rb8
-rw-r--r--db/schema.rb36
-rw-r--r--doc/README.md3
-rw-r--r--doc/administration/environment_variables.md4
-rw-r--r--doc/administration/job_artifacts.md14
-rw-r--r--doc/administration/raketasks/github_import.md4
-rw-r--r--doc/api/README.md83
-rw-r--r--doc/api/branches.md2
-rw-r--r--doc/api/issues.md3
-rw-r--r--doc/api/merge_requests.md2
-rw-r--r--doc/api/oauth2.md2
-rw-r--r--doc/api/session.md13
-rw-r--r--doc/api/users.md4
-rw-r--r--doc/ci/docker/using_docker_build.md8
-rw-r--r--doc/ci/docker/using_docker_images.md12
-rw-r--r--doc/ci/examples/code_climate.md2
-rw-r--r--doc/ci/runners/README.md48
-rw-r--r--doc/development/limit_ee_conflicts.md4
-rw-r--r--doc/development/migration_style_guide.md39
-rw-r--r--doc/development/testing.md2
-rw-r--r--doc/install/kubernetes/index.md2
-rw-r--r--doc/integration/chat_commands.md15
-rw-r--r--doc/integration/slash_commands.md14
-rw-r--r--doc/update/9.3-to-9.4.md317
-rw-r--r--doc/user/admin_area/monitoring/convdev.md29
-rw-r--r--doc/user/admin_area/monitoring/img/convdev_index.pngbin0 -> 31012 bytes
-rw-r--r--doc/user/admin_area/settings/usage_statistics.md3
-rw-r--r--doc/user/discussions/index.md4
-rw-r--r--doc/user/permissions.md1
-rw-r--r--doc/user/profile/account/two_factor_authentication.md55
-rw-r--r--doc/user/profile/img/personal_access_tokens.pngbin0 -> 18555 bytes
-rw-r--r--doc/user/profile/personal_access_tokens.md57
-rw-r--r--doc/user/project/container_registry.md15
-rw-r--r--doc/user/project/integrations/img/jira_service_page.pngbin12228 -> 83466 bytes
-rw-r--r--doc/user/project/integrations/img/merge_request_performance.pngbin66775 -> 60194 bytes
-rw-r--r--doc/user/project/integrations/jira.md8
-rw-r--r--doc/user/project/integrations/prometheus.md8
-rw-r--r--doc/user/project/integrations/slack_slash_commands.md4
-rw-r--r--doc/user/project/issue_board.md5
-rw-r--r--doc/user/project/issues/confidential_issues.md5
-rwxr-xr-xdoc/user/project/issues/img/confidential_issues_issue_page.pngbin14230 -> 90001 bytes
-rw-r--r--doc/user/project/issues/issues_functionalities.md4
-rw-r--r--doc/user/project/new_ci_build_permissions_model.md8
-rw-r--r--doc/user/project/quick_actions.md39
-rw-r--r--doc/user/project/repository/branches/img/delete_merged_branches.pngbin0 -> 42891 bytes
-rw-r--r--doc/user/project/repository/branches/index.md17
-rw-r--r--doc/user/project/settings/import_export.md17
-rw-r--r--doc/user/project/slash_commands.md40
-rw-r--r--doc/workflow/README.md2
-rw-r--r--doc/workflow/time_tracking.md8
-rw-r--r--features/steps/project/issues/award_emoji.rb4
-rw-r--r--lib/api/entities.rb11
-rw-r--r--lib/api/helpers/internal_helpers.rb7
-rw-r--r--lib/api/internal.rb12
-rw-r--r--lib/api/issues.rb2
-rw-r--r--lib/api/merge_requests.rb4
-rw-r--r--lib/api/milestones.rb4
-rw-r--r--lib/api/services.rb4
-rw-r--r--lib/api/settings.rb4
-rw-r--r--lib/api/users.rb1
-rw-r--r--lib/api/v3/services.rb4
-rw-r--r--lib/banzai/reference_parser/base_parser.rb2
-rw-r--r--lib/ci/api/entities.rb16
-rw-r--r--lib/github/import.rb2
-rw-r--r--lib/gitlab/ci/build/image.rb11
-rw-r--r--lib/gitlab/ci/config/entry/image.rb30
-rw-r--r--lib/gitlab/ci/config/entry/service.rb34
-rw-r--r--lib/gitlab/ci/config/entry/services.rb25
-rw-r--r--lib/gitlab/ci/config/entry/validators.rb8
-rw-r--r--lib/gitlab/ci/status/external/common.rb4
-rw-r--r--lib/gitlab/current_settings.rb49
-rw-r--r--lib/gitlab/database.rb16
-rw-r--r--lib/gitlab/database/migration_helpers.rb39
-rw-r--r--lib/gitlab/diff/file.rb109
-rw-r--r--lib/gitlab/diff/line.rb20
-rw-r--r--lib/gitlab/diff/parallel_diff.rb20
-rw-r--r--lib/gitlab/etag_caching/middleware.rb2
-rw-r--r--lib/gitlab/etag_caching/router.rb4
-rw-r--r--lib/gitlab/etag_caching/store.rb2
-rw-r--r--lib/gitlab/fake_application_settings.rb27
-rw-r--r--lib/gitlab/git/diff.rb19
-rw-r--r--lib/gitlab/git/diff_collection.rb2
-rw-r--r--lib/gitlab/git/gitmodules_parser.rb77
-rw-r--r--lib/gitlab/git/repository.rb52
-rw-r--r--lib/gitlab/git_access.rb21
-rw-r--r--lib/gitlab/gitaly_client/util.rb1
-rw-r--r--lib/gitlab/group_hierarchy.rb43
-rw-r--r--lib/gitlab/i18n.rb1
-rw-r--r--lib/gitlab/import_export.rb2
-rw-r--r--lib/gitlab/import_export/import_export.yml7
-rw-r--r--lib/gitlab/import_export/json_hash_builder.rb4
-rw-r--r--lib/gitlab/import_export/relation_factory.rb5
-rw-r--r--lib/gitlab/job_waiter.rb2
-rw-r--r--lib/gitlab/kubernetes.rb12
-rw-r--r--lib/gitlab/quick_actions/command_definition.rb (renamed from lib/gitlab/slash_commands/command_definition.rb)2
-rw-r--r--lib/gitlab/quick_actions/dsl.rb (renamed from lib/gitlab/slash_commands/dsl.rb)6
-rw-r--r--lib/gitlab/quick_actions/extractor.rb (renamed from lib/gitlab/slash_commands/extractor.rb)6
-rw-r--r--lib/gitlab/repo_path.rb21
-rw-r--r--lib/gitlab/slash_commands/base_command.rb (renamed from lib/gitlab/chat_commands/base_command.rb)2
-rw-r--r--lib/gitlab/slash_commands/command.rb (renamed from lib/gitlab/chat_commands/command.rb)14
-rw-r--r--lib/gitlab/slash_commands/deploy.rb (renamed from lib/gitlab/chat_commands/deploy.rb)8
-rw-r--r--lib/gitlab/slash_commands/help.rb (renamed from lib/gitlab/chat_commands/help.rb)4
-rw-r--r--lib/gitlab/slash_commands/issue_command.rb (renamed from lib/gitlab/chat_commands/issue_command.rb)2
-rw-r--r--lib/gitlab/slash_commands/issue_new.rb (renamed from lib/gitlab/chat_commands/issue_new.rb)4
-rw-r--r--lib/gitlab/slash_commands/issue_search.rb (renamed from lib/gitlab/chat_commands/issue_search.rb)2
-rw-r--r--lib/gitlab/slash_commands/issue_show.rb (renamed from lib/gitlab/chat_commands/issue_show.rb)6
-rw-r--r--lib/gitlab/slash_commands/presenters/access.rb (renamed from lib/gitlab/chat_commands/presenters/access.rb)2
-rw-r--r--lib/gitlab/slash_commands/presenters/base.rb (renamed from lib/gitlab/chat_commands/presenters/base.rb)2
-rw-r--r--lib/gitlab/slash_commands/presenters/deploy.rb (renamed from lib/gitlab/chat_commands/presenters/deploy.rb)2
-rw-r--r--lib/gitlab/slash_commands/presenters/help.rb (renamed from lib/gitlab/chat_commands/presenters/help.rb)2
-rw-r--r--lib/gitlab/slash_commands/presenters/issue_base.rb (renamed from lib/gitlab/chat_commands/presenters/issue_base.rb)2
-rw-r--r--lib/gitlab/slash_commands/presenters/issue_new.rb (renamed from lib/gitlab/chat_commands/presenters/issue_new.rb)2
-rw-r--r--lib/gitlab/slash_commands/presenters/issue_search.rb (renamed from lib/gitlab/chat_commands/presenters/issue_search.rb)2
-rw-r--r--lib/gitlab/slash_commands/presenters/issue_show.rb (renamed from lib/gitlab/chat_commands/presenters/issue_show.rb)2
-rw-r--r--lib/gitlab/slash_commands/result.rb (renamed from lib/gitlab/chat_commands/result.rb)2
-rw-r--r--lib/tasks/gitlab/gitaly.rake20
-rw-r--r--lib/tasks/migrate/add_limits_mysql.rake2
-rw-r--r--locale/bg/gitlab.po3
-rw-r--r--locale/de/gitlab.po3
-rw-r--r--locale/en/gitlab.po736
-rw-r--r--locale/es/gitlab.po247
-rw-r--r--locale/fr/gitlab.po207
-rw-r--r--locale/fr/gitlab.po.time_stamp0
-rw-r--r--locale/gitlab.pot737
-rw-r--r--locale/pt_BR/gitlab.po3
-rw-r--r--locale/zh_CN/gitlab.po3
-rw-r--r--locale/zh_HK/gitlab.po54
-rw-r--r--locale/zh_TW/gitlab.po3
-rw-r--r--package.json6
-rw-r--r--rubocop/cop/migration/add_timestamps.rb25
-rw-r--r--rubocop/cop/migration/datetime.rb36
-rw-r--r--rubocop/cop/migration/timestamps.rb27
-rw-r--r--rubocop/cop/rspec/single_line_hook.rb38
-rw-r--r--rubocop/rubocop.rb4
-rw-r--r--spec/controllers/admin/identities_controller_spec.rb5
-rw-r--r--spec/controllers/admin/services_controller_spec.rb4
-rw-r--r--spec/controllers/autocomplete_controller_spec.rb4
-rw-r--r--spec/controllers/groups/group_members_controller_spec.rb56
-rw-r--r--spec/controllers/groups_controller_spec.rb9
-rw-r--r--spec/controllers/notification_settings_controller_spec.rb23
-rw-r--r--spec/controllers/profiles/personal_access_tokens_controller_spec.rb8
-rw-r--r--spec/controllers/projects/commit_controller_spec.rb8
-rw-r--r--spec/controllers/projects/compare_controller_spec.rb12
-rw-r--r--spec/controllers/projects/forks_controller_spec.rb16
-rw-r--r--spec/controllers/projects/group_links_controller_spec.rb5
-rw-r--r--spec/controllers/projects/issues_controller_spec.rb31
-rw-r--r--spec/controllers/projects/jobs_controller_spec.rb136
-rw-r--r--spec/controllers/projects/merge_requests_controller_spec.rb61
-rw-r--r--spec/controllers/projects/pipelines_controller_spec.rb37
-rw-r--r--spec/controllers/projects/project_members_controller_spec.rb61
-rw-r--r--spec/controllers/projects/snippets_controller_spec.rb20
-rw-r--r--spec/controllers/projects/tags_controller_spec.rb8
-rw-r--r--spec/controllers/projects_controller_spec.rb12
-rw-r--r--spec/controllers/search_controller_spec.rb4
-rw-r--r--spec/controllers/sent_notifications_controller_spec.rb25
-rw-r--r--spec/controllers/sessions_controller_spec.rb8
-rw-r--r--spec/controllers/snippets_controller_spec.rb4
-rw-r--r--spec/controllers/users_controller_spec.rb8
-rw-r--r--spec/factories/application_settings.rb4
-rw-r--r--spec/factories/ci/builds.rb4
-rw-r--r--spec/features/abuse_report_spec.rb2
-rw-r--r--spec/features/admin/admin_abuse_reports_spec.rb2
-rw-r--r--spec/features/admin/admin_active_tab_spec.rb2
-rw-r--r--spec/features/admin/admin_appearance_spec.rb8
-rw-r--r--spec/features/admin/admin_broadcast_messages_spec.rb2
-rw-r--r--spec/features/admin/admin_browse_spam_logs_spec.rb2
-rw-r--r--spec/features/admin/admin_browses_logs_spec.rb2
-rw-r--r--spec/features/admin/admin_builds_spec.rb2
-rw-r--r--spec/features/admin/admin_cohorts_spec.rb2
-rw-r--r--spec/features/admin/admin_conversational_development_index_spec.rb2
-rw-r--r--spec/features/admin/admin_deploy_keys_spec.rb2
-rw-r--r--spec/features/admin/admin_disables_git_access_protocol_spec.rb2
-rw-r--r--spec/features/admin/admin_disables_two_factor_spec.rb4
-rw-r--r--spec/features/admin/admin_groups_spec.rb2
-rw-r--r--spec/features/admin/admin_health_check_spec.rb2
-rw-r--r--spec/features/admin/admin_hook_logs_spec.rb2
-rw-r--r--spec/features/admin/admin_hooks_spec.rb2
-rw-r--r--spec/features/admin/admin_labels_spec.rb2
-rw-r--r--spec/features/admin/admin_manage_applications_spec.rb2
-rw-r--r--spec/features/admin/admin_projects_spec.rb2
-rw-r--r--spec/features/admin/admin_requests_profiles_spec.rb2
-rw-r--r--spec/features/admin/admin_runners_spec.rb7
-rw-r--r--spec/features/admin/admin_settings_spec.rb7
-rw-r--r--spec/features/admin/admin_system_info_spec.rb2
-rw-r--r--spec/features/admin/admin_users_impersonation_tokens_spec.rb4
-rw-r--r--spec/features/admin/admin_users_spec.rb11
-rw-r--r--spec/features/admin/admin_uses_repository_checks_spec.rb2
-rw-r--r--spec/features/atom/issues_spec.rb2
-rw-r--r--spec/features/auto_deploy_spec.rb2
-rw-r--r--spec/features/boards/add_issues_modal_spec.rb2
-rw-r--r--spec/features/boards/boards_spec.rb18
-rw-r--r--spec/features/boards/issue_ordering_spec.rb2
-rw-r--r--spec/features/boards/keyboard_shortcut_spec.rb2
-rw-r--r--spec/features/boards/modal_filter_spec.rb2
-rw-r--r--spec/features/boards/new_issue_spec.rb18
-rw-r--r--spec/features/boards/sidebar_spec.rb2
-rw-r--r--spec/features/boards/sub_group_project_spec.rb2
-rw-r--r--spec/features/calendar_spec.rb2
-rw-r--r--spec/features/ci_lint_spec.rb2
-rw-r--r--spec/features/commits_spec.rb4
-rw-r--r--spec/features/container_registry_spec.rb2
-rw-r--r--spec/features/copy_as_gfm_spec.rb2
-rw-r--r--spec/features/cycle_analytics_spec.rb8
-rw-r--r--spec/features/dashboard/active_tab_spec.rb2
-rw-r--r--spec/features/dashboard/activity_spec.rb2
-rw-r--r--spec/features/dashboard/archived_projects_spec.rb2
-rw-r--r--spec/features/dashboard/datetime_on_tooltips_spec.rb6
-rw-r--r--spec/features/dashboard/group_spec.rb2
-rw-r--r--spec/features/dashboard/groups_list_spec.rb8
-rw-r--r--spec/features/dashboard/help_spec.rb2
-rw-r--r--spec/features/dashboard/issuables_counter_spec.rb2
-rw-r--r--spec/features/dashboard/issues_spec.rb2
-rw-r--r--spec/features/dashboard/label_filter_spec.rb2
-rw-r--r--spec/features/dashboard/merge_requests_spec.rb6
-rw-r--r--spec/features/dashboard/milestone_filter_spec.rb2
-rw-r--r--spec/features/dashboard/milestone_tabs_spec.rb4
-rw-r--r--spec/features/dashboard/project_member_activity_index_spec.rb12
-rw-r--r--spec/features/dashboard/projects_spec.rb2
-rw-r--r--spec/features/dashboard/shortcuts_spec.rb2
-rw-r--r--spec/features/dashboard/snippets_spec.rb4
-rw-r--r--spec/features/dashboard/user_filters_projects_spec.rb2
-rw-r--r--spec/features/dashboard_issues_spec.rb2
-rw-r--r--spec/features/dashboard_milestones_spec.rb2
-rw-r--r--spec/features/discussion_comments/commit_spec.rb2
-rw-r--r--spec/features/discussion_comments/issue_spec.rb2
-rw-r--r--spec/features/discussion_comments/merge_request_spec.rb2
-rw-r--r--spec/features/discussion_comments/snippets_spec.rb2
-rw-r--r--spec/features/expand_collapse_diffs_spec.rb8
-rw-r--r--spec/features/explore/groups_list_spec.rb2
-rw-r--r--spec/features/explore/new_menu_spec.rb4
-rw-r--r--spec/features/gitlab_flavored_markdown_spec.rb2
-rw-r--r--spec/features/global_search_spec.rb2
-rw-r--r--spec/features/groups/activity_spec.rb2
-rw-r--r--spec/features/groups/empty_states_spec.rb2
-rw-r--r--spec/features/groups/group_name_toggle_spec.rb2
-rw-r--r--spec/features/groups/group_settings_spec.rb13
-rw-r--r--spec/features/groups/labels/edit_spec.rb2
-rw-r--r--spec/features/groups/members/last_owner_cannot_leave_group_spec.rb2
-rw-r--r--spec/features/groups/members/list_spec.rb2
-rw-r--r--spec/features/groups/members/member_cannot_request_access_to_his_project_spec.rb2
-rw-r--r--spec/features/groups/members/member_leaves_group_spec.rb2
-rw-r--r--spec/features/groups/members/owner_manages_access_requests_spec.rb2
-rw-r--r--spec/features/groups/members/sorting_spec.rb2
-rw-r--r--spec/features/groups/members/user_requests_access_spec.rb2
-rw-r--r--spec/features/groups/milestone_spec.rb2
-rw-r--r--spec/features/groups/show_spec.rb2
-rw-r--r--spec/features/groups_spec.rb18
-rw-r--r--spec/features/help_pages_spec.rb27
-rw-r--r--spec/features/issuables/default_sort_order_spec.rb8
-rw-r--r--spec/features/issuables/issuable_list_spec.rb2
-rw-r--r--spec/features/issues/award_emoji_spec.rb10
-rw-r--r--spec/features/issues/award_spec.rb2
-rw-r--r--spec/features/issues/bulk_assignment_labels_spec.rb4
-rw-r--r--spec/features/issues/create_branch_merge_request_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.rb2
-rw-r--r--spec/features/issues/filtered_search/dropdown_author_spec.rb2
-rw-r--r--spec/features/issues/filtered_search/dropdown_hint_spec.rb2
-rw-r--r--spec/features/issues/filtered_search/dropdown_label_spec.rb2
-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/filtered_search/visual_tokens_spec.rb2
-rw-r--r--spec/features/issues/form_spec.rb16
-rw-r--r--spec/features/issues/gfm_autocomplete_spec.rb4
-rw-r--r--spec/features/issues/issue_sidebar_spec.rb2
-rw-r--r--spec/features/issues/markdown_toolbar_spec.rb2
-rw-r--r--spec/features/issues/move_spec.rb2
-rw-r--r--spec/features/issues/note_polling_spec.rb6
-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.rb16
-rw-r--r--spec/features/issues_spec.rb19
-rw-r--r--spec/features/login_spec.rb38
-rw-r--r--spec/features/merge_requests/assign_issues_spec.rb2
-rw-r--r--spec/features/merge_requests/award_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.rb18
-rw-r--r--spec/features/merge_requests/conflicts_spec.rb12
-rw-r--r--spec/features/merge_requests/create_new_mr_spec.rb2
-rw-r--r--spec/features/merge_requests/created_from_fork_spec.rb4
-rw-r--r--spec/features/merge_requests/deleted_source_branch_spec.rb2
-rw-r--r--spec/features/merge_requests/diff_notes_avatars_spec.rb2
-rw-r--r--spec/features/merge_requests/diff_notes_resolve_spec.rb6
-rw-r--r--spec/features/merge_requests/diffs_spec.rb6
-rw-r--r--spec/features/merge_requests/discussion_spec.rb2
-rw-r--r--spec/features/merge_requests/edit_mr_spec.rb2
-rw-r--r--spec/features/merge_requests/filter_by_labels_spec.rb2
-rw-r--r--spec/features/merge_requests/filter_by_milestone_spec.rb2
-rw-r--r--spec/features/merge_requests/filter_merge_requests_spec.rb2
-rw-r--r--spec/features/merge_requests/form_spec.rb18
-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.rb6
-rw-r--r--spec/features/merge_requests/merge_when_pipeline_succeeds_spec.rb4
-rw-r--r--spec/features/merge_requests/mini_pipeline_graph_spec.rb2
-rw-r--r--spec/features/merge_requests/only_allow_merge_if_build_succeeds_spec.rb2
-rw-r--r--spec/features/merge_requests/pipelines_spec.rb4
-rw-r--r--spec/features/merge_requests/target_branch_spec.rb2
-rw-r--r--spec/features/merge_requests/toggle_whitespace_changes_spec.rb2
-rw-r--r--spec/features/merge_requests/toggler_behavior_spec.rb2
-rw-r--r--spec/features/merge_requests/update_merge_requests_spec.rb2
-rw-r--r--spec/features/merge_requests/user_posts_diff_notes_spec.rb2
-rw-r--r--spec/features/merge_requests/user_posts_notes_spec.rb2
-rw-r--r--spec/features/merge_requests/user_sees_system_notes_spec.rb2
-rw-r--r--spec/features/merge_requests/user_uses_slash_commands_spec.rb24
-rw-r--r--spec/features/merge_requests/versions_spec.rb2
-rw-r--r--spec/features/merge_requests/widget_deployments_spec.rb2
-rw-r--r--spec/features/merge_requests/widget_spec.rb6
-rw-r--r--spec/features/merge_requests/wip_message_spec.rb2
-rw-r--r--spec/features/milestone_spec.rb2
-rw-r--r--spec/features/milestones/milestones_spec.rb101
-rw-r--r--spec/features/milestones/show_spec.rb2
-rw-r--r--spec/features/participants_autocomplete_spec.rb2
-rw-r--r--spec/features/profile_spec.rb2
-rw-r--r--spec/features/profiles/account_spec.rb11
-rw-r--r--spec/features/profiles/chat_names_spec.rb2
-rw-r--r--spec/features/profiles/keys_spec.rb2
-rw-r--r--spec/features/profiles/oauth_applications_spec.rb2
-rw-r--r--spec/features/profiles/password_spec.rb2
-rw-r--r--spec/features/profiles/personal_access_tokens_spec.rb2
-rw-r--r--spec/features/profiles/preferences_spec.rb2
-rw-r--r--spec/features/profiles/user_changes_notified_of_own_activity_spec.rb2
-rw-r--r--spec/features/projects/activity/rss_spec.rb2
-rw-r--r--spec/features/projects/badges/coverage_spec.rb4
-rw-r--r--spec/features/projects/badges/list_spec.rb2
-rw-r--r--spec/features/projects/blobs/blob_show_spec.rb3
-rw-r--r--spec/features/projects/blobs/edit_spec.rb10
-rw-r--r--spec/features/projects/branches/download_buttons_spec.rb2
-rw-r--r--spec/features/projects/branches/new_branch_ref_dropdown_spec.rb2
-rw-r--r--spec/features/projects/branches_spec.rb4
-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/commit/mini_pipeline_graph_spec.rb2
-rw-r--r--spec/features/projects/commit/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/diffs/diff_show_spec.rb133
-rw-r--r--spec/features/projects/edit_spec.rb2
-rw-r--r--spec/features/projects/environments/environment_metrics_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.rb15
-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/find_files_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.rb2
-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/gfm_autocomplete_load_spec.rb2
-rw-r--r--spec/features/projects/group_links_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/import_file_spec.rb4
-rw-r--r--spec/features/projects/import_export/namespace_export_file_spec.rb2
-rw-r--r--spec/features/projects/import_export/test_project_export.tar.gzbin681478 -> 681481 bytes
-rw-r--r--spec/features/projects/issuable_templates_spec.rb6
-rw-r--r--spec/features/projects/issues/list_spec.rb2
-rw-r--r--spec/features/projects/issues/rss_spec.rb2
-rw-r--r--spec/features/projects/jobs_spec.rb156
-rw-r--r--spec/features/projects/labels/issues_sorted_by_priority_spec.rb4
-rw-r--r--spec/features/projects/labels/subscription_spec.rb2
-rw-r--r--spec/features/projects/labels/update_prioritization_spec.rb4
-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/group_links_spec.rb2
-rw-r--r--spec/features/projects/members/group_member_cannot_leave_group_project_spec.rb2
-rw-r--r--spec/features/projects/members/group_member_cannot_request_access_to_his_group_project_spec.rb2
-rw-r--r--spec/features/projects/members/group_members_spec.rb2
-rw-r--r--spec/features/projects/members/group_requester_cannot_request_access_to_project_spec.rb2
-rw-r--r--spec/features/projects/members/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/members/owner_cannot_leave_project_spec.rb2
-rw-r--r--spec/features/projects/members/owner_cannot_request_access_to_his_project_spec.rb2
-rw-r--r--spec/features/projects/members/sorting_spec.rb2
-rw-r--r--spec/features/projects/members/user_requests_access_spec.rb2
-rw-r--r--spec/features/projects/merge_request_button_spec.rb4
-rw-r--r--spec/features/projects/merge_requests/list_spec.rb2
-rw-r--r--spec/features/projects/milestones/milestone_spec.rb2
-rw-r--r--spec/features/projects/milestones/milestones_sorting_spec.rb2
-rw-r--r--spec/features/projects/milestones/new_spec.rb18
-rw-r--r--spec/features/projects/new_project_spec.rb2
-rw-r--r--spec/features/projects/pages_spec.rb2
-rw-r--r--spec/features/projects/pipeline_schedules_spec.rb4
-rw-r--r--spec/features/projects/pipelines/pipeline_spec.rb22
-rw-r--r--spec/features/projects/pipelines/pipelines_spec.rb44
-rw-r--r--spec/features/projects/project_settings_spec.rb25
-rw-r--r--spec/features/projects/ref_switcher_spec.rb2
-rw-r--r--spec/features/projects/services/jira_service_spec.rb2
-rw-r--r--spec/features/projects/services/mattermost_slash_command_spec.rb2
-rw-r--r--spec/features/projects/services/slack_service_spec.rb2
-rw-r--r--spec/features/projects/services/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.rb2
-rw-r--r--spec/features/projects/settings/visibility_settings_spec.rb4
-rw-r--r--spec/features/projects/shortcuts_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/snippets_spec.rb16
-rw-r--r--spec/features/projects/sub_group_issuables_spec.rb2
-rw-r--r--spec/features/projects/tags/download_buttons_spec.rb2
-rw-r--r--spec/features/projects/tree/rss_spec.rb2
-rw-r--r--spec/features/projects/user_create_dir_spec.rb2
-rw-r--r--spec/features/projects/view_on_env_spec.rb12
-rw-r--r--spec/features/projects/wiki/markdown_preview_spec.rb2
-rw-r--r--spec/features/projects/wiki/shortcuts_spec.rb2
-rw-r--r--spec/features/projects/wiki/user_creates_wiki_page_spec.rb18
-rw-r--r--spec/features/projects/wiki/user_git_access_wiki_page_spec.rb2
-rw-r--r--spec/features/projects/wiki/user_updates_wiki_page_spec.rb16
-rw-r--r--spec/features/projects/wiki/user_views_project_wiki_page_spec.rb2
-rw-r--r--spec/features/projects/wiki/user_views_wiki_in_project_page_spec.rb2
-rw-r--r--spec/features/projects_spec.rb12
-rw-r--r--spec/features/protected_branches_spec.rb4
-rw-r--r--spec/features/protected_tags_spec.rb4
-rw-r--r--spec/features/reportable_note/commit_spec.rb2
-rw-r--r--spec/features/reportable_note/issue_spec.rb2
-rw-r--r--spec/features/reportable_note/merge_request_spec.rb2
-rw-r--r--spec/features/reportable_note/snippets_spec.rb2
-rw-r--r--spec/features/runners_spec.rb9
-rw-r--r--spec/features/search_spec.rb6
-rw-r--r--spec/features/security/project/internal_access_spec.rb16
-rw-r--r--spec/features/security/project/public_access_spec.rb16
-rw-r--r--spec/features/signup_spec.rb8
-rw-r--r--spec/features/snippets/create_snippet_spec.rb2
-rw-r--r--spec/features/snippets/edit_snippet_spec.rb2
-rw-r--r--spec/features/snippets/explore_spec.rb4
-rw-r--r--spec/features/snippets/internal_snippet_spec.rb2
-rw-r--r--spec/features/snippets/notes_on_personal_snippets_spec.rb18
-rw-r--r--spec/features/snippets/search_snippets_spec.rb4
-rw-r--r--spec/features/snippets/user_snippets_spec.rb2
-rw-r--r--spec/features/tags/master_creates_tag_spec.rb96
-rw-r--r--spec/features/tags/master_deletes_tag_spec.rb2
-rw-r--r--spec/features/tags/master_updates_tag_spec.rb13
-rw-r--r--spec/features/tags/master_views_tags_spec.rb2
-rw-r--r--spec/features/task_lists_spec.rb4
-rw-r--r--spec/features/todos/target_state_spec.rb2
-rw-r--r--spec/features/todos/todos_filtering_spec.rb2
-rw-r--r--spec/features/todos/todos_sorting_spec.rb8
-rw-r--r--spec/features/todos/todos_spec.rb16
-rw-r--r--spec/features/triggers_spec.rb4
-rw-r--r--spec/features/u2f_spec.rb58
-rw-r--r--spec/features/unsubscribe_links_spec.rb4
-rw-r--r--spec/features/uploads/user_uploads_avatar_to_group_spec.rb2
-rw-r--r--spec/features/uploads/user_uploads_avatar_to_profile_spec.rb2
-rw-r--r--spec/features/uploads/user_uploads_file_to_note_spec.rb2
-rw-r--r--spec/features/user_callout_spec.rb2
-rw-r--r--spec/features/user_can_display_performance_bar_spec.rb2
-rw-r--r--spec/features/users/projects_spec.rb2
-rw-r--r--spec/features/users/rss_spec.rb2
-rw-r--r--spec/features/users/snippets_spec.rb2
-rw-r--r--spec/features/users_spec.rb6
-rw-r--r--spec/features/variables_spec.rb2
-rw-r--r--spec/finders/groups_finder_spec.rb65
-rw-r--r--spec/finders/issues_finder_spec.rb26
-rw-r--r--spec/finders/merge_requests_finder_spec.rb42
-rw-r--r--spec/finders/personal_access_tokens_finder_spec.rb84
-rw-r--r--spec/finders/personal_projects_finder_spec.rb4
-rw-r--r--spec/finders/pipelines_finder_spec.rb2
-rw-r--r--spec/finders/todos_finder_spec.rb4
-rw-r--r--spec/helpers/application_helper_spec.rb20
-rw-r--r--spec/helpers/blame_helper_spec.rb59
-rw-r--r--spec/helpers/diff_helper_spec.rb50
-rw-r--r--spec/helpers/projects_helper_spec.rb37
-rw-r--r--spec/initializers/8_metrics_spec.rb1
-rw-r--r--spec/javascripts/bootstrap_linked_tabs_spec.js15
-rw-r--r--spec/javascripts/commits_spec.js13
-rw-r--r--spec/javascripts/groups/mock_data.js4
-rw-r--r--spec/javascripts/issue_show/components/app_spec.js2
-rw-r--r--spec/javascripts/merge_request_tabs_spec.js25
-rw-r--r--spec/javascripts/notes_spec.js110
-rw-r--r--spec/javascripts/pipeline_schedules/interval_pattern_input_spec.js2
-rw-r--r--spec/javascripts/pipelines/pipeline_url_spec.js4
-rw-r--r--spec/javascripts/pipelines/pipelines_actions_spec.js2
-rw-r--r--spec/javascripts/pipelines/pipelines_artifacts_spec.js2
-rw-r--r--spec/javascripts/pipelines/pipelines_spec.js2
-rw-r--r--spec/javascripts/pipelines/time_ago_spec.js2
-rw-r--r--spec/javascripts/pipelines_spec.js5
-rw-r--r--spec/javascripts/project_title_spec.js76
-rw-r--r--spec/javascripts/test_bundle.js8
-rw-r--r--spec/javascripts/vue_mr_widget/components/mr_widget_related_links_spec.js246
-rw-r--r--spec/javascripts/vue_mr_widget/mr_widget_options_spec.js7
-rw-r--r--spec/javascripts/vue_mr_widget/stores/mr_widget_store_spec.js12
-rw-r--r--spec/javascripts/vue_shared/components/commit_spec.js2
-rw-r--r--spec/javascripts/vue_shared/components/header_ci_component_spec.js2
-rw-r--r--spec/javascripts/vue_shared/components/pipelines_table_row_spec.js20
-rw-r--r--spec/javascripts/vue_shared/components/pipelines_table_spec.js18
-rw-r--r--spec/javascripts/vue_shared/components/time_ago_tooltip_spec.js13
-rw-r--r--spec/lib/banzai/filter/external_issue_reference_filter_spec.rb4
-rw-r--r--spec/lib/banzai/filter/redactor_filter_spec.rb8
-rw-r--r--spec/lib/banzai/reference_parser/base_parser_spec.rb2
-rw-r--r--spec/lib/banzai/reference_parser/commit_parser_spec.rb4
-rw-r--r--spec/lib/banzai/reference_parser/commit_range_parser_spec.rb4
-rw-r--r--spec/lib/banzai/reference_parser/external_issue_parser_spec.rb4
-rw-r--r--spec/lib/banzai/reference_parser/label_parser_spec.rb4
-rw-r--r--spec/lib/banzai/reference_parser/milestone_parser_spec.rb4
-rw-r--r--spec/lib/ci/gitlab_ci_yaml_processor_spec.rb173
-rw-r--r--spec/lib/extracts_path_spec.rb5
-rw-r--r--spec/lib/gitlab/auth/unique_ips_limiter_spec.rb4
-rw-r--r--spec/lib/gitlab/badge/build/status_spec.rb8
-rw-r--r--spec/lib/gitlab/checks/change_access_spec.rb8
-rw-r--r--spec/lib/gitlab/ci/build/image_spec.rb61
-rw-r--r--spec/lib/gitlab/ci/config/entry/cache_spec.rb4
-rw-r--r--spec/lib/gitlab/ci/config/entry/environment_spec.rb4
-rw-r--r--spec/lib/gitlab/ci/config/entry/global_spec.rb36
-rw-r--r--spec/lib/gitlab/ci/config/entry/image_spec.rb113
-rw-r--r--spec/lib/gitlab/ci/config/entry/job_spec.rb18
-rw-r--r--spec/lib/gitlab/ci/config/entry/jobs_spec.rb8
-rw-r--r--spec/lib/gitlab/ci/config/entry/service_spec.rb119
-rw-r--r--spec/lib/gitlab/ci/config/entry/services_spec.rb43
-rw-r--r--spec/lib/gitlab/ci/status/build/cancelable_spec.rb4
-rw-r--r--spec/lib/gitlab/ci/status/build/common_spec.rb8
-rw-r--r--spec/lib/gitlab/ci/status/build/factory_spec.rb4
-rw-r--r--spec/lib/gitlab/ci/status/build/play_spec.rb4
-rw-r--r--spec/lib/gitlab/ci/status/build/retryable_spec.rb4
-rw-r--r--spec/lib/gitlab/ci/status/build/stop_spec.rb4
-rw-r--r--spec/lib/gitlab/ci/status/external/common_spec.rb13
-rw-r--r--spec/lib/gitlab/ci/status/pipeline/common_spec.rb4
-rw-r--r--spec/lib/gitlab/current_settings_spec.rb31
-rw-r--r--spec/lib/gitlab/database/migration_helpers_spec.rb37
-rw-r--r--spec/lib/gitlab/database_spec.rb65
-rw-r--r--spec/lib/gitlab/diff/file_spec.rb301
-rw-r--r--spec/lib/gitlab/email/handler/create_note_handler_spec.rb2
-rw-r--r--spec/lib/gitlab/etag_caching/middleware_spec.rb33
-rw-r--r--spec/lib/gitlab/etag_caching/router_spec.rb44
-rw-r--r--spec/lib/gitlab/fake_application_settings_spec.rb32
-rw-r--r--spec/lib/gitlab/gfm/reference_rewriter_spec.rb4
-rw-r--r--spec/lib/gitlab/git/diff_spec.rb2
-rw-r--r--spec/lib/gitlab/git/gitmodules_parser_spec.rb28
-rw-r--r--spec/lib/gitlab/git/repository_spec.rb48
-rw-r--r--spec/lib/gitlab/git_access_spec.rb111
-rw-r--r--spec/lib/gitlab/git_access_wiki_spec.rb3
-rw-r--r--spec/lib/gitlab/gitaly_client/notifications_spec.rb5
-rw-r--r--spec/lib/gitlab/gitaly_client/ref_spec.rb15
-rw-r--r--spec/lib/gitlab/gitaly_client_spec.rb24
-rw-r--r--spec/lib/gitlab/group_hierarchy_spec.rb24
-rw-r--r--spec/lib/gitlab/highlight_spec.rb4
-rw-r--r--spec/lib/gitlab/i18n_spec.rb4
-rw-r--r--spec/lib/gitlab/import_export/all_models.yml3
-rw-r--r--spec/lib/gitlab/import_export/project.json38
-rw-r--r--spec/lib/gitlab/import_export/project_tree_restorer_spec.rb7
-rw-r--r--spec/lib/gitlab/import_export/project_tree_saver_spec.rb10
-rw-r--r--spec/lib/gitlab/import_export/safe_model_attributes.yml11
-rw-r--r--spec/lib/gitlab/kubernetes_spec.rb10
-rw-r--r--spec/lib/gitlab/ldap/adapter_spec.rb16
-rw-r--r--spec/lib/gitlab/ldap/user_spec.rb16
-rw-r--r--spec/lib/gitlab/middleware/rails_queue_duration_spec.rb8
-rw-r--r--spec/lib/gitlab/o_auth/auth_hash_spec.rb12
-rw-r--r--spec/lib/gitlab/o_auth/user_spec.rb84
-rw-r--r--spec/lib/gitlab/quick_actions/command_definition_spec.rb (renamed from spec/lib/gitlab/slash_commands/command_definition_spec.rb)2
-rw-r--r--spec/lib/gitlab/quick_actions/dsl_spec.rb (renamed from spec/lib/gitlab/slash_commands/dsl_spec.rb)4
-rw-r--r--spec/lib/gitlab/quick_actions/extractor_spec.rb (renamed from spec/lib/gitlab/slash_commands/extractor_spec.rb)4
-rw-r--r--spec/lib/gitlab/redis_spec.rb13
-rw-r--r--spec/lib/gitlab/repo_path_spec.rb75
-rw-r--r--spec/lib/gitlab/saml/user_spec.rb62
-rw-r--r--spec/lib/gitlab/serializer/pagination_spec.rb4
-rw-r--r--spec/lib/gitlab/slash_commands/command_spec.rb (renamed from spec/lib/gitlab/chat_commands/command_spec.rb)8
-rw-r--r--spec/lib/gitlab/slash_commands/deploy_spec.rb (renamed from spec/lib/gitlab/chat_commands/deploy_spec.rb)2
-rw-r--r--spec/lib/gitlab/slash_commands/issue_new_spec.rb (renamed from spec/lib/gitlab/chat_commands/issue_new_spec.rb)2
-rw-r--r--spec/lib/gitlab/slash_commands/issue_search_spec.rb (renamed from spec/lib/gitlab/chat_commands/issue_search_spec.rb)2
-rw-r--r--spec/lib/gitlab/slash_commands/issue_show_spec.rb (renamed from spec/lib/gitlab/chat_commands/issue_show_spec.rb)2
-rw-r--r--spec/lib/gitlab/slash_commands/presenters/access_spec.rb (renamed from spec/lib/gitlab/chat_commands/presenters/access_spec.rb)2
-rw-r--r--spec/lib/gitlab/slash_commands/presenters/deploy_spec.rb (renamed from spec/lib/gitlab/chat_commands/presenters/deploy_spec.rb)2
-rw-r--r--spec/lib/gitlab/slash_commands/presenters/issue_new_spec.rb (renamed from spec/lib/gitlab/chat_commands/presenters/issue_new_spec.rb)2
-rw-r--r--spec/lib/gitlab/slash_commands/presenters/issue_search_spec.rb (renamed from spec/lib/gitlab/chat_commands/presenters/issue_search_spec.rb)6
-rw-r--r--spec/lib/gitlab/slash_commands/presenters/issue_show_spec.rb (renamed from spec/lib/gitlab/chat_commands/presenters/issue_show_spec.rb)2
-rw-r--r--spec/lib/gitlab/template/issue_template_spec.rb9
-rw-r--r--spec/lib/gitlab/template/merge_request_template_spec.rb9
-rw-r--r--spec/lib/gitlab/workhorse_spec.rb2
-rw-r--r--spec/lib/json_web_token/rsa_token_spec.rb8
-rw-r--r--spec/lib/json_web_token/token_spec.rb5
-rw-r--r--spec/mailers/notify_spec.rb37
-rw-r--r--spec/migrations/README.md87
-rw-r--r--spec/migrations/convert_custom_notification_settings_to_columns_spec.rb118
-rw-r--r--spec/migrations/rename_more_reserved_project_names_spec.rb4
-rw-r--r--spec/migrations/rename_reserved_project_names_spec.rb4
-rw-r--r--spec/models/application_setting_spec.rb4
-rw-r--r--spec/models/broadcast_message_spec.rb19
-rw-r--r--spec/models/ci/build_spec.rb46
-rw-r--r--spec/models/ci/legacy_stage_spec.rb11
-rw-r--r--spec/models/ci/pipeline_spec.rb4
-rw-r--r--spec/models/commit_spec.rb12
-rw-r--r--spec/models/commit_status_spec.rb42
-rw-r--r--spec/models/concerns/access_requestable_spec.rb8
-rw-r--r--spec/models/concerns/issuable_spec.rb16
-rw-r--r--spec/models/concerns/mentionable_spec.rb4
-rw-r--r--spec/models/concerns/milestoneish_spec.rb31
-rw-r--r--spec/models/concerns/reactive_caching_spec.rb23
-rw-r--r--spec/models/concerns/token_authenticatable_spec.rb5
-rw-r--r--spec/models/deployment_spec.rb4
-rw-r--r--spec/models/diff_viewer/base_spec.rb150
-rw-r--r--spec/models/diff_viewer/server_side_spec.rb36
-rw-r--r--spec/models/environment_spec.rb2
-rw-r--r--spec/models/generic_commit_status_spec.rb9
-rw-r--r--spec/models/group_spec.rb27
-rw-r--r--spec/models/issue_spec.rb18
-rw-r--r--spec/models/merge_request_diff_file_spec.rb11
-rw-r--r--spec/models/merge_request_diff_spec.rb9
-rw-r--r--spec/models/merge_request_spec.rb40
-rw-r--r--spec/models/milestone_spec.rb29
-rw-r--r--spec/models/note_spec.rb8
-rw-r--r--spec/models/notification_setting_spec.rb30
-rw-r--r--spec/models/project_services/bamboo_service_spec.rb8
-rw-r--r--spec/models/project_services/bugzilla_service_spec.rb8
-rw-r--r--spec/models/project_services/buildkite_service_spec.rb8
-rw-r--r--spec/models/project_services/campfire_service_spec.rb8
-rw-r--r--spec/models/project_services/chat_message/wiki_page_message_spec.rb40
-rw-r--r--spec/models/project_services/custom_issue_tracker_service_spec.rb8
-rw-r--r--spec/models/project_services/drone_ci_service_spec.rb8
-rw-r--r--spec/models/project_services/emails_on_push_service_spec.rb8
-rw-r--r--spec/models/project_services/external_wiki_service_spec.rb8
-rw-r--r--spec/models/project_services/flowdock_service_spec.rb8
-rw-r--r--spec/models/project_services/gemnasium_service_spec.rb8
-rw-r--r--spec/models/project_services/hipchat_service_spec.rb8
-rw-r--r--spec/models/project_services/irker_service_spec.rb8
-rw-r--r--spec/models/project_services/jira_service_spec.rb8
-rw-r--r--spec/models/project_services/kubernetes_service_spec.rb96
-rw-r--r--spec/models/project_services/microsoft_teams_service_spec.rb8
-rw-r--r--spec/models/project_services/pivotaltracker_service_spec.rb8
-rw-r--r--spec/models/project_services/prometheus_service_spec.rb8
-rw-r--r--spec/models/project_services/pushover_service_spec.rb8
-rw-r--r--spec/models/project_services/redmine_service_spec.rb8
-rw-r--r--spec/models/project_services/teamcity_service_spec.rb8
-rw-r--r--spec/models/project_spec.rb28
-rw-r--r--spec/models/project_team_spec.rb4
-rw-r--r--spec/models/project_wiki_spec.rb5
-rw-r--r--spec/models/route_spec.rb9
-rw-r--r--spec/models/user_spec.rb50
-rw-r--r--spec/policies/ci/build_policy_spec.rb12
-rw-r--r--spec/policies/project_policy_spec.rb12
-rw-r--r--spec/policies/project_snippet_policy_spec.rb12
-rw-r--r--spec/requests/api/award_emoji_spec.rb4
-rw-r--r--spec/requests/api/commit_statuses_spec.rb53
-rw-r--r--spec/requests/api/commits_spec.rb12
-rw-r--r--spec/requests/api/deploy_keys_spec.rb8
-rw-r--r--spec/requests/api/files_spec.rb4
-rw-r--r--spec/requests/api/helpers_spec.rb38
-rw-r--r--spec/requests/api/internal_spec.rb102
-rw-r--r--spec/requests/api/jobs_spec.rb114
-rw-r--r--spec/requests/api/keys_spec.rb4
-rw-r--r--spec/requests/api/labels_spec.rb12
-rw-r--r--spec/requests/api/merge_requests_spec.rb26
-rw-r--r--spec/requests/api/milestones_spec.rb46
-rw-r--r--spec/requests/api/notes_spec.rb12
-rw-r--r--spec/requests/api/pipelines_spec.rb12
-rw-r--r--spec/requests/api/projects_spec.rb43
-rw-r--r--spec/requests/api/runner_spec.rb71
-rw-r--r--spec/requests/api/settings_spec.rb8
-rw-r--r--spec/requests/api/system_hooks_spec.rb4
-rw-r--r--spec/requests/api/templates_spec.rb12
-rw-r--r--spec/requests/api/users_spec.rb57
-rw-r--r--spec/requests/ci/api/builds_spec.rb24
-rw-r--r--spec/requests/ci/api/runners_spec.rb9
-rw-r--r--spec/requests/git_http_spec.rb72
-rw-r--r--spec/requests/jwt_controller_spec.rb4
-rw-r--r--spec/routing/project_routing_spec.rb8
-rw-r--r--spec/routing/routing_spec.rb4
-rw-r--r--spec/rubocop/cop/migration/add_timestamps_spec.rb90
-rw-r--r--spec/rubocop/cop/migration/datetime_spec.rb90
-rw-r--r--spec/rubocop/cop/migration/timestamps_spec.rb99
-rw-r--r--spec/rubocop/cop/rspec/single_line_hook_spec.rb66
-rw-r--r--spec/serializers/build_details_entity_spec.rb6
-rw-r--r--spec/serializers/environment_serializer_spec.rb12
-rw-r--r--spec/serializers/job_entity_spec.rb (renamed from spec/serializers/build_entity_spec.rb)47
-rw-r--r--spec/serializers/pipeline_details_entity_spec.rb22
-rw-r--r--spec/serializers/pipeline_entity_spec.rb8
-rw-r--r--spec/serializers/pipeline_serializer_spec.rb8
-rw-r--r--spec/serializers/stage_entity_spec.rb11
-rw-r--r--spec/services/auth/container_registry_authentication_service_spec.rb24
-rw-r--r--spec/services/ci/update_build_queue_service_spec.rb8
-rw-r--r--spec/services/create_deployment_service_spec.rb4
-rw-r--r--spec/services/groups/create_service_spec.rb8
-rw-r--r--spec/services/issues/create_service_spec.rb8
-rw-r--r--spec/services/issues/move_service_spec.rb10
-rw-r--r--spec/services/issues/update_service_spec.rb16
-rw-r--r--spec/services/members/create_service_spec.rb4
-rw-r--r--spec/services/merge_requests/build_service_spec.rb4
-rw-r--r--spec/services/merge_requests/create_service_spec.rb8
-rw-r--r--spec/services/merge_requests/merge_service_spec.rb4
-rw-r--r--spec/services/merge_requests/update_service_spec.rb12
-rw-r--r--spec/services/notes/quick_actions_service_spec.rb (renamed from spec/services/notes/slash_commands_service_spec.rb)16
-rw-r--r--spec/services/notification_service_spec.rb3
-rw-r--r--spec/services/pages_service_spec.rb12
-rw-r--r--spec/services/preview_markdown_service_spec.rb18
-rw-r--r--spec/services/projects/transfer_service_spec.rb12
-rw-r--r--spec/services/quick_actions/interpret_service_spec.rb (renamed from spec/services/slash_commands/interpret_service_spec.rb)12
-rw-r--r--spec/services/spam_service_spec.rb4
-rw-r--r--spec/spec_helper.rb16
-rw-r--r--spec/support/capybara.rb9
-rw-r--r--spec/support/chat_slash_commands_shared_examples.rb2
-rw-r--r--spec/support/features/issuable_slash_commands_shared_examples.rb20
-rw-r--r--spec/support/kubernetes_helpers.rb33
-rw-r--r--spec/support/login_helpers.rb57
-rw-r--r--spec/support/matchers/gitaly_matchers.rb9
-rw-r--r--spec/support/milestone_tabs_examples.rb24
-rw-r--r--spec/support/project_features_apply_to_issuables_shared_examples.rb2
-rw-r--r--spec/support/quick_actions_helpers.rb (renamed from spec/support/slash_commands_helpers.rb)2
-rw-r--r--spec/support/reference_parser_shared_examples.rb8
-rw-r--r--spec/support/services/issuable_create_service_slash_commands_shared_examples.rb6
-rw-r--r--spec/support/services/issuable_update_service_shared_examples.rb4
-rw-r--r--spec/support/slack_mattermost_notifications_shared_examples.rb8
-rw-r--r--spec/support/time_tracking_shared_examples.rb8
-rw-r--r--spec/support/unique_ip_check_shared_examples.rb8
-rw-r--r--spec/support/updating_mentions_shared_examples.rb12
-rw-r--r--spec/support/wait_for_requests.rb14
-rw-r--r--spec/tasks/gitlab/backup_rake_spec.rb4
-rw-r--r--spec/tasks/gitlab/gitaly_rake_spec.rb11
-rw-r--r--spec/views/profiles/show.html.haml_spec.rb19
-rw-r--r--spec/views/projects/diffs/_viewer.html.haml_spec.rb71
-rw-r--r--spec/views/shared/notes/_form.html.haml_spec.rb6
-rw-r--r--spec/workers/emails_on_push_worker_spec.rb4
-rw-r--r--spec/workers/expire_build_artifacts_worker_spec.rb8
-rw-r--r--spec/workers/git_garbage_collect_worker_spec.rb4
-rw-r--r--spec/workers/post_receive_spec.rb6
-rw-r--r--spec/workers/stuck_ci_jobs_worker_spec.rb12
-rw-r--r--yarn.lock282
1052 files changed, 13051 insertions, 5216 deletions
diff --git a/.codeclimate.yml b/.codeclimate.yml
index e5636a13783..42afed54371 100644
--- a/.codeclimate.yml
+++ b/.codeclimate.yml
@@ -10,10 +10,10 @@ engines:
languages:
- ruby
- javascript
+ exclude_paths:
+ - "lib/api/v3/*"
eslint:
enabled: true
- fixme:
- enabled: true
rubocop:
enabled: true
ratings:
@@ -35,4 +35,13 @@ exclude_paths:
- node_modules/
- spec/
- vendor/
-- lib/api/v3/
+- .yarn-cache/
+- tmp/
+- builds/
+- coverage/
+- public/
+- shared/
+- webpack-report/
+- log/
+- backups/
+- coverage-javascript/
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index b442e48a3d0..f0c266485b6 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -441,21 +441,40 @@ gitlab:assets:compile:
- webpack-report/
karma:
+ image: "dev.gitlab.org:5005/gitlab/gitlab-build-images:ruby-2.3.3-golang-1.8-git-2.7-chrome-59.0-node-7.1-postgresql-9.6"
stage: test
<<: *use-pg
<<: *dedicated-runner
<<: *except-docs
variables:
BABEL_ENV: "coverage"
+ CHROME_LOG_FILE: "chrome_debug.log"
script:
- bundle exec rake karma
coverage: '/^Statements *: (\d+\.\d+%)/'
artifacts:
name: coverage-javascript
expire_in: 31d
+ when: always
paths:
+ - chrome_debug.log
- coverage-javascript/
+codeclimate:
+ before_script: []
+ image: docker:latest
+ stage: test
+ variables:
+ SETUP_DB: "false"
+ DOCKER_DRIVER: overlay
+ services:
+ - docker:dind
+ script:
+ - docker pull codeclimate/codeclimate
+ - docker run --env CODECLIMATE_CODE="$PWD" --volume "$PWD":/code --volume /var/run/docker.sock:/var/run/docker.sock --volume /tmp/cc:/tmp/cc codeclimate/codeclimate analyze -f json > codeclimate.json
+ artifacts:
+ paths: [codeclimate.json]
+
coverage:
stage: post-test
services: []
diff --git a/.rubocop.yml b/.rubocop.yml
index 66a40f2cf57..4537e710dd4 100644
--- a/.rubocop.yml
+++ b/.rubocop.yml
@@ -1064,6 +1064,13 @@ RSpec/NotToNot:
RSpec/RepeatedDescription:
Enabled: false
+# Ensure RSpec hook blocks are always multi-line.
+RSpec/SingleLineHook:
+ Enabled: true
+ Exclude:
+ - 'spec/factories/*'
+ - 'spec/requests/api/v3/*'
+
# Checks for stubbed test subjects.
RSpec/SubjectStub:
Enabled: false
diff --git a/CHANGELOG.md b/CHANGELOG.md
index e5567dc3b39..f43858a00a5 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,21 @@
documentation](doc/development/changelog.md) for instructions on adding your own
entry.
+## 9.2.6 (2017-06-16)
+
+- Fix the last coverage in trace log should be extracted. !11128 (dosuken123)
+- Respect merge, instead of push, permissions for protected actions. !11648
+- Fix pipeline_schedules pages throwing error 500. !11706 (dosuken123)
+- Make backup task to continue on corrupt repositories. !11962
+- Fix incorrect ETag cache key when relative instance URL is used. !11964
+- Fix math rendering on blob pages.
+- Invalidate cache for issue and MR counters more granularly.
+- Fix terminals support for Kubernetes Service.
+- Fix LFS timeouts when trying to save large files.
+- Strip trailing whitespaces in submodule URLs.
+- Make sure reCAPTCHA configuration is loaded when spam checks are initiated.
+- Remove foreigh key on ci_trigger_schedules only if it exists.
+
## 9.2.5 (2017-06-07)
- No changes.
diff --git a/app/assets/javascripts/api.js b/app/assets/javascripts/api.js
index 6680834a8d1..56fa0d71a9a 100644
--- a/app/assets/javascripts/api.js
+++ b/app/assets/javascripts/api.js
@@ -77,7 +77,7 @@ const Api = {
dataType: 'json',
})
.done(label => callback(label))
- .error(message => callback(message.responseJSON));
+ .fail(message => callback(message.responseJSON));
},
// Return group projects list. Filtered by query
@@ -134,7 +134,7 @@ const Api = {
dataType: 'json',
})
.done(file => callback(null, file))
- .error(callback);
+ .fail(callback);
},
users(query, options) {
diff --git a/app/assets/javascripts/boards/filtered_search_boards.js b/app/assets/javascripts/boards/filtered_search_boards.js
index b37698fe9ca..3f083655f95 100644
--- a/app/assets/javascripts/boards/filtered_search_boards.js
+++ b/app/assets/javascripts/boards/filtered_search_boards.js
@@ -11,7 +11,6 @@ export default class FilteredSearchBoards extends gl.FilteredSearchManager {
// Issue boards is slightly different, we handle all the requests async
// instead or reloading the page, we just re-fire the list ajax requests
this.isHandledAsync = true;
-
this.cantEdit = cantEdit;
}
diff --git a/app/assets/javascripts/commit/pipelines/pipelines_table.js b/app/assets/javascripts/commit/pipelines/pipelines_table.js
index 082fbafb740..70ba83ce5b9 100644
--- a/app/assets/javascripts/commit/pipelines/pipelines_table.js
+++ b/app/assets/javascripts/commit/pipelines/pipelines_table.js
@@ -1,6 +1,6 @@
import Vue from 'vue';
import Visibility from 'visibilityjs';
-import pipelinesTableComponent from '../../vue_shared/components/pipelines_table';
+import pipelinesTableComponent from '../../vue_shared/components/pipelines_table.vue';
import PipelinesService from '../../pipelines/services/pipelines_service';
import PipelineStore from '../../pipelines/stores/pipelines_store';
import eventHub from '../../pipelines/event_hub';
diff --git a/app/assets/javascripts/dispatcher.js b/app/assets/javascripts/dispatcher.js
index 5f87a05067b..88b4b567fa9 100644
--- a/app/assets/javascripts/dispatcher.js
+++ b/app/assets/javascripts/dispatcher.js
@@ -79,7 +79,18 @@ import initSettingsPanels from './settings_panels';
path = page.split(':');
shortcut_handler = null;
- new GfmAutoComplete(gl.GfmAutoComplete && gl.GfmAutoComplete.dataSources).setup();
+ $('.js-gfm-input').each((i, el) => {
+ const gfm = new GfmAutoComplete(gl.GfmAutoComplete && gl.GfmAutoComplete.dataSources);
+ const enableGFM = gl.utils.convertPermissionToBoolean(el.dataset.supportsAutocomplete);
+ gfm.setup($(el), {
+ emojis: true,
+ members: enableGFM,
+ issues: enableGFM,
+ milestones: enableGFM,
+ mergeRequests: enableGFM,
+ labels: enableGFM,
+ });
+ });
function initBlob() {
new LineHighlighter();
@@ -176,7 +187,7 @@ import initSettingsPanels from './settings_panels';
case 'groups:milestones:update':
new ZenMode();
new gl.DueDateSelectors();
- new gl.GLForm($('.milestone-form'));
+ new gl.GLForm($('.milestone-form'), true);
break;
case 'projects:compare:show':
new gl.Diff();
@@ -188,7 +199,7 @@ import initSettingsPanels from './settings_panels';
case 'projects:issues:new':
case 'projects:issues:edit':
shortcut_handler = new ShortcutsNavigation();
- new gl.GLForm($('.issue-form'));
+ new gl.GLForm($('.issue-form'), true);
new IssuableForm($('.issue-form'));
new LabelsSelect();
new MilestoneSelect();
@@ -199,7 +210,7 @@ import initSettingsPanels from './settings_panels';
case 'projects:merge_requests:edit':
new gl.Diff();
shortcut_handler = new ShortcutsNavigation();
- new gl.GLForm($('.merge-request-form'));
+ new gl.GLForm($('.merge-request-form'), true);
new IssuableForm($('.merge-request-form'));
new LabelsSelect();
new MilestoneSelect();
@@ -208,22 +219,24 @@ import initSettingsPanels from './settings_panels';
break;
case 'projects:tags:new':
new ZenMode();
- new gl.GLForm($('.tag-form'));
+ new gl.GLForm($('.tag-form'), true);
new RefSelectDropdown($('.js-branch-select'), window.gl.availableRefs);
break;
case 'projects:snippets:new':
case 'projects:snippets:edit':
case 'projects:snippets:create':
case 'projects:snippets:update':
+ new gl.GLForm($('.snippet-form'), true);
+ break;
case 'snippets:new':
case 'snippets:edit':
case 'snippets:create':
case 'snippets:update':
- new gl.GLForm($('.snippet-form'));
+ new gl.GLForm($('.snippet-form'), false);
break;
case 'projects:releases:edit':
new ZenMode();
- new gl.GLForm($('.release-form'));
+ new gl.GLForm($('.release-form'), true);
break;
case 'projects:merge_requests:show':
new gl.Diff();
@@ -471,7 +484,7 @@ import initSettingsPanels from './settings_panels';
new gl.Wikis();
shortcut_handler = new ShortcutsWiki();
new ZenMode();
- new gl.GLForm($('.wiki-form'));
+ new gl.GLForm($('.wiki-form'), true);
break;
case 'snippets':
shortcut_handler = new ShortcutsNavigation();
diff --git a/app/assets/javascripts/dropzone_input.js b/app/assets/javascripts/dropzone_input.js
index 98ddcc20036..73675d300be 100644
--- a/app/assets/javascripts/dropzone_input.js
+++ b/app/assets/javascripts/dropzone_input.js
@@ -287,6 +287,10 @@ window.DropzoneInput = (function() {
$uploadingErrorMessage.html(message);
};
+ closeAlertMessage = function() {
+ return form.find('.div-dropzone-alert').alert('close');
+ };
+
form.find('.markdown-selector').click(function(e) {
e.preventDefault();
$(this).closest('.gfm-form').find('.div-dropzone').click();
diff --git a/app/assets/javascripts/environments/components/environment_item.vue b/app/assets/javascripts/environments/components/environment_item.vue
index de2269118cd..809c147bf25 100644
--- a/app/assets/javascripts/environments/components/environment_item.vue
+++ b/app/assets/javascripts/environments/components/environment_item.vue
@@ -9,7 +9,7 @@ import StopComponent from './environment_stop.vue';
import RollbackComponent from './environment_rollback.vue';
import TerminalButtonComponent from './environment_terminal_button.vue';
import MonitoringButtonComponent from './environment_monitoring.vue';
-import CommitComponent from '../../vue_shared/components/commit';
+import CommitComponent from '../../vue_shared/components/commit.vue';
import eventHub from '../event_hub';
/**
@@ -535,10 +535,10 @@ export default {
</span>
</div>
- <div class="table-section section-30 environments-actions table-button-footer" role="gridcell">
+ <div class="table-section section-30 table-button-footer" role="gridcell">
<div
v-if="!model.isFolder"
- class="btn-group environment-action-buttons"
+ class="btn-group table-action-buttons"
role="group">
<actions-component
diff --git a/app/assets/javascripts/environments/stores/environments_store.js b/app/assets/javascripts/environments/stores/environments_store.js
index 8a2f6a473de..a5773dd7e4f 100644
--- a/app/assets/javascripts/environments/stores/environments_store.js
+++ b/app/assets/javascripts/environments/stores/environments_store.js
@@ -158,5 +158,4 @@ export default class EnvironmentsStore {
return environments.filter(env => env.isFolder && env.isOpen);
}
-
}
diff --git a/app/assets/javascripts/filtered_search/dropdown_hint.js b/app/assets/javascripts/filtered_search/dropdown_hint.js
index 2af242a69df..5838b1bdbb7 100644
--- a/app/assets/javascripts/filtered_search/dropdown_hint.js
+++ b/app/assets/javascripts/filtered_search/dropdown_hint.js
@@ -56,7 +56,7 @@ class DropdownHint extends gl.FilteredSearchDropdown {
}
renderContent() {
- const dropdownData = gl.FilteredSearchTokenKeys.get()
+ const dropdownData = this.tokenKeys.get()
.map(tokenKey => ({
icon: `fa-${tokenKey.icon}`,
hint: tokenKey.key,
diff --git a/app/assets/javascripts/gfm_auto_complete.js b/app/assets/javascripts/gfm_auto_complete.js
index 401dec1a370..105762cb1ba 100644
--- a/app/assets/javascripts/gfm_auto_complete.js
+++ b/app/assets/javascripts/gfm_auto_complete.js
@@ -34,7 +34,7 @@ class GfmAutoComplete {
const $input = $(input);
$input.off('focus.setupAtWho').on('focus.setupAtWho', this.setupAtWho.bind(this, $input));
// This triggers at.js again
- // Needed for slash commands with suffixes (ex: /label ~)
+ // Needed for quick actions with suffixes (ex: /label ~)
$input.on('inserted-commands.atwho', $input.trigger.bind($input, 'keyup'));
$input.on('clear-commands-cache.atwho', () => this.clearCache());
});
@@ -48,8 +48,8 @@ class GfmAutoComplete {
if (this.enableMap.mergeRequests) this.setupMergeRequests($input);
if (this.enableMap.labels) this.setupLabels($input);
- // We don't instantiate the slash commands autocomplete for note and issue/MR edit forms
- $input.filter('[data-supports-slash-commands="true"]').atwho({
+ // We don't instantiate the quick actions autocomplete for note and issue/MR edit forms
+ $input.filter('[data-supports-quick-actions="true"]').atwho({
at: '/',
alias: 'commands',
searchKey: 'search',
diff --git a/app/assets/javascripts/groups/components/group_item.vue b/app/assets/javascripts/groups/components/group_item.vue
index 32815b9f73e..b1db34b9c50 100644
--- a/app/assets/javascripts/groups/components/group_item.vue
+++ b/app/assets/javascripts/groups/components/group_item.vue
@@ -27,7 +27,7 @@ export default {
if (this.group.hasSubgroups) {
eventHub.$emit('toggleSubGroups', this.group);
} else {
- window.location.href = this.group.webUrl;
+ window.location.href = this.group.groupPath;
}
}
},
@@ -192,7 +192,7 @@ export default {
<div
class="avatar-container s40 hidden-xs">
<a
- :href="group.webUrl">
+ :href="group.groupPath">
<img
class="avatar s40"
:src="group.avatarUrl"
@@ -202,7 +202,7 @@ export default {
<div
class="title">
<a
- :href="group.webUrl">{{fullPath}}</a>
+ :href="group.groupPath">{{fullPath}}</a>
<template v-if="group.permissions.humanGroupAccess">
as
<span class="access-type">{{group.permissions.humanGroupAccess}}</span>
diff --git a/app/assets/javascripts/groups/stores/groups_store.js b/app/assets/javascripts/groups/stores/groups_store.js
index 67ee7d140ce..f6dc4290fd5 100644
--- a/app/assets/javascripts/groups/stores/groups_store.js
+++ b/app/assets/javascripts/groups/stores/groups_store.js
@@ -122,6 +122,7 @@ export default class GroupsStore {
canEdit: rawGroup.can_edit,
description: rawGroup.description,
webUrl: rawGroup.web_url,
+ groupPath: rawGroup.group_path,
parentId: rawGroup.parent_id,
visibility: rawGroup.visibility,
leavePath: rawGroup.leave_path,
diff --git a/app/assets/javascripts/issue_show/components/fields/description.vue b/app/assets/javascripts/issue_show/components/fields/description.vue
index 30a1be5cb50..54650d2f184 100644
--- a/app/assets/javascripts/issue_show/components/fields/description.vue
+++ b/app/assets/javascripts/issue_show/components/fields/description.vue
@@ -41,7 +41,7 @@
<textarea
id="issue-description"
class="note-textarea js-gfm-input js-autosize markdown-area"
- data-supports-slash-commands="false"
+ data-supports-quick-actionss="false"
aria-label="Description"
v-model="formState.description"
ref="textarea"
diff --git a/app/assets/javascripts/lib/utils/datetime_utility.js b/app/assets/javascripts/lib/utils/datetime_utility.js
index 54c0da3fc9c..bfcc50996cc 100644
--- a/app/assets/javascripts/lib/utils/datetime_utility.js
+++ b/app/assets/javascripts/lib/utils/datetime_utility.js
@@ -34,7 +34,7 @@ window.dateFormat = dateFormat;
w.gl.utils.localTimeAgo = function($timeagoEls, setTimeago = true) {
$timeagoEls.each((i, el) => {
- el.setAttribute('title', gl.utils.formatDate(el.getAttribute('datetime')));
+ el.setAttribute('title', el.getAttribute('title'));
if (setTimeago) {
// Recreate with custom template
diff --git a/app/assets/javascripts/locale/en/app.js b/app/assets/javascripts/locale/en/app.js
index 0bb76c80b7a..d634af959e5 100644
--- a/app/assets/javascripts/locale/en/app.js
+++ b/app/assets/javascripts/locale/en/app.js
@@ -1 +1 @@
-var locales = locales || {}; locales['en'] = {"domain":"app","locale_data":{"app":{"":{"Project-Id-Version":"gitlab 1.0.0","Report-Msgid-Bugs-To":"","PO-Revision-Date":"2017-04-12 22:36-0500","Last-Translator":"FULL NAME <EMAIL@ADDRESS>","Language-Team":"English","Language":"en","MIME-Version":"1.0","Content-Type":"text/plain; charset=UTF-8","Content-Transfer-Encoding":"8bit","Plural-Forms":"nplurals=2; plural=n != 1;","lang":"en","domain":"app","plural_forms":"nplurals=2; plural=n != 1;"},"Are you sure you want to delete this pipeline schedule?":[""],"ByAuthor|by":[""],"Cancel":[""],"Commit":["",""],"Cron Timezone":[""],"Cycle Analytics gives an overview of how much time it takes to go from idea to production in your project.":[""],"CycleAnalyticsStage|Code":[""],"CycleAnalyticsStage|Issue":[""],"CycleAnalyticsStage|Plan":[""],"CycleAnalyticsStage|Production":[""],"CycleAnalyticsStage|Review":[""],"CycleAnalyticsStage|Staging":[""],"CycleAnalyticsStage|Test":[""],"Delete":[""],"Deploy":["",""],"Description":[""],"Edit":[""],"Edit Pipeline Schedule %{id}":[""],"Failed to change the owner":[""],"Failed to remove the pipeline schedule":[""],"Filter":[""],"FirstPushedBy|First":[""],"FirstPushedBy|pushed by":[""],"From issue creation until deploy to production":[""],"From merge request merge until deploy to production":[""],"Interval Pattern":[""],"Introducing Cycle Analytics":[""],"Last %d day":["",""],"Last Pipeline":[""],"Limited to showing %d event at most":["",""],"Median":[""],"New Issue":["",""],"New Pipeline Schedule":[""],"No schedules":[""],"Not available":[""],"Not enough data":[""],"OpenedNDaysAgo|Opened":[""],"Owner":[""],"Pipeline Health":[""],"Pipeline Schedule":[""],"Pipeline Schedules":[""],"PipelineSchedules|Activated":[""],"PipelineSchedules|Active":[""],"PipelineSchedules|All":[""],"PipelineSchedules|Inactive":[""],"PipelineSchedules|Next Run":[""],"PipelineSchedules|None":[""],"PipelineSchedules|Provide a short description for this pipeline":[""],"PipelineSchedules|Take ownership":[""],"PipelineSchedules|Target":[""],"ProjectLifecycle|Stage":[""],"Read more":[""],"Related Commits":[""],"Related Deployed Jobs":[""],"Related Issues":[""],"Related Jobs":[""],"Related Merge Requests":[""],"Related Merged Requests":[""],"Save pipeline schedule":[""],"Schedule a new pipeline":[""],"Select a timezone":[""],"Select target branch":[""],"Showing %d event":["",""],"Target Branch":[""],"The coding stage shows the time from the first commit to creating the merge request. The data will automatically be added here once you create your first merge request.":[""],"The collection of events added to the data gathered for that stage.":[""],"The issue stage shows the time it takes from creating an issue to assigning the issue to a milestone, or add the issue to a list on your Issue Board. Begin creating issues to see data for this stage.":[""],"The phase of the development lifecycle.":[""],"The planning stage shows the time from the previous step to pushing your first commit. This time will be added automatically once you push your first commit.":[""],"The production stage shows the total time it takes between creating an issue and deploying the code to production. The data will be automatically added once you have completed the full idea to production cycle.":[""],"The review stage shows the time from creating the merge request to merging it. The data will automatically be added after you merge your first merge request.":[""],"The staging stage shows the time between merging the MR and deploying code to the production environment. The data will be automatically added once you deploy to production for the first time.":[""],"The testing stage shows the time GitLab CI takes to run every pipeline for the related merge request. The data will automatically be added after your first pipeline finishes running.":[""],"The time taken by each data entry gathered by that stage.":[""],"The value lying at the midpoint of a series of observed values. E.g., between 3, 5, 9, the median is 5. Between 3, 5, 7, 8, the median is (5+7)/2 = 6.":[""],"Time before an issue gets scheduled":[""],"Time before an issue starts implementation":[""],"Time between merge request creation and merge/close":[""],"Time until first merge request":[""],"Time|hr":["",""],"Time|min":["",""],"Time|s":[""],"Total Time":[""],"Total test time for all commits/merges":[""],"Want to see the data? Please ask an administrator for access.":[""],"We don't have enough data to show this stage.":[""],"You need permission.":[""],"day":["",""]}}}; \ No newline at end of file
+var locales = locales || {}; locales['en'] = {"domain":"app","locale_data":{"app":{"":{"Project-Id-Version":"gitlab 1.0.0","Report-Msgid-Bugs-To":"","PO-Revision-Date":"2017-04-12 22:36-0500","Last-Translator":"FULL NAME <EMAIL@ADDRESS>","Language-Team":"English","Language":"en","MIME-Version":"1.0","Content-Type":"text/plain; charset=UTF-8","Content-Transfer-Encoding":"8bit","Plural-Forms":"nplurals=2; plural=n != 1;","lang":"en","domain":"app","plural_forms":"nplurals=2; plural=n != 1;"},"%{commit_author_link} committed %{commit_timeago}":[""],"About auto deploy":[""],"Active":[""],"Activity":[""],"Add Changelog":[""],"Add Contribution guide":[""],"Add License":[""],"Add an SSH key to your profile to pull or push via SSH.":[""],"Add new directory":[""],"Archived project! Repository is read-only":[""],"Are you sure you want to delete this pipeline schedule?":[""],"Attach a file by drag &amp; drop or %{upload_link}":[""],"Branch":["",""],"Branch <strong>%{branch_name}</strong> was created. To set up auto deploy, choose a GitLab CI Yaml template and commit your changes. %{link_to_autodeploy_doc}":[""],"Branches":[""],"Browse files":[""],"ByAuthor|by":[""],"CI configuration":[""],"Cancel":[""],"ChangeTypeActionLabel|Pick into branch":[""],"ChangeTypeActionLabel|Revert in branch":[""],"ChangeTypeAction|Cherry-pick":[""],"Changelog":[""],"Charts":[""],"Cherry-pick this commit":[""],"Cherry-pick this merge request":[""],"CiStatusLabel|canceled":[""],"CiStatusLabel|created":[""],"CiStatusLabel|failed":[""],"CiStatusLabel|manual action":[""],"CiStatusLabel|passed":[""],"CiStatusLabel|passed with warnings":[""],"CiStatusLabel|pending":[""],"CiStatusLabel|skipped":[""],"CiStatusLabel|waiting for manual action":[""],"CiStatusText|blocked":[""],"CiStatusText|canceled":[""],"CiStatusText|created":[""],"CiStatusText|failed":[""],"CiStatusText|manual":[""],"CiStatusText|passed":[""],"CiStatusText|pending":[""],"CiStatusText|skipped":[""],"CiStatus|running":[""],"Commit":["",""],"Commit message":[""],"CommitBoxTitle|Commit":[""],"CommitMessage|Add %{file_name}":[""],"Commits":[""],"Commits|History":[""],"Committed by":[""],"Compare":[""],"Contribution guide":[""],"Contributors":[""],"Copy URL to clipboard":[""],"Copy commit SHA to clipboard":[""],"Create New Directory":[""],"Create directory":[""],"Create empty bare repository":[""],"Create merge request":[""],"Create new...":[""],"CreateNewFork|Fork":[""],"CreateTag|Tag":[""],"Cron Timezone":[""],"Cron syntax":[""],"Custom notification events":[""],"Custom notification levels are the same as participating levels. With custom notification levels you will also receive notifications for select events. To find out more, check out %{notification_link}.":[""],"Cycle Analytics":[""],"Cycle Analytics gives an overview of how much time it takes to go from idea to production in your project.":[""],"CycleAnalyticsStage|Code":[""],"CycleAnalyticsStage|Issue":[""],"CycleAnalyticsStage|Plan":[""],"CycleAnalyticsStage|Production":[""],"CycleAnalyticsStage|Review":[""],"CycleAnalyticsStage|Staging":[""],"CycleAnalyticsStage|Test":[""],"Define a custom pattern with cron syntax":[""],"Delete":[""],"Deploy":["",""],"Description":[""],"Directory name":[""],"Don't show again":[""],"Download":[""],"Download tar":[""],"Download tar.bz2":[""],"Download tar.gz":[""],"Download zip":[""],"DownloadArtifacts|Download":[""],"DownloadCommit|Email Patches":[""],"DownloadCommit|Plain Diff":[""],"DownloadSource|Download":[""],"Edit":[""],"Edit Pipeline Schedule %{id}":[""],"Every day (at 4:00am)":[""],"Every month (on the 1st at 4:00am)":[""],"Every week (Sundays at 4:00am)":[""],"Failed to change the owner":[""],"Failed to remove the pipeline schedule":[""],"Files":[""],"Find by path":[""],"Find file":[""],"FirstPushedBy|First":[""],"FirstPushedBy|pushed by":[""],"Fork":["",""],"ForkedFromProjectPath|Forked from":[""],"From issue creation until deploy to production":[""],"From merge request merge until deploy to production":[""],"Go to your fork":[""],"GoToYourFork|Fork":[""],"Home":[""],"Housekeeping successfully started":[""],"Import repository":[""],"Interval Pattern":[""],"Introducing Cycle Analytics":[""],"LFSStatus|Disabled":[""],"LFSStatus|Enabled":[""],"Last %d day":["",""],"Last Pipeline":[""],"Last Update":[""],"Last commit":[""],"Learn more in the":[""],"Learn more in the|pipeline schedules documentation":[""],"Leave group":[""],"Leave project":[""],"Limited to showing %d event at most":["",""],"Median":[""],"MissingSSHKeyWarningLink|add an SSH key":[""],"New Issue":["",""],"New Pipeline Schedule":[""],"New branch":[""],"New directory":[""],"New file":[""],"New issue":[""],"New merge request":[""],"New schedule":[""],"New snippet":[""],"New tag":[""],"No repository":[""],"No schedules":[""],"Not available":[""],"Not enough data":[""],"Notification events":[""],"NotificationEvent|Close issue":[""],"NotificationEvent|Close merge request":[""],"NotificationEvent|Failed pipeline":[""],"NotificationEvent|Merge merge request":[""],"NotificationEvent|New issue":[""],"NotificationEvent|New merge request":[""],"NotificationEvent|New note":[""],"NotificationEvent|Reassign issue":[""],"NotificationEvent|Reassign merge request":[""],"NotificationEvent|Reopen issue":[""],"NotificationEvent|Successful pipeline":[""],"NotificationLevel|Custom":[""],"NotificationLevel|Disabled":[""],"NotificationLevel|Global":[""],"NotificationLevel|On mention":[""],"NotificationLevel|Participate":[""],"NotificationLevel|Watch":[""],"OfSearchInADropdown|Filter":[""],"OpenedNDaysAgo|Opened":[""],"Options":[""],"Owner":[""],"Pipeline":[""],"Pipeline Health":[""],"Pipeline Schedule":[""],"Pipeline Schedules":[""],"PipelineSchedules|Activated":[""],"PipelineSchedules|Active":[""],"PipelineSchedules|All":[""],"PipelineSchedules|Inactive":[""],"PipelineSchedules|Next Run":[""],"PipelineSchedules|None":[""],"PipelineSchedules|Provide a short description for this pipeline":[""],"PipelineSchedules|Take ownership":[""],"PipelineSchedules|Target":[""],"PipelineSheduleIntervalPattern|Custom":[""],"Pipeline|with stage":[""],"Pipeline|with stages":[""],"Project '%{project_name}' queued for deletion.":[""],"Project '%{project_name}' was successfully created.":[""],"Project '%{project_name}' was successfully updated.":[""],"Project '%{project_name}' will be deleted.":[""],"Project access must be granted explicitly to each user.":[""],"Project export could not be deleted.":[""],"Project export has been deleted.":[""],"Project export link has expired. Please generate a new export from your project settings.":[""],"Project export started. A download link will be sent by email.":[""],"Project home":[""],"ProjectFeature|Disabled":[""],"ProjectFeature|Everyone with access":[""],"ProjectFeature|Only team members":[""],"ProjectFileTree|Name":[""],"ProjectLastActivity|Never":[""],"ProjectLifecycle|Stage":[""],"ProjectNetworkGraph|Graph":[""],"Read more":[""],"Readme":[""],"RefSwitcher|Branches":[""],"RefSwitcher|Tags":[""],"Related Commits":[""],"Related Deployed Jobs":[""],"Related Issues":[""],"Related Jobs":[""],"Related Merge Requests":[""],"Related Merged Requests":[""],"Remind later":[""],"Remove project":[""],"Request Access":[""],"Revert this commit":[""],"Revert this merge request":[""],"Save pipeline schedule":[""],"Schedule a new pipeline":[""],"Scheduling Pipelines":[""],"Search branches and tags":[""],"Select Archive Format":[""],"Select a timezone":[""],"Select target branch":[""],"Set a password on your account to pull or push via %{protocol}":[""],"Set up CI":[""],"Set up Koding":[""],"Set up auto deploy":[""],"SetPasswordToCloneLink|set a password":[""],"Showing %d event":["",""],"Source code":[""],"StarProject|Star":[""],"Start a %{new_merge_request} with these changes":[""],"Start a <strong>new merge request</strong> with these changes":[""],"Switch branch/tag":[""],"Tag":["",""],"Tags":[""],"Target Branch":[""],"The coding stage shows the time from the first commit to creating the merge request. The data will automatically be added here once you create your first merge request.":[""],"The collection of events added to the data gathered for that stage.":[""],"The fork relationship has been removed.":[""],"The issue stage shows the time it takes from creating an issue to assigning the issue to a milestone, or add the issue to a list on your Issue Board. Begin creating issues to see data for this stage.":[""],"The phase of the development lifecycle.":[""],"The pipelines schedule runs pipelines in the future, repeatedly, for specific branches or tags. Those scheduled pipelines will inherit limited project access based on their associated user.":[""],"The planning stage shows the time from the previous step to pushing your first commit. This time will be added automatically once you push your first commit.":[""],"The production stage shows the total time it takes between creating an issue and deploying the code to production. The data will be automatically added once you have completed the full idea to production cycle.":[""],"The project can be accessed by any logged in user.":[""],"The project can be accessed without any authentication.":[""],"The repository for this project does not exist.":[""],"The review stage shows the time from creating the merge request to merging it. The data will automatically be added after you merge your first merge request.":[""],"The staging stage shows the time between merging the MR and deploying code to the production environment. The data will be automatically added once you deploy to production for the first time.":[""],"The testing stage shows the time GitLab CI takes to run every pipeline for the related merge request. The data will automatically be added after your first pipeline finishes running.":[""],"The time taken by each data entry gathered by that stage.":[""],"The value lying at the midpoint of a series of observed values. E.g., between 3, 5, 9, the median is 5. Between 3, 5, 7, 8, the median is (5+7)/2 = 6.":[""],"This means you can not push code until you create an empty repository or import existing one.":[""],"Time before an issue gets scheduled":[""],"Time before an issue starts implementation":[""],"Time between merge request creation and merge/close":[""],"Time until first merge request":[""],"Timeago|%s days ago":[""],"Timeago|%s days remaining":[""],"Timeago|%s hours remaining":[""],"Timeago|%s minutes ago":[""],"Timeago|%s minutes remaining":[""],"Timeago|%s months ago":[""],"Timeago|%s months remaining":[""],"Timeago|%s seconds remaining":[""],"Timeago|%s weeks ago":[""],"Timeago|%s weeks remaining":[""],"Timeago|%s years ago":[""],"Timeago|%s years remaining":[""],"Timeago|1 day remaining":[""],"Timeago|1 hour remaining":[""],"Timeago|1 minute remaining":[""],"Timeago|1 month remaining":[""],"Timeago|1 week remaining":[""],"Timeago|1 year remaining":[""],"Timeago|Past due":[""],"Timeago|a day ago":[""],"Timeago|a month ago":[""],"Timeago|a week ago":[""],"Timeago|a while":[""],"Timeago|a year ago":[""],"Timeago|about %s hours ago":[""],"Timeago|about a minute ago":[""],"Timeago|about an hour ago":[""],"Timeago|in %s days":[""],"Timeago|in %s hours":[""],"Timeago|in %s minutes":[""],"Timeago|in %s months":[""],"Timeago|in %s seconds":[""],"Timeago|in %s weeks":[""],"Timeago|in %s years":[""],"Timeago|in 1 day":[""],"Timeago|in 1 hour":[""],"Timeago|in 1 minute":[""],"Timeago|in 1 month":[""],"Timeago|in 1 week":[""],"Timeago|in 1 year":[""],"Timeago|less than a minute ago":[""],"Time|hr":["",""],"Time|min":["",""],"Time|s":[""],"Total Time":[""],"Total test time for all commits/merges":[""],"Unstar":[""],"Upload New File":[""],"Upload file":[""],"Use your global notification setting":[""],"VisibilityLevel|Internal":[""],"VisibilityLevel|Private":[""],"VisibilityLevel|Public":[""],"Want to see the data? Please ask an administrator for access.":[""],"We don't have enough data to show this stage.":[""],"Withdraw Access Request":[""],"You are going to remove %{project_name_with_namespace}.\\nRemoved project CANNOT be restored!\\nAre you ABSOLUTELY sure?":[""],"You are going to remove the fork relationship to source project %{forked_from_project}. Are you ABSOLUTELY sure?":[""],"You are going to transfer %{project_name_with_namespace} to another owner. Are you ABSOLUTELY sure?":[""],"You can only add files when you are on a branch":[""],"You must sign in to star a project":[""],"You need permission.":[""],"You will not get any notifications via email":[""],"You will only receive notifications for the events you choose":[""],"You will only receive notifications for threads you have participated in":[""],"You will receive notifications for any activity":[""],"You will receive notifications only for comments in which you were @mentioned":[""],"You won't be able to pull or push project code via %{protocol} until you %{set_password_link} on your account":[""],"You won't be able to pull or push project code via SSH until you %{add_ssh_key_link} to your profile":[""],"Your name":[""],"day":["",""],"new merge request":[""],"notification emails":[""],"parent":["",""]}}}; \ No newline at end of file
diff --git a/app/assets/javascripts/locale/es/app.js b/app/assets/javascripts/locale/es/app.js
index 6977625f4d8..f198809cc20 100644
--- a/app/assets/javascripts/locale/es/app.js
+++ b/app/assets/javascripts/locale/es/app.js
@@ -1 +1 @@
-var locales = locales || {}; locales['es'] = {"domain":"app","locale_data":{"app":{"":{"Project-Id-Version":"gitlab 1.0.0","Report-Msgid-Bugs-To":"","PO-Revision-Date":"2017-06-07 12:29-0500","Language-Team":"Spanish","Language":"es","MIME-Version":"1.0","Content-Type":"text/plain; charset=UTF-8","Content-Transfer-Encoding":"8bit","Plural-Forms":"nplurals=2; plural=n != 1;","Last-Translator":"Bob Van Landuyt <bob@gitlab.com>","X-Generator":"Poedit 2.0.2","lang":"es","domain":"app","plural_forms":"nplurals=2; plural=n != 1;"},"About auto deploy":["Acerca del auto despliegue"],"Activity":["Actividad"],"Add Changelog":["Agregar Changelog"],"Add Contribution guide":["Agregar guía de contribución"],"Add License":["Agregar Licencia"],"Add an SSH key to your profile to pull or push via SSH.":["Agregar una clave SSH a tu perfil para actualizar o enviar a través de SSH."],"Add new directory":["Agregar nuevo directorio"],"Archived project! Repository is read-only":["¡Proyecto archivado! El repositorio es de sólo lectura"],"Branch":["Rama","Ramas"],"Branch <strong>%{branch_name}</strong> was created. To set up auto deploy, choose a GitLab CI Yaml template and commit your changes. %{link_to_autodeploy_doc}":["La rama <strong>%{branch_name}</strong> fue creada. Para configurar el auto despliegue, escoge una plantilla Yaml para GitLab CI y envía tus cambios. %{link_to_autodeploy_doc}"],"Branches":["Ramas"],"ByAuthor|by":["por"],"CI configuration":["Configuración de CI"],"Changelog":["Changelog"],"Charts":["Gráficos"],"CiStatusLabel|canceled":["cancelado"],"CiStatusLabel|created":["creado"],"CiStatusLabel|failed":["fallado"],"CiStatusLabel|manual action":["acción manual"],"CiStatusLabel|passed":["pasó"],"CiStatusLabel|passed with warnings":["pasó con advertencias"],"CiStatusLabel|pending":["pendiente"],"CiStatusLabel|skipped":["omitido"],"CiStatusLabel|waiting for manual action":["esperando acción manual"],"CiStatusText|blocked":["bloqueado"],"CiStatusText|canceled":["cancelado"],"CiStatusText|created":["creado"],"CiStatusText|failed":["fallado"],"CiStatusText|manual":["manual"],"CiStatusText|passed":["pasó"],"CiStatusText|pending":["pendiente"],"CiStatusText|skipped":["omitido"],"CiStatus|running":["en ejecución"],"Commit":["Cambio","Cambios"],"CommitMessage|Add %{file_name}":["Agregar %{file_name}"],"Commits":["Cambios"],"Commits|History":["Historial"],"Compare":["Comparar"],"Contribution guide":["Guía de contribución"],"Contributors":["Contribuidores"],"Copy URL to clipboard":["Copiar URL al portapapeles"],"Copy commit SHA to clipboard":["Copiar SHA del cambio al portapapeles"],"Create New Directory":["Crear Nuevo Directorio"],"Create directory":["Crear directorio"],"Create empty bare repository":["Crear repositorio vacío"],"Create merge request":["Crear solicitud de fusión"],"CreateNewFork|Fork":["Bifurcar"],"Custom notification events":["Eventos de notificaciones personalizadas"],"Custom notification levels are the same as participating levels. With custom notification levels you will also receive notifications for select events. To find out more, check out %{notification_link}.":["Los niveles de notificación personalizados son los mismos que los niveles participantes. Con los niveles de notificación personalizados, también recibirá notificaciones para eventos seleccionados. Para obtener más información, consulte %{notification_link}."],"Cycle Analytics":["Cycle Analytics"],"Cycle Analytics gives an overview of how much time it takes to go from idea to production in your project.":["Cycle Analytics ofrece una visión general de cuánto tiempo tarda en pasar de idea a producción en su proyecto."],"CycleAnalyticsStage|Code":["Código"],"CycleAnalyticsStage|Issue":["Incidencia"],"CycleAnalyticsStage|Plan":["Planificación"],"CycleAnalyticsStage|Production":["Producción"],"CycleAnalyticsStage|Review":["Revisión"],"CycleAnalyticsStage|Staging":["Puesta en escena"],"CycleAnalyticsStage|Test":["Pruebas"],"Deploy":["Despliegue","Despliegues"],"Directory name":["Nombre del directorio"],"Don't show again":["No mostrar de nuevo"],"Download tar":["Descargar tar"],"Download tar.bz2":["Descargar tar.bz2"],"Download tar.gz":["Descargar tar.gz"],"Download zip":["Descargar zip"],"DownloadArtifacts|Download":["Descargar"],"DownloadSource|Download":["Descargar"],"Files":["Archivos"],"Find by path":["Buscar por ruta"],"Find file":["Buscar archivo"],"FirstPushedBy|First":["Primer"],"FirstPushedBy|pushed by":["enviado por"],"ForkedFromProjectPath|Forked from":["Bifurcado de"],"Forks":["Bifurcaciones"],"From issue creation until deploy to production":["Desde la creación de la incidencia hasta el despliegue a producción"],"From merge request merge until deploy to production":["Desde la integración de la solicitud de fusión hasta el despliegue a producción"],"Go to your fork":["Ir a tu bifurcación"],"GoToYourFork|Fork":["Bifurcación"],"Home":["Inicio"],"Housekeeping successfully started":["Servicio de limpieza iniciado con éxito"],"Import repository":["Importar repositorio"],"Introducing Cycle Analytics":["Introducción a Cycle Analytics"],"LFSStatus|Disabled":["Deshabilitado"],"LFSStatus|Enabled":["Habilitado"],"Last %d day":["Último %d día","Últimos %d días"],"Last Update":["Última actualización"],"Last commit":["Último cambio"],"Leave group":["Abandonar grupo"],"Leave project":["Abandonar proyecto"],"Limited to showing %d event at most":["Limitado a mostrar máximo %d evento","Limitado a mostrar máximo %d eventos"],"Median":["Mediana"],"MissingSSHKeyWarningLink|add an SSH key":["agregar una clave SSH"],"New Issue":["Nueva incidencia","Nuevas incidencias"],"New branch":["Nueva rama"],"New directory":["Nuevo directorio"],"New file":["Nuevo archivo"],"New issue":["Nueva incidencia"],"New merge request":["Nueva solicitud de fusión"],"New snippet":["Nuevo fragmento de código"],"New tag":["Nueva etiqueta"],"No repository":["No hay repositorio"],"Not available":["No disponible"],"Not enough data":["No hay suficientes datos"],"Notification events":["Eventos de notificación"],"NotificationEvent|Close issue":["Cerrar incidencia"],"NotificationEvent|Close merge request":["Cerrar solicitud de fusión"],"NotificationEvent|Failed pipeline":["Pipeline fallido"],"NotificationEvent|Merge merge request":["Integrar solicitud de fusión"],"NotificationEvent|New issue":["Nueva incidencia"],"NotificationEvent|New merge request":["Nueva solicitud de fusión"],"NotificationEvent|New note":["Nueva nota"],"NotificationEvent|Reassign issue":["Reasignar incidencia"],"NotificationEvent|Reassign merge request":["Reasignar solicitud de fusión"],"NotificationEvent|Reopen issue":["Reabrir incidencia"],"NotificationEvent|Successful pipeline":["Pipeline exitoso"],"NotificationLevel|Custom":["Personalizado"],"NotificationLevel|Disabled":["Deshabilitado"],"NotificationLevel|Global":["Global"],"NotificationLevel|On mention":["Cuando me mencionan"],"NotificationLevel|Participate":["Participación"],"NotificationLevel|Watch":["Vigilancia"],"OpenedNDaysAgo|Opened":["Abierto"],"Pipeline Health":["Estado del Pipeline"],"Project '%{project_name}' queued for deletion.":["Proyecto ‘%{project_name}’ en cola para eliminación."],"Project '%{project_name}' was successfully created.":["Proyecto ‘%{project_name}’ fue creado satisfactoriamente."],"Project '%{project_name}' was successfully updated.":["Proyecto ‘%{project_name}’ fue actualizado satisfactoriamente."],"Project '%{project_name}' will be deleted.":["Proyecto ‘%{project_name}’ será eliminado."],"Project access must be granted explicitly to each user.":["El acceso al proyecto debe concederse explícitamente a cada usuario."],"Project export could not be deleted.":["No se pudo eliminar la exportación del proyecto."],"Project export has been deleted.":["La exportación del proyecto ha sido eliminada."],"Project export link has expired. Please generate a new export from your project settings.":["El enlace de exportación del proyecto ha caducado. Por favor, genera una nueva exportación desde la configuración del proyecto."],"Project export started. A download link will be sent by email.":["Se inició la exportación del proyecto. Se enviará un enlace de descarga por correo electrónico."],"Project home":["Inicio del proyecto"],"ProjectFeature|Disabled":["Deshabilitada"],"ProjectFeature|Everyone with access":["Todos con acceso"],"ProjectFeature|Only team members":["Solo miembros del equipo"],"ProjectFileTree|Name":["Nombre"],"ProjectLastActivity|Never":["Nunca"],"ProjectLifecycle|Stage":["Etapa"],"ProjectNetworkGraph|Graph":["Historial gráfico"],"Read more":["Leer más"],"Readme":["Readme"],"RefSwitcher|Branches":["Ramas"],"RefSwitcher|Tags":["Etiquetas"],"Related Commits":["Cambios Relacionados"],"Related Deployed Jobs":["Trabajos Desplegados Relacionados"],"Related Issues":["Incidencias Relacionadas"],"Related Jobs":["Trabajos Relacionados"],"Related Merge Requests":["Solicitudes de fusión Relacionadas"],"Related Merged Requests":["Solicitudes de fusión Relacionadas"],"Remind later":["Recordar después"],"Remove project":["Eliminar proyecto"],"Request Access":["Solicitar acceso"],"Search branches and tags":["Buscar ramas y etiquetas"],"Select Archive Format":["Seleccionar formato de archivo"],"Set a password on your account to pull or push via %{protocol}":["Establezca una contraseña en su cuenta para actualizar o enviar a través de% {protocol}"],"Set up CI":["Configurar CI"],"Set up Koding":["Configurar Koding"],"Set up auto deploy":["Configurar auto despliegue"],"SetPasswordToCloneLink|set a password":["establecer una contraseña"],"Showing %d event":["Mostrando %d evento","Mostrando %d eventos"],"Source code":["Código fuente"],"StarProject|Star":["Destacar"],"Switch branch/tag":["Cambiar rama/etiqueta"],"Tag":["Etiqueta","Etiquetas"],"Tags":["Etiquetas"],"The coding stage shows the time from the first commit to creating the merge request. The data will automatically be added here once you create your first merge request.":["La etapa de desarrollo muestra el tiempo desde el primer cambio hasta la creación de la solicitud de fusión. Los datos serán automáticamente incorporados aquí una vez creada tu primera solicitud de fusión."],"The collection of events added to the data gathered for that stage.":["La colección de eventos agregados a los datos recopilados para esa etapa."],"The fork relationship has been removed.":["La relación con la bifurcación se ha eliminado."],"The issue stage shows the time it takes from creating an issue to assigning the issue to a milestone, or add the issue to a list on your Issue Board. Begin creating issues to see data for this stage.":["La etapa de incidencia muestra el tiempo que toma desde la creación de un tema hasta asignar el tema a un hito, o añadir el tema a una lista en el panel de temas. Empieza a crear temas para ver los datos de esta etapa."],"The phase of the development lifecycle.":["La etapa del ciclo de vida de desarrollo."],"The planning stage shows the time from the previous step to pushing your first commit. This time will be added automatically once you push your first commit.":["La etapa de planificación muestra el tiempo desde el paso anterior hasta el envío de tu primera confirmación. Este tiempo se añadirá automáticamente una vez que usted envíe el primer cambio."],"The production stage shows the total time it takes between creating an issue and deploying the code to production. The data will be automatically added once you have completed the full idea to production cycle.":["La etapa de producción muestra el tiempo total que tarda entre la creación de una incidencia y el despliegue del código a producción. Los datos se añadirán automáticamente una vez haya finalizado por completo el ciclo de idea a producción."],"The project can be accessed by any logged in user.":["El proyecto puede ser accedido por cualquier usuario conectado."],"The project can be accessed without any authentication.":["El proyecto puede accederse sin ninguna autenticación."],"The repository for this project does not exist.":["El repositorio para este proyecto no existe."],"The review stage shows the time from creating the merge request to merging it. The data will automatically be added after you merge your first merge request.":["La etapa de revisión muestra el tiempo desde la creación de la solicitud de fusión hasta que los cambios se fusionaron. Los datos se añadirán automáticamente después de fusionar su primera solicitud de fusión."],"The staging stage shows the time between merging the MR and deploying code to the production environment. The data will be automatically added once you deploy to production for the first time.":["La etapa de puesta en escena muestra el tiempo entre la fusión y el despliegue de código en el entorno de producción. Los datos se añadirán automáticamente una vez que se despliega a producción por primera vez."],"The testing stage shows the time GitLab CI takes to run every pipeline for the related merge request. The data will automatically be added after your first pipeline finishes running.":["La etapa de pruebas muestra el tiempo que GitLab CI toma para ejecutar cada pipeline para la solicitud de fusión relacionada. Los datos se añadirán automáticamente luego de que el primer pipeline termine de ejecutarse."],"The time taken by each data entry gathered by that stage.":["El tiempo utilizado por cada entrada de datos obtenido por esa etapa."],"The value lying at the midpoint of a series of observed values. E.g., between 3, 5, 9, the median is 5. Between 3, 5, 7, 8, the median is (5+7)/2 = 6.":["El valor en el punto medio de una serie de valores observados. Por ejemplo, entre 3, 5, 9, la mediana es 5. Entre 3, 5, 7, 8, la mediana es (5 + 7) / 2 = 6."],"This means you can not push code until you create an empty repository or import existing one.":["Esto significa que no puede enviar código hasta que cree un repositorio vacío o importe uno existente."],"Time before an issue gets scheduled":["Tiempo antes de que una incidencia sea programada"],"Time before an issue starts implementation":["Tiempo antes de que empieze la implementación de una incidencia"],"Time between merge request creation and merge/close":["Tiempo entre la creación de la solicitud de fusión y la integración o cierre de ésta"],"Time until first merge request":["Tiempo hasta la primera solicitud de fusión"],"Timeago|%s days ago":["hace %s días"],"Timeago|%s days remaining":["%s días restantes"],"Timeago|%s hours remaining":["%s horas restantes"],"Timeago|%s minutes ago":["hace %s minutos"],"Timeago|%s minutes remaining":["%s minutos restantes"],"Timeago|%s months ago":["hace %s meses"],"Timeago|%s months remaining":["%s meses restantes"],"Timeago|%s seconds remaining":["%s segundos restantes"],"Timeago|%s weeks ago":["hace %s semanas"],"Timeago|%s weeks remaining":["%s semanas restantes"],"Timeago|%s years ago":["hace %s años"],"Timeago|%s years remaining":["%s años restantes"],"Timeago|1 day remaining":["1 día restante"],"Timeago|1 hour remaining":["1 hora restante"],"Timeago|1 minute remaining":["1 minuto restante"],"Timeago|1 month remaining":["1 mes restante"],"Timeago|1 week remaining":["1 semana restante"],"Timeago|1 year remaining":["1 año restante"],"Timeago|Past due":["Atrasado"],"Timeago|a day ago":["hace un día"],"Timeago|a month ago":["hace 1 mes"],"Timeago|a week ago":["hace 1 semana"],"Timeago|a while":["hace un momento"],"Timeago|a year ago":["hace 1 año"],"Timeago|about %s hours ago":["hace alrededor de %s horas"],"Timeago|about a minute ago":["hace alrededor de 1 minuto"],"Timeago|about an hour ago":["hace alrededor de 1 hora"],"Timeago|in %s days":["en %s días"],"Timeago|in %s hours":["en %s horas"],"Timeago|in %s minutes":["en %s minutos"],"Timeago|in %s months":["en %s meses"],"Timeago|in %s seconds":["en %s segundos"],"Timeago|in %s weeks":["en %s semanas"],"Timeago|in %s years":["en %s años"],"Timeago|in 1 day":["en 1 día"],"Timeago|in 1 hour":["en 1 hora"],"Timeago|in 1 minute":["en 1 minuto"],"Timeago|in 1 month":["en 1 mes"],"Timeago|in 1 week":["en 1 semana"],"Timeago|in 1 year":["en 1 año"],"Timeago|less than a minute ago":["hace menos de 1 minuto"],"Time|hr":["hr","hrs"],"Time|min":["min","mins"],"Time|s":["s"],"Total Time":["Tiempo Total"],"Total test time for all commits/merges":["Tiempo total de pruebas para todos los cambios o integraciones"],"Unstar":["No Destacar"],"Upload New File":["Subir nuevo archivo"],"Upload file":["Subir archivo"],"Use your global notification setting":["Utiliza tu configuración de notificación global"],"VisibilityLevel|Internal":["Interno"],"VisibilityLevel|Private":["Privado"],"VisibilityLevel|Public":["Público"],"Want to see the data? Please ask an administrator for access.":["¿Quieres ver los datos? Por favor pide acceso al administrador."],"We don't have enough data to show this stage.":["No hay suficientes datos para mostrar en esta etapa."],"Withdraw Access Request":["Retirar Solicitud de Acceso"],"You are going to remove %{project_name_with_namespace}.\\nRemoved project CANNOT be restored!\\nAre you ABSOLUTELY sure?":["Va a eliminar %{project_name_with_namespace}.\\n¡El proyecto eliminado NO puede ser restaurado!\\n¿Estás TOTALMENTE seguro?"],"You are going to remove the fork relationship to source project %{forked_from_project}. Are you ABSOLUTELY sure?":["Vas a eliminar el enlace de la bifurcación con el proyecto original %{forked_from_project}. ¿Estás TOTALMENTE seguro?"],"You are going to transfer %{project_name_with_namespace} to another owner. Are you ABSOLUTELY sure?":["Vas a transferir %{project_name_with_namespace} a otro propietario. ¿Estás TOTALMENTE seguro?"],"You can only add files when you are on a branch":["Sólo puede agregar archivos cuando estas en una rama"],"You must sign in to star a project":["Debes iniciar sesión para destacar un proyecto"],"You need permission.":["Necesitas permisos."],"You will not get any notifications via email":["No recibirás ninguna notificación por correo electrónico"],"You will only receive notifications for the events you choose":["Solo recibirás notificaciones de los eventos que elijas"],"You will only receive notifications for threads you have participated in":["Solo recibirás notificaciones de los temas en los que has participado"],"You will receive notifications for any activity":["Recibirás notificaciones para cualquier actividad"],"You will receive notifications only for comments in which you were @mentioned":["Recibirás notificaciones sólo para los comentarios en los que se te mencionó"],"You won't be able to pull or push project code via %{protocol} until you %{set_password_link} on your account":["No podrás actualizar o enviar código al proyecto a través de %{protocol} hasta que %{set_password_link} en tu cuenta"],"You won't be able to pull or push project code via SSH until you %{add_ssh_key_link} to your profile":["No podrás actualizar o enviar código al proyecto a través de SSH hasta que %{add_ssh_key_link} en su perfil"],"Your name":["Tu nombre"],"committed":["cambió"],"day":["día","días"],"notification emails":["correos electrónicos de notificación"]}}}; \ No newline at end of file
+var locales = locales || {}; locales['es'] = {"domain":"app","locale_data":{"app":{"":{"Project-Id-Version":"gitlab 1.0.0","Report-Msgid-Bugs-To":"","PO-Revision-Date":"2017-06-15 21:59-0500","Language-Team":"Spanish","Language":"es","MIME-Version":"1.0","Content-Type":"text/plain; charset=UTF-8","Content-Transfer-Encoding":"8bit","Plural-Forms":"nplurals=2; plural=n != 1;","Last-Translator":"Bob Van Landuyt <bob@gitlab.com>","X-Generator":"Poedit 2.0.2","lang":"es","domain":"app","plural_forms":"nplurals=2; plural=n != 1;"},"%{commit_author_link} committed %{commit_timeago}":["%{commit_author_link} cambió %{commit_timeago}"],"About auto deploy":["Acerca del auto despliegue"],"Active":["Activo"],"Activity":["Actividad"],"Add Changelog":["Agregar Changelog"],"Add Contribution guide":["Agregar guía de contribución"],"Add License":["Agregar Licencia"],"Add an SSH key to your profile to pull or push via SSH.":["Agregar una clave SSH a tu perfil para actualizar o enviar a través de SSH."],"Add new directory":["Agregar nuevo directorio"],"Archived project! Repository is read-only":["¡Proyecto archivado! El repositorio es de solo lectura"],"Are you sure you want to delete this pipeline schedule?":["¿Estás seguro que deseas eliminar esta programación del pipeline?"],"Attach a file by drag &amp; drop or %{upload_link}":["Adjunte un archivo arrastrando &amp; soltando o %{upload_link}"],"Branch":["Rama","Ramas"],"Branch <strong>%{branch_name}</strong> was created. To set up auto deploy, choose a GitLab CI Yaml template and commit your changes. %{link_to_autodeploy_doc}":["La rama <strong>%{branch_name}</strong> fue creada. Para configurar el auto despliegue, escoge una plantilla Yaml para GitLab CI y envía tus cambios. %{link_to_autodeploy_doc}"],"Branches":["Ramas"],"Browse files":["Examinar los archivos"],"ByAuthor|by":["por"],"CI configuration":["Configuración de CI"],"Cancel":["Cancelar"],"ChangeTypeActionLabel|Pick into branch":["Escoger en la rama"],"ChangeTypeActionLabel|Revert in branch":["Revertir en la rama"],"ChangeTypeAction|Cherry-pick":["Cherry-pick"],"ChangeTypeAction|Revert":["Revertir"],"Changelog":["Changelog"],"Charts":["Gráficos"],"Cherry-pick this commit":["Escoger este cambio"],"Cherry-pick this merge request":["Escoger esta solicitud de fusión"],"CiStatusLabel|canceled":["cancelado"],"CiStatusLabel|created":["creado"],"CiStatusLabel|failed":["fallido"],"CiStatusLabel|manual action":["acción manual"],"CiStatusLabel|passed":["pasó"],"CiStatusLabel|passed with warnings":["pasó con advertencias"],"CiStatusLabel|pending":["pendiente"],"CiStatusLabel|skipped":["omitido"],"CiStatusLabel|waiting for manual action":["esperando acción manual"],"CiStatusText|blocked":["bloqueado"],"CiStatusText|canceled":["cancelado"],"CiStatusText|created":["creado"],"CiStatusText|failed":["fallado"],"CiStatusText|manual":["manual"],"CiStatusText|passed":["pasó"],"CiStatusText|pending":["pendiente"],"CiStatusText|skipped":["omitido"],"CiStatus|running":["en ejecución"],"Commit":["Cambio","Cambios"],"Commit message":["Mensaje del cambio"],"CommitBoxTitle|Commit":["Cambio"],"CommitMessage|Add %{file_name}":["Agregar %{file_name}"],"Commits":["Cambios"],"Commits|History":["Historial"],"Committed by":["Enviado por"],"Compare":["Comparar"],"Contribution guide":["Guía de contribución"],"Contributors":["Contribuidores"],"Copy URL to clipboard":["Copiar URL al portapapeles"],"Copy commit SHA to clipboard":["Copiar SHA del cambio al portapapeles"],"Create New Directory":["Crear Nuevo Directorio"],"Create directory":["Crear directorio"],"Create empty bare repository":["Crear repositorio vacío"],"Create merge request":["Crear solicitud de fusión"],"Create new...":["Crear nuevo..."],"CreateNewFork|Fork":["Bifurcar"],"CreateTag|Tag":["Etiqueta"],"Cron Timezone":["Zona horaria del Cron"],"Cron syntax":["Sintaxis de Cron"],"Custom notification events":["Eventos de notificaciones personalizadas"],"Custom notification levels are the same as participating levels. With custom notification levels you will also receive notifications for select events. To find out more, check out %{notification_link}.":["Los niveles de notificación personalizados son los mismos que los niveles participantes. Con los niveles de notificación personalizados, también recibirá notificaciones para eventos seleccionados. Para obtener más información, consulte %{notification_link}."],"Cycle Analytics":["Cycle Analytics"],"Cycle Analytics gives an overview of how much time it takes to go from idea to production in your project.":["Cycle Analytics ofrece una visión general de cuánto tiempo tarda en pasar de idea a producción en su proyecto."],"CycleAnalyticsStage|Code":["Código"],"CycleAnalyticsStage|Issue":["Incidencia"],"CycleAnalyticsStage|Plan":["Planificación"],"CycleAnalyticsStage|Production":["Producción"],"CycleAnalyticsStage|Review":["Revisión"],"CycleAnalyticsStage|Staging":["Puesta en escena"],"CycleAnalyticsStage|Test":["Pruebas"],"Define a custom pattern with cron syntax":["Definir un patrón personalizado con la sintaxis de cron"],"Delete":["Eliminar"],"Deploy":["Despliegue","Despliegues"],"Description":["Descripción"],"Directory name":["Nombre del directorio"],"Don't show again":["No mostrar de nuevo"],"Download":["Descargar"],"Download tar":["Descargar tar"],"Download tar.bz2":["Descargar tar.bz2"],"Download tar.gz":["Descargar tar.gz"],"Download zip":["Descargar zip"],"DownloadArtifacts|Download":["Descargar"],"DownloadCommit|Email Patches":["Parches por correo electrónico"],"DownloadCommit|Plain Diff":["Diferencias en texto plano"],"DownloadSource|Download":["Descargar"],"Edit":["Editar"],"Edit Pipeline Schedule %{id}":["Editar Programación del Pipeline %{id}"],"Every day (at 4:00am)":["Todos los días (a las 4:00 am)"],"Every month (on the 1st at 4:00am)":["Todos los meses (el día 1 a las 4:00 am)"],"Every week (Sundays at 4:00am)":["Todas las semanas (domingos a las 4:00 am)"],"Failed to change the owner":["Error al cambiar el propietario"],"Failed to remove the pipeline schedule":["Error al eliminar la programación del pipeline"],"Files":["Archivos"],"Find by path":["Buscar por ruta"],"Find file":["Buscar archivo"],"FirstPushedBy|First":["Primer"],"FirstPushedBy|pushed by":["enviado por"],"Fork":["Bifurcación","Bifurcaciones"],"ForkedFromProjectPath|Forked from":["Bifurcado de"],"From issue creation until deploy to production":["Desde la creación de la incidencia hasta el despliegue a producción"],"From merge request merge until deploy to production":["Desde la integración de la solicitud de fusión hasta el despliegue a producción"],"Go to your fork":["Ir a tu bifurcación"],"GoToYourFork|Fork":["Bifurcación"],"Home":["Inicio"],"Housekeeping successfully started":["Servicio de limpieza iniciado con éxito"],"Import repository":["Importar repositorio"],"Interval Pattern":["Patrón de intervalo"],"Introducing Cycle Analytics":["Introducción a Cycle Analytics"],"LFSStatus|Disabled":["Deshabilitado"],"LFSStatus|Enabled":["Habilitado"],"Last %d day":["Último %d día","Últimos %d días"],"Last Pipeline":["Último Pipeline"],"Last Update":["Última actualización"],"Last commit":["Último cambio"],"Learn more in the":["Más información en la"],"Learn more in the|pipeline schedules documentation":["documentación sobre la programación de pipelines"],"Leave group":["Abandonar grupo"],"Leave project":["Abandonar proyecto"],"Limited to showing %d event at most":["Limitado a mostrar máximo %d evento","Limitado a mostrar máximo %d eventos"],"Median":["Mediana"],"MissingSSHKeyWarningLink|add an SSH key":["agregar una clave SSH"],"New Issue":["Nueva incidencia","Nuevas incidencias"],"New Pipeline Schedule":["Nueva Programación del Pipeline"],"New branch":["Nueva rama"],"New directory":["Nuevo directorio"],"New file":["Nuevo archivo"],"New issue":["Nueva incidencia"],"New merge request":["Nueva solicitud de fusión"],"New schedule":["Nueva programación"],"New snippet":["Nuevo fragmento de código"],"New tag":["Nueva etiqueta"],"No repository":["No hay repositorio"],"No schedules":["No hay programaciones"],"Not available":["No disponible"],"Not enough data":["No hay suficientes datos"],"Notification events":["Eventos de notificación"],"NotificationEvent|Close issue":["Cerrar incidencia"],"NotificationEvent|Close merge request":["Cerrar solicitud de fusión"],"NotificationEvent|Failed pipeline":["Pipeline fallido"],"NotificationEvent|Merge merge request":["Integrar solicitud de fusión"],"NotificationEvent|New issue":["Nueva incidencia"],"NotificationEvent|New merge request":["Nueva solicitud de fusión"],"NotificationEvent|New note":["Nueva nota"],"NotificationEvent|Reassign issue":["Reasignar incidencia"],"NotificationEvent|Reassign merge request":["Reasignar solicitud de fusión"],"NotificationEvent|Reopen issue":["Reabrir incidencia"],"NotificationEvent|Successful pipeline":["Pipeline exitoso"],"NotificationLevel|Custom":["Personalizado"],"NotificationLevel|Disabled":["Deshabilitado"],"NotificationLevel|Global":["Global"],"NotificationLevel|On mention":["Cuando me mencionan"],"NotificationLevel|Participate":["Participación"],"NotificationLevel|Watch":["Vigilancia"],"OfSearchInADropdown|Filter":["Filtrar"],"OpenedNDaysAgo|Opened":["Abierto"],"Options":["Opciones"],"Owner":["Propietario"],"Pipeline":["Pipeline"],"Pipeline Health":["Estado del Pipeline"],"Pipeline Schedule":["Programación del Pipeline"],"Pipeline Schedules":["Programaciones de los Pipelines"],"PipelineSchedules|Activated":["Activado"],"PipelineSchedules|Active":["Activos"],"PipelineSchedules|All":["Todos"],"PipelineSchedules|Inactive":["Inactivos"],"PipelineSchedules|Next Run":["Próxima Ejecución"],"PipelineSchedules|None":["Ninguno"],"PipelineSchedules|Provide a short description for this pipeline":["Proporcione una breve descripción para este pipeline"],"PipelineSchedules|Take ownership":["Tomar posesión"],"PipelineSchedules|Target":["Destino"],"PipelineSheduleIntervalPattern|Custom":["Personalizado"],"Pipeline|with stage":["con etapa"],"Pipeline|with stages":["con etapas"],"Project '%{project_name}' queued for deletion.":["Proyecto ‘%{project_name}’ en cola para eliminación."],"Project '%{project_name}' was successfully created.":["Proyecto ‘%{project_name}’ fue creado satisfactoriamente."],"Project '%{project_name}' was successfully updated.":["Proyecto ‘%{project_name}’ fue actualizado satisfactoriamente."],"Project '%{project_name}' will be deleted.":["Proyecto ‘%{project_name}’ será eliminado."],"Project access must be granted explicitly to each user.":["El acceso al proyecto debe concederse explícitamente a cada usuario."],"Project export could not be deleted.":["No se pudo eliminar la exportación del proyecto."],"Project export has been deleted.":["La exportación del proyecto ha sido eliminada."],"Project export link has expired. Please generate a new export from your project settings.":["El enlace de exportación del proyecto ha caducado. Por favor, genera una nueva exportación desde la configuración del proyecto."],"Project export started. A download link will be sent by email.":["Se inició la exportación del proyecto. Se enviará un enlace de descarga por correo electrónico."],"Project home":["Inicio del proyecto"],"ProjectFeature|Disabled":["Deshabilitada"],"ProjectFeature|Everyone with access":["Todos con acceso"],"ProjectFeature|Only team members":["Solo miembros del equipo"],"ProjectFileTree|Name":["Nombre"],"ProjectLastActivity|Never":["Nunca"],"ProjectLifecycle|Stage":["Etapa"],"ProjectNetworkGraph|Graph":["Historial gráfico"],"Read more":["Leer más"],"Readme":["Léeme"],"RefSwitcher|Branches":["Ramas"],"RefSwitcher|Tags":["Etiquetas"],"Related Commits":["Cambios Relacionados"],"Related Deployed Jobs":["Trabajos Desplegados Relacionados"],"Related Issues":["Incidencias Relacionadas"],"Related Jobs":["Trabajos Relacionados"],"Related Merge Requests":["Solicitudes de fusión Relacionadas"],"Related Merged Requests":["Solicitudes de fusión Relacionadas"],"Remind later":["Recordar después"],"Remove project":["Eliminar proyecto"],"Request Access":["Solicitar acceso"],"Revert this commit":["Revertir este cambio"],"Revert this merge request":["Revertir esta solicitud de fusión"],"Save pipeline schedule":["Guardar programación del pipeline"],"Schedule a new pipeline":["Programar un nuevo pipeline"],"Scheduling Pipelines":["Programación de Pipelines"],"Search branches and tags":["Buscar ramas y etiquetas"],"Select Archive Format":["Seleccionar formato de archivo"],"Select a timezone":["Selecciona una zona horaria"],"Select target branch":["Selecciona una rama de destino"],"Set a password on your account to pull or push via %{protocol}":["Establezca una contraseña en su cuenta para actualizar o enviar a través de %{protocol}"],"Set up CI":["Configurar CI"],"Set up Koding":["Configurar Koding"],"Set up auto deploy":["Configurar auto despliegue"],"SetPasswordToCloneLink|set a password":["establecer una contraseña"],"Showing %d event":["Mostrando %d evento","Mostrando %d eventos"],"Source code":["Código fuente"],"StarProject|Star":["Destacar"],"Start a %{new_merge_request} with these changes":["Iniciar una %{new_merge_request} con estos cambios"],"Switch branch/tag":["Cambiar rama/etiqueta"],"Tag":["Etiqueta","Etiquetas"],"Tags":["Etiquetas"],"Target Branch":["Rama de destino"],"The coding stage shows the time from the first commit to creating the merge request. The data will automatically be added here once you create your first merge request.":["La etapa de desarrollo muestra el tiempo desde el primer cambio hasta la creación de la solicitud de fusión. Los datos serán automáticamente incorporados aquí una vez creada tu primera solicitud de fusión."],"The collection of events added to the data gathered for that stage.":["La colección de eventos agregados a los datos recopilados para esa etapa."],"The fork relationship has been removed.":["La relación con la bifurcación se ha eliminado."],"The issue stage shows the time it takes from creating an issue to assigning the issue to a milestone, or add the issue to a list on your Issue Board. Begin creating issues to see data for this stage.":["La etapa de incidencia muestra el tiempo que toma desde la creación de un tema hasta asignar el tema a un hito, o añadir el tema a una lista en el panel de temas. Empieza a crear temas para ver los datos de esta etapa."],"The phase of the development lifecycle.":["La etapa del ciclo de vida de desarrollo."],"The pipelines schedule runs pipelines in the future, repeatedly, for specific branches or tags. Those scheduled pipelines will inherit limited project access based on their associated user.":["La programación de pipelines ejecuta pipelines en el futuro, repetidamente, para ramas o etiquetas específicas. Los pipelines programados heredarán acceso limitado al proyecto basado en su usuario asociado."],"The planning stage shows the time from the previous step to pushing your first commit. This time will be added automatically once you push your first commit.":["La etapa de planificación muestra el tiempo desde el paso anterior hasta el envío de tu primera confirmación. Este tiempo se añadirá automáticamente una vez que usted envíe el primer cambio."],"The production stage shows the total time it takes between creating an issue and deploying the code to production. The data will be automatically added once you have completed the full idea to production cycle.":["La etapa de producción muestra el tiempo total que tarda entre la creación de una incidencia y el despliegue del código a producción. Los datos se añadirán automáticamente una vez haya finalizado por completo el ciclo de idea a producción."],"The project can be accessed by any logged in user.":["El proyecto puede ser accedido por cualquier usuario conectado."],"The project can be accessed without any authentication.":["El proyecto puede accederse sin ninguna autenticación."],"The repository for this project does not exist.":["El repositorio para este proyecto no existe."],"The review stage shows the time from creating the merge request to merging it. The data will automatically be added after you merge your first merge request.":["La etapa de revisión muestra el tiempo desde la creación de la solicitud de fusión hasta que los cambios se fusionaron. Los datos se añadirán automáticamente después de fusionar su primera solicitud de fusión."],"The staging stage shows the time between merging the MR and deploying code to the production environment. The data will be automatically added once you deploy to production for the first time.":["La etapa de puesta en escena muestra el tiempo entre la fusión y el despliegue de código en el entorno de producción. Los datos se añadirán automáticamente una vez que se despliega a producción por primera vez."],"The testing stage shows the time GitLab CI takes to run every pipeline for the related merge request. The data will automatically be added after your first pipeline finishes running.":["La etapa de pruebas muestra el tiempo que GitLab CI toma para ejecutar cada pipeline para la solicitud de fusión relacionada. Los datos se añadirán automáticamente luego de que el primer pipeline termine de ejecutarse."],"The time taken by each data entry gathered by that stage.":["El tiempo utilizado por cada entrada de datos obtenido por esa etapa."],"The value lying at the midpoint of a series of observed values. E.g., between 3, 5, 9, the median is 5. Between 3, 5, 7, 8, the median is (5+7)/2 = 6.":["El valor en el punto medio de una serie de valores observados. Por ejemplo, entre 3, 5, 9, la mediana es 5. Entre 3, 5, 7, 8, la mediana es (5 + 7) / 2 = 6."],"This means you can not push code until you create an empty repository or import existing one.":["Esto significa que no puede enviar código hasta que cree un repositorio vacío o importe uno existente."],"Time before an issue gets scheduled":["Tiempo antes de que una incidencia sea programada"],"Time before an issue starts implementation":["Tiempo antes de que empieze la implementación de una incidencia"],"Time between merge request creation and merge/close":["Tiempo entre la creación de la solicitud de fusión y la integración o cierre de ésta"],"Time until first merge request":["Tiempo hasta la primera solicitud de fusión"],"Timeago|%s days ago":["hace %s días"],"Timeago|%s days remaining":["%s días restantes"],"Timeago|%s hours remaining":["%s horas restantes"],"Timeago|%s minutes ago":["hace %s minutos"],"Timeago|%s minutes remaining":["%s minutos restantes"],"Timeago|%s months ago":["hace %s meses"],"Timeago|%s months remaining":["%s meses restantes"],"Timeago|%s seconds remaining":["%s segundos restantes"],"Timeago|%s weeks ago":["hace %s semanas"],"Timeago|%s weeks remaining":["%s semanas restantes"],"Timeago|%s years ago":["hace %s años"],"Timeago|%s years remaining":["%s años restantes"],"Timeago|1 day remaining":["1 día restante"],"Timeago|1 hour remaining":["1 hora restante"],"Timeago|1 minute remaining":["1 minuto restante"],"Timeago|1 month remaining":["1 mes restante"],"Timeago|1 week remaining":["1 semana restante"],"Timeago|1 year remaining":["1 año restante"],"Timeago|Past due":["Atrasado"],"Timeago|a day ago":["hace un día"],"Timeago|a month ago":["hace un mes"],"Timeago|a week ago":["hace una semana"],"Timeago|a while":["hace un momento"],"Timeago|a year ago":["hace un año"],"Timeago|about %s hours ago":["hace alrededor de %s horas"],"Timeago|about a minute ago":["hace alrededor de 1 minuto"],"Timeago|about an hour ago":["hace alrededor de 1 hora"],"Timeago|in %s days":["en %s días"],"Timeago|in %s hours":["en %s horas"],"Timeago|in %s minutes":["en %s minutos"],"Timeago|in %s months":["en %s meses"],"Timeago|in %s seconds":["en %s segundos"],"Timeago|in %s weeks":["en %s semanas"],"Timeago|in %s years":["en %s años"],"Timeago|in 1 day":["en 1 día"],"Timeago|in 1 hour":["en 1 hora"],"Timeago|in 1 minute":["en 1 minuto"],"Timeago|in 1 month":["en 1 mes"],"Timeago|in 1 week":["en 1 semana"],"Timeago|in 1 year":["en 1 año"],"Timeago|less than a minute ago":["hace menos de 1 minuto"],"Time|hr":["hr","hrs"],"Time|min":["min","mins"],"Time|s":["s"],"Total Time":["Tiempo Total"],"Total test time for all commits/merges":["Tiempo total de pruebas para todos los cambios o integraciones"],"Unstar":["No Destacar"],"Upload New File":["Subir nuevo archivo"],"Upload file":["Subir archivo"],"Use your global notification setting":["Utiliza tu configuración de notificación global"],"VisibilityLevel|Internal":["Interno"],"VisibilityLevel|Private":["Privado"],"VisibilityLevel|Public":["Público"],"Want to see the data? Please ask an administrator for access.":["¿Quieres ver los datos? Por favor pide acceso al administrador."],"We don't have enough data to show this stage.":["No hay suficientes datos para mostrar en esta etapa."],"Withdraw Access Request":["Retirar Solicitud de Acceso"],"You are going to remove %{project_name_with_namespace}.\\nRemoved project CANNOT be restored!\\nAre you ABSOLUTELY sure?":["Va a eliminar %{project_name_with_namespace}.\\n¡El proyecto eliminado NO puede ser restaurado!\\n¿Estás TOTALMENTE seguro?"],"You are going to remove the fork relationship to source project %{forked_from_project}. Are you ABSOLUTELY sure?":["Vas a eliminar el enlace de la bifurcación con el proyecto original %{forked_from_project}. ¿Estás TOTALMENTE seguro?"],"You are going to transfer %{project_name_with_namespace} to another owner. Are you ABSOLUTELY sure?":["Vas a transferir %{project_name_with_namespace} a otro propietario. ¿Estás TOTALMENTE seguro?"],"You can only add files when you are on a branch":["Solo puedes agregar archivos cuando estás en una rama"],"You must sign in to star a project":["Debes iniciar sesión para destacar un proyecto"],"You need permission.":["Necesitas permisos."],"You will not get any notifications via email":["No recibirás ninguna notificación por correo electrónico"],"You will only receive notifications for the events you choose":["Solo recibirás notificaciones de los eventos que elijas"],"You will only receive notifications for threads you have participated in":["Solo recibirás notificaciones de los temas en los que has participado"],"You will receive notifications for any activity":["Recibirás notificaciones por cualquier actividad"],"You will receive notifications only for comments in which you were @mentioned":["Recibirás notificaciones solo para los comentarios en los que se te mencionó"],"You won't be able to pull or push project code via %{protocol} until you %{set_password_link} on your account":["No podrás actualizar o enviar código al proyecto a través de %{protocol} hasta que %{set_password_link} en tu cuenta"],"You won't be able to pull or push project code via SSH until you %{add_ssh_key_link} to your profile":["No podrás actualizar o enviar código al proyecto a través de SSH hasta que %{add_ssh_key_link} en su perfil"],"Your name":["Tu nombre"],"day":["día","días"],"new merge request":["nueva solicitud de fusión"],"notification emails":["correos electrónicos de notificación"],"parent":["padre","padres"]}}}; \ No newline at end of file
diff --git a/app/assets/javascripts/locale/fr/app.js b/app/assets/javascripts/locale/fr/app.js
new file mode 100644
index 00000000000..f9904ea61ea
--- /dev/null
+++ b/app/assets/javascripts/locale/fr/app.js
@@ -0,0 +1 @@
+var locales = locales || {}; locales['fr'] = {"domain":"app","locale_data":{"app":{"":{"Project-Id-Version":"gitlab 1.0.0","Report-Msgid-Bugs-To":"","POT-Creation-Date":"2017-06-15 20:38+0000","MIME-Version":"1.0","Content-Type":"text/plain; charset=UTF-8","Content-Transfer-Encoding":"8bit","PO-Revision-Date":"2017-06-14 04:21-0400","Last-Translator":"Dremor <egeorget@opmbx.org>","Language-Team":"French (https://www.transifex.com/gitlab-fr/teams/75145/fr/)","Language":"fr","Plural-Forms":"nplurals=2; plural=(n > 1);","X-Generator":"Zanata 3.9.6","lang":"fr","domain":"app","plural_forms":"nplurals=2; plural=(n > 1);"},"ByAuthor|by":["par"],"Commit":["Validation","Validations"],"Cycle Analytics gives an overview of how much time it takes to go from idea to production in your project.":["L’analyseur de cycle permet d’avoir une vue d’ensemble du temps nécessaire pour aller d’une idée à sa mise en production pour votre projet."],"CycleAnalyticsStage|Code":["Code"],"CycleAnalyticsStage|Issue":["Incident"],"CycleAnalyticsStage|Plan":["Planification"],"CycleAnalyticsStage|Production":["Production"],"CycleAnalyticsStage|Review":["Examen"],"CycleAnalyticsStage|Staging":["Pré-production"],"CycleAnalyticsStage|Test":["Test"],"Deploy":["Déploiement","Déploiements"],"FirstPushedBy|First":["En premier"],"FirstPushedBy|pushed by":["poussé par"],"From issue creation until deploy to production":["Depuis la création de l'incident jusqu'au déploiement en production"],"From merge request merge until deploy to production":["Depuis la fusion de la demande de fusion jusqu'au déploiement en production"],"Introducing Cycle Analytics":["Introduction à l'analyseur de cycle"],"Last %d day":["Le dernier %d jour","Les derniers %d jours"],"Limited to showing %d event at most":["Limiter l'affichage au plus à %d évènement","Limiter l'affichage au plus à %d évènements"],"Median":["Médian"],"New Issue":["Nouvel incident","Nouveaux incidents"],"Not available":["Indisponible"],"Not enough data":["Données insuffisantes"],"OpenedNDaysAgo|Opened":["Ouvert"],"Pipeline Health":["Santé du Pipeline"],"ProjectLifecycle|Stage":["Étape"],"Read more":["Lire plus"],"Related Commits":["Validations liés"],"Related Deployed Jobs":["Tâches de déploiement liés"],"Related Issues":["Incidents liés"],"Related Jobs":["Tâches liées"],"Related Merge Requests":["Demandes de fusion liées"],"Related Merged Requests":["Demandes fusionnées liées"],"Showing %d event":["Affichage de %d évènement","Affichage de %d évènements"],"The coding stage shows the time from the first commit to creating the merge request. The data will automatically be added here once you create your first merge request.":["L’étape de développement montre le temps entre la première validation et la création de la demande de fusion. Les données seront automatiquement ajoutées ici une fois que vous aurez créé votre première demande de fusion."],"The collection of events added to the data gathered for that stage.":["L’ensemble d’évènements ajoutés aux données récupérées pour cette étape."],"The issue stage shows the time it takes from creating an issue to assigning the issue to a milestone, or add the issue to a list on your Issue Board. Begin creating issues to see data for this stage.":["L'étape des incidents montre le temps nécessaire entre la création d'un incident et son assignation à un jalon, ou son ajout à une liste d'un tableau d'incident. Débutez à créer des incidents pour voir des données pour cette étape."],"The phase of the development lifecycle.":["Les étapes du cycle de développement."],"The planning stage shows the time from the previous step to pushing your first commit. This time will be added automatically once you push your first commit.":["L’étape de planification montre le temps entre l’étape précédente et l’envoi de votre première validation. Ce temps sera automatiquement ajouté quand vous pousserez votre première validation."],"The production stage shows the total time it takes between creating an issue and deploying the code to production. The data will be automatically added once you have completed the full idea to production cycle.":["L’étape de mise en production montre le temps nécessaire entre la création d’un incident et le déploiement du code en production. Les données seront automatiquement ajoutées une fois que vous aurez complété le cycle complet, depuis l’idée jusqu’à la mise en production."],"The review stage shows the time from creating the merge request to merging it. The data will automatically be added after you merge your first merge request.":["L’étape d’évaluation montre le temps entre la création de la demande de fusion et la fusion effective de celle-ci. Ces données seront automatiquement ajoutées après que vous ayez fusionné votre première demande de fusion."],"The staging stage shows the time between merging the MR and deploying code to the production environment. The data will be automatically added once you deploy to production for the first time.":["L’étape de pré-production indique le temps entre la fusion de la RF et le déploiement du code dans l’environnent de production. Les données seront automatiquement ajoutées une fois que vous déploierez en production pour la première fois."],"The testing stage shows the time GitLab CI takes to run every pipeline for the related merge request. The data will automatically be added after your first pipeline finishes running.":["L’étape de test montre le temps que le CI de GitLab met pour exécuter chaque pipeline liés à la demande de fusion. Les données seront automatiquement ajoutées après que votre premier pipeline s’achèvera."],"The time taken by each data entry gathered by that stage.":["Le temps pris par chaque entrée récoltée durant cette étape."],"The value lying at the midpoint of a series of observed values. E.g., between 3, 5, 9, the median is 5. Between 3, 5, 7, 8, the median is (5+7)/2 = 6.":["La valeur située au point médian d’une série de valeur observée. C.à.d., entre 3, 5, 9, le médian est 5. Entre 3, 5, 7, 8, le médian est (5+7)/2 = 6."],"Time before an issue gets scheduled":["Temps avant qu’un incident ne soit planifié"],"Time before an issue starts implementation":["Temps avant que résolution ne débute"],"Time between merge request creation and merge/close":["Temps entre la création d'une demande de fusion et sa fusion/clôture"],"Time until first merge request":["Temps jusqu’à la première demande de fusion"],"Time|hr":["hr","hrs"],"Time|min":["min","mins"],"Time|s":["s"],"Total Time":["Temps total"],"Total test time for all commits/merges":["Temps total de test pour toutes les validations/fusions"],"Want to see the data? Please ask an administrator for access.":["Vous voulez voir les données ? Merci de contacter un administrateur pour en obtenir l’accès."],"We don't have enough data to show this stage.":["Nous n'avons pas suffisamment de données pour afficher cette étape."],"You need permission.":["Vous avez besoin d’une autorisation."],"day":["jour","jours"],"%{commit_author_link} committed %{commit_timeago}":["%{commit_author_link} a validé %{commit_timeago}"],"About auto deploy":["A propos de l'auto-déploiement"],"Active":["Actif"],"Activity":["Activité"],"Add Changelog":["Ajouter un journal des modifications"],"Add Contribution guide":["Ajouter un guide de contribution"],"Add License":["Ajouter une licence"],"Add an SSH key to your profile to pull or push via SSH.":["Ajoutez une clef SSH à votre profil pour pouvoir récupérer et pousser par SSH."],"Add new directory":["Ajouter un nouveau dossier"],"Archived project! Repository is read-only":["Projet archivé ! Le dépôt est en lecture seule"],"Are you sure you want to delete this pipeline schedule?":["Êtes-vous sûr de vouloir supprimer ce pipeline programmé"],"Attach a file by drag &amp; drop or %{upload_link}":["Attachez un fichier par glisser &amp; déposer ou %{upload_link}"],"Branch":["Branche","Branches"],"#~ \"Branch <strong>%{branch_name}</strong> was created. To set up auto deploy, cho\"#~ \"ose a GitLab CI Yaml template and commit your changes. %{link_to_autodeploy_do\"#~ \"c}\"":["#~ \"La branche <strong>%{branch_name}</strong> a été crée. Pour mettre en place le\"#~ \" déploiement automatisé, sélectionnez un modèle de fichier Yaml pour Gitlab CI\"#~ \", et validez les modifications. %{link_to_autodeploy_doc}\""],"Branches":["Branches"],"Browse files":["Parcourir les fichiers"],"CI configuration":["Configuration du CI"],"Cancel":["Annuler"],"ChangeTypeActionLabel|Pick into branch":["Sélectionner dans la branche"],"ChangeTypeActionLabel|Revert in branch":["Annuler dans la branche"],"ChangeTypeAction|Cherry-pick":["Sélectionner"],"ChangeType|commit":["validation"],"ChangeType|merge request":["demande de fusion"],"Changelog":["Journal des modifications"],"Charts":["Graphiques"],"Cherry-pick this commit":["Sélectionner cette validation"],"Cherry-pick this merge-request":["Sélectionner cette demande de fusion"],"CiStatusLabel|canceled":["annulé"],"CiStatusLabel|created":["créé"],"CiStatusLabel|failed":["échoué"],"CiStatusLabel|manual action":["action manuelle"],"CiStatusLabel|passed":["passé"],"CiStatusLabel|passed with warnings":["passé avec des avertissements"],"CiStatusLabel|pending":["en attente"],"CiStatusLabel|skipped":["ignoré"],"CiStatusLabel|waiting for manual action":["en attente d'action manuelle"],"CiStatusText|blocked":["bloqué"],"CiStatusText|canceled":["annulé "],"CiStatusText|created":["créé"],"CiStatusText|failed":["échoué"],"CiStatusText|manual":["manuel"],"CiStatusText|passed":["passé"],"CiStatusText|pending":["en attente"],"CiStatusText|skipped":["ignoré"],"CiStatus|running":["en cours"],"Commit message":["Message de validation"],"CommitMessage|Add %{file_name}":["Ajout de %{file_name}"],"Commits":["Validations"],"Commits|History":["Historique"],"Committed by":["Validé par"],"Compare":["Comparer"],"Contribution guide":["Guilde de contribution"],"Contributors":["Contributeurs"],"Copy URL to clipboard":["Copier l'URL dans le presse-papier"],"Copy commit SHA to clipboard":["Copier le SAH de la validation"],"Create New Directory":["Créer un nouveau dossier"],"Create directory":["Créer un dossier"],"Create empty bare repository":["Créer un dépôt vide"],"Create merge request":["Créer une demande de fusion"],"Create new...":["Créer nouveau..."],"CreateNewFork|Fork":["Fork"],"CreateTag|Tag":["Étiquette"],"Cron Timezone":["Fuseau horaire de Cron"],"Cron syntax":["Syntaxe CRON"],"Custom":["Personnalisé"],"Custom notification events":["Événements de notification personnalisés"],"#~ \"Custom notification levels are the same as participating levels. With custom n\"#~ \"otification levels you will also receive notifications for select events. To f\"#~ \"ind out more, check out %{notification_link}.\"":["#~ \"Le niveau de notification Personnalisé est similaire au niveau Participation. \"#~ \"Il permet cependant également de recevoir des notifications pour des événement\"#~ \"s sélectionnés. Pour plus d’information, vous pouvez consulter %{notification_\"#~ \"link}.\""],"Cycle Analytics":["Analyseur de cycle"],"Define a custom pattern with cron syntax":["Définir un schéma personnalisé avec une syntaxe CRON"],"Delete":["Supprimer"],"Description":["Description"],"Directory name":["Nom du dossier"],"Don't show again":["Ne plus montrer"],"Download":["Télécharger"],"Download tar":["Télécharger tar"],"Download tar.bz2":["Télécharger tar.bz2"],"Download tar.gz":["Télécharger tar.gz"],"Download zip":["Télécharger zip"],"DownloadArtifacts|Download":["Télécharger"],"DownloadCommit|Email Patches":["Patch email"],"DownloadCommit|Plain Diff":["Diff simple"],"DownloadSource|Download":["Télécharger"],"Edit":["Éditer"],"Edit Pipeline Schedule %{id}":["Éditer le pipeline programmé %{id}"],"Every day (at 4:00am)":["Chaque jour (à 4:00 du matin)"],"Every month (on the 1st at 4:00am)":["Chaque mois (le 1er à 4:00 du matin)"],"Every week (Sundays at 4:00am)":["Chaque semaine (Dimanche à 4:00 du matin)"],"Failed to change the owner":["Échec du changement de propriétaire"],"Failed to remove the pipeline schedule":["Échec de la suppression du pipeline programmé"],"Files":["Fichiers"],"Find by path":["Rechercher par chemin"],"Find file":["Rechercher un fichier"],"Fork":["Fork","Forks"],"ForkedFromProjectPath|Forked from":["Forké depuis"],"Go to your fork":["Aller à votre fork"],"GoToYourFork|Fork":["Fork"],"Home":["Accueil"],"Housekeeping successfully started":["Maintenance démarrée avec succès"],"Import repository":["Importer un dépôt"],"Interval Pattern":["Schéma d’intervalle"],"LFSStatus|Disabled":["Désactivé"],"LFSStatus|Enabled":["Activé"],"Last Pipeline":["Dernier pipeline"],"Last Update":["Dernière mise à jour"],"Last commit":["Dernière validation"],"Learn more in the":["En apprendre plus dans le"],"Leave group":["Quitter le groupe"],"Leave project":["Quitter le projet"],"MissingSSHKeyWarningLink|add an SSH key":["ajouter un clef SSH"],"New Pipeline Schedule":["Nouveau pipeline programmé"],"New branch":["Nouvelle branche"],"New directory":["Nouveau dossier"],"New file":["Nouveau Fichier"],"New issue":["Nouvel incident"],"New merge request":["Nouvelle demande de fusion"],"New schedule":["Nouveau programme"],"New snippet":["Nouvel extrait de code"],"New tag":["Nouvelle étiquette"],"No repository":["Pas de dépôt"],"No schedules":["Aucun programme"],"Notification events":["Événement de notifications"],"NotificationEvent|Close issue":["Clore l'incident"],"NotificationEvent|Close merge request":["Clore la demande de fusion"],"NotificationEvent|Failed pipeline":["Pipeline échoué"],"NotificationEvent|Merge merge request":["Fusionner le demande de fusion"],"NotificationEvent|New issue":["Nouvel incident"],"NotificationEvent|New merge request":["Nouvelle demande de fusion"],"NotificationEvent|New note":["Nouvelle note"],"NotificationEvent|Reassign issue":["Réassigner l'incident"],"NotificationEvent|Reassign merge request":["Réassigner la demande de fusion"],"NotificationEvent|Reopen issue":["Ré-ouvrir l'incident"],"NotificationEvent|Successful pipeline":["Pipeline réussi"],"NotificationLevel|Custom":["Personnalisé"],"NotificationLevel|Disabled":["Désactivé"],"NotificationLevel|Global":["Global"],"NotificationLevel|On mention":["En cas de mention"],"NotificationLevel|Participate":["Participation"],"NotificationLevel|Watch":["Surveillé"],"OfSearchInADropdown|Filter":["Filtre"],"Options":["Options"],"Owner":["Propriétaire"],"Pipeline":["Pipeline"],"Pipeline Schedule":["Programmation de pipeline"],"Pipeline Schedules":["Programmations de pipeline"],"PipelineSchedules|Activated":["Activé"],"PipelineSchedules|Active":["Active"],"PipelineSchedules|All":["Tous"],"PipelineSchedules|Inactive":["Inactive"],"PipelineSchedules|Next Run":["Prochaine exécution"],"PipelineSchedules|None":["Aucune"],"PipelineSchedules|Provide a short description for this pipeline":["Indiquez une courte description"],"PipelineSchedules|Take ownership":["S’approprier"],"PipelineSchedules|Target":["Cible"],"Project '%{project_name}' queued for deletion.":["Projet '%{project_name}' en attente de suppression."],"Project '%{project_name}' was successfully created.":["Projet '%{project_name}' créé avec succès."],"Project '%{project_name}' was successfully updated.":["Projet '%{project_name}' mis à jour avec succès."],"Project '%{project_name}' will be deleted.":["Projet '%{project_name}' sera supprimé."],"Project access must be granted explicitly to each user.":["L’accès au projet doit être explicitement accordé à chaque utilisateur."],"Project export could not be deleted.":["L'export du projet n'a pas pu être supprimé."],"Project export has been deleted.":["L'export du projet a été supprimé."],"#~ \"Project export link has expired. Please generate a new export from your projec\"#~ \"t settings.\"":["#~ \"Le lien de l’export du projet a expiré. Merci de générer un nouvel export depu\"#~ \"is les paramètres du projet.\""],"Project export started. A download link will be sent by email.":["#~ \"L'export du projet a débuté. Un lien de téléchargement sera envoyé par courrie\"#~ \"l.\""],"Project home":["Accueil du projet"],"ProjectFeature|Disabled":["Désactivé"],"ProjectFeature|Everyone with access":["Toute personne ayant accès"],"ProjectFeature|Only team members":["Seulement les membres de l'équipe"],"ProjectFileTree|Name":["Nom"],"ProjectLastActivity|Never":["Jamais"],"ProjectNetworkGraph|Graph":["Graphique "],"Readme":["LisezMoi"],"RefSwitcher|Branches":["Branches"],"RefSwitcher|Tags":["Étiquettes"],"Remind later":["Me le rappeler ultérieurement"],"Remove project":["Supprimer le projet"],"Request Access":["Demander l'accès"],"Revert this commit":["Annuler cette validation"],"Revert this merge-request":["Annuler cette demande de fusion"],"Save pipeline schedule":["Sauvegarder le pipeline programmé"],"Schedule a new pipeline":["Programmer un nouveau pipeline"],"Scheduling Pipelines":["Programmer des pipelines"],"Search branches and tags":["Rechercher dans les branches et les étiquettes"],"Select Archive Format":["Sélectionnez le format de l'archive"],"Select a timezone":["Sélectionnez un fuseau horaire"],"Select target branch":["Sélectionnez une branche cible"],"Set a password on your account to pull or push via %{protocol}":["#~ \"Définissez un mot de passe pour votre compte pour pouvoir tirer ou pousser par\"#~ \" %{protocol}\""],"Set up CI":["Mettre en place le CI"],"Set up Koding":["Mettre en place Koding"],"Set up auto deploy":["Mettre en place l’auto-déploiement "],"SetPasswordToCloneLink|set a password":["définir un mot de passe"],"Source code":["Code source"],"StarProject|Star":["S'abonner"],"Start a <strong>new merge request</strong> with these changes":["Créer une <strong>nouvelle demande de fusion</strong> avec ces changements"],"Switch branch/tag":["Changer de branche / d'étiquette"],"Tag":["Étiquette","Étiquettes"],"Tags":["Étiquettes"],"Target Branch":["Branche cible"],"The fork relationship has been removed.":["La relation de fork a été supprimée."],"#~ \"The pipelines schedule runs pipelines in the future, repeatedly, for specific \"#~ \"branches or tags. Those scheduled pipelines will inherit limited project acces\"#~ \"s based on their associated user.\"":["#~ \"Les pipelines programmés exécutent des pipelines dans le futur, de façon répét\"#~ \"ée, pour les branches et étiquettes spécifiées. Ces pipelines programmés hérit\"#~ \"ent d’un accès partiel au projet basé sur l’utilisateur que leurs est associé.\""],"The project can be accessed by any logged in user.":["Votre projet peut être accédé par n’importe quel utilisateur authentifié"],"The project can be accessed without any authentication.":["Votre projet peut être accédé sans aucune authentification."],"The repository for this project does not exist.":["Le dépôt pour ce projet n'existe pas."],"#~ \"This means you can not push code until you create an empty repository or impor\"#~ \"t existing one.\"":["#~ \"Cela signifie que vous ne pouvez pas pousser du code tant que vous ne créez pa\"#~ \"s un dépôt vide, ou importez une dépôt existant.\""],"Timeago|%s days ago":["Il y a %s jours"],"Timeago|%s days remaining":["Il reste %s jours"],"Timeago|%s hours remaining":["Il reste %s heures"],"Timeago|%s minutes ago":["Il y a %s minutes"],"Timeago|%s minutes remaining":["Il reste %s minutes"],"Timeago|%s months ago":["Il y a %s mois"],"Timeago|%s months remaining":["Il reste %s mois"],"Timeago|%s seconds remaining":["Il reste %s secondes"],"Timeago|%s weeks ago":["Il y a %s semaines"],"Timeago|%s weeks remaining":["Il reste %s semaines"],"Timeago|%s years ago":["Il y a %s ans"],"Timeago|%s years remaining":["Il reste %s ans"],"Timeago|1 day remaining":["Il reste un jour"],"Timeago|1 hour remaining":["Il reste une heure"],"Timeago|1 minute remaining":["Il reste une minute"],"Timeago|1 month remaining":["Il reste un mois"],"Timeago|1 week remaining":["Il reste une semaine"],"Timeago|1 year remaining":["Il reste un an"],"Timeago|Past due":["En retard"],"Timeago|a day ago":["Il y a un jour"],"Timeago|a month ago":["Il y a un mois"],"Timeago|a week ago":["Il y a une semaine"],"Timeago|a while":["Il y a un moment"],"Timeago|a year ago":["Il y a un an"],"Timeago|about %s hours ago":["Il y a environ %s heures"],"Timeago|about a minute ago":["Il y a environ une minute"],"Timeago|about an hour ago":["Il y a environ une heure"],"Timeago|in %s days":["Dans %s jours"],"Timeago|in %s hours":["Dans %s heures"],"Timeago|in %s minutes":["Dans %s minutes"],"Timeago|in %s months":["Dans %s mois"],"Timeago|in %s seconds":["Dans %s secondes"],"Timeago|in %s weeks":["Dans %s semaines"],"Timeago|in %s years":["Dans %s années"],"Timeago|in 1 day":["Dans 1 jour"],"Timeago|in 1 hour":["Dans 1 heure"],"Timeago|in 1 minute":["Dans 1 minute"],"Timeago|in 1 month":["Dans 1 mois"],"Timeago|in 1 week":["Dans 1 semaine"],"Timeago|in 1 year":["Dans 1 an"],"Timeago|less than a minute ago":["il y a moins d'une minute"],"Unstar":["Se désabonner"],"Upload New File":["Téléverser un nouveau fichier"],"Upload file":["Téléverser un fichier"],"Use your global notification setting":["Utiliser vos paramètres de notification globaux"],"VisibilityLevel|Internal":["Interne"],"VisibilityLevel|Private":["Privé"],"VisibilityLevel|Public":["Publique"],"Withdraw Access Request":["Retirer la demande d'accès"],"#~ \"You are going to remove %{project_name_with_namespace}.\\n\"#~ \"Removed project CANNOT be restored!\\n\"#~ \"Are you ABSOLUTELY sure?\"":["#~ \"Vous êtes sur le point de supprimer %{project_name_with_namespace}.\\n\"#~ \"Les projets supprimés NE PEUVENT PAS être restaurés !\\n\"#~ \"Êtes vous ABSOLUMENT sûr ? \""],"#~ \"You are going to remove the fork relationship to source project %{forked_from_\"#~ \"project}. Are you ABSOLUTELY sure?\"":["#~ \"Vous allez supprimer la relation de fork avec le projet source %{forked_from_p\"#~ \"roject}. Êtes-vous VRAIMENT sûr.\""],"#~ \"You are going to transfer %{project_name_with_namespace} to another owner. Are\"#~ \" you ABSOLUTELY sure?\"":["#~ \"Vous allez transférer %{project_name_with_namespace} à un nouveau propriétaire\"#~ \". Êtes vous VRAIMENT sûr ?\""],"You can only add files when you are on a branch":["Vous ne pouvez ajouter de fichier que dans une branche"],"You must sign in to star a project":["Vous devez vous identifier pour vous abonner à un projet"],"You will not get any notifications via email":["Vous ne recevrez aucune notification par courriel"],"You will only receive notifications for the events you choose":["#~ \"Vous ne recevrez de notification que pour les évènements que vous aurez choisi\"#~ \"s\""],"You will only receive notifications for threads you have participated in":["#~ \"Vous ne recevrez de notification que pour les sujets auxquels vous avez partic\"#~ \"ipé\""],"You will receive notifications for any activity":["Vous recevrez des notifications pour n’importe quelles activités"],"You will receive notifications only for comments in which you were @mentioned":["#~ \"Vous ne recevrez de notifications que pour les commentaires où vous êtes @ment\"#~ \"ionné\""],"#~ \"You won't be able to pull or push project code via %{protocol} until you %{set\"#~ \"_password_link} on your account\"":["#~ \"Vous ne pourrez pas récupérer ou pousser de code par %{protocol} tant que vo\"#~ \"us n'aurez pas %{set_password_link} pour votre compte\""],"#~ \"You won't be able to pull or push project code via SSH until you %{add_ssh_key\"#~ \"_link} to your profile\"":["#~ \"Vous ne pourrez pas récupérer ou pousser de code par SSH tant que vous n’aur\"#~ \"ez pas %{add_ssh_key_link} dans votre profil\""],"Your name":["Votre nom"],"notification emails":["courriels de notification"],"parent":["parent","parents"],"pipeline schedules documentation":["documentation des pipeline programmés"],"with stage":["avec l'étape","avec les étapes"]}}}; \ No newline at end of file
diff --git a/app/assets/javascripts/locale/zh_HK/app.js b/app/assets/javascripts/locale/zh_HK/app.js
index c87cab5423f..5b6e56fea4a 100644
--- a/app/assets/javascripts/locale/zh_HK/app.js
+++ b/app/assets/javascripts/locale/zh_HK/app.js
@@ -1 +1 @@
-var locales = locales || {}; locales['zh_HK'] = {"domain":"app","locale_data":{"app":{"":{"Project-Id-Version":"gitlab 1.0.0","Report-Msgid-Bugs-To":"","POT-Creation-Date":"2017-06-15 14:57+0200","MIME-Version":"1.0","Content-Type":"text/plain; charset=UTF-8","Content-Transfer-Encoding":"8bit","PO-Revision-Date":"2017-06-15 10:34-0400","Last-Translator":"Huang Tao <htve@outlook.com>","Language-Team":"Chinese (Hong Kong) (https://translate.zanata.org/project/view/GitLab)","Language":"zh-HK","Plural-Forms":"nplurals=1; plural=0;","X-Generator":"Zanata 3.9.6","lang":"zh_HK","domain":"app","plural_forms":"nplurals=1; plural=0;"},"%{commit_author_link} committed %{commit_timeago}":["ç”± %{commit_author_link} æ交於 %{commit_timeago}"],"About auto deploy":["關於自動部署"],"Active":["啟用"],"Activity":["活動"],"Add Changelog":["添加更新日誌"],"Add Contribution guide":["添加貢ç»æŒ‡å—"],"Add License":["添加許å¯è­‰"],"Add an SSH key to your profile to pull or push via SSH.":["新增壹個用於推é€æˆ–拉å–çš„ SSH 秘鑰到賬號中。"],"Add new directory":["添加新目錄"],"Archived project! Repository is read-only":["歸檔項目ï¼å­˜å„²åº«ç‚ºåªè®€"],"Are you sure you want to delete this pipeline schedule?":["確定è¦åˆªé™¤æ­¤æµæ°´ç·šè¨ˆåŠƒå—Žï¼Ÿ"],"Attach a file by drag &amp; drop or %{upload_link}":["拖放文件到此處或者 %{upload_link}"],"Branch":["分支"],"Branch <strong>%{branch_name}</strong> was created. To set up auto deploy, choose a GitLab CI Yaml template and commit your changes. %{link_to_autodeploy_doc}":["分支 <strong>%{branch_name}</strong> 已創建。如需設置自動部署, è«‹é¸æ“‡åˆé©çš„ GitLab CI Yaml 模æ¿ä½µæ交更改。%{link_to_autodeploy_doc}"],"Branches":["分支"],"Browse files":["ç€è¦½æ–‡ä»¶"],"ByAuthor|by":["作者:"],"CI configuration":["CI é…ç½®"],"Cancel":["å–消"],"ChangeTypeActionLabel|Pick into branch":["挑é¸åˆ°åˆ†æ”¯"],"ChangeTypeActionLabel|Revert in branch":["還原分支"],"ChangeTypeAction|Cherry-pick":["優é¸"],"ChangeTypeAction|Revert":["還原"],"Changelog":["更新日誌"],"Charts":["統計圖"],"Cherry-pick this commit":["優é¸æ­¤æ交"],"Cherry-pick this merge request":["優é¸æ­¤åˆä½µè«‹æ±‚"],"CiStatusLabel|canceled":["å·²å–消"],"CiStatusLabel|created":["已創建"],"CiStatusLabel|failed":["已失敗"],"CiStatusLabel|manual action":["手動æ“作"],"CiStatusLabel|passed":["已通éŽ"],"CiStatusLabel|passed with warnings":["已通éŽä½†æœ‰è­¦å‘Š"],"CiStatusLabel|pending":["等待中"],"CiStatusLabel|skipped":["已跳éŽ"],"CiStatusLabel|waiting for manual action":["等待手動æ“作"],"CiStatusText|blocked":["已阻塞"],"CiStatusText|canceled":["å·²å–消"],"CiStatusText|created":["已創建"],"CiStatusText|failed":["已失敗"],"CiStatusText|manual":["待手動"],"CiStatusText|passed":["已通éŽ"],"CiStatusText|pending":["等待中"],"CiStatusText|skipped":["已跳éŽ"],"CiStatus|running":["é‹è¡Œä¸­"],"Commit":["æ交"],"Commit message":["æ交信æ¯"],"CommitBoxTitle|Commit":["æ交"],"CommitMessage|Add %{file_name}":["添加 %{file_name}"],"Commits":["æ交"],"Commits|History":["æ­·å²"],"Committed by":["æ交於"],"Compare":["比較"],"Contribution guide":["è²¢ç»æŒ‡å—"],"Contributors":["è²¢ç»è€…"],"Copy URL to clipboard":["複製URL到剪貼æ¿"],"Copy commit SHA to clipboard":["複製æ交 SHA 到剪貼æ¿"],"Create New Directory":["創建新目錄"],"Create directory":["創建目錄"],"Create empty bare repository":["創建空的存儲庫"],"Create merge request":["創建åˆä½µè«‹æ±‚"],"Create new...":["創建..."],"CreateNewFork|Fork":["派生"],"CreateTag|Tag":["標籤"],"Cron Timezone":["Cron 時å€"],"Cron syntax":["Cron 語法"],"Custom notification events":["自定義通知事件"],"Custom notification levels are the same as participating levels. With custom notification levels you will also receive notifications for select events. To find out more, check out %{notification_link}.":["自定義通知級別繼承自åƒèˆ‡ç´šåˆ¥ã€‚使用自定義通知級別,您會收到åƒèˆ‡ç´šåˆ¥åŠé¸å®šäº‹ä»¶çš„通知。想了解更多信æ¯ï¼Œè«‹æŸ¥çœ‹ %{notification_link}."],"Cycle Analytics":["週期分æž"],"Cycle Analytics gives an overview of how much time it takes to go from idea to production in your project.":["週期分æžæ¦‚述了項目從想法到產å“實ç¾çš„å„階段所需的時間。"],"CycleAnalyticsStage|Code":["編碼"],"CycleAnalyticsStage|Issue":["è­°é¡Œ"],"CycleAnalyticsStage|Plan":["計劃"],"CycleAnalyticsStage|Production":["生產"],"CycleAnalyticsStage|Review":["評審"],"CycleAnalyticsStage|Staging":["é ç™¼å¸ƒ"],"CycleAnalyticsStage|Test":["測試"],"Define a custom pattern with cron syntax":["使用 Cron 語法定義自定義模å¼"],"Delete":["刪除"],"Deploy":["部署"],"Description":["æè¿°"],"Directory name":["目錄å稱"],"Don't show again":["ä¸å†é¡¯ç¤º"],"Download":["下載"],"Download tar":["下載 tar"],"Download tar.bz2":["下載 tar.bz2"],"Download tar.gz":["下載 tar.gz"],"Download zip":["下載 zip"],"DownloadArtifacts|Download":["下載"],"DownloadCommit|Email Patches":["é›»å­éƒµä»¶è£œä¸"],"DownloadCommit|Plain Diff":["Diff 文件"],"DownloadSource|Download":["下載"],"Edit":["編輯"],"Edit Pipeline Schedule %{id}":["編輯 %{id} æµæ°´ç·šè¨ˆåŠƒ"],"Every day (at 4:00am)":["æ¯æ—¥åŸ·è¡Œï¼ˆæ·©æ™¨4點)"],"Every month (on the 1st at 4:00am)":["æ¯æœˆåŸ·è¡Œï¼ˆæ¯æœˆ1日淩晨4點)"],"Every week (Sundays at 4:00am)":["æ¯é€±åŸ·è¡Œï¼ˆå‘¨æ—¥æ·©æ™¨4點)"],"Failed to change the owner":["無法變更所有者"],"Failed to remove the pipeline schedule":["無法刪除æµæ°´ç·šè¨ˆåŠƒ"],"Files":["文件"],"Find by path":["按路徑查找"],"Find file":["查找文件"],"FirstPushedBy|First":["首次推é€"],"FirstPushedBy|pushed by":["推é€è€…:"],"Fork":["派生"],"ForkedFromProjectPath|Forked from":["派生自"],"From issue creation until deploy to production":["從創建議題到部署到生產環境"],"From merge request merge until deploy to production":["從åˆä½µè«‹æ±‚çš„åˆä½µåˆ°éƒ¨ç½²è‡³ç”Ÿç”¢ç’°å¢ƒ"],"Go to your fork":["跳轉到派生項目"],"GoToYourFork|Fork":["跳轉到派生項目"],"Home":["首é "],"Housekeeping successfully started":["已開始維護"],"Import repository":["導入存儲庫"],"Interval Pattern":["é‡è¤‡é€±æœŸ"],"Introducing Cycle Analytics":["週期分æžç°¡ä»‹"],"LFSStatus|Disabled":["åœç”¨"],"LFSStatus|Enabled":["啟用"],"Last %d day":["最近 %d 天"],"Last Pipeline":["最新æµæ°´ç·š"],"Last Update":["最後更新"],"Last commit":["最後æ交"],"Learn more in the":["了解更多"],"Learn more in the|pipeline schedules documentation":["æµæ°´ç·šè¨ˆåŠƒæ–‡æª”"],"Leave group":["退出群組"],"Leave project":["退出項目"],"Limited to showing %d event at most":["最多顯示 %d 個事件"],"Median":["中ä½æ•¸"],"MissingSSHKeyWarningLink|add an SSH key":["添加壹個 SSH 公鑰"],"New Issue":["新建議題"],"New Pipeline Schedule":["創建æµæ°´ç·šè¨ˆåŠƒ"],"New branch":["新增分支"],"New directory":["新增目錄"],"New file":["新增文件"],"New issue":["æ–°è­°é¡Œ"],"New merge request":["新增åˆä½µè«‹æ±‚"],"New schedule":["新增计划"],"New snippet":["新代碼片段"],"New tag":["新增標籤"],"No repository":["沒有存儲庫"],"No schedules":["沒有計劃"],"Not available":["ä¸å¯ç”¨"],"Not enough data":["數據ä¸è¶³"],"Notification events":["通知事件"],"NotificationEvent|Close issue":["關閉議題"],"NotificationEvent|Close merge request":["關閉åˆä½µè«‹æ±‚"],"NotificationEvent|Failed pipeline":["æµæ°´ç·šå¤±æ•—"],"NotificationEvent|Merge merge request":["åˆä½µè«‹æ±‚被åˆä½µ"],"NotificationEvent|New issue":["新增議題"],"NotificationEvent|New merge request":["æ–°åˆä½µè«‹æ±‚"],"NotificationEvent|New note":["新增評論"],"NotificationEvent|Reassign issue":["é‡æ–°æŒ‡æ´¾è­°é¡Œ"],"NotificationEvent|Reassign merge request":["é‡æ–°æŒ‡æ´¾åˆä½µè«‹æ±‚"],"NotificationEvent|Reopen issue":["é‡æ–°æ‰“é–‹è­°é¡Œ"],"NotificationEvent|Successful pipeline":["æµæ°´ç·šæˆåŠŸå®Œæˆ"],"NotificationLevel|Custom":["自定義"],"NotificationLevel|Disabled":["åœç”¨"],"NotificationLevel|Global":["全局"],"NotificationLevel|On mention":["æåŠ"],"NotificationLevel|Participate":["åƒèˆ‡"],"NotificationLevel|Watch":["關注"],"OfSearchInADropdown|Filter":["篩é¸"],"OpenedNDaysAgo|Opened":["開始於"],"Options":["æ“作"],"Owner":["所有者"],"Pipeline":["æµæ°´ç·š"],"Pipeline Health":["æµæ°´ç·šå¥åº·æŒ‡æ¨™"],"Pipeline Schedule":["æµæ°´ç·šè¨ˆåŠƒ"],"Pipeline Schedules":["æµæ°´ç·šè¨ˆåŠƒ"],"PipelineSchedules|Activated":["是å¦å•Ÿç”¨"],"PipelineSchedules|Active":["已啟用"],"PipelineSchedules|All":["所有"],"PipelineSchedules|Inactive":["未啟用"],"PipelineSchedules|Next Run":["下次é‹è¡Œæ™‚é–“"],"PipelineSchedules|None":["ç„¡"],"PipelineSchedules|Provide a short description for this pipeline":["為此æµæ°´ç·šæ供簡短æè¿°"],"PipelineSchedules|Take ownership":["å–得所有者"],"PipelineSchedules|Target":["目標"],"PipelineSheduleIntervalPattern|Custom":["自定義"],"Pipeline|with stage":["於階段"],"Pipeline|with stages":["於階段"],"Project '%{project_name}' queued for deletion.":["é …ç›® '%{project_name}' 已進入刪除隊列。"],"Project '%{project_name}' was successfully created.":["é …ç›® '%{project_name}' 已創建æˆåŠŸã€‚"],"Project '%{project_name}' was successfully updated.":["é …ç›® '%{project_name}' 已更新完æˆã€‚"],"Project '%{project_name}' will be deleted.":["é …ç›® '%{project_name}' 將被刪除。"],"Project access must be granted explicitly to each user.":["項目訪å•æ¬Šé™å¿…須明確授權給æ¯å€‹ç”¨æˆ¶ã€‚"],"Project export could not be deleted.":["無法刪除項目導出。"],"Project export has been deleted.":["項目導出已被刪除。"],"Project export link has expired. Please generate a new export from your project settings.":["項目導出éˆæŽ¥å·²éŽæœŸã€‚請從項目設置中é‡æ–°ç”Ÿæˆé …目導出。"],"Project export started. A download link will be sent by email.":["項目導出已開始。下載éˆæŽ¥å°‡é€šéŽé›»å­éƒµä»¶ç™¼é€ã€‚"],"Project home":["項目首é "],"ProjectFeature|Disabled":["åœç”¨"],"ProjectFeature|Everyone with access":["任何人都å¯è¨ªå•"],"ProjectFeature|Only team members":["åªé™åœ˜éšŠæˆå“¡"],"ProjectFileTree|Name":["å稱"],"ProjectLastActivity|Never":["從未"],"ProjectLifecycle|Stage":["階段"],"ProjectNetworkGraph|Graph":["分支圖"],"Read more":["了解更多"],"Readme":["自述文件"],"RefSwitcher|Branches":["分支"],"RefSwitcher|Tags":["標籤"],"Related Commits":["相關的æ交"],"Related Deployed Jobs":["相關的部署作業"],"Related Issues":["相關的議題"],"Related Jobs":["相關的作業"],"Related Merge Requests":["相關的åˆä½µè«‹æ±‚"],"Related Merged Requests":["相關已åˆä½µçš„åˆä½µè«‹æ±‚"],"Remind later":["ç¨å¾Œæ醒"],"Remove project":["刪除項目"],"Request Access":["申請訪å•"],"Revert this commit":["還原此æ交"],"Revert this merge request":["還原此åˆä½µè«‹æ±‚"],"Save pipeline schedule":["ä¿å­˜æµæ°´ç·šè¨ˆåŠƒ"],"Schedule a new pipeline":["新建æµæ°´ç·šè¨ˆåŠƒ"],"Scheduling Pipelines":["æµæ°´ç·šè¨ˆåŠƒ"],"Search branches and tags":["æœç´¢åˆ†æ”¯å’Œæ¨™ç±¤"],"Select Archive Format":["é¸æ“‡ä¸‹è¼‰æ ¼å¼"],"Select a timezone":["é¸æ“‡æ™‚å€"],"Select target branch":["é¸æ“‡ç›®æ¨™åˆ†æ”¯"],"Set a password on your account to pull or push via %{protocol}":["為賬號添加壹個用於推é€æˆ–拉å–çš„ %{protocol} 密碼。"],"Set up CI":["設置 CI"],"Set up Koding":["設置 Koding"],"Set up auto deploy":["設置自動部署"],"SetPasswordToCloneLink|set a password":["設置密碼"],"Showing %d event":["顯示 %d 個事件"],"Source code":["æºä»£ç¢¼"],"StarProject|Star":["星標"],"Start a %{new_merge_request} with these changes":["由此更改 %{new_merge_request}"],"Start a <strong>new merge request</strong> with these changes":["由此更改創建<strong>æ–°åˆä½µè«‹æ±‚</strong>"],"Switch branch/tag":["切æ›åˆ†æ”¯/標籤"],"Tag":["標籤"],"Tags":["標籤"],"Target Branch":["目標分支"],"The coding stage shows the time from the first commit to creating the merge request. The data will automatically be added here once you create your first merge request.":["編碼階段概述了從第壹次æ交到創建åˆä½µè«‹æ±‚的時間。創建第壹個åˆä½µè«‹æ±‚後,數據將自動添加到此處。"],"The collection of events added to the data gathered for that stage.":["與該階段相關的事件。"],"The fork relationship has been removed.":["派生關係已被刪除。"],"The issue stage shows the time it takes from creating an issue to assigning the issue to a milestone, or add the issue to a list on your Issue Board. Begin creating issues to see data for this stage.":["議題階段概述了從創建議題到將議題添加到è£ç¨‹ç¢‘或議題看æ¿çš„時間。創建第壹個議題後,數據將自動添加到此處.。"],"The phase of the development lifecycle.":["項目生命週期中的å„個階段。"],"The pipelines schedule runs pipelines in the future, repeatedly, for specific branches or tags. Those scheduled pipelines will inherit limited project access based on their associated user.":["æµæ°´ç·šè¨ˆåŠƒæœƒé€±æœŸæ€§é‡è¤‡é‹è¡ŒæŒ‡å®šåˆ†æ”¯æˆ–標籤的æµæ°´ç·šã€‚這些æµæ°´ç·šå°‡æ ¹æ“šå…¶é—œè¯ç”¨æˆ¶ç¹¼æ‰¿æœ‰é™çš„項目訪å•æ¬Šé™ã€‚"],"The planning stage shows the time from the previous step to pushing your first commit. This time will be added automatically once you push your first commit.":["計劃階段概述了從議題添加到日程到推é€é¦–次æ交的時間。當首次推é€æ交後,數據將自動添加到此處。"],"The production stage shows the total time it takes between creating an issue and deploying the code to production. The data will be automatically added once you have completed the full idea to production cycle.":["生產階段概述了從創建議題到將代碼部署到生產環境的時間。當完æˆå®Œæ•´çš„想法到部署生產,數據將自動添加到此處。"],"The project can be accessed by any logged in user.":["該項目å…許已登錄的用戶訪å•ã€‚"],"The project can be accessed without any authentication.":["該項目å…許任何人訪å•ã€‚"],"The repository for this project does not exist.":["此項目的存儲庫ä¸å­˜åœ¨ã€‚"],"The review stage shows the time from creating the merge request to merging it. The data will automatically be added after you merge your first merge request.":["評審階段概述了從創建åˆä½µè«‹æ±‚到åˆä½µçš„時間。當創建第壹個åˆä½µè«‹æ±‚後,數據將自動添加到此處。"],"The staging stage shows the time between merging the MR and deploying code to the production environment. The data will be automatically added once you deploy to production for the first time.":["é ç™¼å¸ƒéšŽæ®µæ¦‚述了åˆä½µè«‹æ±‚çš„åˆä½µåˆ°éƒ¨ç½²ä»£ç¢¼åˆ°ç”Ÿç”¢ç’°å¢ƒçš„總時間。當首次部署到生產環境後,數據將自動添加到此處。"],"The testing stage shows the time GitLab CI takes to run every pipeline for the related merge request. The data will automatically be added after your first pipeline finishes running.":["測試階段概述了 GitLab CI 為相關åˆä½µè«‹æ±‚é‹è¡Œæ¯å€‹æµæ°´ç·šæ‰€éœ€çš„時間。當第壹個æµæ°´ç·šé‹è¡Œå®Œæˆå¾Œï¼Œæ•¸æ“šå°‡è‡ªå‹•æ·»åŠ åˆ°æ­¤è™•ã€‚"],"The time taken by each data entry gathered by that stage.":["該階段æ¯æ¢æ•¸æ“šæ‰€èŠ±çš„時間"],"The value lying at the midpoint of a series of observed values. E.g., between 3, 5, 9, the median is 5. Between 3, 5, 7, 8, the median is (5+7)/2 = 6.":["中ä½æ•¸æ˜¯å£¹å€‹æ•¸åˆ—中最中間的值。例如在 3ã€5ã€9 之間,中ä½æ•¸æ˜¯ 5。在 3ã€5ã€7ã€8 之間,中ä½æ•¸æ˜¯ (5 + 7)/ 2 = 6。"],"This means you can not push code until you create an empty repository or import existing one.":["在創建壹個空的存儲庫或導入ç¾æœ‰å­˜å„²åº«ä¹‹å‰ï¼Œæ‚¨å°‡ç„¡æ³•æŽ¨é€ä»£ç¢¼ã€‚"],"Time before an issue gets scheduled":["議題被列入日程表的時間"],"Time before an issue starts implementation":["開始進行編碼å‰çš„時間"],"Time between merge request creation and merge/close":["從創建åˆä½µè«‹æ±‚到被åˆä½µæˆ–關閉的時間"],"Time until first merge request":["創建第壹個åˆä½µè«‹æ±‚之å‰çš„時間"],"Timeago|%s days ago":["%s 天å‰"],"Timeago|%s days remaining":["剩餘 %s 天"],"Timeago|%s hours remaining":["剩餘 %s å°æ™‚"],"Timeago|%s minutes ago":["%s 分é˜å‰"],"Timeago|%s minutes remaining":["剩餘 %s 分é˜"],"Timeago|%s months ago":["%s 個月å‰"],"Timeago|%s months remaining":["剩餘 %s 月"],"Timeago|%s seconds remaining":["剩餘 %s 秒"],"Timeago|%s weeks ago":["%s 星期å‰"],"Timeago|%s weeks remaining":["剩餘 %s 星期"],"Timeago|%s years ago":["%s å¹´å‰"],"Timeago|%s years remaining":["剩餘 %s å¹´"],"Timeago|1 day remaining":["剩餘 1 天"],"Timeago|1 hour remaining":["剩餘 1 å°æ™‚"],"Timeago|1 minute remaining":["剩餘 1 分é˜"],"Timeago|1 month remaining":["剩餘 1 個月"],"Timeago|1 week remaining":["剩餘 1 星期"],"Timeago|1 year remaining":["剩餘 1 å¹´"],"Timeago|Past due":["逾期"],"Timeago|a day ago":["1 天å‰"],"Timeago|a month ago":["1 個月å‰"],"Timeago|a week ago":["1 星期å‰"],"Timeago|a while":["剛剛"],"Timeago|a year ago":["1 å¹´å‰"],"Timeago|about %s hours ago":["ç´„ %s å°æ™‚å‰"],"Timeago|about a minute ago":["ç´„ 1 分é˜å‰"],"Timeago|about an hour ago":["ç´„ 1 å°æ™‚å‰"],"Timeago|in %s days":[" %s 天後"],"Timeago|in %s hours":[" %s å°æ™‚後"],"Timeago|in %s minutes":[" %s 分é˜å¾Œ"],"Timeago|in %s months":[" %s 個月後"],"Timeago|in %s seconds":[" %s 秒後"],"Timeago|in %s weeks":[" %s 星期後"],"Timeago|in %s years":[" %s 年後"],"Timeago|in 1 day":[" 1 天後"],"Timeago|in 1 hour":[" 1 å°æ™‚後"],"Timeago|in 1 minute":[" 1 分é˜å¾Œ"],"Timeago|in 1 month":[" 1 月後"],"Timeago|in 1 week":[" 1 星期後"],"Timeago|in 1 year":[" 1 年後"],"Timeago|less than a minute ago":["ä¸åˆ° 1 分é˜å‰"],"Time|hr":["å°æ™‚"],"Time|min":["分é˜"],"Time|s":["秒"],"Total Time":["總時間"],"Total test time for all commits/merges":["所有æ交和åˆä½µçš„總測試時間"],"Unstar":["å–消星標"],"Upload New File":["上傳新文件"],"Upload file":["上傳文件"],"Use your global notification setting":["使用全局通知設置"],"VisibilityLevel|Internal":["內部"],"VisibilityLevel|Private":["ç§æœ‰"],"VisibilityLevel|Public":["公開"],"Want to see the data? Please ask an administrator for access.":["權é™ä¸è¶³ã€‚如需查看相關數據,請å‘管ç†å“¡ç”³è«‹æ¬Šé™ã€‚"],"We don't have enough data to show this stage.":["該階段的數據ä¸è¶³ï¼Œç„¡æ³•é¡¯ç¤ºã€‚"],"Withdraw Access Request":["å–消訪å•è«‹æ±‚"],"You are going to remove %{project_name_with_namespace}.\\nRemoved project CANNOT be restored!\\nAre you ABSOLUTELY sure?":["å³å°‡è¦åˆªé™¤ %{project_name_with_namespace}。\\n已刪除的項目無法æ¢è¤‡ï¼\\n確定繼續嗎?"],"You are going to remove the fork relationship to source project %{forked_from_project}. Are you ABSOLUTELY sure?":["å³å°‡åˆªé™¤èˆ‡æºé …ç›® %{forked_from_project} 的派生關系。確定繼續嗎?"],"You are going to transfer %{project_name_with_namespace} to another owner. Are you ABSOLUTELY sure?":["å³å°‡ %{project_name_with_namespace} 轉義給å¦å£¹å€‹æ‰€æœ‰è€…。確定繼續嗎?"],"You can only add files when you are on a branch":["åªèƒ½åœ¨åˆ†æ”¯ä¸Šæ·»åŠ æ–‡ä»¶"],"You must sign in to star a project":["必須登錄æ‰èƒ½å°é …目加星標"],"You need permission.":["需è¦ç›¸é—œçš„權é™ã€‚"],"You will not get any notifications via email":["ä¸æœƒæ”¶åˆ°ä»»ä½•é€šçŸ¥éƒµä»¶"],"You will only receive notifications for the events you choose":["åªæŽ¥æ”¶æ‚¨é¸æ“‡çš„事件通知"],"You will only receive notifications for threads you have participated in":["åªæŽ¥æ”¶æ‚¨åƒèˆ‡çš„主題的通知"],"You will receive notifications for any activity":["接收所有活動的通知"],"You will receive notifications only for comments in which you were @mentioned":["åªæŽ¥æ”¶è©•è«–中æåŠ(@)您的通知"],"You won't be able to pull or push project code via %{protocol} until you %{set_password_link} on your account":["在賬號上 %{set_password_link} 之å‰å°‡ç„¡æ³•é€šéŽ %{protocol} 拉å–或推é€ä»£ç¢¼ã€‚"],"You won't be able to pull or push project code via SSH until you %{add_ssh_key_link} to your profile":["在賬號中 %{add_ssh_key_link} 之å‰å°‡ç„¡æ³•é€šéŽ SSH 拉å–或推é€ä»£ç¢¼ã€‚"],"Your name":["您的åå­—"],"day":["天"],"new merge request":["新建åˆä½µè«‹æ±‚"],"notification emails":["通知郵件"],"parent":["父級"]}}}; \ No newline at end of file
+var locales = locales || {}; locales['zh_HK'] = {"domain":"app","locale_data":{"app":{"":{"Project-Id-Version":"gitlab 1.0.0","Report-Msgid-Bugs-To":"","POT-Creation-Date":"2017-06-15 21:59-0500","MIME-Version":"1.0","Content-Type":"text/plain; charset=UTF-8","Content-Transfer-Encoding":"8bit","PO-Revision-Date":"2017-06-19 09:57-0400","Last-Translator":"Huang Tao <htve@outlook.com>","Language-Team":"Chinese (Hong Kong) (https://translate.zanata.org/project/view/GitLab)","Language":"zh-HK","X-Generator":"Zanata 3.9.6","Plural-Forms":"nplurals=1; plural=0","lang":"zh_HK","domain":"app","plural_forms":"nplurals=1; plural=0"},"%{commit_author_link} committed %{commit_timeago}":["ç”± %{commit_author_link} æ交於 %{commit_timeago}"],"About auto deploy":["關於自動部署"],"Active":["啟用"],"Activity":["活動"],"Add Changelog":["添加更新日誌"],"Add Contribution guide":["添加貢ç»æŒ‡å—"],"Add License":["添加許å¯è­‰"],"Add an SSH key to your profile to pull or push via SSH.":["新增壹個用於推é€æˆ–拉å–çš„ SSH 秘鑰到賬號中。"],"Add new directory":["添加新目錄"],"Archived project! Repository is read-only":["歸檔項目ï¼å­˜å„²åº«ç‚ºåªè®€"],"Are you sure you want to delete this pipeline schedule?":["確定è¦åˆªé™¤æ­¤æµæ°´ç·šè¨ˆåŠƒå—Žï¼Ÿ"],"Attach a file by drag &amp; drop or %{upload_link}":["拖放文件到此處或者 %{upload_link}"],"Branch":["分支"],"Branch <strong>%{branch_name}</strong> was created. To set up auto deploy, choose a GitLab CI Yaml template and commit your changes. %{link_to_autodeploy_doc}":["分支 <strong>%{branch_name}</strong> 已創建。如需設置自動部署, è«‹é¸æ“‡åˆé©çš„ GitLab CI Yaml 模æ¿ä½µæ交更改。%{link_to_autodeploy_doc}"],"Branches":["分支"],"Browse files":["ç€è¦½æ–‡ä»¶"],"ByAuthor|by":["作者:"],"CI configuration":["CI é…ç½®"],"Cancel":["å–消"],"ChangeTypeActionLabel|Pick into branch":["挑é¸åˆ°åˆ†æ”¯"],"ChangeTypeActionLabel|Revert in branch":["還原分支"],"ChangeTypeAction|Cherry-pick":["優é¸"],"ChangeTypeAction|Revert":["還原"],"Changelog":["更新日誌"],"Charts":["統計圖"],"Cherry-pick this commit":["優é¸æ­¤æ交"],"Cherry-pick this merge request":["優é¸æ­¤åˆä½µè«‹æ±‚"],"CiStatusLabel|canceled":["å·²å–消"],"CiStatusLabel|created":["已創建"],"CiStatusLabel|failed":["已失敗"],"CiStatusLabel|manual action":["手動æ“作"],"CiStatusLabel|passed":["已通éŽ"],"CiStatusLabel|passed with warnings":["已通éŽä½†æœ‰è­¦å‘Š"],"CiStatusLabel|pending":["等待中"],"CiStatusLabel|skipped":["已跳éŽ"],"CiStatusLabel|waiting for manual action":["等待手動æ“作"],"CiStatusText|blocked":["已阻塞"],"CiStatusText|canceled":["å·²å–消"],"CiStatusText|created":["已創建"],"CiStatusText|failed":["已失敗"],"CiStatusText|manual":["待手動"],"CiStatusText|passed":["已通éŽ"],"CiStatusText|pending":["等待中"],"CiStatusText|skipped":["已跳éŽ"],"CiStatus|running":["é‹è¡Œä¸­"],"Commit":["æ交"],"Commit message":["æ交信æ¯"],"CommitBoxTitle|Commit":["æ交"],"CommitMessage|Add %{file_name}":["添加 %{file_name}"],"Commits":["æ交"],"Commits|History":["æ­·å²"],"Committed by":["æ交於"],"Compare":["比較"],"Contribution guide":["è²¢ç»æŒ‡å—"],"Contributors":["è²¢ç»è€…"],"Copy URL to clipboard":["複製URL到剪貼æ¿"],"Copy commit SHA to clipboard":["複製æ交 SHA 到剪貼æ¿"],"Create New Directory":["創建新目錄"],"Create directory":["創建目錄"],"Create empty bare repository":["創建空的存儲庫"],"Create merge request":["創建åˆä½µè«‹æ±‚"],"Create new...":["創建..."],"CreateNewFork|Fork":["派生"],"CreateTag|Tag":["標籤"],"Cron Timezone":["Cron 時å€"],"Cron syntax":["Cron 語法"],"Custom notification events":["自定義通知事件"],"Custom notification levels are the same as participating levels. With custom notification levels you will also receive notifications for select events. To find out more, check out %{notification_link}.":["自定義通知級別繼承自åƒèˆ‡ç´šåˆ¥ã€‚使用自定義通知級別,您會收到åƒèˆ‡ç´šåˆ¥åŠé¸å®šäº‹ä»¶çš„通知。想了解更多信æ¯ï¼Œè«‹æŸ¥çœ‹ %{notification_link}."],"Cycle Analytics":["週期分æž"],"Cycle Analytics gives an overview of how much time it takes to go from idea to production in your project.":["週期分æžæ¦‚述了項目從想法到產å“實ç¾çš„å„階段所需的時間。"],"CycleAnalyticsStage|Code":["編碼"],"CycleAnalyticsStage|Issue":["è­°é¡Œ"],"CycleAnalyticsStage|Plan":["計劃"],"CycleAnalyticsStage|Production":["生產"],"CycleAnalyticsStage|Review":["評審"],"CycleAnalyticsStage|Staging":["é ç™¼å¸ƒ"],"CycleAnalyticsStage|Test":["測試"],"Define a custom pattern with cron syntax":["使用 Cron 語法定義自定義模å¼"],"Delete":["刪除"],"Deploy":["部署"],"Description":["æè¿°"],"Directory name":["目錄å稱"],"Don't show again":["ä¸å†é¡¯ç¤º"],"Download":["下載"],"Download tar":["下載 tar"],"Download tar.bz2":["下載 tar.bz2"],"Download tar.gz":["下載 tar.gz"],"Download zip":["下載 zip"],"DownloadArtifacts|Download":["下載"],"DownloadCommit|Email Patches":["é›»å­éƒµä»¶è£œä¸"],"DownloadCommit|Plain Diff":["差異文件"],"DownloadSource|Download":["下載"],"Edit":["編輯"],"Edit Pipeline Schedule %{id}":["編輯 %{id} æµæ°´ç·šè¨ˆåŠƒ"],"Every day (at 4:00am)":["æ¯æ—¥åŸ·è¡Œï¼ˆæ·©æ™¨ 4 點)"],"Every month (on the 1st at 4:00am)":["æ¯æœˆåŸ·è¡Œï¼ˆæ¯æœˆ 1 日淩晨 4 點)"],"Every week (Sundays at 4:00am)":["æ¯é€±åŸ·è¡Œï¼ˆå‘¨æ—¥æ·©æ™¨ 4 點)"],"Failed to change the owner":["無法變更所有者"],"Failed to remove the pipeline schedule":["無法刪除æµæ°´ç·šè¨ˆåŠƒ"],"Files":["文件"],"Find by path":["按路徑查找"],"Find file":["查找文件"],"FirstPushedBy|First":["首次推é€"],"FirstPushedBy|pushed by":["推é€è€…:"],"Fork":["派生"],"ForkedFromProjectPath|Forked from":["派生自"],"From issue creation until deploy to production":["從創建議題到部署到生產環境"],"From merge request merge until deploy to production":["從åˆä½µè«‹æ±‚çš„åˆä½µåˆ°éƒ¨ç½²è‡³ç”Ÿç”¢ç’°å¢ƒ"],"Go to your fork":["跳轉到派生項目"],"GoToYourFork|Fork":["跳轉到派生項目"],"Home":["首é "],"Housekeeping successfully started":["已開始維護"],"Import repository":["導入存儲庫"],"Interval Pattern":["循環週期"],"Introducing Cycle Analytics":["週期分æžç°¡ä»‹"],"LFSStatus|Disabled":["åœç”¨"],"LFSStatus|Enabled":["啟用"],"Last %d day":["最近 %d 天"],"Last Pipeline":["最新æµæ°´ç·š"],"Last Update":["最後更新"],"Last commit":["最後æ交"],"Learn more in the":["了解更多"],"Learn more in the|pipeline schedules documentation":["æµæ°´ç·šè¨ˆåŠƒæ–‡æª”"],"Leave group":["退出群組"],"Leave project":["退出項目"],"Limited to showing %d event at most":["最多顯示 %d 個事件"],"Median":["中ä½æ•¸"],"MissingSSHKeyWarningLink|add an SSH key":["添加壹個 SSH 公鑰"],"New Issue":["新建議題"],"New Pipeline Schedule":["創建æµæ°´ç·šè¨ˆåŠƒ"],"New branch":["新增分支"],"New directory":["新增目錄"],"New file":["新增文件"],"New issue":["æ–°è­°é¡Œ"],"New merge request":["新增åˆä½µè«‹æ±‚"],"New schedule":["新增计划"],"New snippet":["新代碼片段"],"New tag":["新增標籤"],"No repository":["沒有存儲庫"],"No schedules":["沒有計劃"],"Not available":["ä¸å¯ç”¨"],"Not enough data":["數據ä¸è¶³"],"Notification events":["通知事件"],"NotificationEvent|Close issue":["關閉議題"],"NotificationEvent|Close merge request":["關閉åˆä½µè«‹æ±‚"],"NotificationEvent|Failed pipeline":["æµæ°´ç·šå¤±æ•—"],"NotificationEvent|Merge merge request":["åˆä½µè«‹æ±‚被åˆä½µ"],"NotificationEvent|New issue":["新增議題"],"NotificationEvent|New merge request":["æ–°åˆä½µè«‹æ±‚"],"NotificationEvent|New note":["新增評論"],"NotificationEvent|Reassign issue":["é‡æ–°æŒ‡æ´¾è­°é¡Œ"],"NotificationEvent|Reassign merge request":["é‡æ–°æŒ‡æ´¾åˆä½µè«‹æ±‚"],"NotificationEvent|Reopen issue":["é‡å•Ÿè­°é¡Œ"],"NotificationEvent|Successful pipeline":["æµæ°´ç·šæˆåŠŸå®Œæˆ"],"NotificationLevel|Custom":["自定義"],"NotificationLevel|Disabled":["åœç”¨"],"NotificationLevel|Global":["全局"],"NotificationLevel|On mention":["æåŠ"],"NotificationLevel|Participate":["åƒèˆ‡"],"NotificationLevel|Watch":["關注"],"OfSearchInADropdown|Filter":["篩é¸"],"OpenedNDaysAgo|Opened":["開始於"],"Options":["æ“作"],"Owner":["所有者"],"Pipeline":["æµæ°´ç·š"],"Pipeline Health":["æµæ°´ç·šå¥åº·æŒ‡æ¨™"],"Pipeline Schedule":["æµæ°´ç·šè¨ˆåŠƒ"],"Pipeline Schedules":["æµæ°´ç·šè¨ˆåŠƒ"],"PipelineSchedules|Activated":["是å¦å•Ÿç”¨"],"PipelineSchedules|Active":["已啟用"],"PipelineSchedules|All":["所有"],"PipelineSchedules|Inactive":["未啟用"],"PipelineSchedules|Next Run":["下次é‹è¡Œæ™‚é–“"],"PipelineSchedules|None":["ç„¡"],"PipelineSchedules|Provide a short description for this pipeline":["為此æµæ°´ç·šæ供簡短æè¿°"],"PipelineSchedules|Take ownership":["å–得所有者"],"PipelineSchedules|Target":["目標"],"PipelineSheduleIntervalPattern|Custom":["自定義"],"Pipeline|with stage":["於階段"],"Pipeline|with stages":["於階段"],"Project '%{project_name}' queued for deletion.":["é …ç›® '%{project_name}' 已進入刪除隊列。"],"Project '%{project_name}' was successfully created.":["é …ç›® '%{project_name}' 已創建æˆåŠŸã€‚"],"Project '%{project_name}' was successfully updated.":["é …ç›® '%{project_name}' 已更新完æˆã€‚"],"Project '%{project_name}' will be deleted.":["é …ç›® '%{project_name}' 將被刪除。"],"Project access must be granted explicitly to each user.":["項目訪å•æ¬Šé™å¿…須明確授權給æ¯å€‹ç”¨æˆ¶ã€‚"],"Project export could not be deleted.":["無法刪除項目導出。"],"Project export has been deleted.":["項目導出已被刪除。"],"Project export link has expired. Please generate a new export from your project settings.":["項目導出éˆæŽ¥å·²éŽæœŸã€‚請從項目設置中é‡æ–°ç”Ÿæˆé …目導出。"],"Project export started. A download link will be sent by email.":["項目導出已開始。下載éˆæŽ¥å°‡é€šéŽé›»å­éƒµä»¶ç™¼é€ã€‚"],"Project home":["項目首é "],"ProjectFeature|Disabled":["åœç”¨"],"ProjectFeature|Everyone with access":["任何人都å¯è¨ªå•"],"ProjectFeature|Only team members":["åªé™åœ˜éšŠæˆå“¡"],"ProjectFileTree|Name":["å稱"],"ProjectLastActivity|Never":["從未"],"ProjectLifecycle|Stage":["階段"],"ProjectNetworkGraph|Graph":["分支圖"],"Read more":["了解更多"],"Readme":["自述文件"],"RefSwitcher|Branches":["分支"],"RefSwitcher|Tags":["標籤"],"Related Commits":["相關的æ交"],"Related Deployed Jobs":["相關的部署作業"],"Related Issues":["相關的議題"],"Related Jobs":["相關的作業"],"Related Merge Requests":["相關的åˆä½µè«‹æ±‚"],"Related Merged Requests":["相關已åˆä½µçš„åˆä½µè«‹æ±‚"],"Remind later":["ç¨å¾Œæ醒"],"Remove project":["刪除項目"],"Request Access":["申請權é™"],"Revert this commit":["還原此æ交"],"Revert this merge request":["還原此åˆä½µè«‹æ±‚"],"Save pipeline schedule":["ä¿å­˜æµæ°´ç·šè¨ˆåŠƒ"],"Schedule a new pipeline":["新建æµæ°´ç·šè¨ˆåŠƒ"],"Scheduling Pipelines":["æµæ°´ç·šè¨ˆåŠƒ"],"Search branches and tags":["æœç´¢åˆ†æ”¯å’Œæ¨™ç±¤"],"Select Archive Format":["é¸æ“‡ä¸‹è¼‰æ ¼å¼"],"Select a timezone":["é¸æ“‡æ™‚å€"],"Select target branch":["é¸æ“‡ç›®æ¨™åˆ†æ”¯"],"Set a password on your account to pull or push via %{protocol}":["為賬號添加壹個用於推é€æˆ–拉å–çš„ %{protocol} 密碼。"],"Set up CI":["設置 CI"],"Set up Koding":["設置 Koding"],"Set up auto deploy":["設置自動部署"],"SetPasswordToCloneLink|set a password":["設置密碼"],"Showing %d event":["顯示 %d 個事件"],"Source code":["æºä»£ç¢¼"],"StarProject|Star":["星標"],"Start a %{new_merge_request} with these changes":["由此更改 %{new_merge_request}"],"Switch branch/tag":["切æ›åˆ†æ”¯/標籤"],"Tag":["標籤"],"Tags":["標籤"],"Target Branch":["目標分支"],"The coding stage shows the time from the first commit to creating the merge request. The data will automatically be added here once you create your first merge request.":["編碼階段概述了從第壹次æ交到創建åˆä½µè«‹æ±‚的時間。創建第壹個åˆä½µè«‹æ±‚後,數據將自動添加到此處。"],"The collection of events added to the data gathered for that stage.":["與該階段相關的事件。"],"The fork relationship has been removed.":["派生關係已被刪除。"],"The issue stage shows the time it takes from creating an issue to assigning the issue to a milestone, or add the issue to a list on your Issue Board. Begin creating issues to see data for this stage.":["議題階段概述了從創建議題到將議題添加到è£ç¨‹ç¢‘或議題看æ¿æ‰€èŠ±è²»çš„時間。創建第壹個議題後,數據將自動添加到此處.。"],"The phase of the development lifecycle.":["項目生命週期中的å„個階段。"],"The pipelines schedule runs pipelines in the future, repeatedly, for specific branches or tags. Those scheduled pipelines will inherit limited project access based on their associated user.":["æµæ°´ç·šè¨ˆåŠƒæœƒé€±æœŸæ€§é‡è¤‡é‹è¡ŒæŒ‡å®šåˆ†æ”¯æˆ–標籤的æµæ°´ç·šã€‚這些æµæ°´ç·šå°‡æ ¹æ“šå…¶é—œè¯ç”¨æˆ¶ç¹¼æ‰¿æœ‰é™çš„項目訪å•æ¬Šé™ã€‚"],"The planning stage shows the time from the previous step to pushing your first commit. This time will be added automatically once you push your first commit.":["計劃階段概述了從議題添加到日程到推é€é¦–次æ交的時間。當首次推é€æ交後,數據將自動添加到此處。"],"The production stage shows the total time it takes between creating an issue and deploying the code to production. The data will be automatically added once you have completed the full idea to production cycle.":["生產階段概述了從創建議題到將代碼部署到生產環境的時間。當完æˆå®Œæ•´çš„想法到部署生產,數據將自動添加到此處。"],"The project can be accessed by any logged in user.":["該項目å…許已登錄的用戶訪å•ã€‚"],"The project can be accessed without any authentication.":["該項目å…許任何人訪å•ã€‚"],"The repository for this project does not exist.":["此項目的存儲庫ä¸å­˜åœ¨ã€‚"],"The review stage shows the time from creating the merge request to merging it. The data will automatically be added after you merge your first merge request.":["評審階段概述了從創建åˆä½µè«‹æ±‚到åˆä½µçš„時間。當創建第壹個åˆä½µè«‹æ±‚後,數據將自動添加到此處。"],"The staging stage shows the time between merging the MR and deploying code to the production environment. The data will be automatically added once you deploy to production for the first time.":["é ç™¼å¸ƒéšŽæ®µæ¦‚述了åˆä½µè«‹æ±‚çš„åˆä½µåˆ°éƒ¨ç½²ä»£ç¢¼åˆ°ç”Ÿç”¢ç’°å¢ƒçš„總時間。當首次部署到生產環境後,數據將自動添加到此處。"],"The testing stage shows the time GitLab CI takes to run every pipeline for the related merge request. The data will automatically be added after your first pipeline finishes running.":["測試階段概述了 GitLab CI 為相關åˆä½µè«‹æ±‚é‹è¡Œæ¯å€‹æµæ°´ç·šæ‰€éœ€çš„時間。當第壹個æµæ°´ç·šé‹è¡Œå®Œæˆå¾Œï¼Œæ•¸æ“šå°‡è‡ªå‹•æ·»åŠ åˆ°æ­¤è™•ã€‚"],"The time taken by each data entry gathered by that stage.":["該階段æ¯æ¢æ•¸æ“šæ‰€èŠ±çš„時間"],"The value lying at the midpoint of a series of observed values. E.g., between 3, 5, 9, the median is 5. Between 3, 5, 7, 8, the median is (5+7)/2 = 6.":["中ä½æ•¸æ˜¯å£¹å€‹æ•¸åˆ—中最中間的值。例如在 3ã€5ã€9 之間,中ä½æ•¸æ˜¯ 5。在 3ã€5ã€7ã€8 之間,中ä½æ•¸æ˜¯ (5 + 7)/ 2 = 6。"],"This means you can not push code until you create an empty repository or import existing one.":["在創建壹個空的存儲庫或導入ç¾æœ‰å­˜å„²åº«ä¹‹å‰ï¼Œæ‚¨å°‡ç„¡æ³•æŽ¨é€ä»£ç¢¼ã€‚"],"Time before an issue gets scheduled":["議題被列入日程表的時間"],"Time before an issue starts implementation":["開始進行編碼å‰çš„時間"],"Time between merge request creation and merge/close":["從創建åˆä½µè«‹æ±‚到被åˆä½µæˆ–關閉的時間"],"Time until first merge request":["創建第壹個åˆä½µè«‹æ±‚之å‰çš„時間"],"Timeago|%s days ago":[" %s 天å‰"],"Timeago|%s days remaining":["剩餘 %s 天"],"Timeago|%s hours remaining":["剩餘 %s å°æ™‚"],"Timeago|%s minutes ago":[" %s 分é˜å‰"],"Timeago|%s minutes remaining":["剩餘 %s 分é˜"],"Timeago|%s months ago":[" %s 個月å‰"],"Timeago|%s months remaining":["剩餘 %s 月"],"Timeago|%s seconds remaining":["剩餘 %s 秒"],"Timeago|%s weeks ago":[" %s 星期å‰"],"Timeago|%s weeks remaining":["剩餘 %s 星期"],"Timeago|%s years ago":[" %s å¹´å‰"],"Timeago|%s years remaining":["剩餘 %s å¹´"],"Timeago|1 day remaining":["剩餘 1 天"],"Timeago|1 hour remaining":["剩餘 1 å°æ™‚"],"Timeago|1 minute remaining":["剩餘 1 分é˜"],"Timeago|1 month remaining":["剩餘 1 個月"],"Timeago|1 week remaining":["剩餘 1 星期"],"Timeago|1 year remaining":["剩餘 1 å¹´"],"Timeago|Past due":["逾期"],"Timeago|a day ago":[" 1 天å‰"],"Timeago|a month ago":[" 1 個月å‰"],"Timeago|a week ago":[" 1 星期å‰"],"Timeago|a while":[" 剛剛"],"Timeago|a year ago":[" 1 å¹´å‰"],"Timeago|about %s hours ago":["ç´„ %s å°æ™‚å‰"],"Timeago|about a minute ago":["ç´„ 1 分é˜å‰"],"Timeago|about an hour ago":["ç´„ 1 å°æ™‚å‰"],"Timeago|in %s days":[" %s 天後"],"Timeago|in %s hours":[" %s å°æ™‚後"],"Timeago|in %s minutes":[" %s 分é˜å¾Œ"],"Timeago|in %s months":[" %s 個月後"],"Timeago|in %s seconds":[" %s 秒後"],"Timeago|in %s weeks":[" %s 星期後"],"Timeago|in %s years":[" %s 年後"],"Timeago|in 1 day":[" 1 天後"],"Timeago|in 1 hour":[" 1 å°æ™‚後"],"Timeago|in 1 minute":[" 1 分é˜å¾Œ"],"Timeago|in 1 month":[" 1 月後"],"Timeago|in 1 week":[" 1 星期後"],"Timeago|in 1 year":[" 1 年後"],"Timeago|less than a minute ago":["ä¸åˆ° 1 分é˜å‰"],"Time|hr":["å°æ™‚"],"Time|min":["分é˜"],"Time|s":["秒"],"Total Time":["總時間"],"Total test time for all commits/merges":["所有æ交和åˆä½µçš„總測試時間"],"Unstar":["å–消星標"],"Upload New File":["上傳新文件"],"Upload file":["上傳文件"],"Use your global notification setting":["使用全局通知設置"],"VisibilityLevel|Internal":["內部"],"VisibilityLevel|Private":["ç§æœ‰"],"VisibilityLevel|Public":["公開"],"Want to see the data? Please ask an administrator for access.":["權é™ä¸è¶³ã€‚如需查看相關數據,請å‘管ç†å“¡ç”³è«‹æ¬Šé™ã€‚"],"We don't have enough data to show this stage.":["該階段的數據ä¸è¶³ï¼Œç„¡æ³•é¡¯ç¤ºã€‚"],"Withdraw Access Request":["å–消權é™ç”³è¯·"],"You are going to remove %{project_name_with_namespace}.\\nRemoved project CANNOT be restored!\\nAre you ABSOLUTELY sure?":["å³å°‡è¦åˆªé™¤ %{project_name_with_namespace}。\\n已刪除的項目無法æ¢è¤‡ï¼\\n確定繼續嗎?"],"You are going to remove the fork relationship to source project %{forked_from_project}. Are you ABSOLUTELY sure?":["å³å°‡åˆªé™¤èˆ‡æºé …ç›® %{forked_from_project} 的派生關系。確定繼續嗎?"],"You are going to transfer %{project_name_with_namespace} to another owner. Are you ABSOLUTELY sure?":["å³å°‡ %{project_name_with_namespace} 轉義給å¦å£¹å€‹æ‰€æœ‰è€…。確定繼續嗎?"],"You can only add files when you are on a branch":["åªèƒ½åœ¨åˆ†æ”¯ä¸Šæ·»åŠ æ–‡ä»¶"],"You have reached your project limit":["您已é”到項目數é‡é™åˆ¶"],"You must sign in to star a project":["必須登錄æ‰èƒ½å°é …目加星標"],"You need permission.":["需è¦ç›¸é—œçš„權é™ã€‚"],"You will not get any notifications via email":["ä¸æœƒæ”¶åˆ°ä»»ä½•é€šçŸ¥éƒµä»¶"],"You will only receive notifications for the events you choose":["åªæŽ¥æ”¶æ‚¨é¸æ“‡çš„事件通知"],"You will only receive notifications for threads you have participated in":["åªæŽ¥æ”¶æ‚¨åƒèˆ‡çš„主題的通知"],"You will receive notifications for any activity":["接收所有活動的通知"],"You will receive notifications only for comments in which you were @mentioned":["åªæŽ¥æ”¶è©•è«–中æåŠ(@)您的通知"],"You won't be able to pull or push project code via %{protocol} until you %{set_password_link} on your account":["在賬號上 %{set_password_link} 之å‰å°‡ç„¡æ³•é€šéŽ %{protocol} 拉å–或推é€ä»£ç¢¼ã€‚"],"You won't be able to pull or push project code via SSH until you %{add_ssh_key_link} to your profile":["在賬號中 %{add_ssh_key_link} 之å‰å°‡ç„¡æ³•é€šéŽ SSH 拉å–或推é€ä»£ç¢¼ã€‚"],"Your name":["您的åå­—"],"day":["天"],"new merge request":["新建åˆä½µè«‹æ±‚"],"notification emails":["通知郵件"],"parent":["父級"]}}}; \ No newline at end of file
diff --git a/app/assets/javascripts/milestone.js b/app/assets/javascripts/milestone.js
index 841b24a60a3..3e07ec4d0aa 100644
--- a/app/assets/javascripts/milestone.js
+++ b/app/assets/javascripts/milestone.js
@@ -4,83 +4,7 @@
(function() {
this.Milestone = (function() {
- Milestone.updateIssue = function(li, issue_url, data) {
- return $.ajax({
- type: "PUT",
- url: issue_url,
- data: data,
- success: function(_data) {
- return Milestone.successCallback(_data, li);
- },
- error: function(data) {
- return new Flash("Issue update failed", 'alert');
- },
- dataType: "json"
- });
- };
-
- Milestone.sortIssues = function(url, data) {
- return $.ajax({
- type: "PUT",
- url,
- data: data,
- success: function(_data) {
- return Milestone.successCallback(_data);
- },
- error: function() {
- return new Flash("Issues update failed", 'alert');
- },
- dataType: "json"
- });
- };
-
- Milestone.sortMergeRequests = function(url, data) {
- return $.ajax({
- type: "PUT",
- url,
- data: data,
- success: function(_data) {
- return Milestone.successCallback(_data);
- },
- error: function(data) {
- return new Flash("Issue update failed", 'alert');
- },
- dataType: "json"
- });
- };
-
- Milestone.updateMergeRequest = function(li, merge_request_url, data) {
- return $.ajax({
- type: "PUT",
- url: merge_request_url,
- data: data,
- success: function(_data) {
- return Milestone.successCallback(_data, li);
- },
- error: function(data) {
- return new Flash("Issue update failed", 'alert');
- },
- dataType: "json"
- });
- };
-
- Milestone.successCallback = function(data, element) {
- var img_tag;
- if (data.assignee) {
- img_tag = $('<img/>');
- img_tag.attr('src', data.assignee.avatar_url);
- img_tag.addClass('avatar s16');
- $(element).find('.assignee-icon img').replaceWith(img_tag);
- } else {
- $(element).find('.assignee-icon').empty();
- }
- };
-
function Milestone() {
- this.issuesSortEndpoint = $('#tab-issues').data('sort-endpoint');
- this.mergeRequestsSortEndpoint = $('#tab-merge-requests').data('sort-endpoint');
-
- this.bindIssuesSorting();
this.bindTabsSwitching();
// Load merge request tab if it is active
@@ -90,22 +14,6 @@
this.loadInitialTab();
}
- Milestone.prototype.bindIssuesSorting = function() {
- if (!this.issuesSortEndpoint) return;
-
- $('#issues-list-unassigned, #issues-list-ongoing, #issues-list-closed').each(function (i, el) {
- this.createSortable(el, {
- group: 'issue-list',
- listEls: $('.issues-sortable-list'),
- fieldName: 'issue',
- sortCallback: (data) => {
- Milestone.sortIssues(this.issuesSortEndpoint, data);
- },
- updateCallback: Milestone.updateIssue,
- });
- }.bind(this));
- };
-
Milestone.prototype.bindTabsSwitching = function() {
return $('a[data-toggle="tab"]').on('show.bs.tab', (e) => {
const $target = $(e.target);
@@ -115,69 +23,6 @@
});
};
- Milestone.prototype.bindMergeRequestSorting = function() {
- if (!this.mergeRequestsSortEndpoint) return;
-
- $("#merge_requests-list-unassigned, #merge_requests-list-ongoing, #merge_requests-list-closed").each(function (i, el) {
- this.createSortable(el, {
- group: 'merge-request-list',
- listEls: $(".merge_requests-sortable-list:not(#merge_requests-list-merged)"),
- fieldName: 'merge_request',
- sortCallback: (data) => {
- Milestone.sortMergeRequests(this.mergeRequestsSortEndpoint, data);
- },
- updateCallback: Milestone.updateMergeRequest,
- });
- }.bind(this));
- };
-
- Milestone.prototype.createSortable = function(el, opts) {
- return Sortable.create(el, {
- group: opts.group,
- filter: '.is-disabled',
- forceFallback: true,
- onStart: function(e) {
- opts.listEls.css('min-height', e.item.offsetHeight);
- },
- onEnd: function () {
- opts.listEls.css("min-height", "0px");
- },
- onUpdate: function(e) {
- var ids = this.toArray(),
- data;
-
- if (ids.length) {
- data = ids.map(function(id) {
- return 'sortable_' + opts.fieldName + '[]=' + id;
- }).join('&');
-
- opts.sortCallback(data);
- }
- },
- onAdd: function (e) {
- var data, issuableId, issuableUrl, newState;
- newState = e.to.dataset.state;
- issuableUrl = e.item.dataset.url;
- data = (function() {
- switch (newState) {
- case 'ongoing':
- return opts.fieldName + '[assignee_id]=' + gon.current_user_id;
- case 'unassigned':
- return opts.fieldName + '[assignee_id]=';
- case 'closed':
- return opts.fieldName + '[state_event]=close';
- }
- })();
- if (e.from.dataset.state === 'closed') {
- data += '&' + opts.fieldName + '[state_event]=reopen';
- }
-
- opts.updateCallback(e.item, issuableUrl, data);
- this.options.onUpdate.call(this, e);
- }
- });
- };
-
Milestone.prototype.loadInitialTab = function() {
const $target = $(`.js-milestone-tabs a[href="${location.hash}"]`);
@@ -199,10 +44,6 @@
.done((data) => {
$(tabElId).html(data.html);
$target.addClass('is-loaded');
-
- if (tabElId === '#tab-merge-requests') {
- this.bindMergeRequestSorting();
- }
});
}
};
diff --git a/app/assets/javascripts/notes.js b/app/assets/javascripts/notes.js
index b0143b12cfe..f12a35f0485 100644
--- a/app/assets/javascripts/notes.js
+++ b/app/assets/javascripts/notes.js
@@ -32,7 +32,7 @@ const normalizeNewlines = function(str) {
(function() {
this.Notes = (function() {
const MAX_VISIBLE_COMMIT_LIST_COUNT = 3;
- const REGEX_SLASH_COMMANDS = /^\/\w+.*$/gm;
+ const REGEX_QUICK_ACTIONS = /^\/\w+.*$/gm;
Notes.interval = null;
@@ -56,6 +56,7 @@ const normalizeNewlines = function(str) {
this.toggleCommitList = this.toggleCommitList.bind(this);
this.postComment = this.postComment.bind(this);
this.clearFlashWrapper = this.clearFlash.bind(this);
+ this.onHashChange = this.onHashChange.bind(this);
this.notes_url = notes_url;
this.note_ids = note_ids;
@@ -127,7 +128,9 @@ const normalizeNewlines = function(str) {
$(document).on('ajax:success', '.js-main-target-form', this.resetMainTargetForm);
$(document).on('ajax:complete', '.js-main-target-form', this.reenableTargetFormSubmitButton);
// when a key is clicked on the notes
- return $(document).on('keydown', '.js-note-text', this.keydownNoteText);
+ $(document).on('keydown', '.js-note-text', this.keydownNoteText);
+ // When the URL fragment/hash has changed, `#note_xxx`
+ return $(window).on('hashchange', this.onHashChange);
};
Notes.prototype.cleanBinding = function() {
@@ -148,6 +151,7 @@ const normalizeNewlines = function(str) {
$(document).off('ajax:success', '.js-main-target-form');
$(document).off('ajax:success', '.js-discussion-note-form');
$(document).off('ajax:complete', '.js-main-target-form');
+ $(window).off('hashchange', this.onHashChange);
};
Notes.initCommentTypeToggle = function (form) {
@@ -280,7 +284,7 @@ const normalizeNewlines = function(str) {
return this.initRefresh();
};
- Notes.prototype.handleSlashCommands = function(noteEntity) {
+ Notes.prototype.handleQuickActions = function(noteEntity) {
var votesBlock;
if (noteEntity.commands_changes) {
if ('merge' in noteEntity.commands_changes) {
@@ -298,8 +302,27 @@ const normalizeNewlines = function(str) {
Notes.prototype.setupNewNote = function($note) {
// Update datetime format on the recent note
gl.utils.localTimeAgo($note.find('.js-timeago'), false);
+
this.collapseLongCommitList();
this.taskList.init();
+
+ // This stops the note highlight, #note_xxx`, from being removed after real time update
+ // The `:target` selector does not re-evaluate after we replace element in the DOM
+ Notes.updateNoteTargetSelector($note);
+ this.$noteToCleanHighlight = $note;
+ };
+
+ Notes.prototype.onHashChange = function() {
+ if (this.$noteToCleanHighlight) {
+ Notes.updateNoteTargetSelector(this.$noteToCleanHighlight);
+ }
+
+ this.$noteToCleanHighlight = null;
+ };
+
+ Notes.updateNoteTargetSelector = function($note) {
+ const hash = gl.utils.getLocationHash();
+ $note.toggleClass('target', hash && $note.filter(`#${hash}`).length > 0);
};
/*
@@ -597,13 +620,12 @@ const normalizeNewlines = function(str) {
$noteEntityEl = $(noteEntity.html);
$noteEntityEl.addClass('fade-in-full');
this.revertNoteEditForm($targetNote);
- gl.utils.localTimeAgo($('.js-timeago', $noteEntityEl));
$noteEntityEl.renderGFM();
- $noteEntityEl.find('.js-task-list-container').taskList('enable');
// Find the note's `li` element by ID and replace it with the updated HTML
$note_li = $('.note-row-' + noteEntity.id);
$note_li.replaceWith($noteEntityEl);
+ this.setupNewNote($noteEntityEl);
if (typeof gl.diffNotesCompileComponents !== 'undefined') {
gl.diffNotesCompileComponents();
@@ -1060,7 +1082,7 @@ const normalizeNewlines = function(str) {
var targetId = $originalContentEl.data('target-id');
var targetType = $originalContentEl.data('target-type');
- new gl.GLForm($editForm.find('form'));
+ new gl.GLForm($editForm.find('form'), this.enableGFM);
$editForm.find('form')
.attr('action', postUrl)
@@ -1198,27 +1220,27 @@ const normalizeNewlines = function(str) {
};
/**
- * Identify if comment has any slash commands
+ * Identify if comment has any quick actions
*/
- Notes.prototype.hasSlashCommands = function(formContent) {
- return REGEX_SLASH_COMMANDS.test(formContent);
+ Notes.prototype.hasQuickActions = function(formContent) {
+ return REGEX_QUICK_ACTIONS.test(formContent);
};
/**
- * Remove slash commands and leave comment with pure message
+ * Remove quick actions and leave comment with pure message
*/
- Notes.prototype.stripSlashCommands = function(formContent) {
- return formContent.replace(REGEX_SLASH_COMMANDS, '').trim();
+ Notes.prototype.stripQuickActions = function(formContent) {
+ return formContent.replace(REGEX_QUICK_ACTIONS, '').trim();
};
/**
- * Gets appropriate description from slash commands found in provided `formContent`
+ * Gets appropriate description from quick actions found in provided `formContent`
*/
- Notes.prototype.getSlashCommandDescription = function (formContent, availableSlashCommands = []) {
+ Notes.prototype.getQuickActionDescription = function (formContent, availableQuickActions = []) {
let tempFormContent;
- // Identify executed slash commands from `formContent`
- const executedCommands = availableSlashCommands.filter((command, index) => {
+ // Identify executed quick actions from `formContent`
+ const executedCommands = availableQuickActions.filter((command, index) => {
const commandRegex = new RegExp(`/${command.name}`);
return commandRegex.test(formContent);
});
@@ -1276,7 +1298,7 @@ const normalizeNewlines = function(str) {
};
/**
- * Create Placeholder System Note DOM element populated with slash command description
+ * Create Placeholder System Note DOM element populated with quick action description
*/
Notes.prototype.createPlaceholderSystemNote = function ({ formContent, uniqueId }) {
const $tempNote = $(
@@ -1325,7 +1347,7 @@ const normalizeNewlines = function(str) {
const { formData, formContent, formAction } = this.getFormData($form);
let noteUniqueId;
let systemNoteUniqueId;
- let hasSlashCommands = false;
+ let hasQuickActions = false;
let $notesContainer;
let tempFormContent;
@@ -1344,9 +1366,9 @@ const normalizeNewlines = function(str) {
}
tempFormContent = formContent;
- if (this.hasSlashCommands(formContent)) {
- tempFormContent = this.stripSlashCommands(formContent);
- hasSlashCommands = true;
+ if (this.hasQuickActions(formContent)) {
+ tempFormContent = this.stripQuickActions(formContent);
+ hasQuickActions = true;
}
// Show placeholder note
@@ -1363,10 +1385,10 @@ const normalizeNewlines = function(str) {
}
// Show placeholder system note
- if (hasSlashCommands) {
+ if (hasQuickActions) {
systemNoteUniqueId = _.uniqueId('tempSystemNote_');
$notesContainer.append(this.createPlaceholderSystemNote({
- formContent: this.getSlashCommandDescription(formContent, AjaxCache.get(gl.GfmAutoComplete.dataSources.commands)),
+ formContent: this.getQuickActionDescription(formContent, AjaxCache.get(gl.GfmAutoComplete.dataSources.commands)),
uniqueId: systemNoteUniqueId,
}));
}
@@ -1388,7 +1410,7 @@ const normalizeNewlines = function(str) {
$notesContainer.find(`#${noteUniqueId}`).remove();
// Reset cached commands list when command is applied
- if (hasSlashCommands) {
+ if (hasQuickActions) {
$form.find('textarea.js-note-text').trigger('clear-commands-cache.atwho');
}
@@ -1422,7 +1444,7 @@ const normalizeNewlines = function(str) {
}
if (note.commands_changes) {
- this.handleSlashCommands(note);
+ this.handleQuickActions(note);
}
$form.trigger('ajax:success', [note]);
@@ -1430,7 +1452,7 @@ const normalizeNewlines = function(str) {
// Submission failed, remove placeholder note and show Flash error message
$notesContainer.find(`#${noteUniqueId}`).remove();
- if (hasSlashCommands) {
+ if (hasQuickActions) {
$notesContainer.find(`#${systemNoteUniqueId}`).remove();
}
diff --git a/app/assets/javascripts/pipeline_schedules/components/interval_pattern_input.js b/app/assets/javascripts/pipeline_schedules/components/interval_pattern_input.js
index 4d623763ca7..901adbe9fce 100644
--- a/app/assets/javascripts/pipeline_schedules/components/interval_pattern_input.js
+++ b/app/assets/javascripts/pipeline_schedules/components/interval_pattern_input.js
@@ -1,4 +1,7 @@
import Vue from 'vue';
+import Translate from '../../vue_shared/translate';
+
+Vue.use(Translate);
const inputNameAttribute = 'schedule[cron]';
@@ -72,11 +75,11 @@ export default {
/>
<label for="custom">
- Custom
+ {{ s__('PipelineSheduleIntervalPattern|Custom') }}
</label>
<span class="cron-syntax-link-wrap">
- (<a :href="cronSyntaxUrl" target="_blank">Cron syntax</a>)
+ (<a :href="cronSyntaxUrl" target="_blank">{{ __('Cron syntax') }}</a>)
</span>
</div>
@@ -92,7 +95,7 @@ export default {
/>
<label class="label-light" for="every-day">
- Every day (at 4:00am)
+ {{ __('Every day (at 4:00am)') }}
</label>
</div>
@@ -108,7 +111,7 @@ export default {
/>
<label class="label-light" for="every-week">
- Every week (Sundays at 4:00am)
+ {{ __('Every week (Sundays at 4:00am)') }}
</label>
</div>
@@ -124,7 +127,7 @@ export default {
/>
<label class="label-light" for="every-month">
- Every month (on the 1st at 4:00am)
+ {{ __('Every month (on the 1st at 4:00am)') }}
</label>
</div>
@@ -133,7 +136,7 @@ export default {
id="schedule_cron"
class="form-control inline cron-interval-input"
type="text"
- placeholder="Define a custom pattern with cron syntax"
+ :placeholder="__('Define a custom pattern with cron syntax')"
required="true"
v-model="cronInterval"
:name="inputNameAttribute"
diff --git a/app/assets/javascripts/pipeline_schedules/components/pipeline_schedules_callout.js b/app/assets/javascripts/pipeline_schedules/components/pipeline_schedules_callout.js
index 5109b110b31..c827b7402dc 100644
--- a/app/assets/javascripts/pipeline_schedules/components/pipeline_schedules_callout.js
+++ b/app/assets/javascripts/pipeline_schedules/components/pipeline_schedules_callout.js
@@ -1,6 +1,10 @@
+import Vue from 'vue';
import Cookies from 'js-cookie';
+import Translate from '../../vue_shared/translate';
import illustrationSvg from '../icons/intro_illustration.svg';
+Vue.use(Translate);
+
const cookieKey = 'pipeline_schedules_callout_dismissed';
export default {
@@ -29,20 +33,18 @@ export default {
</button>
<div class="svg-container" v-html="illustrationSvg"></div>
<div class="user-callout-copy">
- <h4>Scheduling Pipelines</h4>
+ <h4>{{ __('Scheduling Pipelines') }}</h4>
<p>
- The pipelines schedule runs pipelines in the future, repeatedly, for specific branches or tags.
- Those scheduled pipelines will inherit limited project access based on their associated user.
+ {{ __('The pipelines schedule runs pipelines in the future, repeatedly, for specific branches or tags. Those scheduled pipelines will inherit limited project access based on their associated user.') }}
</p>
- <p> Learn more in the
+ <p> {{ __('Learn more in the') }}
<a
:href="docsUrl"
target="_blank"
- rel="nofollow">pipeline schedules documentation</a>. <!-- oneline to prevent extra space before period -->
+ rel="nofollow">{{ s__('Learn more in the|pipeline schedules documentation') }}</a>. <!-- oneline to prevent extra space before period -->
</p>
</div>
</div>
</div>
`,
};
-
diff --git a/app/assets/javascripts/pipelines/components/pipeline_url.vue b/app/assets/javascripts/pipelines/components/pipeline_url.vue
index 4781a8ff1da..8333ec0fbc3 100644
--- a/app/assets/javascripts/pipelines/components/pipeline_url.vue
+++ b/app/assets/javascripts/pipelines/components/pipeline_url.vue
@@ -23,7 +23,7 @@ export default {
};
</script>
<template>
- <td>
+ <div class="table-section section-15 hidden-xs hidden-sm">
<a
:href="pipeline.path"
class="js-pipeline-url-link">
@@ -42,24 +42,26 @@ export default {
class="js-pipeline-url-api api">
API
</span>
- <span
- v-if="pipeline.flags.latest"
- class="js-pipeline-url-lastest label label-success"
- title="Latest pipeline for this branch"
- ref="tooltip">
- latest
- </span>
- <span
- v-if="pipeline.flags.yaml_errors"
- class="js-pipeline-url-yaml label label-danger"
- :title="pipeline.yaml_errors"
- ref="tooltip">
- yaml invalid
- </span>
- <span
- v-if="pipeline.flags.stuck"
- class="js-pipeline-url-stuck label label-warning">
- stuck
- </span>
- </td>
+ <div class="label-container">
+ <span
+ v-if="pipeline.flags.latest"
+ class="js-pipeline-url-latest label label-success"
+ title="Latest pipeline for this branch"
+ ref="tooltip">
+ latest
+ </span>
+ <span
+ v-if="pipeline.flags.yaml_errors"
+ class="js-pipeline-url-yaml label label-danger"
+ :title="pipeline.yaml_errors"
+ ref="tooltip">
+ yaml invalid
+ </span>
+ <span
+ v-if="pipeline.flags.stuck"
+ class="js-pipeline-url-stuck label label-warning">
+ stuck
+ </span>
+ </div>
+ </div>
</template>
diff --git a/app/assets/javascripts/pipelines/components/pipelines.vue b/app/assets/javascripts/pipelines/components/pipelines.vue
new file mode 100644
index 00000000000..fed42d23112
--- /dev/null
+++ b/app/assets/javascripts/pipelines/components/pipelines.vue
@@ -0,0 +1,289 @@
+<script>
+ import Visibility from 'visibilityjs';
+ import PipelinesService from '../services/pipelines_service';
+ import eventHub from '../event_hub';
+ import pipelinesTableComponent from '../../vue_shared/components/pipelines_table.vue';
+ import tablePagination from '../../vue_shared/components/table_pagination.vue';
+ import emptyState from './empty_state.vue';
+ import errorState from './error_state.vue';
+ import navigationTabs from './navigation_tabs.vue';
+ import navigationControls from './nav_controls.vue';
+ import loadingIcon from '../../vue_shared/components/loading_icon.vue';
+ import Poll from '../../lib/utils/poll';
+
+ export default {
+ props: {
+ store: {
+ type: Object,
+ required: true,
+ },
+ },
+ components: {
+ tablePagination,
+ pipelinesTableComponent,
+ emptyState,
+ errorState,
+ navigationTabs,
+ navigationControls,
+ loadingIcon,
+ },
+ data() {
+ const pipelinesData = document.querySelector('#pipelines-list-vue').dataset;
+
+ return {
+ endpoint: pipelinesData.endpoint,
+ cssClass: pipelinesData.cssClass,
+ helpPagePath: pipelinesData.helpPagePath,
+ newPipelinePath: pipelinesData.newPipelinePath,
+ canCreatePipeline: pipelinesData.canCreatePipeline,
+ allPath: pipelinesData.allPath,
+ pendingPath: pipelinesData.pendingPath,
+ runningPath: pipelinesData.runningPath,
+ finishedPath: pipelinesData.finishedPath,
+ branchesPath: pipelinesData.branchesPath,
+ tagsPath: pipelinesData.tagsPath,
+ hasCi: pipelinesData.hasCi,
+ ciLintPath: pipelinesData.ciLintPath,
+ state: this.store.state,
+ apiScope: 'all',
+ pagenum: 1,
+ isLoading: false,
+ hasError: false,
+ isMakingRequest: false,
+ updateGraphDropdown: false,
+ hasMadeRequest: false,
+ };
+ },
+ computed: {
+ canCreatePipelineParsed() {
+ return gl.utils.convertPermissionToBoolean(this.canCreatePipeline);
+ },
+ scope() {
+ const scope = gl.utils.getParameterByName('scope');
+ return scope === null ? 'all' : scope;
+ },
+ shouldRenderErrorState() {
+ return this.hasError && !this.isLoading;
+ },
+
+ /**
+ * The empty state should only be rendered when the request is made to fetch all pipelines
+ * and none is returned.
+ *
+ * @return {Boolean}
+ */
+ shouldRenderEmptyState() {
+ return !this.isLoading &&
+ !this.hasError &&
+ this.hasMadeRequest &&
+ !this.state.pipelines.length &&
+ (this.scope === 'all' || this.scope === null);
+ },
+ /**
+ * When a specific scope does not have pipelines we render a message.
+ *
+ * @return {Boolean}
+ */
+ shouldRenderNoPipelinesMessage() {
+ return !this.isLoading &&
+ !this.hasError &&
+ !this.state.pipelines.length &&
+ this.scope !== 'all' &&
+ this.scope !== null;
+ },
+
+ shouldRenderTable() {
+ return !this.hasError &&
+ !this.isLoading && this.state.pipelines.length;
+ },
+ /**
+ * Pagination should only be rendered when there is more than one page.
+ *
+ * @return {Boolean}
+ */
+ shouldRenderPagination() {
+ return !this.isLoading &&
+ this.state.pipelines.length &&
+ this.state.pageInfo.total > this.state.pageInfo.perPage;
+ },
+
+ hasCiEnabled() {
+ return this.hasCi !== undefined;
+ },
+ paths() {
+ return {
+ allPath: this.allPath,
+ pendingPath: this.pendingPath,
+ finishedPath: this.finishedPath,
+ runningPath: this.runningPath,
+ branchesPath: this.branchesPath,
+ tagsPath: this.tagsPath,
+ };
+ },
+ pageParameter() {
+ return gl.utils.getParameterByName('page') || this.pagenum;
+ },
+ scopeParameter() {
+ return gl.utils.getParameterByName('scope') || this.apiScope;
+ },
+ },
+ created() {
+ this.service = new PipelinesService(this.endpoint);
+
+ const poll = new Poll({
+ resource: this.service,
+ method: 'getPipelines',
+ data: { page: this.pageParameter, scope: this.scopeParameter },
+ successCallback: this.successCallback,
+ errorCallback: this.errorCallback,
+ notificationCallback: this.setIsMakingRequest,
+ });
+
+ if (!Visibility.hidden()) {
+ this.isLoading = true;
+ poll.makeRequest();
+ } else {
+ // If tab is not visible we need to make the first request so we don't show the empty
+ // state without knowing if there are any pipelines
+ this.fetchPipelines();
+ }
+
+ Visibility.change(() => {
+ if (!Visibility.hidden()) {
+ poll.restart();
+ } else {
+ poll.stop();
+ }
+ });
+
+ eventHub.$on('refreshPipelines', this.fetchPipelines);
+ },
+ beforeDestroy() {
+ eventHub.$off('refreshPipelines');
+ },
+ methods: {
+ /**
+ * Will change the page number and update the URL.
+ *
+ * @param {Number} pageNumber desired page to go to.
+ */
+ change(pageNumber) {
+ const param = gl.utils.setParamInURL('page', pageNumber);
+
+ gl.utils.visitUrl(param);
+ return param;
+ },
+
+ fetchPipelines() {
+ if (!this.isMakingRequest) {
+ this.isLoading = true;
+
+ this.service.getPipelines({ scope: this.scopeParameter, page: this.pageParameter })
+ .then(response => this.successCallback(response))
+ .catch(() => this.errorCallback());
+ }
+ },
+ successCallback(resp) {
+ const response = {
+ headers: resp.headers,
+ body: resp.json(),
+ };
+
+ this.store.storeCount(response.body.count);
+ this.store.storePipelines(response.body.pipelines);
+ this.store.storePagination(response.headers);
+
+ this.isLoading = false;
+ this.updateGraphDropdown = true;
+ this.hasMadeRequest = true;
+ },
+
+ errorCallback() {
+ this.hasError = true;
+ this.isLoading = false;
+ this.updateGraphDropdown = false;
+ },
+
+ setIsMakingRequest(isMakingRequest) {
+ this.isMakingRequest = isMakingRequest;
+
+ if (isMakingRequest) {
+ this.updateGraphDropdown = false;
+ }
+ },
+ },
+ };
+</script>
+<template>
+ <div :class="cssClass">
+
+ <div
+ class="top-area scrolling-tabs-container inner-page-scroll-tabs"
+ v-if="!isLoading && !shouldRenderEmptyState">
+ <div class="fade-left">
+ <i
+ class="fa fa-angle-left"
+ aria-hidden="true">
+ </i>
+ </div>
+ <div class="fade-right">
+ <i
+ class="fa fa-angle-right"
+ aria-hidden="true">
+ </i>
+ </div>
+ <navigation-tabs
+ :scope="scope"
+ :count="state.count"
+ :paths="paths"
+ />
+
+ <navigation-controls
+ :new-pipeline-path="newPipelinePath"
+ :has-ci-enabled="hasCiEnabled"
+ :help-page-path="helpPagePath"
+ :ciLintPath="ciLintPath"
+ :can-create-pipeline="canCreatePipelineParsed "
+ />
+ </div>
+
+ <div class="content-list pipelines">
+
+ <loading-icon
+ label="Loading Pipelines"
+ size="3"
+ v-if="isLoading"
+ />
+
+ <empty-state
+ v-if="shouldRenderEmptyState"
+ :help-page-path="helpPagePath"
+ />
+
+ <error-state v-if="shouldRenderErrorState" />
+
+ <div
+ class="blank-state blank-state-no-icon"
+ v-if="shouldRenderNoPipelinesMessage">
+ <h2 class="blank-state-title js-blank-state-title">No pipelines to show.</h2>
+ </div>
+
+ <div
+ class="table-holder"
+ v-if="shouldRenderTable">
+
+ <pipelines-table-component
+ :pipelines="state.pipelines"
+ :service="service"
+ :update-graph-dropdown="updateGraphDropdown"
+ />
+ </div>
+
+ <table-pagination
+ v-if="shouldRenderPagination"
+ :change="change"
+ :pageInfo="state.pageInfo"
+ />
+ </div>
+ </div>
+</template>
diff --git a/app/assets/javascripts/pipelines/components/pipelines_actions.js b/app/assets/javascripts/pipelines/components/pipelines_actions.js
deleted file mode 100644
index b9e066c5db1..00000000000
--- a/app/assets/javascripts/pipelines/components/pipelines_actions.js
+++ /dev/null
@@ -1,91 +0,0 @@
-/* eslint-disable no-new */
-/* global Flash */
-import '~/flash';
-import playIconSvg from 'icons/_icon_play.svg';
-import eventHub from '../event_hub';
-import loadingIconComponent from '../../vue_shared/components/loading_icon.vue';
-
-export default {
- props: {
- actions: {
- type: Array,
- required: true,
- },
-
- service: {
- type: Object,
- required: true,
- },
- },
-
- components: {
- loadingIconComponent,
- },
-
- data() {
- return {
- playIconSvg,
- isLoading: false,
- };
- },
-
- methods: {
- onClickAction(endpoint) {
- this.isLoading = true;
-
- $(this.$refs.tooltip).tooltip('destroy');
-
- this.service.postAction(endpoint)
- .then(() => {
- this.isLoading = false;
- eventHub.$emit('refreshPipelines');
- })
- .catch(() => {
- this.isLoading = false;
- new Flash('An error occured while making the request.');
- });
- },
-
- isActionDisabled(action) {
- if (action.playable === undefined) {
- return false;
- }
-
- return !action.playable;
- },
- },
-
- template: `
- <div class="btn-group" v-if="actions">
- <button
- type="button"
- class="dropdown-toggle btn btn-default has-tooltip js-pipeline-dropdown-manual-actions"
- title="Manual job"
- data-toggle="dropdown"
- data-placement="top"
- aria-label="Manual job"
- ref="tooltip"
- :disabled="isLoading">
- ${playIconSvg}
- <i
- class="fa fa-caret-down"
- aria-hidden="true" />
- <loading-icon v-if="isLoading" />
- </button>
-
- <ul class="dropdown-menu dropdown-menu-align-right">
- <li v-for="action in actions">
- <button
- type="button"
- class="js-pipeline-action-link no-btn btn"
- @click="onClickAction(action.path)"
- :class="{ 'disabled': isActionDisabled(action) }"
- :disabled="isActionDisabled(action)">
- ${playIconSvg}
- <span>{{action.name}}</span>
- </button>
- </li>
- </ul>
- </div>
- `,
-};
diff --git a/app/assets/javascripts/pipelines/components/pipelines_actions.vue b/app/assets/javascripts/pipelines/components/pipelines_actions.vue
new file mode 100644
index 00000000000..97b4de26214
--- /dev/null
+++ b/app/assets/javascripts/pipelines/components/pipelines_actions.vue
@@ -0,0 +1,88 @@
+<script>
+ /* global Flash */
+ import '~/flash';
+ import playIconSvg from 'icons/_icon_play.svg';
+ import eventHub from '../event_hub';
+ import loadingIcon from '../../vue_shared/components/loading_icon.vue';
+
+ export default {
+ props: {
+ actions: {
+ type: Array,
+ required: true,
+ },
+ service: {
+ type: Object,
+ required: true,
+ },
+ },
+ components: {
+ loadingIcon,
+ },
+ data() {
+ return {
+ playIconSvg,
+ isLoading: false,
+ };
+ },
+ methods: {
+ onClickAction(endpoint) {
+ this.isLoading = true;
+
+ $(this.$refs.tooltip).tooltip('destroy');
+
+ this.service.postAction(endpoint)
+ .then(() => {
+ this.isLoading = false;
+ eventHub.$emit('refreshPipelines');
+ })
+ .catch(() => {
+ this.isLoading = false;
+ // eslint-disable-next-line no-new
+ new Flash('An error occured while making the request.');
+ });
+ },
+ isActionDisabled(action) {
+ if (action.playable === undefined) {
+ return false;
+ }
+
+ return !action.playable;
+ },
+ },
+ };
+</script>
+<template>
+ <div class="btn-group">
+ <button
+ type="button"
+ class="dropdown-new btn btn-default has-tooltip js-pipeline-dropdown-manual-actions"
+ title="Manual job"
+ data-toggle="dropdown"
+ data-placement="top"
+ aria-label="Manual job"
+ ref="tooltip"
+ :disabled="isLoading">
+ <span v-html="playIconSvg"></span>
+ <i
+ class="fa fa-caret-down"
+ aria-hidden="true">
+ </i>
+ <loading-icon v-if="isLoading" />
+ </button>
+
+ <ul class="dropdown-menu dropdown-menu-align-right">
+ <li v-for="action in actions">
+ <button
+ type="button"
+ class="js-pipeline-action-link no-btn btn"
+ @click="onClickAction(action.path)"
+ :class="{ disabled: isActionDisabled(action) }"
+ :disabled="isActionDisabled(action)">
+ <span v-html="playIconSvg"></span>
+ <span>{{action.name}}</span>
+ </button>
+ </li>
+ </ul>
+ </div>
+</template>
diff --git a/app/assets/javascripts/pipelines/components/pipelines_artifacts.js b/app/assets/javascripts/pipelines/components/pipelines_artifacts.js
deleted file mode 100644
index f18e2dfadaf..00000000000
--- a/app/assets/javascripts/pipelines/components/pipelines_artifacts.js
+++ /dev/null
@@ -1,33 +0,0 @@
-export default {
- props: {
- artifacts: {
- type: Array,
- required: true,
- },
- },
-
- template: `
- <div class="btn-group" role="group">
- <button
- class="dropdown-toggle btn btn-default build-artifacts has-tooltip js-pipeline-dropdown-download"
- title="Artifacts"
- data-placement="top"
- data-toggle="dropdown"
- aria-label="Artifacts">
- <i class="fa fa-download" aria-hidden="true"></i>
- <i class="fa fa-caret-down" aria-hidden="true"></i>
- </button>
- <ul class="dropdown-menu dropdown-menu-align-right">
- <li v-for="artifact in artifacts">
- <a
- rel="nofollow"
- download
- :href="artifact.path">
- <i class="fa fa-download" aria-hidden="true"></i>
- <span>Download {{artifact.name}} artifacts</span>
- </a>
- </li>
- </ul>
- </div>
- `,
-};
diff --git a/app/assets/javascripts/pipelines/components/pipelines_artifacts.vue b/app/assets/javascripts/pipelines/components/pipelines_artifacts.vue
new file mode 100644
index 00000000000..b4520481cdc
--- /dev/null
+++ b/app/assets/javascripts/pipelines/components/pipelines_artifacts.vue
@@ -0,0 +1,51 @@
+<script>
+ import tooltipMixin from '../../vue_shared/mixins/tooltip';
+
+ export default {
+ props: {
+ artifacts: {
+ type: Array,
+ required: true,
+ },
+ },
+ mixins: [
+ tooltipMixin,
+ ],
+ };
+</script>
+<template>
+ <div
+ class="btn-group"
+ role="group">
+ <button
+ class="dropdown-toggle btn btn-default build-artifacts js-pipeline-dropdown-download"
+ title="Artifacts"
+ data-placement="top"
+ data-toggle="dropdown"
+ aria-label="Artifacts"
+ ref="tooltip">
+ <i
+ class="fa fa-download"
+ aria-hidden="true">
+ </i>
+ <i
+ class="fa fa-caret-down"
+ aria-hidden="true">
+ </i>
+ </button>
+ <ul class="dropdown-menu dropdown-menu-align-right">
+ <li v-for="artifact in artifacts">
+ <a
+ rel="nofollow"
+ download
+ :href="artifact.path">
+ <i
+ class="fa fa-download"
+ aria-hidden="true">
+ </i>
+ <span>Download {{artifact.name}} artifacts</span>
+ </a>
+ </li>
+ </ul>
+ </div>
+</template>
diff --git a/app/assets/javascripts/pipelines/components/time_ago.js b/app/assets/javascripts/pipelines/components/time_ago.js
deleted file mode 100644
index 188f74cc705..00000000000
--- a/app/assets/javascripts/pipelines/components/time_ago.js
+++ /dev/null
@@ -1,98 +0,0 @@
-import iconTimerSvg from 'icons/_icon_timer.svg';
-import '../../lib/utils/datetime_utility';
-
-export default {
- props: {
- finishedTime: {
- type: String,
- required: true,
- },
-
- duration: {
- type: Number,
- required: true,
- },
- },
-
- data() {
- return {
- iconTimerSvg,
- };
- },
-
- updated() {
- $(this.$refs.tooltip).tooltip('fixTitle');
- },
-
- computed: {
- hasDuration() {
- return this.duration > 0;
- },
-
- hasFinishedTime() {
- return this.finishedTime !== '';
- },
-
- localTimeFinished() {
- return gl.utils.formatDate(this.finishedTime);
- },
-
- durationFormated() {
- const date = new Date(this.duration * 1000);
-
- let hh = date.getUTCHours();
- let mm = date.getUTCMinutes();
- let ss = date.getSeconds();
-
- // left pad
- if (hh < 10) {
- hh = `0${hh}`;
- }
- if (mm < 10) {
- mm = `0${mm}`;
- }
- if (ss < 10) {
- ss = `0${ss}`;
- }
-
- return `${hh}:${mm}:${ss}`;
- },
-
- finishedTimeFormated() {
- const timeAgo = gl.utils.getTimeago();
-
- return timeAgo.format(this.finishedTime);
- },
- },
-
- template: `
- <td class="pipelines-time-ago">
- <p
- class="duration"
- v-if="hasDuration">
- <span
- v-html="iconTimerSvg">
- </span>
- {{durationFormated}}
- </p>
-
- <p
- class="finished-at"
- v-if="hasFinishedTime">
-
- <i
- class="fa fa-calendar"
- aria-hidden="true" />
-
- <time
- ref="tooltip"
- data-toggle="tooltip"
- data-placement="top"
- data-container="body"
- :title="localTimeFinished">
- {{finishedTimeFormated}}
- </time>
- </p>
- </td>
- `,
-};
diff --git a/app/assets/javascripts/pipelines/components/time_ago.vue b/app/assets/javascripts/pipelines/components/time_ago.vue
new file mode 100644
index 00000000000..be3f32afa09
--- /dev/null
+++ b/app/assets/javascripts/pipelines/components/time_ago.vue
@@ -0,0 +1,93 @@
+<script>
+ import iconTimerSvg from 'icons/_icon_timer.svg';
+ import '../../lib/utils/datetime_utility';
+ import tooltipMixin from '../../vue_shared/mixins/tooltip';
+ import timeagoMixin from '../../vue_shared/mixins/timeago';
+
+ export default {
+ props: {
+ finishedTime: {
+ type: String,
+ required: true,
+ },
+ duration: {
+ type: Number,
+ required: true,
+ },
+ },
+ mixins: [
+ tooltipMixin,
+ timeagoMixin,
+ ],
+ data() {
+ return {
+ iconTimerSvg,
+ };
+ },
+ computed: {
+ hasDuration() {
+ return this.duration > 0;
+ },
+ hasFinishedTime() {
+ return this.finishedTime !== '';
+ },
+ durationFormated() {
+ const date = new Date(this.duration * 1000);
+
+ let hh = date.getUTCHours();
+ let mm = date.getUTCMinutes();
+ let ss = date.getSeconds();
+
+ // left pad
+ if (hh < 10) {
+ hh = `0${hh}`;
+ }
+ if (mm < 10) {
+ mm = `0${mm}`;
+ }
+ if (ss < 10) {
+ ss = `0${ss}`;
+ }
+
+ return `${hh}:${mm}:${ss}`;
+ },
+ },
+ };
+</script>
+<template>
+ <div class="table-section section-15 pipelines-time-ago">
+ <div
+ class="table-mobile-header"
+ role="rowheader">
+ Duration
+ </div>
+ <div class="table-mobile-content">
+ <p
+ class="duration"
+ v-if="hasDuration">
+ <span
+ v-html="iconTimerSvg">
+ </span>
+ {{durationFormated}}
+ </p>
+
+ <p
+ class="finished-at hidden-xs hidden-sm"
+ v-if="hasFinishedTime">
+
+ <i
+ class="fa fa-calendar"
+ aria-hidden="true">
+ </i>
+
+ <time
+ ref="tooltip"
+ data-placement="top"
+ data-container="body"
+ :title="tooltipTitle(finishedTime)">
+ {{timeFormated(finishedTime)}}
+ </time>
+ </p>
+ </div>
+ </div>
+</script>
diff --git a/app/assets/javascripts/pipelines/index.js b/app/assets/javascripts/pipelines/index.js
deleted file mode 100644
index 48f9181a8d9..00000000000
--- a/app/assets/javascripts/pipelines/index.js
+++ /dev/null
@@ -1,22 +0,0 @@
-import Vue from 'vue';
-import PipelinesStore from './stores/pipelines_store';
-import PipelinesComponent from './pipelines';
-import '../vue_shared/vue_resource_interceptor';
-
-$(() => new Vue({
- el: document.querySelector('#pipelines-list-vue'),
-
- data() {
- const store = new PipelinesStore();
-
- return {
- store,
- };
- },
- components: {
- 'vue-pipelines': PipelinesComponent,
- },
- template: `
- <vue-pipelines :store="store" />
- `,
-}));
diff --git a/app/assets/javascripts/pipelines/pipelines.js b/app/assets/javascripts/pipelines/pipelines.js
deleted file mode 100644
index b530461837c..00000000000
--- a/app/assets/javascripts/pipelines/pipelines.js
+++ /dev/null
@@ -1,293 +0,0 @@
-import Visibility from 'visibilityjs';
-import PipelinesService from './services/pipelines_service';
-import eventHub from './event_hub';
-import pipelinesTableComponent from '../vue_shared/components/pipelines_table';
-import tablePagination from '../vue_shared/components/table_pagination.vue';
-import emptyState from './components/empty_state.vue';
-import errorState from './components/error_state.vue';
-import navigationTabs from './components/navigation_tabs.vue';
-import navigationControls from './components/nav_controls.vue';
-import loadingIcon from '../vue_shared/components/loading_icon.vue';
-import Poll from '../lib/utils/poll';
-
-export default {
- props: {
- store: {
- type: Object,
- required: true,
- },
- },
-
- components: {
- tablePagination,
- pipelinesTableComponent,
- emptyState,
- errorState,
- navigationTabs,
- navigationControls,
- loadingIcon,
- },
-
- data() {
- const pipelinesData = document.querySelector('#pipelines-list-vue').dataset;
-
- return {
- endpoint: pipelinesData.endpoint,
- cssClass: pipelinesData.cssClass,
- helpPagePath: pipelinesData.helpPagePath,
- newPipelinePath: pipelinesData.newPipelinePath,
- canCreatePipeline: pipelinesData.canCreatePipeline,
- allPath: pipelinesData.allPath,
- pendingPath: pipelinesData.pendingPath,
- runningPath: pipelinesData.runningPath,
- finishedPath: pipelinesData.finishedPath,
- branchesPath: pipelinesData.branchesPath,
- tagsPath: pipelinesData.tagsPath,
- hasCi: pipelinesData.hasCi,
- ciLintPath: pipelinesData.ciLintPath,
- state: this.store.state,
- apiScope: 'all',
- pagenum: 1,
- isLoading: false,
- hasError: false,
- isMakingRequest: false,
- updateGraphDropdown: false,
- hasMadeRequest: false,
- };
- },
-
- computed: {
- canCreatePipelineParsed() {
- return gl.utils.convertPermissionToBoolean(this.canCreatePipeline);
- },
-
- scope() {
- const scope = gl.utils.getParameterByName('scope');
- return scope === null ? 'all' : scope;
- },
-
- shouldRenderErrorState() {
- return this.hasError && !this.isLoading;
- },
-
- /**
- * The empty state should only be rendered when the request is made to fetch all pipelines
- * and none is returned.
- *
- * @return {Boolean}
- */
- shouldRenderEmptyState() {
- return !this.isLoading &&
- !this.hasError &&
- this.hasMadeRequest &&
- !this.state.pipelines.length &&
- (this.scope === 'all' || this.scope === null);
- },
-
- /**
- * When a specific scope does not have pipelines we render a message.
- *
- * @return {Boolean}
- */
- shouldRenderNoPipelinesMessage() {
- return !this.isLoading &&
- !this.hasError &&
- !this.state.pipelines.length &&
- this.scope !== 'all' &&
- this.scope !== null;
- },
-
- shouldRenderTable() {
- return !this.hasError &&
- !this.isLoading && this.state.pipelines.length;
- },
-
- /**
- * Pagination should only be rendered when there is more than one page.
- *
- * @return {Boolean}
- */
- shouldRenderPagination() {
- return !this.isLoading &&
- this.state.pipelines.length &&
- this.state.pageInfo.total > this.state.pageInfo.perPage;
- },
-
- hasCiEnabled() {
- return this.hasCi !== undefined;
- },
-
- paths() {
- return {
- allPath: this.allPath,
- pendingPath: this.pendingPath,
- finishedPath: this.finishedPath,
- runningPath: this.runningPath,
- branchesPath: this.branchesPath,
- tagsPath: this.tagsPath,
- };
- },
-
- pageParameter() {
- return gl.utils.getParameterByName('page') || this.pagenum;
- },
-
- scopeParameter() {
- return gl.utils.getParameterByName('scope') || this.apiScope;
- },
- },
-
- created() {
- this.service = new PipelinesService(this.endpoint);
-
- const poll = new Poll({
- resource: this.service,
- method: 'getPipelines',
- data: { page: this.pageParameter, scope: this.scopeParameter },
- successCallback: this.successCallback,
- errorCallback: this.errorCallback,
- notificationCallback: this.setIsMakingRequest,
- });
-
- if (!Visibility.hidden()) {
- this.isLoading = true;
- poll.makeRequest();
- } else {
- // If tab is not visible we need to make the first request so we don't show the empty
- // state without knowing if there are any pipelines
- this.fetchPipelines();
- }
-
- Visibility.change(() => {
- if (!Visibility.hidden()) {
- poll.restart();
- } else {
- poll.stop();
- }
- });
-
- eventHub.$on('refreshPipelines', this.fetchPipelines);
- },
-
- beforeDestroy() {
- eventHub.$off('refreshPipelines');
- },
-
- methods: {
- /**
- * Will change the page number and update the URL.
- *
- * @param {Number} pageNumber desired page to go to.
- */
- change(pageNumber) {
- const param = gl.utils.setParamInURL('page', pageNumber);
-
- gl.utils.visitUrl(param);
- return param;
- },
-
- fetchPipelines() {
- if (!this.isMakingRequest) {
- this.isLoading = true;
-
- this.service.getPipelines({ scope: this.scopeParameter, page: this.pageParameter })
- .then(response => this.successCallback(response))
- .catch(() => this.errorCallback());
- }
- },
-
- successCallback(resp) {
- const response = {
- headers: resp.headers,
- body: resp.json(),
- };
-
- this.store.storeCount(response.body.count);
- this.store.storePipelines(response.body.pipelines);
- this.store.storePagination(response.headers);
-
- this.isLoading = false;
- this.updateGraphDropdown = true;
- this.hasMadeRequest = true;
- },
-
- errorCallback() {
- this.hasError = true;
- this.isLoading = false;
- this.updateGraphDropdown = false;
- },
-
- setIsMakingRequest(isMakingRequest) {
- this.isMakingRequest = isMakingRequest;
-
- if (isMakingRequest) {
- this.updateGraphDropdown = false;
- }
- },
- },
-
- template: `
- <div :class="cssClass">
-
- <div
- class="top-area scrolling-tabs-container inner-page-scroll-tabs"
- v-if="!isLoading && !shouldRenderEmptyState">
- <div class="fade-left">
- <i class="fa fa-angle-left" aria-hidden="true"></i>
- </div>
- <div class="fade-right">
- <i class="fa fa-angle-right" aria-hidden="true"></i>
- </div>
- <navigation-tabs
- :scope="scope"
- :count="state.count"
- :paths="paths" />
-
- <navigation-controls
- :new-pipeline-path="newPipelinePath"
- :has-ci-enabled="hasCiEnabled"
- :help-page-path="helpPagePath"
- :ciLintPath="ciLintPath"
- :can-create-pipeline="canCreatePipelineParsed " />
- </div>
-
- <div class="content-list pipelines">
-
- <loading-icon
- label="Loading Pipelines"
- size="3"
- v-if="isLoading"
- />
-
- <empty-state
- v-if="shouldRenderEmptyState"
- :help-page-path="helpPagePath" />
-
- <error-state v-if="shouldRenderErrorState" />
-
- <div
- class="blank-state blank-state-no-icon"
- v-if="shouldRenderNoPipelinesMessage">
- <h2 class="blank-state-title js-blank-state-title">No pipelines to show.</h2>
- </div>
-
- <div
- class="table-holder"
- v-if="shouldRenderTable">
-
- <pipelines-table-component
- :pipelines="state.pipelines"
- :service="service"
- :update-graph-dropdown="updateGraphDropdown"
- />
- </div>
-
- <table-pagination
- v-if="shouldRenderPagination"
- :change="change"
- :pageInfo="state.pageInfo"
- />
- </div>
- </div>
- `,
-};
diff --git a/app/assets/javascripts/pipelines/pipelines_bundle.js b/app/assets/javascripts/pipelines/pipelines_bundle.js
new file mode 100644
index 00000000000..923d9bfb248
--- /dev/null
+++ b/app/assets/javascripts/pipelines/pipelines_bundle.js
@@ -0,0 +1,24 @@
+import Vue from 'vue';
+import PipelinesStore from './stores/pipelines_store';
+import pipelinesComponent from './components/pipelines.vue';
+
+document.addEventListener('DOMContentLoaded', () => new Vue({
+ el: '#pipelines-list-vue',
+ data() {
+ const store = new PipelinesStore();
+
+ return {
+ store,
+ };
+ },
+ components: {
+ pipelinesComponent,
+ },
+ render(createElement) {
+ return createElement('pipelines-component', {
+ props: {
+ store: this.store,
+ },
+ });
+ },
+}));
diff --git a/app/assets/javascripts/preview_markdown.js b/app/assets/javascripts/preview_markdown.js
index 4a3df2fd465..141333b2b4d 100644
--- a/app/assets/javascripts/preview_markdown.js
+++ b/app/assets/javascripts/preview_markdown.js
@@ -3,7 +3,7 @@
// MarkdownPreview
//
// Handles toggling the "Write" and "Preview" tab clicks, rendering the preview
-// (including the explanation of slash commands), and showing a warning when
+// (including the explanation of quick actions), and showing a warning when
// more than `x` users are referenced.
//
(function () {
diff --git a/app/assets/javascripts/sidebar/components/time_tracking/help_state.js b/app/assets/javascripts/sidebar/components/time_tracking/help_state.js
index b2a77462fe0..142ad437509 100644
--- a/app/assets/javascripts/sidebar/components/time_tracking/help_state.js
+++ b/app/assets/javascripts/sidebar/components/time_tracking/help_state.js
@@ -15,10 +15,10 @@ export default {
<div class="time-tracking-help-state">
<div class="time-tracking-info">
<h4>
- Track time with slash commands
+ Track time with quick actions
</h4>
<p>
- Slash commands can be used in the issues description and comment boxes.
+ Quick actions can be used in the issues description and comment boxes.
</p>
<p>
<code>
diff --git a/app/assets/javascripts/sidebar/components/time_tracking/sidebar_time_tracking.js b/app/assets/javascripts/sidebar/components/time_tracking/sidebar_time_tracking.js
index 244b67b3ad9..650e935b116 100644
--- a/app/assets/javascripts/sidebar/components/time_tracking/sidebar_time_tracking.js
+++ b/app/assets/javascripts/sidebar/components/time_tracking/sidebar_time_tracking.js
@@ -16,10 +16,10 @@ export default {
'issuable-time-tracker': timeTracker,
},
methods: {
- listenForSlashCommands() {
- $(document).on('ajax:success', '.gfm-form', this.slashCommandListened);
+ listenForQuickActions() {
+ $(document).on('ajax:success', '.gfm-form', this.quickActionListened);
},
- slashCommandListened(e, data) {
+ quickActionListened(e, data) {
const subscribedCommands = ['spend_time', 'time_estimate'];
let changedCommands;
if (data !== undefined) {
@@ -35,7 +35,7 @@ export default {
},
},
mounted() {
- this.listenForSlashCommands();
+ this.listenForQuickActions();
},
template: `
<div class="block">
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_memory_usage.js b/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_memory_usage.js
index 8155218681c..76cb71b6c12 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_memory_usage.js
+++ b/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_memory_usage.js
@@ -1,5 +1,5 @@
-import statusCodes from '~/lib/utils/http_status';
-import { bytesToMiB } from '~/lib/utils/number_utils';
+import statusCodes from '../../lib/utils/http_status';
+import { bytesToMiB } from '../../lib/utils/number_utils';
import MemoryGraph from '../../vue_shared/components/memory_graph';
import MRWidgetService from '../services/mr_widget_service';
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_related_links.js b/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_related_links.js
index 205804670fa..686cb38cbb1 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_related_links.js
+++ b/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_related_links.js
@@ -1,42 +1,63 @@
export default {
name: 'MRWidgetRelatedLinks',
props: {
+ isMerged: { type: Boolean, required: true },
relatedLinks: { type: Object, required: true },
},
computed: {
+ // TODO: the following should be handled by i18n
+ closingText() {
+ if (this.isMerged) {
+ return `Closed ${this.issueLabel('closing')}`;
+ }
+
+ return `Closes ${this.issueLabel('closing')}`;
+ },
hasLinks() {
const { closing, mentioned, assignToMe } = this.relatedLinks;
return closing || mentioned || assignToMe;
},
+ // TODO: the following should be handled by i18n
+ mentionedText() {
+ if (this.isMerged) {
+ if (this.hasMultipleIssues(this.relatedLinks.mentioned)) {
+ return 'are mentioned but were not closed';
+ }
+
+ return 'is mentioned but was not closed';
+ }
+
+ if (this.hasMultipleIssues(this.relatedLinks.mentioned)) {
+ return 'are mentioned but will not be closed';
+ }
+
+ return 'is mentioned but will not be closed';
+ },
},
methods: {
hasMultipleIssues(text) {
- return !text ? false : text.match(/<\/a> and <a/);
+ return /<\/a>,? and <a/.test(text);
},
+ // TODO: the following should be handled by i18n
issueLabel(field) {
return this.hasMultipleIssues(this.relatedLinks[field]) ? 'issues' : 'issue';
},
- verbLabel(field) {
- return this.hasMultipleIssues(this.relatedLinks[field]) ? 'are' : 'is';
- },
},
template: `
- <section
- v-if="hasLinks"
- class="mr-info-list mr-links">
+ <div v-if="hasLinks">
<div class="legend"></div>
<p v-if="relatedLinks.closing">
- Closes {{issueLabel('closing')}}
+ {{closingText}}
<span v-html="relatedLinks.closing"></span>.
</p>
<p v-if="relatedLinks.mentioned">
<span class="capitalize">{{issueLabel('mentioned')}}</span>
<span v-html="relatedLinks.mentioned"></span>
- {{verbLabel('mentioned')}} mentioned but will not be closed.
+ {{mentionedText}}
</p>
<p v-if="relatedLinks.assignToMe">
<span v-html="relatedLinks.assignToMe"></span>
</p>
- </section>
+ </div>
`,
};
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_merged.js b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_merged.js
index c7d32d18141..9b8eed9016d 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_merged.js
+++ b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_merged.js
@@ -1,7 +1,9 @@
/* global Flash */
import mrWidgetAuthorTime from '../../components/mr_widget_author_time';
+import mrWidgetRelatedLinks from '../../components/mr_widget_related_links';
import eventHub from '../../event_hub';
+import '../../../flash';
export default {
name: 'MRWidgetMerged',
@@ -11,6 +13,7 @@ export default {
},
components: {
'mr-widget-author-and-time': mrWidgetAuthorTime,
+ 'mr-widget-related-links': mrWidgetRelatedLinks,
},
data() {
return {
@@ -18,6 +21,9 @@ export default {
};
},
computed: {
+ shouldRenderRelatedLinks() {
+ return this.mr.relatedLinks && this.mr.isMerged;
+ },
shouldShowRemoveSourceBranch() {
const { sourceBranchRemoved, isRemovingSourceBranch, canRemoveSourceBranch } = this.mr;
@@ -86,6 +92,10 @@ export default {
aria-hidden="true" />
The source branch is being removed.
</p>
+ <mr-widget-related-links
+ v-if="shouldRenderRelatedLinks"
+ :is-merged="mr.isMerged()"
+ :related-links="mr.relatedLinks" />
</section>
<div
v-if="shouldShowMergedButtons"
diff --git a/app/assets/javascripts/vue_merge_request_widget/mr_widget_options.js b/app/assets/javascripts/vue_merge_request_widget/mr_widget_options.js
index 2339a00ddd0..222d0b7f79e 100644
--- a/app/assets/javascripts/vue_merge_request_widget/mr_widget_options.js
+++ b/app/assets/javascripts/vue_merge_request_widget/mr_widget_options.js
@@ -48,7 +48,7 @@ export default {
return stateMaps.stateToComponentMap[this.mr.state];
},
shouldRenderMergeHelp() {
- return stateMaps.statesToShowHelpWidget.indexOf(this.mr.state) > -1;
+ return !this.mr.isMerged;
},
shouldRenderPipelines() {
return Object.keys(this.mr.pipeline).length || this.mr.hasCI;
@@ -238,9 +238,14 @@ export default {
:is="componentName"
:mr="mr"
:service="service" />
- <mr-widget-related-links
+ <section
v-if="shouldRenderRelatedLinks"
- :related-links="mr.relatedLinks" />
+ class="mr-info-list mr-links">
+ <div class="legend"></div>
+ <mr-widget-related-links
+ :is-merged="mr.isMerged"
+ :related-links="mr.relatedLinks" />
+ </section>
<mr-widget-merge-help v-if="shouldRenderMergeHelp" />
</div>
`,
diff --git a/app/assets/javascripts/vue_merge_request_widget/stores/mr_widget_store.js b/app/assets/javascripts/vue_merge_request_widget/stores/mr_widget_store.js
index 69bc1436284..ad73efb37e1 100644
--- a/app/assets/javascripts/vue_merge_request_widget/stores/mr_widget_store.js
+++ b/app/assets/javascripts/vue_merge_request_widget/stores/mr_widget_store.js
@@ -1,6 +1,18 @@
import Timeago from 'timeago.js';
import { getStateKey } from '../dependencies';
+const unmergedStates = [
+ 'locked',
+ 'conflicts',
+ 'workInProgress',
+ 'readyToMerge',
+ 'checking',
+ 'unresolvedDiscussions',
+ 'pipelineFailed',
+ 'pipelineBlocked',
+ 'autoMergeFailed',
+];
+
export default class MergeRequestStore {
constructor(data) {
@@ -65,6 +77,7 @@ export default class MergeRequestStore {
this.mergeActionsContentPath = data.commit_change_content_path;
this.isRemovingSourceBranch = this.isRemovingSourceBranch || false;
this.isOpen = data.state === 'opened' || data.state === 'reopened' || false;
+ this.isMerged = unmergedStates.indexOf(data.state) === -1;
this.hasMergeableDiscussionsState = data.mergeable_discussions_state === false;
this.canRemoveSourceBranch = currentUser.can_remove_source_branch || false;
this.canMerge = !!data.merge_path;
diff --git a/app/assets/javascripts/vue_merge_request_widget/stores/state_maps.js b/app/assets/javascripts/vue_merge_request_widget/stores/state_maps.js
index 605dd3a1ff4..dd939d98d0f 100644
--- a/app/assets/javascripts/vue_merge_request_widget/stores/state_maps.js
+++ b/app/assets/javascripts/vue_merge_request_widget/stores/state_maps.js
@@ -19,19 +19,6 @@ const stateToComponentMap = {
shaMismatch: 'mr-widget-sha-mismatch',
};
-const statesToShowHelpWidget = [
- 'locked',
- 'conflicts',
- 'workInProgress',
- 'readyToMerge',
- 'checking',
- 'unresolvedDiscussions',
- 'pipelineFailed',
- 'pipelineBlocked',
- 'autoMergeFailed',
-];
-
export default {
stateToComponentMap,
- statesToShowHelpWidget,
};
diff --git a/app/assets/javascripts/vue_shared/components/commit.js b/app/assets/javascripts/vue_shared/components/commit.js
deleted file mode 100644
index ff5ae28e062..00000000000
--- a/app/assets/javascripts/vue_shared/components/commit.js
+++ /dev/null
@@ -1,159 +0,0 @@
-import commitIconSvg from 'icons/_icon_commit.svg';
-import userAvatarLink from './user_avatar/user_avatar_link.vue';
-
-export default {
- props: {
- /**
- * Indicates the existance of a tag.
- * Used to render the correct icon, if true will render `fa-tag` icon,
- * if false will render `fa-code-fork` icon.
- */
- tag: {
- type: Boolean,
- required: false,
- default: false,
- },
-
- /**
- * If provided is used to render the branch name and url.
- * Should contain the following properties:
- * name
- * ref_url
- */
- commitRef: {
- type: Object,
- required: false,
- default: () => ({}),
- },
-
- /**
- * Used to link to the commit sha.
- */
- commitUrl: {
- type: String,
- required: false,
- default: '',
- },
-
- /**
- * Used to show the commit short sha that links to the commit url.
- */
- shortSha: {
- type: String,
- required: false,
- default: '',
- },
-
- /**
- * If provided shows the commit tile.
- */
- title: {
- type: String,
- required: false,
- default: '',
- },
-
- /**
- * If provided renders information about the author of the commit.
- * When provided should include:
- * `avatar_url` to render the avatar icon
- * `web_url` to link to user profile
- * `username` to render alt and title tags
- */
- author: {
- type: Object,
- required: false,
- default: () => ({}),
- },
- },
-
- computed: {
- /**
- * Used to verify if all the properties needed to render the commit
- * ref section were provided.
- *
- * TODO: Improve this! Use lodash _.has when we have it.
- *
- * @returns {Boolean}
- */
- hasCommitRef() {
- return this.commitRef && this.commitRef.name && this.commitRef.ref_url;
- },
-
- /**
- * Used to verify if all the properties needed to render the commit
- * author section were provided.
- *
- * TODO: Improve this! Use lodash _.has when we have it.
- *
- * @returns {Boolean}
- */
- hasAuthor() {
- return this.author &&
- this.author.avatar_url &&
- this.author.path &&
- this.author.username;
- },
-
- /**
- * If information about the author is provided will return a string
- * to be rendered as the alt attribute of the img tag.
- *
- * @returns {String}
- */
- userImageAltDescription() {
- return this.author &&
- this.author.username ? `${this.author.username}'s avatar` : null;
- },
- },
-
- data() {
- return { commitIconSvg };
- },
-
- components: {
- userAvatarLink,
- },
- template: `
- <div class="branch-commit">
-
- <div v-if="hasCommitRef" class="icon-container">
- <i v-if="tag" class="fa fa-tag"></i>
- <i v-if="!tag" class="fa fa-code-fork"></i>
- </div>
-
- <a v-if="hasCommitRef"
- class="ref-name"
- :href="commitRef.ref_url">
- {{commitRef.name}}
- </a>
-
- <div v-html="commitIconSvg" class="commit-icon js-commit-icon"></div>
-
- <a class="commit-sha"
- :href="commitUrl">
- {{shortSha}}
- </a>
-
- <div class="commit-title flex-truncate-parent">
- <span v-if="title" class="flex-truncate-child">
- <user-avatar-link
- v-if="hasAuthor"
- class="avatar-image-container"
- :link-href="author.path"
- :img-src="author.avatar_url"
- :img-alt="userImageAltDescription"
- :tooltip-text="author.username"
- />
- <a class="commit-row-message"
- :href="commitUrl">
- {{title}}
- </a>
- </span>
- <span v-else>
- Cant find HEAD commit for this branch
- </span>
- </div>
- </div>
- `,
-};
diff --git a/app/assets/javascripts/vue_shared/components/commit.vue b/app/assets/javascripts/vue_shared/components/commit.vue
new file mode 100644
index 00000000000..262584769e0
--- /dev/null
+++ b/app/assets/javascripts/vue_shared/components/commit.vue
@@ -0,0 +1,166 @@
+<script>
+ import commitIconSvg from 'icons/_icon_commit.svg';
+ import userAvatarLink from './user_avatar/user_avatar_link.vue';
+
+ export default {
+ props: {
+ /**
+ * Indicates the existance of a tag.
+ * Used to render the correct icon, if true will render `fa-tag` icon,
+ * if false will render `fa-code-fork` icon.
+ */
+ tag: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
+ /**
+ * If provided is used to render the branch name and url.
+ * Should contain the following properties:
+ * name
+ * ref_url
+ */
+ commitRef: {
+ type: Object,
+ required: false,
+ default: () => ({}),
+ },
+ /**
+ * Used to link to the commit sha.
+ */
+ commitUrl: {
+ type: String,
+ required: false,
+ default: '',
+ },
+
+ /**
+ * Used to show the commit short sha that links to the commit url.
+ */
+ shortSha: {
+ type: String,
+ required: false,
+ default: '',
+ },
+ /**
+ * If provided shows the commit tile.
+ */
+ title: {
+ type: String,
+ required: false,
+ default: '',
+ },
+ /**
+ * If provided renders information about the author of the commit.
+ * When provided should include:
+ * `avatar_url` to render the avatar icon
+ * `web_url` to link to user profile
+ * `username` to render alt and title tags
+ */
+ author: {
+ type: Object,
+ required: false,
+ default: () => ({}),
+ },
+ },
+ computed: {
+ /**
+ * Used to verify if all the properties needed to render the commit
+ * ref section were provided.
+ *
+ * TODO: Improve this! Use lodash _.has when we have it.
+ *
+ * @returns {Boolean}
+ */
+ hasCommitRef() {
+ return this.commitRef && this.commitRef.name && this.commitRef.ref_url;
+ },
+ /**
+ * Used to verify if all the properties needed to render the commit
+ * author section were provided.
+ *
+ * TODO: Improve this! Use lodash _.has when we have it.
+ *
+ * @returns {Boolean}
+ */
+ hasAuthor() {
+ return this.author &&
+ this.author.avatar_url &&
+ this.author.path &&
+ this.author.username;
+ },
+ /**
+ * If information about the author is provided will return a string
+ * to be rendered as the alt attribute of the img tag.
+ *
+ * @returns {String}
+ */
+ userImageAltDescription() {
+ return this.author &&
+ this.author.username ? `${this.author.username}'s avatar` : null;
+ },
+ },
+ data() {
+ return { commitIconSvg };
+ },
+ components: {
+ userAvatarLink,
+ },
+ };
+</script>
+<template>
+ <div class="branch-commit">
+ <div v-if="hasCommitRef" class="icon-container hidden-xs">
+ <i
+ v-if="tag"
+ class="fa fa-tag"
+ aria-hidden="true">
+ </i>
+ <i
+ v-if="!tag"
+ class="fa fa-code-fork"
+ aria-hidden="true">
+ </i>
+ </div>
+
+ <a
+ v-if="hasCommitRef"
+ class="ref-name hidden-xs"
+ :href="commitRef.ref_url">
+ {{commitRef.name}}
+ </a>
+
+ <div
+ v-html="commitIconSvg"
+ class="commit-icon js-commit-icon">
+ </div>
+
+ <a
+ class="commit-sha"
+ :href="commitUrl">
+ {{shortSha}}
+ </a>
+
+ <div class="commit-title flex-truncate-parent">
+ <span
+ v-if="title"
+ class="flex-truncate-child">
+ <user-avatar-link
+ v-if="hasAuthor"
+ class="avatar-image-container"
+ :link-href="author.path"
+ :img-src="author.avatar_url"
+ :img-alt="userImageAltDescription"
+ :tooltip-text="author.username"
+ />
+ <a class="commit-row-message"
+ :href="commitUrl">
+ {{title}}
+ </a>
+ </span>
+ <span v-else>
+ Cant find HEAD commit for this branch
+ </span>
+ </div>
+ </div>
+</template>
diff --git a/app/assets/javascripts/vue_shared/components/pipelines_table.js b/app/assets/javascripts/vue_shared/components/pipelines_table.js
deleted file mode 100644
index 48a39f18112..00000000000
--- a/app/assets/javascripts/vue_shared/components/pipelines_table.js
+++ /dev/null
@@ -1,55 +0,0 @@
-import PipelinesTableRowComponent from './pipelines_table_row';
-
-/**
- * Pipelines Table Component.
- *
- * Given an array of objects, renders a table.
- */
-export default {
- props: {
- pipelines: {
- type: Array,
- required: true,
- },
-
- service: {
- type: Object,
- required: true,
- },
-
- updateGraphDropdown: {
- type: Boolean,
- required: false,
- default: false,
- },
- },
-
- components: {
- 'pipelines-table-row-component': PipelinesTableRowComponent,
- },
-
- template: `
- <table class="table ci-table">
- <thead>
- <tr>
- <th class="js-pipeline-status pipeline-status">Status</th>
- <th class="js-pipeline-info pipeline-info">Pipeline</th>
- <th class="js-pipeline-commit pipeline-commit">Commit</th>
- <th class="js-pipeline-stages pipeline-stages">Stages</th>
- <th class="js-pipeline-date pipeline-date"></th>
- <th class="js-pipeline-actions pipeline-actions"></th>
- </tr>
- </thead>
- <tbody>
- <template v-for="model in pipelines"
- v-bind:model="model">
- <tr is="pipelines-table-row-component"
- :pipeline="model"
- :service="service"
- :update-graph-dropdown="updateGraphDropdown"
- />
- </template>
- </tbody>
- </table>
- `,
-};
diff --git a/app/assets/javascripts/vue_shared/components/pipelines_table.vue b/app/assets/javascripts/vue_shared/components/pipelines_table.vue
new file mode 100644
index 00000000000..884f1ce9689
--- /dev/null
+++ b/app/assets/javascripts/vue_shared/components/pipelines_table.vue
@@ -0,0 +1,64 @@
+<script>
+ import pipelinesTableRowComponent from './pipelines_table_row.vue';
+
+ /**
+ * Pipelines Table Component.
+ *
+ * Given an array of objects, renders a table.
+ */
+ export default {
+ props: {
+ pipelines: {
+ type: Array,
+ required: true,
+ },
+ service: {
+ type: Object,
+ required: true,
+ },
+ updateGraphDropdown: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
+ },
+ components: {
+ pipelinesTableRowComponent,
+ },
+ };
+</script>
+<template>
+ <div class="ci-table">
+ <div
+ class="gl-responsive-table-row table-row-header"
+ role="row">
+ <div
+ class="table-section section-10 js-pipeline-status pipeline-status"
+ role="rowheader">
+ Status
+ </div>
+ <div
+ class="table-section section-15 js-pipeline-info pipeline-info"
+ role="rowheader">
+ Pipeline
+ </div>
+ <div
+ class="table-section section-25 js-pipeline-commit pipeline-commit"
+ role="rowheader">
+ Commit
+ </div>
+ <div
+ class="table-section section-15 js-pipeline-stages pipeline-stages"
+ role="rowheader">
+ Stages
+ </div>
+ </div>
+ <pipelines-table-row-component
+ v-for="model in pipelines"
+ :key="model.id"
+ :pipeline="model"
+ :service="service"
+ :update-graph-dropdown="updateGraphDropdown"
+ />
+ </div>
+</template>
diff --git a/app/assets/javascripts/vue_shared/components/pipelines_table_row.js b/app/assets/javascripts/vue_shared/components/pipelines_table_row.vue
index f60f8eeb43d..4d5ebe2e9ed 100644
--- a/app/assets/javascripts/vue_shared/components/pipelines_table_row.js
+++ b/app/assets/javascripts/vue_shared/components/pipelines_table_row.vue
@@ -1,12 +1,13 @@
+<script>
/* eslint-disable no-param-reassign */
-import AsyncButtonComponent from '../../pipelines/components/async_button.vue';
-import PipelinesActionsComponent from '../../pipelines/components/pipelines_actions';
-import PipelinesArtifactsComponent from '../../pipelines/components/pipelines_artifacts';
+import asyncButtonComponent from '../../pipelines/components/async_button.vue';
+import pipelinesActionsComponent from '../../pipelines/components/pipelines_actions.vue';
+import pipelinesArtifactsComponent from '../../pipelines/components/pipelines_artifacts.vue';
import ciBadge from './ci_badge_link.vue';
-import PipelinesStageComponent from '../../pipelines/components/stage.vue';
-import PipelinesUrlComponent from '../../pipelines/components/pipeline_url.vue';
-import PipelinesTimeagoComponent from '../../pipelines/components/time_ago';
-import CommitComponent from './commit';
+import pipelineStage from '../../pipelines/components/stage.vue';
+import pipelineUrl from '../../pipelines/components/pipeline_url.vue';
+import pipelinesTimeago from '../../pipelines/components/time_ago.vue';
+import commitComponent from './commit.vue';
/**
* Pipeline table row.
@@ -19,30 +20,26 @@ export default {
type: Object,
required: true,
},
-
service: {
type: Object,
required: true,
},
-
updateGraphDropdown: {
type: Boolean,
required: false,
default: false,
},
},
-
components: {
- 'async-button-component': AsyncButtonComponent,
- 'pipelines-actions-component': PipelinesActionsComponent,
- 'pipelines-artifacts-component': PipelinesArtifactsComponent,
- 'commit-component': CommitComponent,
- 'dropdown-stage': PipelinesStageComponent,
- 'pipeline-url': PipelinesUrlComponent,
+ asyncButtonComponent,
+ pipelinesActionsComponent,
+ pipelinesArtifactsComponent,
+ commitComponent,
+ pipelineStage,
+ pipelineUrl,
ciBadge,
- 'time-ago': PipelinesTimeagoComponent,
+ pipelinesTimeago,
},
-
computed: {
/**
* If provided, returns the commit tag.
@@ -203,17 +200,37 @@ export default {
}
return {};
},
- },
- template: `
- <tr class="commit">
- <td class="commit-link">
+ displayPipelineActions() {
+ return this.pipeline.flags.retryable ||
+ this.pipeline.flags.cancelable ||
+ this.pipeline.details.manual_actions.length ||
+ this.pipeline.details.artifacts.length;
+ },
+ },
+};
+</script>
+<template>
+ <div class="commit gl-responsive-table-row">
+ <div class="table-section section-10 commit-link">
+ <div class="table-mobile-header"
+ role="rowheader">
+ Status
+ </div>
+ <div class="table-mobile-content">
<ci-badge :status="pipelineStatus"/>
- </td>
+ </div>
+ </div>
- <pipeline-url :pipeline="pipeline"></pipeline-url>
+ <pipeline-url :pipeline="pipeline" />
- <td>
+ <div class="table-section section-25">
+ <div
+ class="table-mobile-header"
+ role="rowheader">
+ Commit
+ </div>
+ <div class="table-mobile-content">
<commit-component
:tag="commitTag"
:commit-ref="commitRef"
@@ -221,52 +238,67 @@ export default {
:short-sha="commitShortSha"
:title="commitTitle"
:author="commitAuthor"/>
- </td>
+ </div>
+ </div>
- <td class="stage-cell">
+ <div class="table-section section-wrap section-15 stage-cell">
+ <div
+ class="table-mobile-header"
+ role="rowheader">
+ Stages
+ </div>
+ <div class="table-mobile-content">
<div class="stage-container dropdown js-mini-pipeline-graph"
v-if="pipeline.details.stages.length > 0"
v-for="stage in pipeline.details.stages">
-
- <dropdown-stage
+ <pipeline-stage
:stage="stage"
- :update-dropdown="updateGraphDropdown"/>
+ :update-dropdown="updateGraphDropdown"
+ />
</div>
- </td>
+ </div>
+ </div>
- <time-ago
- :duration="pipelineDuration"
- :finished-time="pipelineFinishedAt" />
+ <pipelines-timeago
+ :duration="pipelineDuration"
+ :finished-time="pipelineFinishedAt"
+ />
- <td class="pipeline-actions">
- <div class="pull-right btn-group">
- <pipelines-actions-component
- v-if="pipeline.details.manual_actions.length"
- :actions="pipeline.details.manual_actions"
- :service="service" />
+ <div
+ v-if="displayPipelineActions"
+ class="table-section section-20 table-button-footer pipeline-actions">
+ <div class="btn-group table-action-buttons">
+ <pipelines-actions-component
+ v-if="pipeline.details.manual_actions.length"
+ :actions="pipeline.details.manual_actions"
+ :service="service"
+ />
- <pipelines-artifacts-component
- v-if="pipeline.details.artifacts.length"
- :artifacts="pipeline.details.artifacts" />
+ <pipelines-artifacts-component
+ v-if="pipeline.details.artifacts.length"
+ class="hidden-xs hidden-sm"
+ :artifacts="pipeline.details.artifacts"
+ />
- <async-button-component
- v-if="pipeline.flags.retryable"
- :service="service"
- :endpoint="pipeline.retry_path"
- css-class="js-pipelines-retry-button btn-default btn-retry"
- title="Retry"
- icon="repeat" />
+ <async-button-component
+ v-if="pipeline.flags.retryable"
+ :service="service"
+ :endpoint="pipeline.retry_path"
+ css-class="js-pipelines-retry-button btn-default btn-retry"
+ title="Retry"
+ icon="repeat"
+ />
- <async-button-component
- v-if="pipeline.flags.cancelable"
- :service="service"
- :endpoint="pipeline.cancel_path"
- css-class="js-pipelines-cancel-button btn-remove"
- title="Cancel"
- icon="remove"
- confirm-action-message="Are you sure you want to cancel this pipeline?" />
- </div>
- </td>
- </tr>
- `,
-};
+ <async-button-component
+ v-if="pipeline.flags.cancelable"
+ :service="service"
+ :endpoint="pipeline.cancel_path"
+ css-class="js-pipelines-cancel-button btn-remove"
+ title="Cancel"
+ icon="remove"
+ confirm-action-message="Are you sure you want to cancel this pipeline?"
+ />
+ </div>
+ </div>
+ </div>
+</template>
diff --git a/app/assets/javascripts/vue_shared/components/time_ago_tooltip.vue b/app/assets/javascripts/vue_shared/components/time_ago_tooltip.vue
index af2b4c6786e..1c6ef071a6d 100644
--- a/app/assets/javascripts/vue_shared/components/time_ago_tooltip.vue
+++ b/app/assets/javascripts/vue_shared/components/time_ago_tooltip.vue
@@ -20,12 +20,6 @@ export default {
default: 'top',
},
- shortFormat: {
- type: Boolean,
- required: false,
- default: false,
- },
-
cssClass: {
type: String,
required: false,
@@ -37,18 +31,12 @@ export default {
tooltipMixin,
timeagoMixin,
],
-
- computed: {
- timeagoCssClass() {
- return this.shortFormat ? 'js-short-timeago' : 'js-timeago';
- },
- },
};
</script>
<template>
<time
- :class="[timeagoCssClass, cssClass]"
- class="js-timeago js-timeago-render"
+ :class="cssClass"
+ class="js-vue-timeago"
:title="tooltipTitle(time)"
:data-placement="tooltipPlacement"
data-container="body"
diff --git a/app/assets/stylesheets/framework/files.scss b/app/assets/stylesheets/framework/files.scss
index d08df05fd6c..da03e4f5b5e 100644
--- a/app/assets/stylesheets/framework/files.scss
+++ b/app/assets/stylesheets/framework/files.scss
@@ -59,6 +59,44 @@
}
}
+ .file-blame-legend {
+ background-color: $gray-light;
+ text-align: right;
+ padding: 8px $gl-padding;
+ border-bottom: 1px solid $border-color;
+
+ @media (max-width: $screen-xs-max) {
+ text-align: left;
+ }
+
+ .left-label {
+ padding-right: 5px;
+ }
+
+ .right-label {
+ padding-left: 5px;
+ }
+
+ .legend-box {
+ display: inline-block;
+ width: 10px;
+ height: 10px;
+ padding: 0 2px;
+ }
+
+ @for $i from 0 through 5 {
+ .legend-box-#{$i} {
+ background-color: mix($blame-cyan, $blame-blue, $i / 5.0 * 100%);
+ }
+ }
+
+ @for $i from 1 through 4 {
+ .legend-box-#{$i + 5} {
+ background-color: mix($blame-gray, $blame-cyan, $i / 4.0 * 100%);
+ }
+ }
+ }
+
.file-content {
background: $white-light;
@@ -118,6 +156,19 @@
padding: 5px 10px;
min-width: 400px;
background: $gray-light;
+ border-left: 3px solid;
+ }
+
+ @for $i from 0 through 5 {
+ td.blame-commit-age-#{$i} {
+ border-left-color: mix($blame-cyan, $blame-blue, $i / 5.0 * 100%);
+ }
+ }
+
+ @for $i from 1 through 4 {
+ td.blame-commit-age-#{$i + 5} {
+ border-left-color: mix($blame-gray, $blame-cyan, $i / 4.0 * 100%);
+ }
}
td.line-numbers {
diff --git a/app/assets/stylesheets/framework/filters.scss b/app/assets/stylesheets/framework/filters.scss
index cfbaaaa04c7..880ab52fa1b 100644
--- a/app/assets/stylesheets/framework/filters.scss
+++ b/app/assets/stylesheets/framework/filters.scss
@@ -152,7 +152,7 @@
}
.value-container {
- background-color: $filter-value-selected-color;
+ box-shadow: inset 0 0 0 100px $filtered-search-term-shadow-color;
}
}
diff --git a/app/assets/stylesheets/framework/forms.scss b/app/assets/stylesheets/framework/forms.scss
index 432024779fd..61e3897f369 100644
--- a/app/assets/stylesheets/framework/forms.scss
+++ b/app/assets/stylesheets/framework/forms.scss
@@ -125,10 +125,11 @@ label {
.select-wrapper {
position: relative;
- .fa-caret-down {
+ .fa-chevron-down {
position: absolute;
+ font-size: 10px;
right: 10px;
- top: 10px;
+ top: 12px;
color: $gray-darkest;
pointer-events: none;
}
@@ -138,6 +139,12 @@ label {
padding-left: 10px;
padding-right: 10px;
-webkit-appearance: none;
+ -moz-appearance: none;
+ appearance: none;
+
+ &::-ms-expand {
+ display: none;
+ }
}
.form-control-inline {
@@ -148,7 +155,8 @@ label {
margin-top: 35px;
}
-.form-group .control-label {
+.form-group .control-label,
+.form-group .control-label-full-width {
font-weight: normal;
}
diff --git a/app/assets/stylesheets/framework/layout.scss b/app/assets/stylesheets/framework/layout.scss
index 9e8acf4e73c..49bff23452d 100644
--- a/app/assets/stylesheets/framework/layout.scss
+++ b/app/assets/stylesheets/framework/layout.scss
@@ -51,6 +51,10 @@ body {
&.limit-container-width {
max-width: $limited-layout-width;
}
+
+ &.limit-container-width-sm {
+ max-width: 790px;
+ }
}
.alert-wrapper {
diff --git a/app/assets/stylesheets/framework/markdown_area.scss b/app/assets/stylesheets/framework/markdown_area.scss
index 80691a234f8..b21bcc22a87 100644
--- a/app/assets/stylesheets/framework/markdown_area.scss
+++ b/app/assets/stylesheets/framework/markdown_area.scss
@@ -174,3 +174,14 @@
white-space: nowrap;
}
}
+
+@media(max-width: $screen-xs-max) {
+ .atwho-view-ul {
+ width: 350px;
+ }
+
+ .atwho-view ul li {
+ overflow: hidden;
+ text-overflow: ellipsis;
+ }
+}
diff --git a/app/assets/stylesheets/framework/page-header.scss b/app/assets/stylesheets/framework/page-header.scss
index 5f4211147f3..f1ecd050a0a 100644
--- a/app/assets/stylesheets/framework/page-header.scss
+++ b/app/assets/stylesheets/framework/page-header.scss
@@ -59,4 +59,8 @@
margin: 0 2px 0 3px;
}
}
+
+ .ci-status {
+ margin-right: 10px;
+ }
}
diff --git a/app/assets/stylesheets/framework/panels.scss b/app/assets/stylesheets/framework/panels.scss
index 9d8d08dff88..e8d69e62194 100644
--- a/app/assets/stylesheets/framework/panels.scss
+++ b/app/assets/stylesheets/framework/panels.scss
@@ -1,50 +1,60 @@
.panel {
margin-bottom: $gl-padding;
+}
+
+.panel-slim {
+ @extend .panel;
+ margin-bottom: $gl-vert-padding;
+}
+
+
+.panel-heading {
+ padding: $gl-vert-padding $gl-padding;
+ line-height: 36px;
+
+ .controls {
+ margin-top: -2px;
+ float: right;
+ }
+
+ .dropdown-menu-toggle {
+ line-height: 20px;
+ }
- .panel-heading {
- padding: $gl-vert-padding $gl-padding;
- line-height: 36px;
-
- .controls {
- margin-top: -2px;
- float: right;
- }
-
- .dropdown-menu-toggle {
- line-height: 20px;
- }
-
- .badge {
- margin-top: -2px;
- margin-left: 5px;
- }
-
- &.split {
- display: flex;
- align-items: center;
- }
-
- .left {
- flex: 1 1 auto;
- }
-
- .right {
- flex: 0 0 auto;
- text-align: right;
- }
+ .badge {
+ margin-top: -2px;
+ margin-left: 5px;
}
- .panel-body {
- padding: $gl-padding;
+ &.split {
+ display: flex;
+ align-items: center;
+ }
- .form-actions {
- margin: -$gl-padding;
- margin-top: $gl-padding;
- }
+ .left {
+ flex: 1 1 auto;
}
- .panel-title {
- font-size: inherit;
- line-height: inherit;
+ .right {
+ flex: 0 0 auto;
+ text-align: right;
}
}
+
+.panel-empty-heading {
+ border-bottom: 0;
+}
+
+.panel-body {
+ padding: $gl-padding;
+
+ .form-actions {
+ margin: -$gl-padding;
+ margin-top: $gl-padding;
+ }
+}
+
+.panel-title {
+ font-size: inherit;
+ line-height: inherit;
+}
diff --git a/app/assets/stylesheets/framework/responsive-tables.scss b/app/assets/stylesheets/framework/responsive-tables.scss
index f0a4c66aa1a..d2c90908baa 100644
--- a/app/assets/stylesheets/framework/responsive-tables.scss
+++ b/app/assets/stylesheets/framework/responsive-tables.scss
@@ -36,13 +36,58 @@
align-self: stretch;
padding: 10px;
align-items: center;
- height: 62px;
+ min-height: 62px;
&:not(:first-of-type) {
border-top: 1px solid $white-normal;
}
}
}
+
+ &.section-wrap {
+ white-space: normal;
+
+ @media (max-width: $screen-sm-max) {
+ flex-wrap: wrap;
+ }
+ }
+ }
+}
+
+
+.table-button-footer {
+ @media (min-width: $screen-md-min) {
+ text-align: right;
+ }
+
+ @media (max-width: $screen-sm-max) {
+ background-color: $gray-normal;
+ align-self: stretch;
+ border-top: 1px solid $border-color;
+
+ .table-action-buttons {
+ padding: 10px 5px;
+ display: flex;
+
+ .btn {
+ border-radius: 3px;
+ }
+
+ > .btn-group,
+ > .external-url,
+ > .btn {
+ flex: 1 1 28px;
+ margin: 0 5px;
+ }
+
+ .dropdown-new {
+ width: 100%;
+ }
+
+ .dropdown-menu {
+ min-width: initial;
+ }
+ }
}
}
@@ -56,6 +101,7 @@
.table-mobile-header {
color: $gl-text-color-secondary;
+ text-align: left;
@include flex-max-width(40);
@media (min-width: $screen-md-min) {
diff --git a/app/assets/stylesheets/framework/selects.scss b/app/assets/stylesheets/framework/selects.scss
index 5ae833cd5f6..40e654f4838 100644
--- a/app/assets/stylesheets/framework/selects.scss
+++ b/app/assets/stylesheets/framework/selects.scss
@@ -18,19 +18,28 @@
background-image: none;
background-color: transparent;
border: none;
- padding-top: 6px;
- padding-right: 10px;
+ padding-top: 12px;
+ padding-right: 20px;
+ font-size: 10px;
b {
- display: inline-block;
- width: 0;
- height: 0;
- margin-left: 2px;
- vertical-align: middle;
- border-top: 5px dashed;
- border-right: 5px solid transparent;
- border-left: 5px solid transparent;
+ display: none;
+ }
+
+ &::after {
+ content: "\f078";
+ position: absolute;
+ z-index: 1;
+ text-align: center;
+ pointer-events: none;
+ box-sizing: border-box;
color: $gray-darkest;
+ display: inline-block;
+ font: normal normal normal 14px/1 FontAwesome;
+ font-size: inherit;
+ text-rendering: auto;
+ -webkit-font-smoothing: antialiased;
+ -moz-osx-font-smoothing: grayscale;
}
}
@@ -109,10 +118,12 @@
line-height: 15px;
background-color: $gray-light;
background-image: none;
+ padding: 3px 18px 3px 5px;
.select2-search-choice-close {
- top: 4px;
- left: 3px;
+ top: 5px;
+ left: initial;
+ right: 3px;
}
&.select2-search-choice-focus {
diff --git a/app/assets/stylesheets/framework/tw_bootstrap_variables.scss b/app/assets/stylesheets/framework/tw_bootstrap_variables.scss
index c9f345d24be..b666223b120 100644
--- a/app/assets/stylesheets/framework/tw_bootstrap_variables.scss
+++ b/app/assets/stylesheets/framework/tw_bootstrap_variables.scss
@@ -74,9 +74,9 @@ $pagination-hover-color: $gl-text-color;
$pagination-hover-bg: $row-hover;
$pagination-hover-border: $border-color;
-$pagination-active-color: $blue-600;
-$pagination-active-bg: $white-light;
-$pagination-active-border: $border-color;
+$pagination-active-color: $white-light;
+$pagination-active-bg: $gl-link-color;
+$pagination-active-border: $gl-link-color;
$pagination-disabled-color: #cdcdcd;
$pagination-disabled-bg: $gray-light;
diff --git a/app/assets/stylesheets/framework/variables.scss b/app/assets/stylesheets/framework/variables.scss
index 4114a050d9a..71513199d84 100644
--- a/app/assets/stylesheets/framework/variables.scss
+++ b/app/assets/stylesheets/framework/variables.scss
@@ -282,6 +282,7 @@ $dropdown-toggle-active-border-color: darken($border-color, 14%);
/*
* Filtered Search
*/
+$filtered-search-term-shadow-color: rgba(0, 0, 0, 0.09);
$dropdown-hover-color: $blue-400;
/*
@@ -365,6 +366,13 @@ $avatar-border: rgba(0, 0, 0, .1);
$gl-avatar-size: 40px;
/*
+* Blame
+*/
+$blame-gray: #ededed;
+$blame-cyan: #acd5f2;
+$blame-blue: #254e77;
+
+/*
* Builds
*/
$builds-trace-bg: #111;
diff --git a/app/assets/stylesheets/mailers/devise.scss b/app/assets/stylesheets/mailers/devise.scss
deleted file mode 100644
index 9f613710cf4..00000000000
--- a/app/assets/stylesheets/mailers/devise.scss
+++ /dev/null
@@ -1,140 +0,0 @@
-@import "framework/variables";
-
-// NOTE: This stylesheet is for the exclusive use of the `devise_mailer` layout
-// used for Devise email templates, and _should not_ be included in any
-// application stylesheets.
-//
-// Styles defined here are embedded directly into the resulting email HTML via
-// the `premailer` gem.
-
-$body-background-color: #363636;
-$message-background-color: #fafafa;
-
-$header-color: #6b4fbb;
-$body-color: #444;
-$cta-color: #e14329;
-$footer-link-color: #7e7e7e;
-
-$font-family: Helvetica, Arial, sans-serif;
-
-body {
- background-color: $body-background-color;
- font-family: $font-family;
- margin: 0;
- padding: 0;
-}
-
-table {
- -premailer-cellpadding: 0;
- -premailer-cellspacing: 0;
-
- border: 0;
- border-collapse: separate;
-
- &#wrapper {
- background-color: $body-background-color;
- width: 100%;
- }
-
- &#header {
- margin: 0 auto;
- text-align: left;
- width: 600px;
-
- & > td {
- text-align: center;
- }
- }
-
- &#body {
- background-color: $message-background-color;
- border: 1px solid $black;
- border-radius: 4px;
- margin: 0 auto;
- width: 600px;
- }
-
- &#footer {
- color: $footer-link-color;
- font-size: 14px;
- text-align: center;
- width: 100%;
- }
-
- td {
- &#body-container {
- padding: 20px 40px;
- }
- }
-}
-
-.center {
- text-align: center;
-}
-
-#logo {
- border: none;
- outline: none;
- min-height: 88px;
- width: 134px;
-}
-
-#content {
- h2 {
- color: $header-color;
- font-size: 30px;
- font-weight: 400;
- line-height: 34px;
- margin-top: 0;
- }
-
- p {
- color: $body-color;
- font-size: 17px;
- line-height: 24px;
- margin-bottom: 0;
- }
-}
-
-#cta {
- border: 1px solid $cta-color;
- border-radius: 3px;
- display: inline-block;
- margin: 20px 0;
- padding: 12px 24px;
-
- a {
- background-color: $message-background-color;
- color: $cta-color;
- display: inline-block;
- text-decoration: none;
- }
-}
-
-#tanuki {
- padding: 40px 0 0;
-
- img {
- border: none;
- outline: none;
- width: 37px;
- min-height: 36px;
- }
-}
-
-#tagline {
- font-size: 22px;
- font-weight: 100;
- padding: 4px 0 40px;
-}
-
-#social {
- padding: 0 10px 20px;
- width: 600px;
- word-spacing: 20px;
-
- a {
- color: $footer-link-color;
- text-decoration: none;
- }
-}
diff --git a/app/assets/stylesheets/pages/boards.scss b/app/assets/stylesheets/pages/boards.scss
index 740e383dbb5..85109fec91a 100644
--- a/app/assets/stylesheets/pages/boards.scss
+++ b/app/assets/stylesheets/pages/boards.scss
@@ -1,3 +1,5 @@
+@import "./issues/issue_count_badge";
+
[v-cloak] {
display: none;
}
@@ -133,7 +135,7 @@
}
.board-list-component,
- .board-issue-count-holder {
+ .issue-count-badge {
display: none;
}
}
@@ -429,30 +431,6 @@
margin: 5px;
}
-.board-issue-count-holder {
- margin-top: -3px;
-
- .btn {
- line-height: 12px;
- border-top-left-radius: 0;
- border-bottom-left-radius: 0;
- }
-}
-
-.board-issue-count {
- padding-right: 10px;
- padding-left: 10px;
- line-height: 21px;
- border-radius: $border-radius-base;
- border: 1px solid $border-color;
-
- &.has-btn {
- border-top-right-radius: 0;
- border-bottom-right-radius: 0;
- border-width: 1px 0 1px 1px;
- }
-}
-
.page-with-layout-nav.page-with-sub-nav .issue-boards-sidebar {
&.right-sidebar {
top: 0;
diff --git a/app/assets/stylesheets/pages/builds.scss b/app/assets/stylesheets/pages/builds.scss
index 203fd6d07e4..7eee0a71c66 100644
--- a/app/assets/stylesheets/pages/builds.scss
+++ b/app/assets/stylesheets/pages/builds.scss
@@ -72,7 +72,7 @@
display: flex;
justify-content: flex-end;
background: $gray-light;
- border: 1px solid $gray-normal;
+ border: 1px solid $border-color;
color: $gl-text-color;
.truncated-info {
@@ -150,6 +150,7 @@
overflow-y: scroll;
overflow-x: hidden;
padding: 10px 20px 20px 5px;
+ white-space: pre;
}
.environment-information {
diff --git a/app/assets/stylesheets/pages/environments.scss b/app/assets/stylesheets/pages/environments.scss
index b24803678ea..89bd437b362 100644
--- a/app/assets/stylesheets/pages/environments.scss
+++ b/app/assets/stylesheets/pages/environments.scss
@@ -124,43 +124,6 @@
}
.gl-responsive-table-row {
- .environments-actions {
- @media (min-width: $screen-md-min) {
- text-align: right;
- }
-
- @media (max-width: $screen-sm-max) {
- background-color: $gray-normal;
- align-self: stretch;
- border-top: 1px solid $border-color;
-
- .environment-action-buttons {
- padding: 10px 5px;
- display: flex;
-
- .btn {
- border-radius: 3px;
- }
-
- > .btn-group,
- > .external-url,
- > .btn {
- flex: 1;
- flex-basis: 28px;
- margin: 0 5px;
- }
-
- .dropdown-new {
- width: 100%;
- }
-
- .dropdown-menu {
- min-width: initial;
- }
- }
- }
- }
-
.branch-commit {
max-width: 100%;
}
diff --git a/app/assets/stylesheets/pages/events.scss b/app/assets/stylesheets/pages/events.scss
index 5b723f7c722..4c3fa1fb8d4 100644
--- a/app/assets/stylesheets/pages/events.scss
+++ b/app/assets/stylesheets/pages/events.scss
@@ -89,7 +89,6 @@
background: $gray-light;
border-radius: 0;
color: $events-pre-color;
- margin: 0 20px;
overflow: hidden;
}
diff --git a/app/assets/stylesheets/pages/issues.scss b/app/assets/stylesheets/pages/issues.scss
index f923a1104a9..8cdb3f34ae5 100644
--- a/app/assets/stylesheets/pages/issues.scss
+++ b/app/assets/stylesheets/pages/issues.scss
@@ -1,3 +1,5 @@
+@import "./issues/issue_count_badge";
+
.issues-list {
.issue {
padding: 10px 0 10px $gl-padding;
diff --git a/app/assets/stylesheets/pages/issues/issue_count_badge.scss b/app/assets/stylesheets/pages/issues/issue_count_badge.scss
new file mode 100644
index 00000000000..ccb62bfed18
--- /dev/null
+++ b/app/assets/stylesheets/pages/issues/issue_count_badge.scss
@@ -0,0 +1,29 @@
+.issue-count-badge {
+ display: inline-flex;
+ align-items: stretch;
+ height: 24px;
+}
+
+.issue-count-badge-count {
+ display: flex;
+ align-items: center;
+ padding-right: 10px;
+ padding-left: 10px;
+ border: 1px solid $border-color;
+ border-radius: $border-radius-base;
+ line-height: 1;
+
+ &.has-btn {
+ border-right: 0;
+ border-top-right-radius: 0;
+ border-bottom-right-radius: 0;
+ }
+}
+
+.issue-count-badge-add-button {
+ display: flex;
+ align-items: center;
+ border: 1px solid $border-color;
+ border-radius: 0 $border-radius-base $border-radius-base 0;
+ line-height: 1;
+}
diff --git a/app/assets/stylesheets/pages/merge_requests.scss b/app/assets/stylesheets/pages/merge_requests.scss
index 2dc7f73a295..c0bd045f1fc 100644
--- a/app/assets/stylesheets/pages/merge_requests.scss
+++ b/app/assets/stylesheets/pages/merge_requests.scss
@@ -372,6 +372,10 @@
margin-left: 12px;
}
+ &.mr-state-locked + .mr-info-list.mr-links {
+ margin-top: -16px;
+ }
+
&.empty-state {
.artwork {
margin-bottom: $gl-padding;
diff --git a/app/assets/stylesheets/pages/milestone.scss b/app/assets/stylesheets/pages/milestone.scss
index 335e587b8f4..55e0ee1936e 100644
--- a/app/assets/stylesheets/pages/milestone.scss
+++ b/app/assets/stylesheets/pages/milestone.scss
@@ -111,8 +111,8 @@
}
}
-.issues-sortable-list,
-.merge_requests-sortable-list {
+.milestone-issues-list,
+.milestone-merge_requests-list {
.issuable-detail {
display: block;
margin-top: 7px;
@@ -197,6 +197,4 @@
.issuable-row {
background-color: $white-light;
- cursor: -webkit-grab;
- cursor: grab;
}
diff --git a/app/assets/stylesheets/pages/pipeline_schedules.scss b/app/assets/stylesheets/pages/pipeline_schedules.scss
index ab417948931..595eb40fec7 100644
--- a/app/assets/stylesheets/pages/pipeline_schedules.scss
+++ b/app/assets/stylesheets/pages/pipeline_schedules.scss
@@ -12,7 +12,7 @@
.interval-pattern-form-group {
label {
margin-right: 10px;
- font-size: 12px;
+ font-weight: normal;
&[for='custom'] {
margin-right: 0;
diff --git a/app/assets/stylesheets/pages/pipelines.scss b/app/assets/stylesheets/pages/pipelines.scss
index cd9382e8de5..a85ba3a5955 100644
--- a/app/assets/stylesheets/pages/pipelines.scss
+++ b/app/assets/stylesheets/pages/pipelines.scss
@@ -10,17 +10,13 @@
.table-holder {
width: 100%;
-
- @media (max-width: $screen-sm-max) {
- overflow: auto;
- }
}
.commit-title {
margin: 0;
}
- .table.ci-table {
+ .ci-table {
.label {
margin-bottom: 3px;
@@ -30,11 +26,6 @@
color: $black;
}
- .stage-cell {
- min-width: 130px; // Guarantees we show at least 4 stages in line
- width: 20%;
- }
-
.pipelines-time-ago {
text-align: right;
}
@@ -108,39 +99,7 @@
}
}
-.table.ci-table {
-
- &.builds-page tbody tr {
- height: 71px;
- }
-
- tr {
- th {
- padding: 16px 8px;
- border: none;
- }
-
- td {
- padding: 10px 8px;
- }
-
- td.environments-actions {
- padding-right: 0;
- }
-
- td.stage-cell {
- padding: 10px 0;
- }
-
- .commit-link {
- padding: 9px 8px 10px 2px;
- }
- }
-
- tbody {
- border-top-width: 1px;
- }
-
+.ci-table {
.build.retried {
background-color: $gray-lightest;
}
@@ -194,13 +153,6 @@
color: $gl-link-color;
}
- .commit-title {
- max-width: 225px;
- overflow: hidden;
- white-space: nowrap;
- text-overflow: ellipsis;
- }
-
.label {
margin-right: 4px;
}
@@ -253,11 +205,7 @@
}
.stage-cell {
- font-size: 0;
- padding: 0 4px;
-
- > .stage-container > div > button > span > svg,
- > .stage-container > button > svg {
+ .mini-pipeline-graph-dropdown-toggle svg {
height: 22px;
width: 22px;
position: absolute;
@@ -631,6 +579,23 @@
font-weight: normal;
}
+@mixin mini-pipeline-graph-color($color-light, $color-main, $color-dark) {
+ border-color: $color-main;
+ color: $color-main;
+
+ &:hover,
+ &:focus,
+ &:active {
+ background-color: $color-light;
+ border-color: $color-dark;
+ color: $color-dark;
+
+ svg {
+ fill: $color-dark;
+ }
+ }
+}
+
// Dropdown button in mini pipeline graph
.mini-pipeline-graph-dropdown-toggle {
border-radius: 100px;
@@ -670,100 +635,32 @@
// Dropdown button animation in mini pipeline graph
&.ci-status-icon-success {
- border-color: $green-500;
- color: $green-500;
-
- &:hover,
- &:focus,
- &:active {
- background-color: $green-50;
- border-color: $green-600;
- color: $green-600;
-
- svg {
- fill: $green-600;
- }
- }
+ @include mini-pipeline-graph-color($green-50, $green-500, $green-600);
}
&.ci-status-icon-failed {
- border-color: $red-500;
- color: $red-500;
-
- &:hover,
- &:focus,
- &:active {
- background-color: $red-50;
- border-color: $red-600;
- color: $red-600;
-
- svg {
- fill: $red-600;
- }
- }
+ @include mini-pipeline-graph-color($red-50, $red-500, $red-600);
}
&.ci-status-icon-pending,
&.ci-status-icon-success_with_warnings {
- border-color: $orange-500;
- color: $orange-500;
-
- &:hover,
- &:focus,
- &:active {
- background-color: $orange-50;
- border-color: $orange-600;
- color: $orange-600;
-
- svg {
- fill: $orange-600;
- }
- }
+ @include mini-pipeline-graph-color($orange-50, $orange-500, $orange-600);
}
&.ci-status-icon-running {
- border-color: $blue-400;
- color: $blue-400;
-
- &:hover,
- &:focus,
- &:active {
- background-color: $blue-50;
- border-color: $blue-600;
- color: $blue-600;
-
- svg {
- fill: $blue-600;
- }
- }
+ @include mini-pipeline-graph-color($blue-50, $blue-400, $blue-600);
}
&.ci-status-icon-canceled,
&.ci-status-icon-disabled,
&.ci-status-icon-not-found,
&.ci-status-icon-manual {
- border-color: $gl-text-color;
- color: $gl-text-color;
-
- &:hover,
- &:focus,
- &:active {
- background-color: rgba($gl-text-color, 0.1);
- border-color: $gl-text-color;
- }
+ @include mini-pipeline-graph-color(rgba($gl-text-color, 0.1), $gl-text-color, $gl-text-color);
}
&.ci-status-icon-created,
&.ci-status-icon-skipped {
- border-color: $gray-darkest;
- color: $gray-darkest;
-
- &:hover,
- &:focus,
- &:active {
- background-color: rgba($gray-darkest, 0.1);
- border-color: $gray-darkest;
- }
+ @include mini-pipeline-graph-color(rgba($gray-darkest, 0.1), $gray-darkest, $gray-darkest);
}
}
@@ -842,6 +739,10 @@
top: 1px;
vertical-align: text-bottom;
position: relative;
+
+ @media (max-width: $screen-xs-max) {
+ max-width: 60%;
+ }
}
// status icon on the left
@@ -929,8 +830,14 @@
border-color: transparent;
border-style: solid;
top: -6px;
- left: 2px;
+ left: 50%;
+ transform: translate(-50%, 0);
border-width: 0 5px 6px;
+
+ @media (max-width: $screen-sm-max) {
+ left: 100%;
+ margin-left: -12px;
+ }
}
&::before {
@@ -945,6 +852,20 @@
}
/**
+ * Center dropdown menu in mini graph
+ */
+.mini-pipeline-graph-dropdown-menu.dropdown-menu {
+ transform: translate(-80%, 0);
+ min-width: 150px;
+
+ @media(min-width: $screen-md-min) {
+ transform: translate(-50%, 0);
+ right: auto;
+ left: 50%;
+ min-width: 240px;
+ }
+}
+/**
* Terminal
*/
.terminal-icon {
diff --git a/app/assets/stylesheets/pages/status.scss b/app/assets/stylesheets/pages/status.scss
index 4ed8617b6a3..67ad1ae60af 100644
--- a/app/assets/stylesheets/pages/status.scss
+++ b/app/assets/stylesheets/pages/status.scss
@@ -1,142 +1,82 @@
-.container-fluid {
- .ci-status {
- padding: 2px 7px 4px;
- margin-right: 10px;
- border: 1px solid $gray-darker;
- white-space: nowrap;
- border-radius: 4px;
-
- &:hover,
- &:focus {
- text-decoration: none;
- }
-
- svg {
- height: 13px;
- width: 13px;
- position: relative;
- top: 2px;
- overflow: visible;
- }
+@mixin status-color($color-light, $color-main, $color-dark) {
+ color: $color-main;
+ border-color: $color-main;
- &.ci-failed {
- color: $red-500;
- border-color: $red-500;
+ &:not(span):hover {
+ background-color: $color-light;
+ color: $color-dark;
+ border-color: $color-dark;
- &:not(span):hover {
- background-color: $red-50;
- color: $red-600;
- border-color: $red-600;
-
- svg {
- fill: $red-600;
- }
- }
-
- svg {
- fill: $red-500;
- }
+ svg {
+ fill: $color-dark;
}
+ }
- &.ci-success {
- color: $green-600;
- border-color: $green-500;
+ svg {
+ fill: $color-main;
+ }
+}
- &:not(span):hover {
- background-color: $green-50;
- color: $green-700;
- border-color: $green-600;
+.ci-status {
+ padding: 2px 7px 4px;
+ border: 1px solid $gray-darker;
+ white-space: nowrap;
+ border-radius: 4px;
- svg {
- fill: $green-600;
- }
- }
+ &:hover,
+ &:focus {
+ text-decoration: none;
+ }
- svg {
- fill: $green-500;
- }
- }
+ svg {
+ height: 13px;
+ width: 13px;
+ position: relative;
+ top: 2px;
+ overflow: visible;
+ }
- &.ci-canceled,
- &.ci-disabled {
- color: $gl-text-color;
- border-color: $gl-text-color;
+ &.ci-failed {
+ @include status-color($red-50, $red-500, $red-600);
+ }
- &:not(span):hover {
- background-color: rgba($gl-text-color, .07);
- }
+ &.ci-success {
+ @include status-color($green-50, $green-500, $green-700);
+ }
- svg {
- fill: $gl-text-color;
- }
- }
+ &.ci-canceled,
+ &.ci-disabled,
+ &.ci-manual {
+ color: $gl-text-color;
+ border-color: $gl-text-color;
- &.ci-pending,
- &.ci-success_with_warnings,
- &.ci-failed_with_warnings {
- color: $orange-600;
- border-color: $orange-500;
-
- &:not(span):hover {
- background-color: $orange-50;
- color: $orange-700;
- border-color: $orange-600;
-
- svg {
- fill: $orange-600;
- }
- }
-
- svg {
- fill: $orange-500;
- }
+ &:not(span):hover {
+ background-color: rgba($gl-text-color, .07);
}
+ }
- &.ci-info,
- &.ci-running {
- color: $blue-500;
- border-color: $blue-500;
-
- &:not(span):hover {
- background-color: $blue-50;
- color: $blue-600;
- border-color: $blue-600;
-
- svg {
- fill: $blue-600;
- }
- }
-
- svg {
- fill: $blue-500;
- }
- }
+ &.ci-pending,
+ &.ci-failed_with_warnings,
+ &.ci-success_with_warnings {
+ @include status-color($orange-50, $orange-500, $orange-700);
+ }
- &.ci-created,
- &.ci-skipped {
- color: $gl-text-color-secondary;
- border-color: $gl-text-color-secondary;
+ &.ci-info,
+ &.ci-running {
+ @include status-color($blue-50, $blue-500, $blue-600);
+ }
- &:not(span):hover {
- background-color: rgba($gl-text-color-secondary, .07);
- }
+ &.ci-created,
+ &.ci-skipped {
+ color: $gl-text-color-secondary;
+ border-color: $gl-text-color-secondary;
- svg {
- fill: $gl-text-color-secondary;
- }
+ &:not(span):hover {
+ background-color: rgba($gl-text-color-secondary, .07);
}
- &.ci-manual {
- color: $gl-text-color;
- border-color: $gl-text-color;
-
- &:not(span):hover {
- background-color: rgba($gl-text-color, .07);
- }
-
- svg {
- fill: $gl-text-color;
- }
+ svg {
+ fill: $gl-text-color-secondary;
}
}
}
diff --git a/app/assets/stylesheets/pages/wiki.scss b/app/assets/stylesheets/pages/wiki.scss
index b64b89485f7..94d0a39f397 100644
--- a/app/assets/stylesheets/pages/wiki.scss
+++ b/app/assets/stylesheets/pages/wiki.scss
@@ -42,9 +42,7 @@
}
.git-access-header {
- padding: 16px 40px 11px 0;
- line-height: 28px;
- font-size: 18px;
+ padding: $gl-padding 0 $gl-padding-top;
}
.git-clone-holder {
@@ -66,6 +64,7 @@
.git-clone-holder {
width: 480px;
+ padding-bottom: $gl-padding;
}
.nav-controls {
@@ -89,9 +88,9 @@
margin: $gl-padding 0;
h3 {
- font-size: 22px;
+ font-size: 19px;
font-weight: normal;
- margin-top: 1.4em;
+ margin: $gl-padding 0;
}
}
diff --git a/app/controllers/admin/application_settings_controller.rb b/app/controllers/admin/application_settings_controller.rb
index 75fb19e815f..4d4b8a8425f 100644
--- a/app/controllers/admin/application_settings_controller.rb
+++ b/app/controllers/admin/application_settings_controller.rb
@@ -100,6 +100,8 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController
:enabled_git_access_protocol,
:gravatar_enabled,
:help_page_text,
+ :help_page_hide_commercial_content,
+ :help_page_support_url,
:home_page_url,
:housekeeping_bitmaps_enabled,
:housekeeping_enabled,
diff --git a/app/controllers/concerns/milestone_actions.rb b/app/controllers/concerns/milestone_actions.rb
index b2536a1c949..1ff785ac2ca 100644
--- a/app/controllers/concerns/milestone_actions.rb
+++ b/app/controllers/concerns/milestone_actions.rb
@@ -6,7 +6,7 @@ module MilestoneActions
format.html { redirect_to milestone_redirect_path }
format.json do
render json: tabs_json("shared/milestones/_merge_requests_tab", {
- merge_requests: @milestone.merge_requests,
+ merge_requests: @milestone.sorted_merge_requests,
show_project_name: true
})
end
diff --git a/app/controllers/projects/application_controller.rb b/app/controllers/projects/application_controller.rb
index cb4bd0ad5f5..603a51266da 100644
--- a/app/controllers/projects/application_controller.rb
+++ b/app/controllers/projects/application_controller.rb
@@ -80,10 +80,6 @@ class Projects::ApplicationController < ApplicationController
cookies.permanent[:diff_view] = params.delete(:view) if params[:view].present?
end
- def builds_enabled
- return render_404 unless @project.feature_available?(:builds, current_user)
- end
-
def require_pages_enabled!
not_found unless Gitlab.config.pages.enabled
end
diff --git a/app/controllers/projects/git_http_client_controller.rb b/app/controllers/projects/git_http_client_controller.rb
index 928f17e6a8e..7d0e2b3e2ef 100644
--- a/app/controllers/projects/git_http_client_controller.rb
+++ b/app/controllers/projects/git_http_client_controller.rb
@@ -4,7 +4,7 @@ class Projects::GitHttpClientController < Projects::ApplicationController
include ActionController::HttpAuthentication::Basic
include KerberosSpnegoHelper
- attr_reader :authentication_result
+ attr_reader :authentication_result, :redirected_path
delegate :actor, :authentication_abilities, to: :authentication_result, allow_nil: true
@@ -14,7 +14,6 @@ class Projects::GitHttpClientController < Projects::ApplicationController
skip_before_action :verify_authenticity_token
skip_before_action :repository
before_action :authenticate_user
- before_action :ensure_project_found!
private
@@ -68,38 +67,14 @@ class Projects::GitHttpClientController < Projects::ApplicationController
headers['Www-Authenticate'] = challenges.join("\n") if challenges.any?
end
- def ensure_project_found!
- render_not_found if project.blank?
- end
-
def project
- return @project if defined?(@project)
-
- project_id, _ = project_id_with_suffix
- @project =
- if project_id.blank?
- nil
- else
- Project.find_by_full_path("#{params[:namespace_id]}/#{project_id}")
- end
- end
+ parse_repo_path unless defined?(@project)
- # This method returns two values so that we can parse
- # params[:project_id] (untrusted input!) in exactly one place.
- def project_id_with_suffix
- id = params[:project_id] || ''
-
- %w[.wiki.git .git].each do |suffix|
- if id.end_with?(suffix)
- # Be careful to only remove the suffix from the end of 'id'.
- # Accidentally removing it from the middle is how security
- # vulnerabilities happen!
- return [id.slice(0, id.length - suffix.length), suffix]
- end
- end
+ @project
+ end
- # Something is wrong with params[:project_id]; do not pass it on.
- [nil, nil]
+ def parse_repo_path
+ @project, @wiki, @redirected_path = Gitlab::RepoPath.parse("#{params[:namespace_id]}/#{params[:project_id]}")
end
def render_missing_personal_token
@@ -114,14 +89,9 @@ class Projects::GitHttpClientController < Projects::ApplicationController
end
def wiki?
- return @wiki if defined?(@wiki)
-
- _, suffix = project_id_with_suffix
- @wiki = suffix == '.wiki.git'
- end
+ parse_repo_path unless defined?(@wiki)
- def render_not_found
- render plain: 'Not Found', status: :not_found
+ @wiki
end
def handle_basic_authentication(login, password)
diff --git a/app/controllers/projects/git_http_controller.rb b/app/controllers/projects/git_http_controller.rb
index b6b62da7b60..71ae60cb8cd 100644
--- a/app/controllers/projects/git_http_controller.rb
+++ b/app/controllers/projects/git_http_controller.rb
@@ -56,7 +56,7 @@ class Projects::GitHttpController < Projects::GitHttpClientController
end
def access
- @access ||= access_klass.new(access_actor, project, 'http', authentication_abilities: authentication_abilities)
+ @access ||= access_klass.new(access_actor, project, 'http', authentication_abilities: authentication_abilities, redirected_path: redirected_path)
end
def access_actor
diff --git a/app/controllers/projects/graphs_controller.rb b/app/controllers/projects/graphs_controller.rb
index 43fc0c39801..df5221fe95f 100644
--- a/app/controllers/projects/graphs_controller.rb
+++ b/app/controllers/projects/graphs_controller.rb
@@ -5,7 +5,6 @@ class Projects::GraphsController < Projects::ApplicationController
before_action :require_non_empty_project
before_action :assign_ref_vars
before_action :authorize_download_code!
- before_action :builds_enabled, only: :ci
def show
respond_to do |format|
diff --git a/app/controllers/projects/issues_controller.rb b/app/controllers/projects/issues_controller.rb
index ebb163bf9dc..56f76e752d0 100644
--- a/app/controllers/projects/issues_controller.rb
+++ b/app/controllers/projects/issues_controller.rb
@@ -10,11 +10,7 @@ class Projects::IssuesController < Projects::ApplicationController
before_action :redirect_to_external_issue_tracker, only: [:index, :new]
before_action :module_enabled
- before_action :issue, only: [:edit, :update, :show, :referenced_merge_requests,
- :related_branches, :can_create_branch, :realtime_changes, :create_merge_request]
-
- # Allow read any issue
- before_action :authorize_read_issue!, only: [:show, :realtime_changes]
+ before_action :issue, except: [:index, :new, :create, :bulk_update]
# Allow write(create) issue
before_action :authorize_create_issue!, only: [:new, :create]
@@ -229,18 +225,19 @@ class Projects::IssuesController < Projects::ApplicationController
protected
def issue
+ return @issue if defined?(@issue)
# The Sortable default scope causes performance issues when used with find_by
@noteable = @issue ||= @project.issues.where(iid: params[:id]).reorder(nil).take!
+
+ return render_404 unless can?(current_user, :read_issue, @issue)
+
+ @issue
end
alias_method :subscribable_resource, :issue
alias_method :issuable, :issue
alias_method :awardable, :issue
alias_method :spammable, :issue
- def authorize_read_issue!
- return render_404 unless can?(current_user, :read_issue, @issue)
- end
-
def authorize_update_issue!
return render_404 unless can?(current_user, :update_issue, @issue)
end
diff --git a/app/controllers/projects/milestones_controller.rb b/app/controllers/projects/milestones_controller.rb
index ae16f69955a..62410d7f57f 100644
--- a/app/controllers/projects/milestones_controller.rb
+++ b/app/controllers/projects/milestones_controller.rb
@@ -2,7 +2,7 @@ class Projects::MilestonesController < Projects::ApplicationController
include MilestoneActions
before_action :module_enabled
- before_action :milestone, only: [:edit, :update, :destroy, :show, :sort_issues, :sort_merge_requests, :merge_requests, :participants, :labels]
+ before_action :milestone, only: [:edit, :update, :destroy, :show, :merge_requests, :participants, :labels]
# Allow read any milestone
before_action :authorize_read_milestone!
@@ -85,22 +85,6 @@ class Projects::MilestonesController < Projects::ApplicationController
end
end
- def sort_issues
- @milestone.sort_issues(params['sortable_issue'].map(&:to_i))
-
- render json: { saved: true }
- end
-
- def sort_merge_requests
- @merge_requests = @milestone.merge_requests.where(id: params['sortable_merge_request'])
- @merge_requests.each do |merge_request|
- merge_request.position = params['sortable_merge_request'].index(merge_request.id.to_s) + 1
- merge_request.save
- end
-
- render json: { saved: true }
- end
-
protected
def milestone
diff --git a/app/controllers/projects/pipelines_controller.rb b/app/controllers/projects/pipelines_controller.rb
index 6223e7943f8..8effb792689 100644
--- a/app/controllers/projects/pipelines_controller.rb
+++ b/app/controllers/projects/pipelines_controller.rb
@@ -4,7 +4,6 @@ class Projects::PipelinesController < Projects::ApplicationController
before_action :authorize_read_pipeline!
before_action :authorize_create_pipeline!, only: [:new, :create]
before_action :authorize_update_pipeline!, only: [:retry, :cancel]
- before_action :builds_enabled, only: :charts
wrap_parameters Ci::Pipeline
diff --git a/app/finders/groups_finder.rb b/app/finders/groups_finder.rb
index f68610e197c..e6fb112e7f2 100644
--- a/app/finders/groups_finder.rb
+++ b/app/finders/groups_finder.rb
@@ -5,8 +5,10 @@ class GroupsFinder < UnionFinder
end
def execute
- groups = find_union(all_groups, Group).with_route.order_id_desc
- by_parent(groups)
+ items = all_groups.map do |item|
+ by_parent(item)
+ end
+ find_union(items, Group).with_route.order_id_desc
end
private
@@ -16,12 +18,22 @@ class GroupsFinder < UnionFinder
def all_groups
groups = []
- groups << current_user.authorized_groups if current_user
+ if current_user
+ groups << Gitlab::GroupHierarchy.new(groups_for_ancestors, groups_for_descendants).all_groups
+ end
groups << Group.unscoped.public_to_user(current_user)
groups
end
+ def groups_for_ancestors
+ current_user.authorized_groups
+ end
+
+ def groups_for_descendants
+ current_user.groups
+ end
+
def by_parent(groups)
return groups unless params[:parent]
diff --git a/app/finders/issuable_finder.rb b/app/finders/issuable_finder.rb
index 957ad875858..558f8b5e2e5 100644
--- a/app/finders/issuable_finder.rb
+++ b/app/finders/issuable_finder.rb
@@ -41,6 +41,7 @@ class IssuableFinder
items = by_iids(items)
items = by_milestone(items)
items = by_label(items)
+ items = by_created_at(items)
# Filtering by project HAS TO be the last because we use the project IDs yielded by the issuable query thus far
items = by_project(items)
@@ -402,6 +403,18 @@ class IssuableFinder
params[:non_archived].present? ? items.non_archived : items
end
+ def by_created_at(items)
+ if params[:created_after].present?
+ items = items.where(items.klass.arel_table[:created_at].gteq(params[:created_after]))
+ end
+
+ if params[:created_before].present?
+ items = items.where(items.klass.arel_table[:created_at].lteq(params[:created_before]))
+ end
+
+ items
+ end
+
def current_user_related?
params[:scope] == 'created-by-me' || params[:scope] == 'authored' || params[:scope] == 'assigned-to-me'
end
diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb
index 71154da7ec5..06bbed777d5 100644
--- a/app/helpers/application_helper.rb
+++ b/app/helpers/application_helper.rb
@@ -167,9 +167,9 @@ module ApplicationHelper
css_classes = short_format ? 'js-short-timeago' : 'js-timeago'
css_classes << " #{html_class}" unless html_class.blank?
- element = content_tag :time, time.strftime("%b %d, %Y"),
+ element = content_tag :time, l(time, format: "%b %d, %Y"),
class: css_classes,
- title: time.to_time.in_time_zone.to_s(:medium),
+ title: l(time.to_time.in_time_zone, format: :timeago_tooltip),
datetime: time.to_time.getutc.iso8601,
data: {
toggle: 'tooltip',
@@ -204,6 +204,10 @@ module ApplicationHelper
'https://' + promo_host
end
+ def support_url
+ current_application_settings.help_page_support_url.presence || promo_url + '/getting-help/'
+ end
+
def page_filter_path(options = {})
without = options.delete(:without)
add_label = options.delete(:label)
diff --git a/app/helpers/blame_helper.rb b/app/helpers/blame_helper.rb
new file mode 100644
index 00000000000..d1dc4d94560
--- /dev/null
+++ b/app/helpers/blame_helper.rb
@@ -0,0 +1,21 @@
+module BlameHelper
+ def age_map_duration(blame_groups, project)
+ now = Time.zone.now
+ start_date = blame_groups.map { |blame_group| blame_group[:commit].committed_date }
+ .append(project.created_at).min
+
+ {
+ now: now,
+ started_days_ago: (now - start_date).to_i / 1.day
+ }
+ end
+
+ def age_map_class(commit_date, duration)
+ commit_date_days_ago = (duration[:now] - commit_date).to_i / 1.day
+ # Numbers 0 to 10 come from this calculation, but only commits on the oldest
+ # day get number 10 (all other numbers can be multiple days), so the range
+ # is normalized to 0-9
+ age_group = [(10 * commit_date_days_ago) / duration[:started_days_ago], 9].min
+ "blame-commit-age-#{age_group}"
+ end
+end
diff --git a/app/helpers/broadcast_messages_helper.rb b/app/helpers/broadcast_messages_helper.rb
index eb03ced67eb..0a15c29cfb5 100644
--- a/app/helpers/broadcast_messages_helper.rb
+++ b/app/helpers/broadcast_messages_helper.rb
@@ -1,5 +1,5 @@
module BroadcastMessagesHelper
- def broadcast_message(message = BroadcastMessage.current)
+ def broadcast_message(message)
return unless message.present?
content_tag :div, class: 'broadcast-message', style: broadcast_message_style(message) do
diff --git a/app/helpers/diff_helper.rb b/app/helpers/diff_helper.rb
index 2ae3a616933..16a99addd0b 100644
--- a/app/helpers/diff_helper.rb
+++ b/app/helpers/diff_helper.rb
@@ -66,12 +66,12 @@ module DiffHelper
discussions_left = discussions_right = nil
- if left && (left.unchanged? || left.discussable?)
+ if left && left.discussable? && (left.unchanged? || left.removed?)
line_code = diff_file.line_code(left)
discussions_left = @grouped_diff_discussions[line_code]
end
- if right&.discussable?
+ if right && right.discussable? && right.added?
line_code = diff_file.line_code(right)
discussions_right = @grouped_diff_discussions[line_code]
end
@@ -124,6 +124,30 @@ module DiffHelper
!diff_file.deleted_file? && @merge_request && @merge_request.source_project
end
+ def diff_render_error_reason(viewer)
+ case viewer.render_error
+ when :too_large
+ "it is too large"
+ when :server_side_but_stored_externally
+ case viewer.diff_file.external_storage
+ when :lfs
+ 'it is stored in LFS'
+ else
+ 'it is stored externally'
+ end
+ end
+ end
+
+ def diff_render_error_options(viewer)
+ diff_file = viewer.diff_file
+ options = []
+
+ blob_url = namespace_project_blob_path(@project.namespace, @project, tree_join(diff_file.content_sha, diff_file.file_path))
+ options << link_to('view the blob', blob_url)
+
+ options
+ end
+
private
def diff_btn(title, name, selected)
diff --git a/app/helpers/emails_helper.rb b/app/helpers/emails_helper.rb
index 3b24f183785..fdbca789d21 100644
--- a/app/helpers/emails_helper.rb
+++ b/app/helpers/emails_helper.rb
@@ -66,4 +66,17 @@ module EmailsHelper
)
end
end
+
+ def email_default_heading(text)
+ content_tag :h1, text, style: [
+ "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif",
+ 'color:#333333',
+ 'font-size:18px',
+ 'font-weight:400',
+ 'line-height:1.4',
+ 'padding:0',
+ 'margin:0',
+ 'text-align:center'
+ ].join(';')
+ end
end
diff --git a/app/helpers/notes_helper.rb b/app/helpers/notes_helper.rb
index c59d8dafc83..64ad7b280cb 100644
--- a/app/helpers/notes_helper.rb
+++ b/app/helpers/notes_helper.rb
@@ -10,8 +10,8 @@ module NotesHelper
Ability.can_edit_note?(current_user, note)
end
- def note_supports_slash_commands?(note)
- Notes::SlashCommandsService.supported?(note, current_user)
+ def note_supports_quick_actions?(note)
+ Notes::QuickActionsService.supported?(note, current_user)
end
def noteable_json(noteable)
diff --git a/app/helpers/projects_helper.rb b/app/helpers/projects_helper.rb
index 7441b58fddb..0d0459f5a70 100644
--- a/app/helpers/projects_helper.rb
+++ b/app/helpers/projects_helper.rb
@@ -80,7 +80,7 @@ module ProjectsHelper
end
def remove_fork_project_message(project)
- _("You are going to remove the fork relationship to source project %{forked_from_project}. Are you ABSOLUTELY sure?") %
+ _("You are going to remove the fork relationship to source project %{forked_from_project}. Are you ABSOLUTELY sure?") %
{ forked_from_project: @project.forked_from_project.name_with_namespace }
end
@@ -151,14 +151,21 @@ module ProjectsHelper
disabled: disabled_option
)
- content_tag(
- :select,
- options,
- name: "project[project_feature_attributes][#{field}]",
- id: "project_project_feature_attributes_#{field}",
- class: "pull-right form-control #{repo_children_classes(field)}",
- data: { field: field }
- ).html_safe
+ content_tag :div, class: "select-wrapper" do
+ concat(
+ content_tag(
+ :select,
+ options,
+ name: "project[project_feature_attributes][#{field}]",
+ id: "project_project_feature_attributes_#{field}",
+ class: "pull-right form-control select-control #{repo_children_classes(field)} ",
+ data: { field: field }
+ )
+ )
+ concat(
+ icon('chevron-down')
+ )
+ end.html_safe
end
def link_to_autodeploy_doc
@@ -218,6 +225,10 @@ module ProjectsHelper
nav_tabs << :container_registry
end
+ if project.builds_enabled? && can?(current_user, :read_pipeline, project)
+ nav_tabs << :pipelines
+ end
+
tab_ability_map.each do |tab, ability|
if can?(current_user, ability, project)
nav_tabs << tab
@@ -231,7 +242,6 @@ module ProjectsHelper
{
environments: :read_environment,
milestones: :read_milestone,
- pipelines: :read_pipeline,
snippets: :read_project_snippet,
settings: :admin_project,
builds: :read_build,
diff --git a/app/mailers/devise_mailer.rb b/app/mailers/devise_mailer.rb
index f7ed61625f4..962570a0efd 100644
--- a/app/mailers/devise_mailer.rb
+++ b/app/mailers/devise_mailer.rb
@@ -2,7 +2,9 @@ class DeviseMailer < Devise::Mailer
default from: "#{Gitlab.config.gitlab.email_display_name} <#{Gitlab.config.gitlab.email_from}>"
default reply_to: Gitlab.config.gitlab.email_reply_to
- layout 'devise_mailer'
+ layout 'mailer/devise'
+
+ helper EmailsHelper
protected
diff --git a/app/models/application_setting.rb b/app/models/application_setting.rb
index 2192f76499d..668caef0d2c 100644
--- a/app/models/application_setting.rb
+++ b/app/models/application_setting.rb
@@ -37,7 +37,12 @@ class ApplicationSetting < ActiveRecord::Base
validates :home_page_url,
allow_blank: true,
url: true,
- if: :home_page_url_column_exist
+ if: :home_page_url_column_exists?
+
+ validates :help_page_support_url,
+ allow_blank: true,
+ url: true,
+ if: :help_page_support_url_column_exists?
validates :after_sign_out_path,
allow_blank: true,
@@ -215,6 +220,7 @@ class ApplicationSetting < ActiveRecord::Base
domain_whitelist: Settings.gitlab['domain_whitelist'],
gravatar_enabled: Settings.gravatar['enabled'],
help_page_text: nil,
+ help_page_hide_commercial_content: false,
unique_ips_limit_per_user: 10,
unique_ips_limit_time_window: 3600,
unique_ips_limit_enabled: false,
@@ -263,10 +269,14 @@ class ApplicationSetting < ActiveRecord::Base
end
end
- def home_page_url_column_exist
+ def home_page_url_column_exists?
ActiveRecord::Base.connection.column_exists?(:application_settings, :home_page_url)
end
+ def help_page_support_url_column_exists?
+ ActiveRecord::Base.connection.column_exists?(:application_settings, :help_page_support_url)
+ end
+
def sidekiq_throttling_column_exists?
ActiveRecord::Base.connection.column_exists?(:application_settings, :sidekiq_throttling_enabled)
end
diff --git a/app/models/blob_viewer/server_side.rb b/app/models/blob_viewer/server_side.rb
index e6bcacf7f70..fbc1b520c01 100644
--- a/app/models/blob_viewer/server_side.rb
+++ b/app/models/blob_viewer/server_side.rb
@@ -13,14 +13,12 @@ module BlobViewer
end
def render_error
- if blob.stored_externally?
- # Files that are not stored in the repository, like LFS files and
- # build artifacts, can only be rendered using a client-side viewer,
- # since we do not want to read large amounts of data into memory on the
- # server side. Client-side viewers use JS and can fetch the file from
- # `blob_raw_url` using AJAX.
- return :server_side_but_stored_externally
- end
+ # Files that are not stored in the repository, like LFS files and
+ # build artifacts, can only be rendered using a client-side viewer,
+ # since we do not want to read large amounts of data into memory on the
+ # server side. Client-side viewers use JS and can fetch the file from
+ # `blob_raw_url` using AJAX.
+ return :server_side_but_stored_externally if blob.stored_externally?
super
end
diff --git a/app/models/broadcast_message.rb b/app/models/broadcast_message.rb
index cb40f33932a..944725d91c3 100644
--- a/app/models/broadcast_message.rb
+++ b/app/models/broadcast_message.rb
@@ -16,7 +16,7 @@ class BroadcastMessage < ActiveRecord::Base
def self.current
Rails.cache.fetch("broadcast_message_current", expires_in: 1.minute) do
- where("ends_at > :now AND starts_at <= :now", now: Time.zone.now).last
+ where('ends_at > :now AND starts_at <= :now', now: Time.zone.now).order([:created_at, :id]).to_a
end
end
diff --git a/app/models/ci/build.rb b/app/models/ci/build.rb
index cec1ca89a6a..58758f7ca8a 100644
--- a/app/models/ci/build.rb
+++ b/app/models/ci/build.rb
@@ -33,7 +33,7 @@ module Ci
scope :with_artifacts_not_expired, ->() { with_artifacts.where('artifacts_expire_at IS NULL OR artifacts_expire_at > ?', Time.now) }
scope :with_expired_artifacts, ->() { with_artifacts.where('artifacts_expire_at < ?', Time.now) }
scope :last_month, ->() { where('created_at > ?', Date.today - 1.month) }
- scope :manual_actions, ->() { where(when: :manual).relevant }
+ scope :manual_actions, ->() { where(when: :manual, status: COMPLETED_STATUSES + [:manual]) }
mount_uploader :artifacts_file, ArtifactUploader
mount_uploader :artifacts_metadata, ArtifactUploader
@@ -109,7 +109,7 @@ module Ci
end
def playable?
- action? && manual?
+ action? && (manual? || complete?)
end
def action?
diff --git a/app/models/commit_status.rb b/app/models/commit_status.rb
index cb425706a9e..07cec63b939 100644
--- a/app/models/commit_status.rb
+++ b/app/models/commit_status.rb
@@ -112,7 +112,7 @@ class CommitStatus < ActiveRecord::Base
end
def group_name
- name.gsub(/\d+[\s:\/\\]+\d+\s*/, '').strip
+ name.to_s.gsub(/\d+[\s:\/\\]+\d+\s*/, '').strip
end
def failed_but_allowed?
@@ -156,7 +156,7 @@ class CommitStatus < ActiveRecord::Base
end
def sortable_name
- name.split(/(\d+)/).map do |v|
+ name.to_s.split(/(\d+)/).map do |v|
v =~ /\d+/ ? v.to_i : v
end
end
diff --git a/app/models/concerns/issuable.rb b/app/models/concerns/issuable.rb
index ea10d004c9c..8e367576c9d 100644
--- a/app/models/concerns/issuable.rb
+++ b/app/models/concerns/issuable.rb
@@ -67,7 +67,6 @@ module Issuable
scope :authored, ->(user) { where(author_id: user) }
scope :recent, -> { reorder(id: :desc) }
- scope :order_position_asc, -> { reorder(position: :asc) }
scope :of_projects, ->(ids) { where(project_id: ids) }
scope :of_milestones, ->(ids) { where(milestone_id: ids) }
scope :with_milestone, ->(title) { left_joins_milestones.where(milestones: { title: title }) }
@@ -139,7 +138,6 @@ module Issuable
when 'upvotes_desc' then order_upvotes_desc
when 'label_priority' then order_labels_priority(excluded_labels: excluded_labels)
when 'priority' then order_due_date_and_labels_priority(excluded_labels: excluded_labels)
- when 'position_asc' then order_position_asc
else
order_by(method)
end
diff --git a/app/models/concerns/milestoneish.rb b/app/models/concerns/milestoneish.rb
index a3472af5c55..01599ce49c6 100644
--- a/app/models/concerns/milestoneish.rb
+++ b/app/models/concerns/milestoneish.rb
@@ -40,10 +40,18 @@ module Milestoneish
def issues_visible_to_user(user)
memoize_per_user(user, :issues_visible_to_user) do
IssuesFinder.new(user, issues_finder_params)
- .execute.includes(:assignees).where(milestone_id: milestoneish_ids)
+ .execute.preload(:assignees).where(milestone_id: milestoneish_ids)
end
end
+ def sorted_issues(user)
+ issues_visible_to_user(user).preload_associations.sort('label_priority')
+ end
+
+ def sorted_merge_requests
+ merge_requests.sort('label_priority')
+ end
+
def upcoming?
start_date && start_date.future?
end
diff --git a/app/models/diff_viewer/added.rb b/app/models/diff_viewer/added.rb
new file mode 100644
index 00000000000..1909e6ef9d8
--- /dev/null
+++ b/app/models/diff_viewer/added.rb
@@ -0,0 +1,8 @@
+module DiffViewer
+ class Added < Base
+ include Simple
+ include Static
+
+ self.partial_name = 'added'
+ end
+end
diff --git a/app/models/diff_viewer/base.rb b/app/models/diff_viewer/base.rb
new file mode 100644
index 00000000000..0cbe714288d
--- /dev/null
+++ b/app/models/diff_viewer/base.rb
@@ -0,0 +1,87 @@
+module DiffViewer
+ class Base
+ PARTIAL_PATH_PREFIX = 'projects/diffs/viewers'.freeze
+
+ class_attribute :partial_name, :type, :extensions, :file_types, :binary, :switcher_icon, :switcher_title
+
+ # These limits relate to the sum of the old and new blob sizes.
+ # Limits related to the actual size of the diff are enforced in Gitlab::Diff::File.
+ class_attribute :collapse_limit, :size_limit
+
+ delegate :partial_path, :loading_partial_path, :rich?, :simple?, :text?, :binary?, to: :class
+
+ attr_reader :diff_file
+
+ delegate :project, to: :diff_file
+
+ def initialize(diff_file)
+ @diff_file = diff_file
+ @initially_binary = diff_file.binary?
+ end
+
+ def self.partial_path
+ File.join(PARTIAL_PATH_PREFIX, partial_name)
+ end
+
+ def self.rich?
+ type == :rich
+ end
+
+ def self.simple?
+ type == :simple
+ end
+
+ def self.binary?
+ binary
+ end
+
+ def self.text?
+ !binary?
+ end
+
+ def self.can_render?(diff_file, verify_binary: true)
+ can_render_blob?(diff_file.old_blob, verify_binary: verify_binary) &&
+ can_render_blob?(diff_file.new_blob, verify_binary: verify_binary)
+ end
+
+ def self.can_render_blob?(blob, verify_binary: true)
+ return true if blob.nil?
+ return false if verify_binary && binary? != blob.binary?
+ return true if extensions&.include?(blob.extension)
+ return true if file_types&.include?(blob.file_type)
+
+ false
+ end
+
+ def collapsed?
+ return @collapsed if defined?(@collapsed)
+ return @collapsed = true if diff_file.collapsed?
+
+ @collapsed = !diff_file.expanded? && collapse_limit && diff_file.raw_size > collapse_limit
+ end
+
+ def too_large?
+ return @too_large if defined?(@too_large)
+ return @too_large = true if diff_file.too_large?
+
+ @too_large = size_limit && diff_file.raw_size > size_limit
+ end
+
+ def binary_detected_after_load?
+ !@initially_binary && diff_file.binary?
+ end
+
+ # This method is used on the server side to check whether we can attempt to
+ # render the diff_file at all. Human-readable error messages are found in the
+ # `BlobHelper#diff_render_error_reason` helper.
+ def render_error
+ if too_large?
+ :too_large
+ end
+ end
+
+ def prepare!
+ # To be overridden by subclasses
+ end
+ end
+end
diff --git a/app/models/diff_viewer/client_side.rb b/app/models/diff_viewer/client_side.rb
new file mode 100644
index 00000000000..cf41d07f8eb
--- /dev/null
+++ b/app/models/diff_viewer/client_side.rb
@@ -0,0 +1,10 @@
+module DiffViewer
+ module ClientSide
+ extend ActiveSupport::Concern
+
+ included do
+ self.collapse_limit = 1.megabyte
+ self.size_limit = 10.megabytes
+ end
+ end
+end
diff --git a/app/models/diff_viewer/deleted.rb b/app/models/diff_viewer/deleted.rb
new file mode 100644
index 00000000000..9c129bac694
--- /dev/null
+++ b/app/models/diff_viewer/deleted.rb
@@ -0,0 +1,8 @@
+module DiffViewer
+ class Deleted < Base
+ include Simple
+ include Static
+
+ self.partial_name = 'deleted'
+ end
+end
diff --git a/app/models/diff_viewer/image.rb b/app/models/diff_viewer/image.rb
new file mode 100644
index 00000000000..759d9a36ebb
--- /dev/null
+++ b/app/models/diff_viewer/image.rb
@@ -0,0 +1,12 @@
+module DiffViewer
+ class Image < Base
+ include Rich
+ include ClientSide
+
+ self.partial_name = 'image'
+ self.extensions = UploaderHelper::IMAGE_EXT
+ self.binary = true
+ self.switcher_icon = 'picture-o'
+ self.switcher_title = 'image diff'
+ end
+end
diff --git a/app/models/diff_viewer/mode_changed.rb b/app/models/diff_viewer/mode_changed.rb
new file mode 100644
index 00000000000..d487d996f8d
--- /dev/null
+++ b/app/models/diff_viewer/mode_changed.rb
@@ -0,0 +1,8 @@
+module DiffViewer
+ class ModeChanged < Base
+ include Simple
+ include Static
+
+ self.partial_name = 'mode_changed'
+ end
+end
diff --git a/app/models/diff_viewer/no_preview.rb b/app/models/diff_viewer/no_preview.rb
new file mode 100644
index 00000000000..5455fee4490
--- /dev/null
+++ b/app/models/diff_viewer/no_preview.rb
@@ -0,0 +1,9 @@
+module DiffViewer
+ class NoPreview < Base
+ include Simple
+ include Static
+
+ self.partial_name = 'no_preview'
+ self.binary = true
+ end
+end
diff --git a/app/models/diff_viewer/not_diffable.rb b/app/models/diff_viewer/not_diffable.rb
new file mode 100644
index 00000000000..4f9638626ea
--- /dev/null
+++ b/app/models/diff_viewer/not_diffable.rb
@@ -0,0 +1,9 @@
+module DiffViewer
+ class NotDiffable < Base
+ include Simple
+ include Static
+
+ self.partial_name = 'not_diffable'
+ self.binary = true
+ end
+end
diff --git a/app/models/diff_viewer/renamed.rb b/app/models/diff_viewer/renamed.rb
new file mode 100644
index 00000000000..f1fbfd8c6d5
--- /dev/null
+++ b/app/models/diff_viewer/renamed.rb
@@ -0,0 +1,8 @@
+module DiffViewer
+ class Renamed < Base
+ include Simple
+ include Static
+
+ self.partial_name = 'renamed'
+ end
+end
diff --git a/app/models/diff_viewer/rich.rb b/app/models/diff_viewer/rich.rb
new file mode 100644
index 00000000000..3b0ca6e4cff
--- /dev/null
+++ b/app/models/diff_viewer/rich.rb
@@ -0,0 +1,11 @@
+module DiffViewer
+ module Rich
+ extend ActiveSupport::Concern
+
+ included do
+ self.type = :rich
+ self.switcher_icon = 'file-text-o'
+ self.switcher_title = 'rendered diff'
+ end
+ end
+end
diff --git a/app/models/diff_viewer/server_side.rb b/app/models/diff_viewer/server_side.rb
new file mode 100644
index 00000000000..aed1a0791b1
--- /dev/null
+++ b/app/models/diff_viewer/server_side.rb
@@ -0,0 +1,26 @@
+module DiffViewer
+ module ServerSide
+ extend ActiveSupport::Concern
+
+ included do
+ self.collapse_limit = 1.megabyte
+ self.size_limit = 5.megabytes
+ end
+
+ def prepare!
+ diff_file.old_blob&.load_all_data!
+ diff_file.new_blob&.load_all_data!
+ end
+
+ def render_error
+ # Files that are not stored in the repository, like LFS files and
+ # build artifacts, can only be rendered using a client-side viewer,
+ # since we do not want to read large amounts of data into memory on the
+ # server side. Client-side viewers use JS and can fetch the file from
+ # `diff_file_blob_raw_path` and `diff_file_old_blob_raw_path` using AJAX.
+ return :server_side_but_stored_externally if diff_file.stored_externally?
+
+ super
+ end
+ end
+end
diff --git a/app/models/diff_viewer/simple.rb b/app/models/diff_viewer/simple.rb
new file mode 100644
index 00000000000..65750996ee4
--- /dev/null
+++ b/app/models/diff_viewer/simple.rb
@@ -0,0 +1,11 @@
+module DiffViewer
+ module Simple
+ extend ActiveSupport::Concern
+
+ included do
+ self.type = :simple
+ self.switcher_icon = 'code'
+ self.switcher_title = 'source diff'
+ end
+ end
+end
diff --git a/app/models/diff_viewer/static.rb b/app/models/diff_viewer/static.rb
new file mode 100644
index 00000000000..d761328b3f6
--- /dev/null
+++ b/app/models/diff_viewer/static.rb
@@ -0,0 +1,10 @@
+module DiffViewer
+ module Static
+ extend ActiveSupport::Concern
+
+ # We can always render a static viewer, even if the diff is too large.
+ def render_error
+ nil
+ end
+ end
+end
diff --git a/app/models/diff_viewer/text.rb b/app/models/diff_viewer/text.rb
new file mode 100644
index 00000000000..98f4b2aea2a
--- /dev/null
+++ b/app/models/diff_viewer/text.rb
@@ -0,0 +1,15 @@
+module DiffViewer
+ class Text < Base
+ include Simple
+ include ServerSide
+
+ self.partial_name = 'text'
+ self.binary = false
+
+ # Since the text diff viewer doesn't render the old and new blobs in full,
+ # we only need the limits related to the actual size of the diff which are
+ # already enforced in Gitlab::Diff::File.
+ self.collapse_limit = nil
+ self.size_limit = nil
+ end
+end
diff --git a/app/models/environment.rb b/app/models/environment.rb
index 6211a5c1e63..d5b974b2d31 100644
--- a/app/models/environment.rb
+++ b/app/models/environment.rb
@@ -209,7 +209,8 @@ class Environment < ActiveRecord::Base
def etag_cache_key
Gitlab::Routing.url_helpers.namespace_project_environments_path(
project.namespace,
- project)
+ project,
+ format: :json)
end
private
diff --git a/app/models/generic_commit_status.rb b/app/models/generic_commit_status.rb
index 8867ba0d2ff..532b8f4ad69 100644
--- a/app/models/generic_commit_status.rb
+++ b/app/models/generic_commit_status.rb
@@ -11,6 +11,7 @@ class GenericCommitStatus < CommitStatus
def set_default_values
self.context ||= 'default'
self.stage ||= 'external'
+ self.stage_idx ||= 1000000
end
def tags
diff --git a/app/models/issue.rb b/app/models/issue.rb
index 693cc21bb40..f0f525aea21 100644
--- a/app/models/issue.rb
+++ b/app/models/issue.rb
@@ -9,6 +9,9 @@ class Issue < ActiveRecord::Base
include Spammable
include FasterCacheKeys
include RelativePositioning
+ include IgnorableColumn
+
+ ignore_column :position
DueDateStruct = Struct.new(:title, :name).freeze
NoDueDate = DueDateStruct.new('No Due Date', '0').freeze
@@ -44,7 +47,7 @@ class Issue < ActiveRecord::Base
scope :created_after, -> (datetime) { where("created_at >= ?", datetime) }
- scope :include_associations, -> { includes(:labels, project: :namespace) }
+ scope :preload_associations, -> { preload(:labels, project: :namespace) }
after_save :expire_etag_cache
diff --git a/app/models/legacy_diff_note.rb b/app/models/legacy_diff_note.rb
index 7126de2d488..2d5909ab25e 100644
--- a/app/models/legacy_diff_note.rb
+++ b/app/models/legacy_diff_note.rb
@@ -42,7 +42,7 @@ class LegacyDiffNote < Note
end
def for_line?(line)
- !line.meta? && diff_file.line_code(line) == self.line_code
+ line.discussable? && diff_file.line_code(line) == self.line_code
end
def original_line_code
diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb
index dd155252ad5..ea22ab53587 100644
--- a/app/models/merge_request.rb
+++ b/app/models/merge_request.rb
@@ -4,6 +4,9 @@ class MergeRequest < ActiveRecord::Base
include Noteable
include Referable
include Sortable
+ include IgnorableColumn
+
+ ignore_column :position
belongs_to :target_project, class_name: "Project"
belongs_to :source_project, class_name: "Project"
@@ -889,7 +892,7 @@ class MergeRequest < ActiveRecord::Base
!has_commits?
end
- def mergeable_with_slash_command?(current_user, autocomplete_precheck: false, last_diff_sha: nil)
+ def mergeable_with_quick_action?(current_user, autocomplete_precheck: false, last_diff_sha: nil)
return false unless can_be_merged_by?(current_user)
return true if autocomplete_precheck
diff --git a/app/models/merge_request_diff.rb b/app/models/merge_request_diff.rb
index 99dd2130188..f1ee4d3f7a9 100644
--- a/app/models/merge_request_diff.rb
+++ b/app/models/merge_request_diff.rb
@@ -10,6 +10,7 @@ class MergeRequestDiff < ActiveRecord::Base
VALID_CLASSES = [Hash, Rugged::Patch, Rugged::Diff::Delta].freeze
belongs_to :merge_request
+ has_many :merge_request_diff_files, -> { order(:merge_request_diff_id, :relative_order) }
serialize :st_commits # rubocop:disable Cop/ActiverecordSerialize
serialize :st_diffs # rubocop:disable Cop/ActiverecordSerialize
@@ -91,7 +92,7 @@ class MergeRequestDiff < ActiveRecord::Base
head_commit_sha).diffs(options)
else
@raw_diffs ||= {}
- @raw_diffs[options] ||= load_diffs(st_diffs, options)
+ @raw_diffs[options] ||= load_diffs(options)
end
end
@@ -253,24 +254,44 @@ class MergeRequestDiff < ActiveRecord::Base
update_columns_serialized(new_attributes)
end
- def dump_diffs(diffs)
- if diffs.respond_to?(:map)
- diffs.map(&:to_hash)
+ def create_merge_request_diff_files(diffs)
+ rows = diffs.map.with_index do |diff, index|
+ diff.to_hash.merge(
+ merge_request_diff_id: self.id,
+ relative_order: index
+ )
end
+
+ Gitlab::Database.bulk_insert('merge_request_diff_files', rows)
end
- def load_diffs(raw, options)
- if valid_raw_diff?(raw)
- if paths = options[:paths]
- raw = raw.select do |diff|
- paths.include?(diff[:old_path]) || paths.include?(diff[:new_path])
- end
- end
+ def load_diffs(options)
+ return Gitlab::Git::DiffCollection.new([]) unless diffs_from_database
- Gitlab::Git::DiffCollection.new(raw, options)
- else
- Gitlab::Git::DiffCollection.new([])
+ raw = diffs_from_database
+
+ if paths = options[:paths]
+ raw = raw.select do |diff|
+ paths.include?(diff[:old_path]) || paths.include?(diff[:new_path])
+ end
end
+
+ Gitlab::Git::DiffCollection.new(raw, options)
+ end
+
+ def diffs_from_database
+ return @diffs_from_database if defined?(@diffs_from_database)
+
+ @diffs_from_database =
+ if st_diffs.present?
+ if valid_raw_diff?(st_diffs)
+ st_diffs
+ end
+ elsif merge_request_diff_files.present?
+ merge_request_diff_files
+ .as_json(only: Gitlab::Git::Diff::SERIALIZE_KEYS)
+ .map(&:with_indifferent_access)
+ end
end
# Load diffs between branches related to current merge request diff from repo
@@ -285,11 +306,10 @@ class MergeRequestDiff < ActiveRecord::Base
new_attributes[:real_size] = diff_collection.real_size
if diff_collection.any?
- new_diffs = dump_diffs(diff_collection)
new_attributes[:state] = :collected
- end
- new_attributes[:st_diffs] = new_diffs || []
+ create_merge_request_diff_files(diff_collection)
+ end
# Set our state to 'overflow' to make the #empty? and #collected?
# methods (generated by StateMachine) return false.
diff --git a/app/models/merge_request_diff_file.rb b/app/models/merge_request_diff_file.rb
new file mode 100644
index 00000000000..598ebd4d829
--- /dev/null
+++ b/app/models/merge_request_diff_file.rb
@@ -0,0 +1,11 @@
+class MergeRequestDiffFile < ActiveRecord::Base
+ include Gitlab::EncodingHelper
+
+ belongs_to :merge_request_diff
+
+ def utf8_diff
+ return '' if diff.blank?
+
+ encode_utf8(diff) if diff.respond_to?(:encoding)
+ end
+end
diff --git a/app/models/milestone.rb b/app/models/milestone.rb
index b04bed4c014..0a6fc064aec 100644
--- a/app/models/milestone.rb
+++ b/app/models/milestone.rb
@@ -164,38 +164,6 @@ class Milestone < ActiveRecord::Base
write_attribute(:title, sanitize_title(value)) if value.present?
end
- # Sorts the issues for the given IDs.
- #
- # This method runs a single SQL query using a CASE statement to update the
- # position of all issues in the current milestone (scoped to the list of IDs).
- #
- # Given the ids [10, 20, 30] this method produces a SQL query something like
- # the following:
- #
- # UPDATE issues
- # SET position = CASE
- # WHEN id = 10 THEN 1
- # WHEN id = 20 THEN 2
- # WHEN id = 30 THEN 3
- # ELSE position
- # END
- # WHERE id IN (10, 20, 30);
- #
- # This method expects that the IDs given in `ids` are already Fixnums.
- def sort_issues(ids)
- pairs = []
-
- ids.each_with_index do |id, index|
- pairs << id
- pairs << index + 1
- end
-
- conditions = 'WHEN id = ? THEN ? ' * ids.length
-
- issues.where(id: ids).
- update_all(["position = CASE #{conditions} ELSE position END", *pairs])
- end
-
private
def milestone_format_reference(format = :iid)
diff --git a/app/models/note.rb b/app/models/note.rb
index 244bf169c29..3221e653e30 100644
--- a/app/models/note.rb
+++ b/app/models/note.rb
@@ -32,7 +32,7 @@ class Note < ActiveRecord::Base
# Banzai::ObjectRenderer
attr_accessor :user_visible_reference_count
- # Attribute used to store the attributes that have ben changed by slash commands.
+ # Attribute used to store the attributes that have ben changed by quick actions.
attr_accessor :commands_changes
default_value_for :system, false
diff --git a/app/models/notification_setting.rb b/app/models/notification_setting.rb
index 42412a9a44b..b0df7aeb323 100644
--- a/app/models/notification_setting.rb
+++ b/app/models/notification_setting.rb
@@ -41,10 +41,8 @@ class NotificationSetting < ActiveRecord::Base
:success_pipeline
].freeze
- store :events, accessors: EMAIL_EVENTS, coder: JSON
-
- before_create :set_events
- before_save :events_to_boolean
+ store :events, coder: JSON
+ before_save :convert_events
def self.find_or_create_for(source)
setting = find_or_initialize_by(source: source)
@@ -56,21 +54,18 @@ class NotificationSetting < ActiveRecord::Base
setting
end
- # Set all event attributes to false when level is not custom or being initialized for UX reasons
- def set_events
- return if custom?
-
- self.events = {}
- end
+ # 1. Check if this event has a value stored in its database column.
+ # 2. If it does, return that value.
+ # 3. If it doesn't (the value is nil), return the value from the serialized
+ # JSON hash in `events`.
+ (EMAIL_EVENTS - [:failed_pipeline]).each do |event|
+ define_method(event) do
+ bool = super()
- # Validates store accessors values as boolean
- # It is a text field so it does not cast correct boolean values in JSON
- def events_to_boolean
- EMAIL_EVENTS.each do |event|
- bool = ActiveRecord::ConnectionAdapters::Column::TRUE_VALUES.include?(public_send(event))
-
- events[event] = bool
+ bool.nil? ? !!events[event] : bool
end
+
+ alias_method :"#{event}?", event
end
# Allow people to receive failed pipeline notifications if they already have
@@ -78,7 +73,23 @@ class NotificationSetting < ActiveRecord::Base
# custom settings.
def failed_pipeline
bool = super
+ bool = events[:failed_pipeline] if bool.nil?
bool.nil? || bool
end
+ alias_method :failed_pipeline?, :failed_pipeline
+
+ def event_enabled?(event)
+ respond_to?(event) && public_send(event)
+ end
+
+ def convert_events
+ return if events_before_type_cast.nil?
+
+ EMAIL_EVENTS.each do |event|
+ write_attribute(event, public_send(event))
+ end
+
+ write_attribute(:events, nil)
+ end
end
diff --git a/app/models/project_services/kubernetes_service.rb b/app/models/project_services/kubernetes_service.rb
index 8977a7cdafe..48e7802c557 100644
--- a/app/models/project_services/kubernetes_service.rb
+++ b/app/models/project_services/kubernetes_service.rb
@@ -116,30 +116,19 @@ class KubernetesService < DeploymentService
# short time later
def terminals(environment)
with_reactive_cache do |data|
- pods = data.fetch(:pods, nil)
- filter_pods(pods, app: environment.slug).
- flat_map { |pod| terminals_for_pod(api_url, actual_namespace, pod) }.
- each { |terminal| add_terminal_auth(terminal, terminal_auth) }
+ pods = filter_by_label(data[:pods], app: environment.slug)
+ terminals = pods.flat_map { |pod| terminals_for_pod(api_url, actual_namespace, pod) }
+ terminals.each { |terminal| add_terminal_auth(terminal, terminal_auth) }
end
end
- # Caches all pods in the namespace so other calls don't need to block on
- # network access.
+ # Caches resources in the namespace so other calls don't need to block on
+ # network access
def calculate_reactive_cache
return unless active? && project && !project.pending_delete?
- kubeclient = build_kubeclient!
-
- # Store as hashes, rather than as third-party types
- pods = begin
- kubeclient.get_pods(namespace: actual_namespace).as_json
- rescue KubeException => err
- raise err unless err.error_code == 404
- []
- end
-
# We may want to cache extra things in the future
- { pods: pods }
+ { pods: read_pods }
end
TEMPLATE_PLACEHOLDER = 'Kubernetes namespace'.freeze
@@ -166,6 +155,16 @@ class KubernetesService < DeploymentService
)
end
+ # Returns a hash of all pods in the namespace
+ def read_pods
+ kubeclient = build_kubeclient!
+
+ kubeclient.get_pods(namespace: actual_namespace).as_json
+ rescue KubeException => err
+ raise err unless err.error_code == 404
+ []
+ end
+
def kubeclient_ssl_options
opts = { verify_ssl: OpenSSL::SSL::VERIFY_PEER }
@@ -181,11 +180,11 @@ class KubernetesService < DeploymentService
{ bearer_token: token }
end
- def join_api_url(*parts)
+ def join_api_url(api_path)
url = URI.parse(api_url)
prefix = url.path.sub(%r{/+\z}, '')
- url.path = [prefix, *parts].join("/")
+ url.path = [prefix, api_path].join("/")
url.to_s
end
diff --git a/app/models/project_services/mattermost_slash_commands_service.rb b/app/models/project_services/mattermost_slash_commands_service.rb
index 56f42d63b2d..c2f887e24a0 100644
--- a/app/models/project_services/mattermost_slash_commands_service.rb
+++ b/app/models/project_services/mattermost_slash_commands_service.rb
@@ -1,4 +1,4 @@
-class MattermostSlashCommandsService < ChatSlashCommandsService
+class MattermostSlashCommandsService < SlashCommandsService
include TriggersHelper
prop_accessor :token
diff --git a/app/models/project_services/slack_slash_commands_service.rb b/app/models/project_services/slack_slash_commands_service.rb
index 2182c1c7e4b..1c3892a3f75 100644
--- a/app/models/project_services/slack_slash_commands_service.rb
+++ b/app/models/project_services/slack_slash_commands_service.rb
@@ -1,4 +1,4 @@
-class SlackSlashCommandsService < ChatSlashCommandsService
+class SlackSlashCommandsService < SlashCommandsService
include TriggersHelper
def title
diff --git a/app/models/project_services/chat_slash_commands_service.rb b/app/models/project_services/slash_commands_service.rb
index 8b5bc24fd3c..4592cb747a0 100644
--- a/app/models/project_services/chat_slash_commands_service.rb
+++ b/app/models/project_services/slash_commands_service.rb
@@ -1,6 +1,6 @@
# Base class for Chat services
# This class is not meant to be used directly, but only to inherrit from.
-class ChatSlashCommandsService < Service
+class SlashCommandsService < Service
default_value_for :category, 'chat'
prop_accessor :token
@@ -33,10 +33,10 @@ class ChatSlashCommandsService < Service
user = find_chat_user(params)
if user
- Gitlab::ChatCommands::Command.new(project, user, params).execute
+ Gitlab::SlashCommands::Command.new(project, user, params).execute
else
url = authorize_chat_name_url(params)
- Gitlab::ChatCommands::Presenters::Access.new(url).authorize
+ Gitlab::SlashCommands::Presenters::Access.new(url).authorize
end
end
diff --git a/app/models/user.rb b/app/models/user.rb
index 5d128e4b390..782c162e1f3 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -139,21 +139,21 @@ class User < ActiveRecord::Base
presence: true,
uniqueness: { case_sensitive: false }
- validate :namespace_uniq, if: ->(user) { user.username_changed? }
+ validate :namespace_uniq, if: :username_changed?
validate :avatar_type, if: ->(user) { user.avatar.present? && user.avatar_changed? }
- validate :unique_email, if: ->(user) { user.email_changed? }
- validate :owns_notification_email, if: ->(user) { user.notification_email_changed? }
- validate :owns_public_email, if: ->(user) { user.public_email_changed? }
+ validate :unique_email, if: :email_changed?
+ validate :owns_notification_email, if: :notification_email_changed?
+ validate :owns_public_email, if: :public_email_changed?
validate :signup_domain_valid?, on: :create, if: ->(user) { !user.created_by_id }
validates :avatar, file_size: { maximum: 200.kilobytes.to_i }
before_validation :sanitize_attrs
- before_validation :set_notification_email, if: ->(user) { user.email_changed? }
- before_validation :set_public_email, if: ->(user) { user.public_email_changed? }
+ before_validation :set_notification_email, if: :email_changed?
+ before_validation :set_public_email, if: :public_email_changed?
- after_update :update_emails_with_primary_email, if: ->(user) { user.email_changed? }
+ after_update :update_emails_with_primary_email, if: :email_changed?
before_save :ensure_authentication_token, :ensure_incoming_email_token
- before_save :ensure_external_user_rights
+ before_save :ensure_user_rights_and_limits, if: :external_changed?
after_save :ensure_namespace_correct
after_initialize :set_projects_limit
after_destroy :post_destroy_hook
@@ -1033,11 +1033,14 @@ class User < ActiveRecord::Base
super
end
- def ensure_external_user_rights
- return unless external?
-
- self.can_create_group = false
- self.projects_limit = 0
+ def ensure_user_rights_and_limits
+ if external?
+ self.can_create_group = false
+ self.projects_limit = 0
+ else
+ self.can_create_group = gitlab_config.default_can_create_group
+ self.projects_limit = current_application_settings.default_projects_limit
+ end
end
def signup_domain_valid?
diff --git a/app/policies/global_policy.rb b/app/policies/global_policy.rb
index 4757ba71680..2683aaad981 100644
--- a/app/policies/global_policy.rb
+++ b/app/policies/global_policy.rb
@@ -10,7 +10,7 @@ class GlobalPolicy < BasePolicy
can! :access_api
can! :access_git
can! :receive_notifications
- can! :use_slash_commands
+ can! :use_quick_actions
end
end
end
diff --git a/app/policies/project_policy.rb b/app/policies/project_policy.rb
index 3959b895f44..47518dddb61 100644
--- a/app/policies/project_policy.rb
+++ b/app/policies/project_policy.rb
@@ -203,7 +203,7 @@ class ProjectPolicy < BasePolicy
unless project.feature_available?(:builds, user) && repository_enabled
cannot!(*named_abilities(:build))
- cannot!(*named_abilities(:pipeline))
+ cannot!(*named_abilities(:pipeline) - [:read_pipeline])
cannot!(*named_abilities(:pipeline_schedule))
cannot!(*named_abilities(:environment))
cannot!(*named_abilities(:deployment))
diff --git a/app/serializers/build_details_entity.rb b/app/serializers/build_details_entity.rb
index 514c4c2e35f..eeb5399aa8b 100644
--- a/app/serializers/build_details_entity.rb
+++ b/app/serializers/build_details_entity.rb
@@ -1,18 +1,15 @@
-class BuildDetailsEntity < BuildEntity
+class BuildDetailsEntity < JobEntity
expose :coverage, :erased_at, :duration
expose :tag_list, as: :tags
-
expose :user, using: UserEntity
+ expose :runner, using: RunnerEntity
+ expose :pipeline, using: PipelineEntity
expose :erased_by, if: -> (*) { build.erased? }, using: UserEntity
expose :erase_path, if: -> (*) { build.erasable? && can?(current_user, :update_build, project) } do |build|
erase_namespace_project_job_path(project.namespace, project, build)
end
- expose :artifacts, using: BuildArtifactEntity
- expose :runner, using: RunnerEntity
- expose :pipeline, using: PipelineEntity
-
expose :merge_request, if: -> (*) { can?(current_user, :read_merge_request, build.merge_request) } do
expose :iid do |build|
build.merge_request.iid
@@ -28,7 +25,7 @@ class BuildDetailsEntity < BuildEntity
end
expose :raw_path do |build|
- raw_namespace_project_build_path(project.namespace, project, build)
+ raw_namespace_project_job_path(project.namespace, project, build)
end
private
diff --git a/app/serializers/build_serializer.rb b/app/serializers/build_serializer.rb
index 79b67001199..bae9932847f 100644
--- a/app/serializers/build_serializer.rb
+++ b/app/serializers/build_serializer.rb
@@ -1,5 +1,5 @@
class BuildSerializer < BaseSerializer
- entity BuildEntity
+ entity JobEntity
def represent_status(resource)
data = represent(resource, { only: [:status] })
diff --git a/app/serializers/deployment_entity.rb b/app/serializers/deployment_entity.rb
index 8b3de1bed0f..e493c9162fd 100644
--- a/app/serializers/deployment_entity.rb
+++ b/app/serializers/deployment_entity.rb
@@ -24,6 +24,6 @@ class DeploymentEntity < Grape::Entity
expose :user, using: UserEntity
expose :commit, using: CommitEntity
- expose :deployable, using: BuildEntity
- expose :manual_actions, using: BuildEntity
+ expose :deployable, using: JobEntity
+ expose :manual_actions, using: JobEntity
end
diff --git a/app/serializers/group_entity.rb b/app/serializers/group_entity.rb
index 4f506f7e745..7c872a3e986 100644
--- a/app/serializers/group_entity.rb
+++ b/app/serializers/group_entity.rb
@@ -5,11 +5,15 @@ class GroupEntity < Grape::Entity
include GroupsHelper
expose :id, :name, :path, :description, :visibility
- expose :web_url
expose :full_name, :full_path
+ expose :web_url
expose :parent_id
expose :created_at, :updated_at
+ expose :group_path do |group|
+ group_path(group)
+ end
+
expose :permissions do
expose :human_group_access do |group, options|
group.group_members.find_by(user_id: request.current_user)&.human_access
diff --git a/app/serializers/issuable_entity.rb b/app/serializers/issuable_entity.rb
index 65b204d4dd2..bd5211b8e58 100644
--- a/app/serializers/issuable_entity.rb
+++ b/app/serializers/issuable_entity.rb
@@ -5,7 +5,6 @@ class IssuableEntity < Grape::Entity
expose :description
expose :lock_version
expose :milestone_id
- expose :position
expose :state
expose :title
expose :updated_by_id
diff --git a/app/serializers/build_entity.rb b/app/serializers/job_entity.rb
index 67001f4547d..d6de43bcbcb 100644
--- a/app/serializers/build_entity.rb
+++ b/app/serializers/job_entity.rb
@@ -1,11 +1,11 @@
-class BuildEntity < Grape::Entity
+class JobEntity < Grape::Entity
include RequestAwareEntity
expose :id
expose :name
expose :build_path do |build|
- path_to(:namespace_project_job, build)
+ build.target_url || path_to(:namespace_project_job, build)
end
expose :retry_path, if: -> (*) { retryable? } do |build|
diff --git a/app/serializers/job_group_entity.rb b/app/serializers/job_group_entity.rb
index 04487e59009..8554de55517 100644
--- a/app/serializers/job_group_entity.rb
+++ b/app/serializers/job_group_entity.rb
@@ -4,7 +4,7 @@ class JobGroupEntity < Grape::Entity
expose :name
expose :size
expose :detailed_status, as: :status, with: StatusEntity
- expose :jobs, with: BuildEntity
+ expose :jobs, with: JobEntity
private
diff --git a/app/services/git_push_service.rb b/app/services/git_push_service.rb
index f080e6326a1..fb1d4aed58b 100644
--- a/app/services/git_push_service.rb
+++ b/app/services/git_push_service.rb
@@ -101,12 +101,12 @@ class GitPushService < BaseService
UpdateMergeRequestsWorker
.perform_async(@project.id, current_user.id, params[:oldrev], params[:newrev], params[:ref])
- SystemHookPushWorker.perform_async(build_push_data.dup, :push_hooks)
-
EventCreateService.new.push(@project, current_user, build_push_data)
+ Ci::CreatePipelineService.new(@project, current_user, build_push_data).execute(:push)
+
+ SystemHookPushWorker.perform_async(build_push_data.dup, :push_hooks)
@project.execute_hooks(build_push_data.dup, :push_hooks)
@project.execute_services(build_push_data.dup, :push_hooks)
- Ci::CreatePipelineService.new(@project, current_user, build_push_data).execute(:push)
if push_remove_branch?
AfterBranchDeleteService
diff --git a/app/services/git_tag_push_service.rb b/app/services/git_tag_push_service.rb
index 7c424fba428..9917a39b795 100644
--- a/app/services/git_tag_push_service.rb
+++ b/app/services/git_tag_push_service.rb
@@ -8,10 +8,12 @@ class GitTagPushService < BaseService
@push_data = build_push_data
EventCreateService.new.push(project, current_user, @push_data)
+ Ci::CreatePipelineService.new(project, current_user, @push_data).execute(:push)
+
SystemHooksService.new.execute_hooks(build_system_push_data.dup, :tag_push_hooks)
project.execute_hooks(@push_data.dup, :tag_push_hooks)
project.execute_services(@push_data.dup, :tag_push_hooks)
- Ci::CreatePipelineService.new(project, current_user, @push_data).execute(:push)
+
ProjectCacheWorker.perform_async(project.id, [], [:commit_count, :repository_size])
true
diff --git a/app/services/issuable_base_service.rb b/app/services/issuable_base_service.rb
index a65d6e11c47..c8db4728aea 100644
--- a/app/services/issuable_base_service.rb
+++ b/app/services/issuable_base_service.rb
@@ -142,9 +142,9 @@ class IssuableBaseService < BaseService
LabelsFinder.new(current_user, project_id: @project.id).execute
end
- def merge_slash_commands_into_params!(issuable)
+ def merge_quick_actions_into_params!(issuable)
description, command_params =
- SlashCommands::InterpretService.new(project, current_user).
+ QuickActions::InterpretService.new(project, current_user).
execute(params[:description], issuable)
# Avoid a description already set on an issuable to be overwritten by a nil
@@ -162,7 +162,7 @@ class IssuableBaseService < BaseService
end
def create(issuable)
- merge_slash_commands_into_params!(issuable)
+ merge_quick_actions_into_params!(issuable)
filter_params(issuable)
params.delete(:state_event)
@@ -236,8 +236,9 @@ class IssuableBaseService < BaseService
)
if old_assignees != issuable.assignees
- assignees = old_assignees + issuable.assignees.to_a
- invalidate_cache_counts(assignees.compact, issuable)
+ new_assignees = issuable.assignees.to_a
+ affected_assignees = (old_assignees + new_assignees) - (old_assignees & new_assignees)
+ invalidate_cache_counts(affected_assignees.compact, issuable)
end
after_update(issuable)
diff --git a/app/services/merge_requests/update_service.rb b/app/services/merge_requests/update_service.rb
index 5c843a258fb..75a65aecd1a 100644
--- a/app/services/merge_requests/update_service.rb
+++ b/app/services/merge_requests/update_service.rb
@@ -7,7 +7,7 @@ module MergeRequests
params.except!(:target_project_id)
params.except!(:source_branch)
- merge_from_slash_command(merge_request) if params[:merge]
+ merge_from_quick_action(merge_request) if params[:merge]
if merge_request.closed_without_fork?
params.except!(:target_branch, :force_remove_source_branch)
@@ -74,9 +74,9 @@ module MergeRequests
end
end
- def merge_from_slash_command(merge_request)
+ def merge_from_quick_action(merge_request)
last_diff_sha = params.delete(:merge)
- return unless merge_request.mergeable_with_slash_command?(current_user, last_diff_sha: last_diff_sha)
+ return unless merge_request.mergeable_with_quick_action?(current_user, last_diff_sha: last_diff_sha)
merge_request.update(merge_error: nil)
diff --git a/app/services/notes/create_service.rb b/app/services/notes/create_service.rb
index f3954f6f8c4..06971483992 100644
--- a/app/services/notes/create_service.rb
+++ b/app/services/notes/create_service.rb
@@ -9,11 +9,11 @@ module Notes
# We execute commands (extracted from `params[:note]`) on the noteable
# **before** we save the note because if the note consists of commands
# only, there is no need be create a note!
- slash_commands_service = SlashCommandsService.new(project, current_user)
+ quick_actions_service = QuickActionsService.new(project, current_user)
- if slash_commands_service.supported?(note)
+ if quick_actions_service.supported?(note)
options = { merge_request_diff_head_sha: merge_request_diff_head_sha }
- content, command_params = slash_commands_service.extract_commands(note, options)
+ content, command_params = quick_actions_service.extract_commands(note, options)
only_commands = content.empty?
@@ -30,7 +30,7 @@ module Notes
end
if command_params.present?
- slash_commands_service.execute(command_params, note)
+ quick_actions_service.execute(command_params, note)
# We must add the error after we call #save because errors are reset
# when #save is called
diff --git a/app/services/notes/slash_commands_service.rb b/app/services/notes/quick_actions_service.rb
index ad1e6f6774a..8f81b54164a 100644
--- a/app/services/notes/slash_commands_service.rb
+++ b/app/services/notes/quick_actions_service.rb
@@ -1,5 +1,5 @@
module Notes
- class SlashCommandsService < BaseService
+ class QuickActionsService < BaseService
UPDATE_SERVICES = {
'Issue' => Issues::UpdateService,
'MergeRequest' => MergeRequests::UpdateService
@@ -22,7 +22,7 @@ module Notes
def extract_commands(note, options = {})
return [note.note, {}] unless supported?(note)
- SlashCommands::InterpretService.new(project, current_user, options).
+ QuickActions::InterpretService.new(project, current_user, options).
execute(note.note, note.noteable)
end
diff --git a/app/services/notification_recipient_service.rb b/app/services/notification_recipient_service.rb
index 988bd0a7cdb..8d1820bc504 100644
--- a/app/services/notification_recipient_service.rb
+++ b/app/services/notification_recipient_service.rb
@@ -8,7 +8,7 @@ class NotificationRecipientService
@project = project
end
- def build_recipients(target, current_user, action: nil, previous_assignee: nil, skip_current_user: true)
+ def build_recipients(target, current_user, action:, previous_assignee: nil, skip_current_user: true)
custom_action = build_custom_key(action, target)
recipients = target.participants(current_user)
@@ -59,7 +59,7 @@ class NotificationRecipientService
return [] if notification_setting.mention? || notification_setting.disabled?
- return [] if notification_setting.custom? && !notification_setting.public_send(custom_action)
+ return [] if notification_setting.custom? && !notification_setting.event_enabled?(custom_action)
return [] if (notification_setting.watch? || notification_setting.participating?) && NotificationSetting::EXCLUDED_WATCHER_EVENTS.include?(custom_action)
@@ -176,7 +176,7 @@ class NotificationRecipientService
if notification_level
settings = resource.notification_settings.where(level: NotificationSetting.levels[notification_level])
- settings = settings.select { |setting| setting.events[action] } if action.present?
+ settings = settings.select { |setting| setting.event_enabled?(action) } if action.present?
settings.map(&:user_id)
else
resource.notification_settings.pluck(:user_id)
@@ -225,7 +225,7 @@ class NotificationRecipientService
def user_ids_with_global_level_custom(ids, action)
settings = settings_with_global_level_of(:custom, ids)
- settings = settings.select { |setting| setting.events[action] }
+ settings = settings.select { |setting| setting.event_enabled?(action) }
settings.map(&:user_id)
end
diff --git a/app/services/notification_service.rb b/app/services/notification_service.rb
index 646ccbdb2bf..3a98a5f6b64 100644
--- a/app/services/notification_service.rb
+++ b/app/services/notification_service.rb
@@ -273,7 +273,7 @@ class NotificationService
end
def issue_moved(issue, new_issue, current_user)
- recipients = NotificationRecipientService.new(issue.project).build_recipients(issue, current_user)
+ recipients = NotificationRecipientService.new(issue.project).build_recipients(issue, current_user, action: 'moved')
recipients.map do |recipient|
email = mailer.issue_moved_email(recipient, issue, new_issue, current_user)
diff --git a/app/services/preview_markdown_service.rb b/app/services/preview_markdown_service.rb
index 10d45bbf73c..4ee2c1796bd 100644
--- a/app/services/preview_markdown_service.rb
+++ b/app/services/preview_markdown_service.rb
@@ -1,6 +1,6 @@
class PreviewMarkdownService < BaseService
def execute
- text, commands = explain_slash_commands(params[:text])
+ text, commands = explain_quick_actions(params[:text])
users = find_user_references(text)
success(
@@ -12,11 +12,11 @@ class PreviewMarkdownService < BaseService
private
- def explain_slash_commands(text)
+ def explain_quick_actions(text)
return text, [] unless %w(Issue MergeRequest).include?(commands_target_type)
- slash_commands_service = SlashCommands::InterpretService.new(project, current_user)
- slash_commands_service.explain(text, find_commands_target)
+ quick_actions_service = QuickActions::InterpretService.new(project, current_user)
+ quick_actions_service.explain(text, find_commands_target)
end
def find_user_references(text)
@@ -36,10 +36,10 @@ class PreviewMarkdownService < BaseService
end
def commands_target_type
- params[:slash_commands_target_type]
+ params[:quick_actions_target_type]
end
def commands_target_id
- params[:slash_commands_target_id]
+ params[:quick_actions_target_id]
end
end
diff --git a/app/services/projects/autocomplete_service.rb b/app/services/projects/autocomplete_service.rb
index 015f2828921..fc85f398935 100644
--- a/app/services/projects/autocomplete_service.rb
+++ b/app/services/projects/autocomplete_service.rb
@@ -32,7 +32,7 @@ module Projects
issuable: noteable,
current_user: current_user
}
- SlashCommands::InterpretService.command_definitions.map do |definition|
+ QuickActions::InterpretService.command_definitions.map do |definition|
next unless definition.available?(opts)
definition.to_h(opts)
diff --git a/app/services/slash_commands/interpret_service.rb b/app/services/quick_actions/interpret_service.rb
index b6b411d2185..6816b137361 100644
--- a/app/services/slash_commands/interpret_service.rb
+++ b/app/services/quick_actions/interpret_service.rb
@@ -1,13 +1,13 @@
-module SlashCommands
+module QuickActions
class InterpretService < BaseService
- include Gitlab::SlashCommands::Dsl
+ include Gitlab::QuickActions::Dsl
attr_reader :issuable
# Takes a text and interprets the commands that are extracted from it.
# Returns the content without commands, and hash of changes to be applied to a record.
def execute(content, issuable)
- return [content, {}] unless current_user.can?(:use_slash_commands)
+ return [content, {}] unless current_user.can?(:use_quick_actions)
@issuable = issuable
@updates = {}
@@ -20,7 +20,7 @@ module SlashCommands
# Takes a text and interprets the commands that are extracted from it.
# Returns the content without commands, and array of changes explained.
def explain(content, issuable)
- return [content, []] unless current_user.can?(:use_slash_commands)
+ return [content, []] unless current_user.can?(:use_quick_actions)
@issuable = issuable
@@ -32,7 +32,7 @@ module SlashCommands
private
def extractor
- Gitlab::SlashCommands::Extractor.new(self.class.command_definitions)
+ Gitlab::QuickActions::Extractor.new(self.class.command_definitions)
end
desc do
@@ -71,7 +71,7 @@ module SlashCommands
last_diff_sha = params && params[:merge_request_diff_head_sha]
issuable.is_a?(MergeRequest) &&
issuable.persisted? &&
- issuable.mergeable_with_slash_command?(current_user, autocomplete_precheck: !last_diff_sha, last_diff_sha: last_diff_sha)
+ issuable.mergeable_with_quick_action?(current_user, autocomplete_precheck: !last_diff_sha, last_diff_sha: last_diff_sha)
end
command :merge do
@updates[:merge] = params[:merge_request_diff_head_sha]
@@ -410,7 +410,7 @@ module SlashCommands
params '@user'
command :cc
- desc 'Define target branch for MR'
+ desc 'Set target branch'
explanation do |branch_name|
"Sets target branch to #{branch_name}."
end
diff --git a/app/services/users/refresh_authorized_projects_service.rb b/app/services/users/refresh_authorized_projects_service.rb
index 3e07b811027..f028f5eb0d4 100644
--- a/app/services/users/refresh_authorized_projects_service.rb
+++ b/app/services/users/refresh_authorized_projects_service.rb
@@ -34,7 +34,7 @@ module Users
# Keep trying until we obtain the lease. If we don't do so we may end up
# not updating the list of authorized projects properly. To prevent
# hammering Redis too much we'll wait for a bit between retries.
- sleep(1)
+ sleep(0.1)
end
begin
diff --git a/app/views/admin/application_settings/_form.html.haml b/app/views/admin/application_settings/_form.html.haml
index d552704df88..95dffdafabe 100644
--- a/app/views/admin/application_settings/_form.html.haml
+++ b/app/views/admin/application_settings/_form.html.haml
@@ -180,11 +180,25 @@
.col-sm-10
= f.text_area :sign_in_text, class: 'form-control', rows: 4
.help-block Markdown enabled
+
+ %fieldset
+ %legend Help Page
.form-group
= f.label :help_page_text, class: 'control-label col-sm-2'
.col-sm-10
= f.text_area :help_page_text, class: 'form-control', rows: 4
.help-block Markdown enabled
+ .form-group
+ .col-sm-offset-2.col-sm-10
+ .checkbox
+ = f.label :help_page_hide_commercial_content do
+ = f.check_box :help_page_hide_commercial_content
+ Hide marketing-related entries from help
+ .form-group
+ = f.label :help_page_support_url, 'Support page URL', class: 'control-label col-sm-2'
+ .col-sm-10
+ = f.text_field :help_page_support_url, class: 'form-control', placeholder: 'http://company.example.com/getting-help', :'aria-describedby' => 'support_help_block'
+ %span.help-block#support_help_block Alternate support URL for help page
%fieldset
%legend Pages
@@ -299,8 +313,9 @@
%fieldset
%legend Metrics - Prometheus
%p
- Setup Prometheus to measure a variety of statistics that partially overlap and complement Influx based metrics.
- This setting requires a
+ Enable a Prometheus metrics endpoint at `#{metrics_path}` to expose a variety of statistics on the health and performance of GitLab. Additional information on authenticating and connecting to the metrics endpoint is available
+ = link_to 'here', admin_health_check_path
+ \. This setting requires a
= link_to 'restart', help_page_path('administration/restart_gitlab')
to take effect.
= link_to icon('question-circle'), help_page_path('administration/monitoring/performance/introduction')
diff --git a/app/views/admin/broadcast_messages/_form.html.haml b/app/views/admin/broadcast_messages/_form.html.haml
index 2269fb1fd8c..5a4ed1c3a2a 100644
--- a/app/views/admin/broadcast_messages/_form.html.haml
+++ b/app/views/admin/broadcast_messages/_form.html.haml
@@ -21,11 +21,11 @@
.form-group.js-toggle-colors-container.hide
= f.label :color, "Background Color", class: 'control-label'
.col-sm-10
- = f.text_field :color, class: "form-control"
+ = f.color_field :color, class: "form-control"
.form-group.js-toggle-colors-container.hide
= f.label :font, "Font Color", class: 'control-label'
.col-sm-10
- = f.text_field :font, class: "form-control"
+ = f.color_field :font, class: "form-control"
.form-group
= f.label :starts_at, class: 'control-label'
.col-sm-10.datetime-controls
diff --git a/app/views/devise/mailer/confirmation_instructions.html.haml b/app/views/devise/mailer/confirmation_instructions.html.haml
index 086bb8e083d..a508b7537a2 100644
--- a/app/views/devise/mailer/confirmation_instructions.html.haml
+++ b/app/views/devise/mailer/confirmation_instructions.html.haml
@@ -1,16 +1,15 @@
-.center
- - if @resource.unconfirmed_email.present?
- #content
- %h2= @resource.unconfirmed_email
- %p Click the link below to confirm your email address.
- #cta
- = link_to 'Confirm your email address', confirmation_url(@resource, confirmation_token: @token)
- - else
- #content
- - if Gitlab.com?
- %h2 Thanks for signing up to GitLab!
- - else
- %h2 Welcome, #{@resource.name}!
- %p To get started, click the link below to confirm your account.
- #cta
- = link_to 'Confirm your account', confirmation_url(@resource, confirmation_token: @token)
+- if @resource.unconfirmed_email.present?
+ #content
+ = email_default_heading(@resource.unconfirmed_email)
+ %p Click the link below to confirm your email address.
+ #cta
+ = link_to 'Confirm your email address', confirmation_url(@resource, confirmation_token: @token)
+- else
+ #content
+ - if Gitlab.com?
+ = email_default_heading('Thanks for signing up to GitLab!')
+ - else
+ = email_default_heading("Welcome, #{@resource.name}!")
+ %p To get started, click the link below to confirm your account.
+ #cta
+ = link_to 'Confirm your account', confirmation_url(@resource, confirmation_token: @token)
diff --git a/app/views/devise/mailer/password_change.html.haml b/app/views/devise/mailer/password_change.html.haml
index 3349ee84807..5ec515285f2 100644
--- a/app/views/devise/mailer/password_change.html.haml
+++ b/app/views/devise/mailer/password_change.html.haml
@@ -1,10 +1,8 @@
-.center
- #content
- %h2 Hello, #{@resource.name}!
- %p
- The password for your GitLab account on
- #{link_to(Gitlab.config.gitlab.url, Gitlab.config.gitlab.url)}
- has successfully been changed.
- %p
- If you did not initiate this change, please contact your administrator
- immediately.
+= email_default_heading("Hello, #{@resource.name}!")
+%p
+ The password for your GitLab account on
+ #{link_to(Gitlab.config.gitlab.url, Gitlab.config.gitlab.url)}
+ has successfully been changed.
+%p
+ If you did not initiate this change, please contact your administrator
+ immediately.
diff --git a/app/views/devise/mailer/reset_password_instructions.html.haml b/app/views/devise/mailer/reset_password_instructions.html.haml
index e91c9522520..47e192afa52 100644
--- a/app/views/devise/mailer/reset_password_instructions.html.haml
+++ b/app/views/devise/mailer/reset_password_instructions.html.haml
@@ -1,12 +1,10 @@
-.center
- #content
- %h2 Hello, #{@resource.name}!
- %p
- Someone, hopefully you, has requested to reset the password for your
- GitLab account on #{link_to(Gitlab.config.gitlab.url, Gitlab.config.gitlab.url)}.
- %p
- If you did not perform this request, you can safely ignore this email.
- %p
- Otherwise, click the link below to complete the process.
- #cta
- = link_to('Reset password', edit_password_url(@resource, reset_password_token: @token))
+= email_default_heading("Hello, #{@resource.name}!")
+%p
+ Someone, hopefully you, has requested to reset the password for your
+ GitLab account on #{link_to(Gitlab.config.gitlab.url, Gitlab.config.gitlab.url)}.
+%p
+ If you did not perform this request, you can safely ignore this email.
+%p
+ Otherwise, click the link below to complete the process.
+#cta
+ = link_to('Reset password', edit_password_url(@resource, reset_password_token: @token))
diff --git a/app/views/devise/mailer/unlock_instructions.html.haml b/app/views/devise/mailer/unlock_instructions.html.haml
index 9990d1ccac6..79e3a35cc9a 100644
--- a/app/views/devise/mailer/unlock_instructions.html.haml
+++ b/app/views/devise/mailer/unlock_instructions.html.haml
@@ -1,9 +1,8 @@
-.center
- #content
- %h2 Hello, #{@resource.name}!
- %p
- Your GitLab account has been locked due to an excessive amount of unsuccessful
- sign in attempts. Your account will automatically unlock in #{time_ago_in_words(Devise.unlock_in.from_now)}
- or you may click the link below to unlock now.
- #cta
- = link_to('Unlock account', unlock_url(@resource, unlock_token: @token))
+#content
+ = email_default_heading("Hello, #{@resource.name}!")
+ %p
+ Your GitLab account has been locked due to an excessive amount of unsuccessful
+ sign in attempts. Your account will automatically unlock in #{time_ago_in_words(Devise.unlock_in.from_now)}
+ or you may click the link below to unlock now.
+ #cta
+ = link_to('Unlock account', unlock_url(@resource, unlock_token: @token))
diff --git a/app/views/discussions/_diff_with_notes.html.haml b/app/views/discussions/_diff_with_notes.html.haml
index 70042dee20f..4a41be972da 100644
--- a/app/views/discussions/_diff_with_notes.html.haml
+++ b/app/views/discussions/_diff_with_notes.html.haml
@@ -2,8 +2,9 @@
- blob = discussion.blob
.diff-file.file-holder
- .js-file-title.file-title
- = render "projects/diffs/file_header", diff_file: diff_file, url: discussion_path(discussion), show_toggle: false
+ .js-file-title.file-title.file-title-flex-parent
+ .file-header-content
+ = render "projects/diffs/file_header", diff_file: diff_file, url: discussion_path(discussion), show_toggle: false
.diff-content.code.js-syntax-highlight
%table
diff --git a/app/views/help/index.html.haml b/app/views/help/index.html.haml
index 31d0e589c26..c25eae63eec 100644
--- a/app/views/help/index.html.haml
+++ b/app/views/help/index.html.haml
@@ -1,4 +1,9 @@
%div
+- if current_application_settings.help_page_text.present?
+ = markdown_field(current_application_settings, :help_page_text)
+ %hr
+
+- unless current_application_settings.help_page_hide_commercial_content?
%h1
GitLab
Community Edition
@@ -18,13 +23,9 @@
Used by more than 100,000 organizations, GitLab is the most popular solution to manage git repositories on-premises.
%br
Read more about GitLab at #{link_to promo_host, promo_url, target: '_blank', rel: 'noopener noreferrer'}.
- - if current_application_settings.help_page_text.present?
- %hr
- = markdown_field(current_application_settings, :help_page_text)
-
-%hr
+ %hr
-.row
+.row.prepend-top-default
.col-md-8
.documentation-index
= markdown(@help_index)
@@ -33,8 +34,9 @@
.panel-heading
Quick help
%ul.well-list
- %li= link_to 'See our website for getting help', promo_url + '/getting-help/'
+ %li= link_to 'See our website for getting help', support_url
%li= link_to 'Use the search bar on the top of this page', '#', onclick: 'Shortcuts.focusSearch(event)'
%li= link_to 'Use shortcuts', '#', onclick: 'Shortcuts.toggleHelp()'
- %li= link_to 'Get a support subscription', 'https://about.gitlab.com/pricing/'
- %li= link_to 'Compare GitLab editions', 'https://about.gitlab.com/features/#compare'
+ - unless current_application_settings.help_page_hide_commercial_content?
+ %li= link_to 'Get a support subscription', 'https://about.gitlab.com/pricing/'
+ %li= link_to 'Compare GitLab editions', 'https://about.gitlab.com/features/#compare'
diff --git a/app/views/help/show.html.haml b/app/views/help/show.html.haml
index f6ebd76af9d..c07c148a12a 100644
--- a/app/views/help/show.html.haml
+++ b/app/views/help/show.html.haml
@@ -1,3 +1,3 @@
- page_title @path.split("/").reverse.map(&:humanize)
-.documentation.wiki
+.documentation.wiki.prepend-top-default
= markdown @markdown
diff --git a/app/views/layouts/_broadcast.html.haml b/app/views/layouts/_broadcast.html.haml
index 3a7e0929c16..bcd2f03e83c 100644
--- a/app/views/layouts/_broadcast.html.haml
+++ b/app/views/layouts/_broadcast.html.haml
@@ -1 +1,2 @@
-= broadcast_message
+- BroadcastMessage.current.each do |message|
+ = broadcast_message(message)
diff --git a/app/views/layouts/_mailer.html.haml b/app/views/layouts/_mailer.html.haml
new file mode 100644
index 00000000000..983ed22a506
--- /dev/null
+++ b/app/views/layouts/_mailer.html.haml
@@ -0,0 +1,74 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional //EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+%html{ lang: "en" }
+ %head
+ %meta{ content: "text/html; charset=UTF-8", "http-equiv" => "Content-Type" }/
+ %meta{ content: "width=device-width, initial-scale=1", name: "viewport" }/
+ %meta{ content: "IE=edge", "http-equiv" => "X-UA-Compatible" }/
+ %title= message.subject
+ :css
+ /* CLIENT-SPECIFIC STYLES */
+ body, table, td, a { -webkit-text-size-adjust: 100%; -ms-text-size-adjust: 100%; }
+ table, td { mso-table-lspace: 0pt; mso-table-rspace: 0pt; }
+ img { -ms-interpolation-mode: bicubic; }
+
+ /* iOS BLUE LINKS */
+ a[x-apple-data-detectors] {
+ color: inherit !important;
+ text-decoration: none !important;
+ font-size: inherit !important;
+ font-family: inherit !important;
+ font-weight: inherit !important;
+ line-height: inherit !important;
+ }
+
+ /* ANDROID MARGIN HACK */
+ body { margin:0 !important; }
+ div[style*="margin: 16px 0"] { margin:0 !important; }
+
+ @media only screen and (max-width: 639px) {
+ body, #body {
+ min-width: 320px !important;
+ }
+ table.wrapper {
+ width: 100% !important;
+ min-width: 320px !important;
+ }
+ table.wrapper > tbody > tr > td {
+ border-left: 0 !important;
+ border-right: 0 !important;
+ border-radius: 0 !important;
+ padding-left: 10px !important;
+ padding-right: 10px !important;
+ }
+ }
+ %body{ style: "background-color:#fafafa;margin:0;padding:0;text-align:center;min-width:640px;width:100%;height:100%;font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;" }
+ %table#body{ border: "0", cellpadding: "0", cellspacing: "0", style: "background-color:#fafafa;margin:0;padding:0;text-align:center;min-width:640px;width:100%;" }
+ %tbody
+ %tr.line
+ %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;background-color:#6b4fbb;height:4px;font-size:4px;line-height:4px;" }  
+ %tr.header
+ %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;padding:25px 0;font-size:13px;line-height:1.6;color:#5c5c5c;" }
+ = header_logo
+ %tr
+ %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;" }
+ %table.wrapper{ border: "0", cellpadding: "0", cellspacing: "0", style: "width:640px;margin:0 auto;border-collapse:separate;border-spacing:0;" }
+ %tbody
+ %tr
+ %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;background-color:#ffffff;text-align:left;padding:18px 25px;border:1px solid #ededed;border-radius:3px;overflow:hidden;" }
+ %table.content{ border: "0", cellpadding: "0", cellspacing: "0", style: "width:100%;border-collapse:separate;border-spacing:0;" }
+ %tbody
+ = yield
+
+ %tr.footer
+ %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;padding:25px 0;font-size:13px;line-height:1.6;color:#5c5c5c;" }
+ %img{ alt: "GitLab", height: "33", src: image_url('mailers/gitlab_footer_logo.gif'), style: "display:block;margin:0 auto 1em;", width: "90" }/
+ %div
+ %a{ href: profile_notifications_url, style: "color:#3777b0;text-decoration:none;" } Manage all notifications
+ &middot;
+ %a{ href: help_url, style: "color:#3777b0;text-decoration:none;" } Help
+ %div
+ You're receiving this email because of your account on
+ = succeed "." do
+ %a{ href: root_url, style: "color:#3777b0;text-decoration:none;" }= Gitlab.config.gitlab.host
+
+ = yield :additional_footer
diff --git a/app/views/layouts/devise_mailer.html.haml b/app/views/layouts/devise_mailer.html.haml
deleted file mode 100644
index e1e1f9ae516..00000000000
--- a/app/views/layouts/devise_mailer.html.haml
+++ /dev/null
@@ -1,34 +0,0 @@
-!!! 5
-%html
- %head
- %meta{ content: 'text/html; charset=UTF-8', 'http-equiv'=> 'Content-Type' }
- = stylesheet_link_tag 'mailers/devise'
-
- %body
- %table#wrapper
- %tr
- %td
- %table#header
- %td{ valign: "top" }
- = image_tag('mailers/gitlab_header_logo.png', id: 'logo', alt: 'GitLab Wordmark')
-
- %table#body
- %tr
- %td#body-container
- = yield
-
- - if Gitlab.com?
- %table#footer
- %tr
- %td#tanuki
- = image_tag('mailers/gitlab_tanuki_2x.png', alt: 'GitLab Logo')
- %tr
- %td#tagline
- Everyone can contribute
- %tr
- %td#social
- = link_to 'Blog', 'https://about.gitlab.com/blog/'
- = link_to 'Twitter', 'https://twitter.com/gitlab'
- = link_to 'Facebook', 'https://www.facebook.com/gitlab/'
- = link_to 'YouTube', 'https://www.youtube.com/channel/UCnMGQ8QHMAnVIsI3xJrihhg'
- = link_to 'LinkedIn', 'https://www.linkedin.com/company/gitlab-com'
diff --git a/app/views/layouts/mailer.html.haml b/app/views/layouts/mailer.html.haml
index 53268cc22f8..28dcbce7183 100644
--- a/app/views/layouts/mailer.html.haml
+++ b/app/views/layouts/mailer.html.haml
@@ -1,72 +1 @@
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional //EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
-%html{ lang: "en" }
- %head
- %meta{ content: "text/html; charset=UTF-8", "http-equiv" => "Content-Type" }/
- %meta{ content: "width=device-width, initial-scale=1", name: "viewport" }/
- %meta{ content: "IE=edge", "http-equiv" => "X-UA-Compatible" }/
- %title= message.subject
- :css
- /* CLIENT-SPECIFIC STYLES */
- body, table, td, a { -webkit-text-size-adjust: 100%; -ms-text-size-adjust: 100%; }
- table, td { mso-table-lspace: 0pt; mso-table-rspace: 0pt; }
- img { -ms-interpolation-mode: bicubic; }
-
- /* iOS BLUE LINKS */
- a[x-apple-data-detectors] {
- color: inherit !important;
- text-decoration: none !important;
- font-size: inherit !important;
- font-family: inherit !important;
- font-weight: inherit !important;
- line-height: inherit !important;
- }
-
- /* ANDROID MARGIN HACK */
- body { margin:0 !important; }
- div[style*="margin: 16px 0"] { margin:0 !important; }
-
- @media only screen and (max-width: 639px) {
- body, #body {
- min-width: 320px !important;
- }
- table.wrapper {
- width: 100% !important;
- min-width: 320px !important;
- }
- table.wrapper > tbody > tr > td {
- border-left: 0 !important;
- border-right: 0 !important;
- border-radius: 0 !important;
- padding-left: 10px !important;
- padding-right: 10px !important;
- }
- }
- %body{ style: "background-color:#fafafa;margin:0;padding:0;text-align:center;min-width:640px;width:100%;height:100%;font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;" }
- %table#body{ border: "0", cellpadding: "0", cellspacing: "0", style: "background-color:#fafafa;margin:0;padding:0;text-align:center;min-width:640px;width:100%;" }
- %tbody
- %tr.line
- %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;background-color:#6b4fbb;height:4px;font-size:4px;line-height:4px;" }  
- %tr.header
- %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;padding:25px 0;font-size:13px;line-height:1.6;color:#5c5c5c;" }
- = header_logo
- %tr
- %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;" }
- %table.wrapper{ border: "0", cellpadding: "0", cellspacing: "0", style: "width:640px;margin:0 auto;border-collapse:separate;border-spacing:0;" }
- %tbody
- %tr
- %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;background-color:#ffffff;text-align:left;padding:18px 25px;border:1px solid #ededed;border-radius:3px;overflow:hidden;" }
- %table.content{ border: "0", cellpadding: "0", cellspacing: "0", style: "width:100%;border-collapse:separate;border-spacing:0;" }
- %tbody
- = yield
-
- %tr.footer
- %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;padding:25px 0;font-size:13px;line-height:1.6;color:#5c5c5c;" }
- %img{ alt: "GitLab", height: "33", src: image_url('mailers/gitlab_footer_logo.gif'), style: "display:block;margin:0 auto 1em;", width: "90" }/
- %div
- %a{ href: profile_notifications_url, style: "color:#3777b0;text-decoration:none;" } Manage all notifications
- &middot;
- %a{ href: help_url, style: "color:#3777b0;text-decoration:none;" } Help
- %div
- You're receiving this email because of your account on
- = succeed "." do
- %a{ href: root_url, style: "color:#3777b0;text-decoration:none;" }= Gitlab.config.gitlab.host
+= render 'layouts/mailer'
diff --git a/app/views/layouts/mailer/devise.html.haml b/app/views/layouts/mailer/devise.html.haml
new file mode 100644
index 00000000000..054b2c2fa26
--- /dev/null
+++ b/app/views/layouts/mailer/devise.html.haml
@@ -0,0 +1,21 @@
+- if Gitlab.com?
+ - content_for :additional_footer do
+ %tr
+ %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:13px;line-height:1.6;color:#5c5c5c;" }
+ %div
+ Everyone can contribute
+ %div
+ = link_to 'Blog', 'https://about.gitlab.com/blog/', style: "color:#3777b0;text-decoration:none;"
+ &middot;
+ = link_to 'Twitter', 'https://twitter.com/gitlab', style: "color:#3777b0;text-decoration:none;"
+ &middot;
+ = link_to 'Facebook', 'https://www.facebook.com/gitlab/', style: "color:#3777b0;text-decoration:none;"
+ &middot;
+ = link_to 'YouTube', 'https://www.youtube.com/channel/UCnMGQ8QHMAnVIsI3xJrihhg', style: "color:#3777b0;text-decoration:none;"
+ &middot;
+ = link_to 'LinkedIn', 'https://www.linkedin.com/company/gitlab-com', style: "color:#3777b0;text-decoration:none;"
+
+= render layout: 'layouts/mailer' do
+ %tr
+ %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;color:#333333;font-size:15px;font-weight:400;line-height:1.4;padding:15px 5px;text-align:center;" }
+ = yield
diff --git a/app/views/profiles/show.html.haml b/app/views/profiles/show.html.haml
index fcfd350f0da..15672289c65 100644
--- a/app/views/profiles/show.html.haml
+++ b/app/views/profiles/show.html.haml
@@ -42,10 +42,17 @@
- if current_user.ldap_user?
Some options are unavailable for LDAP accounts
.col-lg-9
- .form-group
- = f.label :name, class: "label-light"
- = f.text_field :name, class: "form-control", required: true
- %span.help-block Enter your name, so people you know can recognize you.
+ .row
+ .form-group.col-md-9
+ = f.label :name, class: "label-light"
+ = f.text_field :name, class: "form-control", required: true
+ %span.help-block Enter your name, so people you know can recognize you.
+
+ .form-group.col-md-3
+ = f.label :id, class: 'label-light' do
+ User ID
+ = f.text_field :id, class: 'form-control', readonly: true
+
.form-group
= f.label :email, class: "label-light"
diff --git a/app/views/projects/_visibility_select.html.haml b/app/views/projects/_visibility_select.html.haml
index 65fc0a36ca9..4026b9e3c46 100644
--- a/app/views/projects/_visibility_select.html.haml
+++ b/app/views/projects/_visibility_select.html.haml
@@ -1,5 +1,7 @@
- if can_change_visibility_level?(@project, current_user)
- = form.select(model_method, visibility_select_options(@project, selected_level), {}, class: 'form-control visibility-select')
+ .select-wrapper
+ = form.select(model_method, visibility_select_options(@project, selected_level), {}, class: 'form-control visibility-select select-control')
+ = icon('chevron-down')
- else
.info.js-locked{ data: { help_block: visibility_level_description(@project.visibility_level, @project) } }
= visibility_level_icon(@project.visibility_level)
diff --git a/app/views/projects/_zen.html.haml b/app/views/projects/_zen.html.haml
index 3b3d08ddd3c..afc40ca4eab 100644
--- a/app/views/projects/_zen.html.haml
+++ b/app/views/projects/_zen.html.haml
@@ -1,10 +1,15 @@
- @gfm_form = true
- current_text ||= nil
-- supports_slash_commands = local_assigns.fetch(:supports_slash_commands, false)
+- supports_autocomplete = local_assigns.fetch(:supports_autocomplete, true)
+- supports_quick_actions = local_assigns.fetch(:supports_quick_actions, false)
.zen-backdrop
- classes << ' js-gfm-input js-autosize markdown-area'
- if defined?(f) && f
- = f.text_area attr, class: classes, placeholder: placeholder, data: { supports_slash_commands: supports_slash_commands }
+ = f.text_area attr,
+ class: classes,
+ placeholder: placeholder,
+ data: { supports_quick_actions: supports_quick_actions,
+ supports_autocomplete: supports_autocomplete }
- else
= text_area_tag attr, current_text, class: classes, placeholder: placeholder
%a.zen-control.zen-control-leave.js-zen-leave{ href: "#" }
diff --git a/app/views/projects/blame/_age_map_legend.html.haml b/app/views/projects/blame/_age_map_legend.html.haml
new file mode 100644
index 00000000000..533dc20ffb3
--- /dev/null
+++ b/app/views/projects/blame/_age_map_legend.html.haml
@@ -0,0 +1,12 @@
+%span.left-label Newer
+%span.legend-box.legend-box-0
+%span.legend-box.legend-box-1
+%span.legend-box.legend-box-2
+%span.legend-box.legend-box-3
+%span.legend-box.legend-box-4
+%span.legend-box.legend-box-5
+%span.legend-box.legend-box-6
+%span.legend-box.legend-box-7
+%span.legend-box.legend-box-8
+%span.legend-box.legend-box-9
+%span.right-label Older
diff --git a/app/views/projects/blame/show.html.haml b/app/views/projects/blame/show.html.haml
index a6ee2b2f7b8..ce937ee1842 100644
--- a/app/views/projects/blame/show.html.haml
+++ b/app/views/projects/blame/show.html.haml
@@ -1,4 +1,5 @@
- @no_container = true
+- project_duration = age_map_duration(@blame_groups, @project)
- page_title "Annotate", @blob.path, @ref
= render "projects/commits/head"
@@ -8,15 +9,16 @@
.file-holder
= render "projects/blob/header", blob: @blob, blame: true
-
+ .file-blame-legend
+ = render 'age_map_legend'
.table-responsive.file-content.blame.code.js-syntax-highlight
%table
- current_line = 1
- @blame_groups.each do |blame_group|
%tr
- %td.blame-commit
+ - commit = blame_group[:commit]
+ %td.blame-commit{ class: age_map_class(commit.committed_date, project_duration) }
.commit
- - commit = blame_group[:commit]
= author_avatar(commit, size: 36)
.commit-row-title
%strong
diff --git a/app/views/projects/blob/_upload.html.haml b/app/views/projects/blob/_upload.html.haml
index e14885f264b..24314e03b46 100644
--- a/app/views/projects/blob/_upload.html.haml
+++ b/app/views/projects/blob/_upload.html.haml
@@ -9,8 +9,10 @@
.dropzone
.dropzone-previews.blob-upload-dropzone-previews
%p.dz-message.light
- Attach a file by drag &amp; drop or
- = link_to 'click to upload', '#', class: "markdown-selector"
+ - upload_link = link_to n_('UploadLink|click to upload'), '#', class: "markdown-selector"
+ - dropzone_text = _('Attach a file by drag &amp; drop or %{upload_link}') % { upload_link: upload_link }
+ #{ dropzone_text.html_safe }
+
%br
.dropzone-alerts.alert.alert-danger.data{ style: "display:none" }
@@ -18,7 +20,7 @@
.form-actions
= button_tag button_title, class: 'btn btn-small btn-create btn-upload-file', id: 'submit-all'
- = link_to "Cancel", '#', class: "btn btn-cancel", "data-dismiss" => "modal"
+ = link_to _("Cancel"), '#', class: "btn btn-cancel", "data-dismiss" => "modal"
- unless can?(current_user, :push_code, @project)
.inline.prepend-left-10
diff --git a/app/views/projects/boards/components/_board.html.haml b/app/views/projects/boards/components/_board.html.haml
index 55c4d51be14..539ee087b14 100644
--- a/app/views/projects/boards/components/_board.html.haml
+++ b/app/views/projects/boards/components/_board.html.haml
@@ -9,11 +9,11 @@
%span.has-tooltip{ ":title" => '(list.label ? list.label.description : "")',
data: { container: "body", placement: "bottom" } }
{{ list.title }}
- .board-issue-count-holder.pull-right.clearfix{ "v-if" => 'list.type !== "blank"' }
- %span.board-issue-count.pull-left{ ":class" => '{ "has-btn": list.type !== "closed" && !disabled }' }
+ .issue-count-badge.pull-right.clearfix{ "v-if" => 'list.type !== "blank"' }
+ %span.issue-count-badge-count.pull-left{ ":class" => '{ "has-btn": list.type !== "closed" && !disabled }' }
{{ list.issuesSize }}
- if can?(current_user, :admin_issue, @project)
- %button.btn.btn-small.btn-default.pull-right.has-tooltip.js-no-trigger-collapse{ type: "button",
+ %button.issue-count-badge-add-button.btn.btn-small.btn-default.has-tooltip.js-no-trigger-collapse{ type: "button",
"@click" => "showNewIssueForm",
"v-if" => 'list.type !== "closed"',
"aria-label" => "New issue",
diff --git a/app/views/projects/buttons/_download.html.haml b/app/views/projects/buttons/_download.html.haml
index 3cf91bf07f7..a73ddd5eb33 100644
--- a/app/views/projects/buttons/_download.html.haml
+++ b/app/views/projects/buttons/_download.html.haml
@@ -2,7 +2,7 @@
- if !project.empty_repo? && can?(current_user, :download_code, project)
.project-action-button.dropdown.inline>
- %button.btn.has-tooltip{ title: 'Download', 'data-toggle' => 'dropdown', 'aria-label' => s_('DownloadSource|Download') }
+ %button.btn.has-tooltip{ title: s_('DownloadSource|Download'), 'data-toggle' => 'dropdown', 'aria-label' => s_('DownloadSource|Download') }
= icon('download')
= icon("caret-down")
%span.sr-only= _('Select Archive Format')
diff --git a/app/views/projects/buttons/_dropdown.html.haml b/app/views/projects/buttons/_dropdown.html.haml
index 312c349da3a..960b57a8008 100644
--- a/app/views/projects/buttons/_dropdown.html.haml
+++ b/app/views/projects/buttons/_dropdown.html.haml
@@ -1,6 +1,6 @@
- if current_user
.project-action-button.dropdown.inline
- %a.btn.dropdown-toggle.has-tooltip{ href: '#', title: 'Create new...', 'data-toggle' => 'dropdown', 'data-container' => 'body', 'aria-label' => 'Create new...' }
+ %a.btn.dropdown-toggle.has-tooltip{ href: '#', title: _('Create new...'), 'data-toggle' => 'dropdown', 'data-container' => 'body', 'aria-label' => _('Create new...') }
= icon('plus')
= icon("caret-down")
%ul.dropdown-menu.dropdown-menu-align-right.project-home-dropdown
diff --git a/app/views/projects/buttons/_fork.html.haml b/app/views/projects/buttons/_fork.html.haml
index 7a08bb37494..42f8c75f57b 100644
--- a/app/views/projects/buttons/_fork.html.haml
+++ b/app/views/projects/buttons/_fork.html.haml
@@ -4,11 +4,15 @@
= link_to namespace_project_path(current_user, current_user.fork_of(@project)), title: _('Go to your fork'), class: 'btn has-tooltip' do
= custom_icon('icon_fork')
%span= s_('GoToYourFork|Fork')
+ - elsif !current_user.can_create_project?
+ = link_to new_namespace_project_fork_path(@project.namespace, @project), title: _('You have reached your project limit'), class: 'btn has-tooltip disabled' do
+ = custom_icon('icon_fork')
+ %span= s_('CreateNewFork|Fork')
- else
= link_to new_namespace_project_fork_path(@project.namespace, @project), class: 'btn' do
= custom_icon('icon_fork')
%span= s_('CreateNewFork|Fork')
.count-with-arrow
%span.arrow
- = link_to namespace_project_forks_path(@project.namespace, @project), title: n_('Forks', @project.forks_count), class: 'count' do
+ = link_to namespace_project_forks_path(@project.namespace, @project), title: n_('Fork', 'Forks', @project.forks_count), class: 'count' do
= @project.forks_count
diff --git a/app/views/projects/commit/_change.html.haml b/app/views/projects/commit/_change.html.haml
index 281d823da52..208d4908721 100644
--- a/app/views/projects/commit/_change.html.haml
+++ b/app/views/projects/commit/_change.html.haml
@@ -1,35 +1,36 @@
- case type.to_s
- when 'revert'
- - label = 'Revert'
- - branch_label = 'Revert in branch'
+ - label = s_('ChangeTypeAction|Revert')
+ - branch_label = s_('ChangeTypeActionLabel|Revert in branch')
+ - revert_merge_request = _('Revert this merge request')
+ - revert_commit = _('Revert this commit')
+ - title = commit.merged_merge_request(current_user) ? revert_merge_request : revert_commit
- when 'cherry-pick'
- - label = 'Cherry-pick'
- - branch_label = 'Pick into branch'
+ - label = s_('ChangeTypeAction|Cherry-pick')
+ - branch_label = s_('ChangeTypeActionLabel|Pick into branch')
+ - title = commit.merged_merge_request(current_user) ? _('Cherry-pick this merge request') : _('Cherry-pick this commit')
.modal{ id: "modal-#{type}-commit" }
.modal-dialog
.modal-content
.modal-header
%a.close{ href: "#", "data-dismiss" => "modal" } ×
- %h3.page-title== #{label} this #{commit.change_type_title(current_user)}
+ %h3.page-title= title
.modal-body
= form_tag [type.underscore, @project.namespace.becomes(Namespace), @project, commit], method: :post, remote: false, class: "form-horizontal js-#{type}-form js-requires-input" do
.form-group.branch
= label_tag 'start_branch', branch_label, class: 'control-label'
.col-sm-10
= hidden_field_tag :start_branch, @project.default_branch, id: 'start_branch'
- = dropdown_tag(@project.default_branch, options: { title: "Switch branch", filter: true, placeholder: "Search branches", toggle_class: 'js-project-refs-dropdown dynamic', dropdown_class: 'dropdown-menu-selectable', data: { field_name: "start_branch", selected: @project.default_branch, start_branch: @project.default_branch, refs_url: namespace_project_branches_path(@project.namespace, @project), submit_form_on_click: false } })
+ = dropdown_tag(@project.default_branch, options: { title: n_("BranchSwitcherTitle|Switch branch"), filter: true, placeholder: n_("BranchSwitcherPlaceholder|Search branches"), toggle_class: 'js-project-refs-dropdown dynamic', dropdown_class: 'dropdown-menu-selectable', data: { field_name: "start_branch", selected: @project.default_branch, start_branch: @project.default_branch, refs_url: namespace_project_branches_path(@project.namespace, @project), submit_form_on_click: false } })
- if can?(current_user, :push_code, @project)
- .checkbox
- = label_tag do
- = check_box_tag 'create_merge_request', 1, true, class: 'js-create-merge-request', id: nil
- Start a <strong>new merge request</strong> with these changes
+ = render 'shared/new_merge_request_checkbox'
- else
= hidden_field_tag 'create_merge_request', 1, id: nil
.form-actions
= submit_tag label, class: 'btn btn-create'
- = link_to "Cancel", '#', class: "btn btn-cancel", "data-dismiss" => "modal"
+ = link_to _("Cancel"), '#', class: "btn btn-cancel", "data-dismiss" => "modal"
- unless can?(current_user, :push_code, @project)
.inline.prepend-left-10
diff --git a/app/views/projects/commit/_commit_box.html.haml b/app/views/projects/commit/_commit_box.html.haml
index aab50310234..7fe44816bae 100644
--- a/app/views/projects/commit/_commit_box.html.haml
+++ b/app/views/projects/commit/_commit_box.html.haml
@@ -1,17 +1,17 @@
.page-content-header
.header-main-content
%strong
- Commit
+ #{ s_('CommitBoxTitle|Commit') }
%span.commit-sha= @commit.short_id
- = clipboard_button(text: @commit.id, title: "Copy commit SHA to clipboard")
+ = clipboard_button(text: @commit.id, title: _("Copy commit SHA to clipboard"))
%span.hidden-xs authored
#{time_ago_with_tooltip(@commit.authored_date)}
- %span by
+ %span= s_('ByAuthor|by')
= author_avatar(@commit, size: 24)
%strong
= commit_author_link(@commit, avatar: true, size: 24)
- if @commit.different_committer?
- %span.light Committed by
+ %span.light= _('Committed by')
%strong
= commit_committer_link(@commit, avatar: true, size: 24)
#{time_ago_with_tooltip(@commit.committed_date)}
@@ -22,15 +22,15 @@
= icon('comment')
= @notes_count
= link_to namespace_project_tree_path(@project.namespace, @project, @commit), class: "btn btn-default append-right-10 hidden-xs hidden-sm" do
- Browse files
+ #{ _('Browse files') }
.dropdown.inline
%a.btn.btn-default.dropdown-toggle{ data: { toggle: "dropdown" } }
- %span Options
+ %span= _('Options')
= icon('caret-down')
%ul.dropdown-menu.dropdown-menu-align-right
%li.visible-xs-block.visible-sm-block
= link_to namespace_project_tree_path(@project.namespace, @project, @commit) do
- Browse Files
+ _('Browse Files')
- unless @commit.has_been_reverted?(current_user)
%li.clearfix
= revert_commit_link(@commit, namespace_project_commit_path(@project.namespace, @project, @commit.id), has_tooltip: false)
@@ -38,13 +38,13 @@
= cherry_pick_commit_link(@commit, namespace_project_commit_path(@project.namespace, @project, @commit.id), has_tooltip: false)
- if can_collaborate_with_project?
%li.clearfix
- = link_to "Tag", new_namespace_project_tag_path(@project.namespace, @project, ref: @commit)
+ = link_to s_("CreateTag|Tag"), new_namespace_project_tag_path(@project.namespace, @project, ref: @commit)
%li.divider
%li.dropdown-header
- Download
+ #{ _('Download') }
- unless @commit.parents.length > 1
- %li= link_to "Email Patches", namespace_project_commit_path(@project.namespace, @project, @commit, format: :patch)
- %li= link_to "Plain Diff", namespace_project_commit_path(@project.namespace, @project, @commit, format: :diff)
+ %li= link_to s_("DownloadCommit|Email Patches"), namespace_project_commit_path(@project.namespace, @project, @commit, format: :patch)
+ %li= link_to s_("DownloadCommit|Plain Diff"), namespace_project_commit_path(@project.namespace, @project, @commit, format: :diff)
.commit-box
%h3.commit-title
@@ -57,7 +57,7 @@
.well-segment.branch-info
.icon-container.commit-icon
= custom_icon("icon_commit")
- %span.cgray= pluralize(@commit.parents.count, "parent")
+ %span.cgray= n_('parent', 'parents', @commit.parents.count)
- @commit.parents.each do |parent|
= link_to parent.short_id, namespace_project_commit_path(@project.namespace, @project, parent), class: "commit-sha"
%span.commit-info.branches
@@ -69,11 +69,11 @@
.status-icon-container{ class: "ci-status-icon-#{@commit.status}" }
= link_to namespace_project_pipeline_path(@project.namespace, @project, last_pipeline.id) do
= ci_icon_for_status(last_pipeline.status)
- Pipeline
+ #{ _('Pipeline') }
= link_to "##{last_pipeline.id}", namespace_project_pipeline_path(@project.namespace, @project, last_pipeline.id)
= ci_label_for_status(last_pipeline.status)
- if last_pipeline.stages_count.nonzero?
- with #{"stage".pluralize(last_pipeline.stages_count)}
+ #{ n_(s_('Pipeline|with stage'), s_('Pipeline|with stages'), last_pipeline.stages_count) }
.mr-widget-pipeline-graph
= render 'shared/mini_pipeline_graph', pipeline: last_pipeline, klass: 'js-commit-pipeline-graph'
in
diff --git a/app/views/projects/commits/_commit.html.haml b/app/views/projects/commits/_commit.html.haml
index 7a03c3561af..11de6915961 100644
--- a/app/views/projects/commits/_commit.html.haml
+++ b/app/views/projects/commits/_commit.html.haml
@@ -30,9 +30,11 @@
%pre.commit-row-description.js-toggle-content
= preserve(markdown(commit.description, pipeline: :single_line, author: commit.author))
.commiter
- = commit_author_link(commit, avatar: false, size: 24)
- #{ _('committed') }
- #{time_ago_with_tooltip(commit.committed_date)}
+ - commit_author_link = commit_author_link(commit, avatar: false, size: 24)
+ - commit_timeago = time_ago_with_tooltip(commit.committed_date)
+ - commit_text = _('%{commit_author_link} committed %{commit_timeago}') % { commit_author_link: commit_author_link, commit_timeago: commit_timeago }
+ #{ commit_text.html_safe }
+
.commit-actions.flex-row.hidden-xs
- if commit.status(ref)
diff --git a/app/views/projects/deployments/_deployment.html.haml b/app/views/projects/deployments/_deployment.html.haml
index d956cb2cc1a..9b2ec9ae41c 100644
--- a/app/views/projects/deployments/_deployment.html.haml
+++ b/app/views/projects/deployments/_deployment.html.haml
@@ -20,7 +20,7 @@
.table-mobile-header{ role: 'rowheader' } Created
%span.table-mobile-content= time_ago_with_tooltip(deployment.created_at)
- .table-section.section-20.environments-actions.table-button-footer{ role: 'gridcell' }
- .btn-group.environment-action-buttons
+ .table-section.section-20.table-button-footer{ role: 'gridcell' }
+ .btn-group.table-action-button
= render 'projects/deployments/actions', deployment: deployment
= render 'projects/deployments/rollback', deployment: deployment
diff --git a/app/views/projects/diffs/_collapsed.html.haml b/app/views/projects/diffs/_collapsed.html.haml
new file mode 100644
index 00000000000..8772bd4705f
--- /dev/null
+++ b/app/views/projects/diffs/_collapsed.html.haml
@@ -0,0 +1,5 @@
+- diff_file = viewer.diff_file
+- url = url_for(params.merge(action: :diff_for_path, old_path: diff_file.old_path, new_path: diff_file.new_path, file_identifier: diff_file.file_identifier))
+.nothing-here-block.diff-collapsed{ data: { diff_for_path: url } }
+ This diff is collapsed.
+ %a.click-to-expand Click to expand it.
diff --git a/app/views/projects/diffs/_content.html.haml b/app/views/projects/diffs/_content.html.haml
index ec1c434a4b8..68f74f702ea 100644
--- a/app/views/projects/diffs/_content.html.haml
+++ b/app/views/projects/diffs/_content.html.haml
@@ -1,27 +1,2 @@
-- blob = diff_file.blob
-
.diff-content
- - if diff_file.too_large?
- .nothing-here-block This diff could not be displayed because it is too large.
- - elsif blob.truncated?
- .nothing-here-block The file could not be displayed because it is too large.
- - elsif blob.readable_text?
- - if !diff_file.diffable?
- .nothing-here-block This diff was suppressed by a .gitattributes entry.
- - elsif diff_file.collapsed?
- - url = url_for(params.merge(action: :diff_for_path, old_path: diff_file.old_path, new_path: diff_file.new_path, file_identifier: diff_file.file_identifier))
- .nothing-here-block.diff-collapsed{ data: { diff_for_path: url } }
- This diff is collapsed.
- %a.click-to-expand
- Click to expand it.
- - elsif diff_file.diff_lines.length > 0
- = render "projects/diffs/viewers/text", diff_file: diff_file
- - else
- - if diff_file.mode_changed?
- .nothing-here-block File mode changed
- - elsif diff_file.renamed_file?
- .nothing-here-block File moved
- - elsif blob.image?
- = render "projects/diffs/viewers/image", diff_file: diff_file
- - else
- .nothing-here-block No preview for this file type
+ = render 'projects/diffs/viewer', viewer: diff_file.rich_viewer || diff_file.simple_viewer
diff --git a/app/views/projects/diffs/_render_error.html.haml b/app/views/projects/diffs/_render_error.html.haml
new file mode 100644
index 00000000000..47a9ac3ee6b
--- /dev/null
+++ b/app/views/projects/diffs/_render_error.html.haml
@@ -0,0 +1,6 @@
+.nothing-here-block
+ This #{viewer.switcher_title} could not be displayed because #{diff_render_error_reason(viewer)}.
+
+ You can
+ = diff_render_error_options(viewer).to_sentence(two_words_connector: ' or ', last_word_connector: ', or ').html_safe
+ instead.
diff --git a/app/views/projects/diffs/_viewer.html.haml b/app/views/projects/diffs/_viewer.html.haml
new file mode 100644
index 00000000000..5c4d1760871
--- /dev/null
+++ b/app/views/projects/diffs/_viewer.html.haml
@@ -0,0 +1,16 @@
+- hidden = local_assigns.fetch(:hidden, false)
+
+.diff-viewer{ data: { type: viewer.type }, class: ('hidden' if hidden) }
+ - if viewer.render_error
+ = render 'projects/diffs/render_error', viewer: viewer
+ - elsif viewer.collapsed?
+ = render 'projects/diffs/collapsed', viewer: viewer
+ - else
+ - viewer.prepare!
+
+ -# In the rare case where the first kilobyte of the file looks like text,
+ -# but the file turns out to actually be binary after loading all data,
+ -# we fall back on the binary No Preview viewer.
+ - viewer = DiffViewer::NoPreview.new(viewer.diff_file) if viewer.binary_detected_after_load?
+
+ = render viewer.partial_path, viewer: viewer
diff --git a/app/views/projects/diffs/viewers/_added.html.haml b/app/views/projects/diffs/viewers/_added.html.haml
new file mode 100644
index 00000000000..8004fe16688
--- /dev/null
+++ b/app/views/projects/diffs/viewers/_added.html.haml
@@ -0,0 +1,2 @@
+.nothing-here-block
+ File added
diff --git a/app/views/projects/diffs/viewers/_deleted.html.haml b/app/views/projects/diffs/viewers/_deleted.html.haml
new file mode 100644
index 00000000000..0ac7b4ca8f6
--- /dev/null
+++ b/app/views/projects/diffs/viewers/_deleted.html.haml
@@ -0,0 +1,2 @@
+.nothing-here-block
+ File deleted
diff --git a/app/views/projects/diffs/viewers/_image.html.haml b/app/views/projects/diffs/viewers/_image.html.haml
index ea75373581e..19d08181223 100644
--- a/app/views/projects/diffs/viewers/_image.html.haml
+++ b/app/views/projects/diffs/viewers/_image.html.haml
@@ -1,3 +1,4 @@
+- diff_file = viewer.diff_file
- blob = diff_file.blob
- old_blob = diff_file.old_blob
- blob_raw_path = diff_file_blob_raw_path(diff_file)
diff --git a/app/views/projects/diffs/viewers/_mode_changed.html.haml b/app/views/projects/diffs/viewers/_mode_changed.html.haml
new file mode 100644
index 00000000000..69bc96bbdad
--- /dev/null
+++ b/app/views/projects/diffs/viewers/_mode_changed.html.haml
@@ -0,0 +1,3 @@
+- diff_file = viewer.diff_file
+.nothing-here-block
+ File mode changed from #{diff_file.a_mode} to #{diff_file.b_mode}
diff --git a/app/views/projects/diffs/viewers/_no_preview.html.haml b/app/views/projects/diffs/viewers/_no_preview.html.haml
new file mode 100644
index 00000000000..befe070af2b
--- /dev/null
+++ b/app/views/projects/diffs/viewers/_no_preview.html.haml
@@ -0,0 +1,2 @@
+.nothing-here-block
+ No preview for this file type
diff --git a/app/views/projects/diffs/viewers/_not_diffable.html.haml b/app/views/projects/diffs/viewers/_not_diffable.html.haml
new file mode 100644
index 00000000000..b2c677ec59c
--- /dev/null
+++ b/app/views/projects/diffs/viewers/_not_diffable.html.haml
@@ -0,0 +1,2 @@
+.nothing-here-block
+ This diff was suppressed by a .gitattributes entry.
diff --git a/app/views/projects/diffs/viewers/_renamed.html.haml b/app/views/projects/diffs/viewers/_renamed.html.haml
new file mode 100644
index 00000000000..ef05ee38d8d
--- /dev/null
+++ b/app/views/projects/diffs/viewers/_renamed.html.haml
@@ -0,0 +1,2 @@
+.nothing-here-block
+ File moved
diff --git a/app/views/projects/diffs/viewers/_text.html.haml b/app/views/projects/diffs/viewers/_text.html.haml
index 120d3540223..509e68598c9 100644
--- a/app/views/projects/diffs/viewers/_text.html.haml
+++ b/app/views/projects/diffs/viewers/_text.html.haml
@@ -1,5 +1,5 @@
+- diff_file = viewer.diff_file
- blob = diff_file.blob
-- blob.load_all_data!
- total_lines = blob.lines.size
- total_lines -= 1 if total_lines > 0 && blob.lines.last.blank?
- if diff_view == :parallel
diff --git a/app/views/projects/edit.html.haml b/app/views/projects/edit.html.haml
index c3dab68cea5..296e37e20e6 100644
--- a/app/views/projects/edit.html.haml
+++ b/app/views/projects/edit.html.haml
@@ -99,9 +99,9 @@
Git Large File Storage
= link_to icon('question-circle'), help_page_path('workflow/lfs/manage_large_binaries_with_git_lfs')
.col-md-3
- = f.select :lfs_enabled, [%w(Enabled true), %w(Disabled false)], {}, selected: @project.lfs_enabled?, class: 'pull-right form-control project-repo-select', data: { field: 'lfs_enabled' }
-
-
+ .select-wrapper
+ = f.select :lfs_enabled, [%w(Enabled true), %w(Disabled false)], {}, selected: @project.lfs_enabled?, class: 'pull-right form-control project-repo-select select-control', data: { field: 'lfs_enabled' }
+ = icon('chevron-down')
- if Gitlab.config.registry.enabled
.form-group.js-container-registry{ style: ("display: none;" if @project.project_feature.send(:repository_access_level) == 0) }
.checkbox
diff --git a/app/views/projects/issues/_issue_by_email.html.haml b/app/views/projects/issues/_issue_by_email.html.haml
index da65157a10b..35b7d1b920c 100644
--- a/app/views/projects/issues/_issue_by_email.html.haml
+++ b/app/views/projects/issues/_issue_by_email.html.haml
@@ -20,7 +20,7 @@
%p
The subject will be used as the title of the new issue, and the message will be the description.
- = link_to 'Slash commands', help_page_path('user/project/slash_commands'), target: '_blank', tabindex: -1
+ = link_to 'Quick actions', help_page_path('user/project/quick_actions'), target: '_blank', tabindex: -1
and styling with
= link_to 'Markdown', help_page_path('user/markdown'), target: '_blank', tabindex: -1
are supported.
diff --git a/app/views/projects/jobs/_sidebar.html.haml b/app/views/projects/jobs/_sidebar.html.haml
index 8b9e6e57ec4..93e8a4e385c 100644
--- a/app/views/projects/jobs/_sidebar.html.haml
+++ b/app/views/projects/jobs/_sidebar.html.haml
@@ -72,6 +72,7 @@
.title
%span{ class: "ci-status-icon-#{@build.pipeline.status}" }
= ci_icon_for_status(@build.pipeline.status)
+ Pipeline
= link_to "##{@build.pipeline.id}", namespace_project_pipeline_path(@project.namespace, @project, @build.pipeline), class: 'link-commit'
from
= link_to "#{@build.pipeline.ref}", namespace_project_branch_path(@project.namespace, @project, @build.pipeline.ref), class: 'link-commit'
diff --git a/app/views/projects/merge_requests/conflicts/_submit_form.html.haml b/app/views/projects/merge_requests/conflicts/_submit_form.html.haml
index 62c9748c510..e675e1830d0 100644
--- a/app/views/projects/merge_requests/conflicts/_submit_form.html.haml
+++ b/app/views/projects/merge_requests/conflicts/_submit_form.html.haml
@@ -1,7 +1,7 @@
.form-horizontal.resolve-conflicts-form
.form-group
%label.col-sm-2.control-label{ "for" => "commit-message" }
- Commit message
+ #{ _('Commit message') }
.col-sm-10
.commit-message-container
.max-width-marker
diff --git a/app/views/projects/pipeline_schedules/_form.html.haml b/app/views/projects/pipeline_schedules/_form.html.haml
index 25ae4e0e18f..467f8844e33 100644
--- a/app/views/projects/pipeline_schedules/_form.html.haml
+++ b/app/views/projects/pipeline_schedules/_form.html.haml
@@ -7,7 +7,7 @@
.form-group
.col-md-9
= f.label :description, _('Description'), class: 'label-light'
- = f.text_field :description, class: 'form-control', required: true, autofocus: true, placeholder: _('PipelineSchedules|Provide a short description for this pipeline')
+ = f.text_field :description, class: 'form-control', required: true, autofocus: true, placeholder: s_('PipelineSchedules|Provide a short description for this pipeline')
.form-group
.col-md-9
= f.label :cron, _('Interval Pattern'), class: 'label-light'
@@ -15,19 +15,19 @@
.form-group
.col-md-9
= f.label :cron_timezone, _('Cron Timezone'), class: 'label-light'
- = dropdown_tag(_("Select a timezone"), options: { toggle_class: 'btn js-timezone-dropdown', title: _("Select a timezone"), filter: true, placeholder: _("Filter"), data: { data: timezone_data } } )
+ = dropdown_tag(_("Select a timezone"), options: { toggle_class: 'btn js-timezone-dropdown', title: _("Select a timezone"), filter: true, placeholder: _("OfSearchInADropdown|Filter"), data: { data: timezone_data } } )
= f.text_field :cron_timezone, value: @schedule.cron_timezone, id: 'schedule_cron_timezone', class: 'hidden', name: 'schedule[cron_timezone]', required: true
.form-group
.col-md-9
= f.label :ref, _('Target Branch'), class: 'label-light'
- = dropdown_tag(_("Select target branch"), options: { toggle_class: 'btn js-target-branch-dropdown git-revision-dropdown-toggle', dropdown_class: 'git-revision-dropdown', title: _("Select target branch"), filter: true, placeholder: _("Filter"), data: { data: @project.repository.branch_names, default_branch: @project.default_branch } } )
+ = dropdown_tag(_("Select target branch"), options: { toggle_class: 'btn js-target-branch-dropdown', dropdown_class: 'git-revision-dropdown', title: _("Select target branch"), filter: true, placeholder: _("OfSearchInADropdown|Filter"), data: { data: @project.repository.branch_names, default_branch: @project.default_branch } } )
= f.text_field :ref, value: @schedule.ref, id: 'schedule_ref', class: 'hidden', name: 'schedule[ref]', required: true
.form-group
.col-md-9
- = f.label :active, _('PipelineSchedules|Activated'), class: 'label-light'
+ = f.label :active, s_('PipelineSchedules|Activated'), class: 'label-light'
%div
= f.check_box :active, required: false, value: @schedule.active?
- Active
+ = _('Active')
.footer-block.row-content-block
= f.submit _('Save pipeline schedule'), class: 'btn btn-create', tabindex: 3
= link_to _('Cancel'), pipeline_schedules_path(@project), class: 'btn btn-cancel'
diff --git a/app/views/projects/pipeline_schedules/_pipeline_schedule.html.haml b/app/views/projects/pipeline_schedules/_pipeline_schedule.html.haml
index 2d3344a4aaf..966d6cd8495 100644
--- a/app/views/projects/pipeline_schedules/_pipeline_schedule.html.haml
+++ b/app/views/projects/pipeline_schedules/_pipeline_schedule.html.haml
@@ -13,12 +13,12 @@
= ci_icon_for_status(pipeline_schedule.last_pipeline.status)
%span ##{pipeline_schedule.last_pipeline.id}
- else
- = _("PipelineSchedules|None")
+ = s_("PipelineSchedules|None")
%td.next-run-cell
- if pipeline_schedule.active?
= time_ago_with_tooltip(pipeline_schedule.real_next_run)
- else
- = _("PipelineSchedules|Inactive")
+ = s_("PipelineSchedules|Inactive")
%td
- if pipeline_schedule.owner
= image_tag avatar_icon(pipeline_schedule.owner, 20), class: "avatar s20"
diff --git a/app/views/projects/pipeline_schedules/index.html.haml b/app/views/projects/pipeline_schedules/index.html.haml
index 4a96ee652d2..c296152e54f 100644
--- a/app/views/projects/pipeline_schedules/index.html.haml
+++ b/app/views/projects/pipeline_schedules/index.html.haml
@@ -14,7 +14,7 @@
.nav-controls
= link_to new_namespace_project_pipeline_schedule_path(@project.namespace, @project), class: 'btn btn-create' do
- %span New schedule
+ %span= _('New schedule')
- if @schedules.present?
%ul.content-list
diff --git a/app/views/projects/project_members/_new_project_member.html.haml b/app/views/projects/project_members/_new_project_member.html.haml
index 247c4bdbe2d..8bf2246662a 100644
--- a/app/views/projects/project_members/_new_project_member.html.haml
+++ b/app/views/projects/project_members/_new_project_member.html.haml
@@ -6,7 +6,9 @@
= users_select_tag(:user_ids, multiple: true, class: "input-clamp", scope: :all, email_user: true, placeholder: "Search for members to update or invite")
.form-group
= label_tag :access_level, "Choose a role permission", class: "label-light"
- = select_tag :access_level, options_for_select(ProjectMember.access_level_roles, @project_member.access_level), class: "form-control project-access-select"
+ .select-wrapper
+ = select_tag :access_level, options_for_select(ProjectMember.access_level_roles, @project_member.access_level), class: "form-control project-access-select select-control"
+ = icon('chevron-down')
.help-block.append-bottom-10
= link_to "Read more", help_page_path("user/permissions"), class: "vlink"
about role permissions
diff --git a/app/views/projects/project_members/_new_shared_group.html.haml b/app/views/projects/project_members/_new_shared_group.html.haml
index b7cc8dd7062..643569db646 100644
--- a/app/views/projects/project_members/_new_shared_group.html.haml
+++ b/app/views/projects/project_members/_new_shared_group.html.haml
@@ -8,7 +8,7 @@
= label_tag :link_group_access, "Max access level", class: "label-light"
.select-wrapper
= select_tag :link_group_access, options_for_select(ProjectGroupLink.access_options, ProjectGroupLink.default_access), class: "form-control select-control"
- = icon('caret-down')
+ = icon('chevron-down')
.help-block.append-bottom-10
= link_to "Read more", help_page_path("user/permissions"), class: "vlink"
about role permissions
diff --git a/app/views/projects/wikis/_form.html.haml b/app/views/projects/wikis/_form.html.haml
index 6cb7c1e9c4d..c10b3004bc3 100644
--- a/app/views/projects/wikis/_form.html.haml
+++ b/app/views/projects/wikis/_form.html.haml
@@ -5,13 +5,13 @@
= f.hidden_field :title, value: @page.title
.form-group
- = f.label :format, class: 'control-label'
- .col-sm-10
+ .col-sm-12= f.label :format, class: 'control-label-full-width'
+ .col-sm-12
= f.select :format, options_for_select(ProjectWiki::MARKUPS, {selected: @page.format}), {}, class: "form-control"
.form-group
- = f.label :content, class: 'control-label'
- .col-sm-10
+ .col-sm-12= f.label :content, class: 'control-label-full-width'
+ .col-sm-12
= render layout: 'projects/md_preview', locals: { url: namespace_project_wiki_preview_markdown_path(@project.namespace, @project, @page.slug) } do
= render 'projects/zen', f: f, attr: :content, classes: 'note-textarea', placeholder: 'Write your content or drag files here...'
= render 'shared/notes/hints'
@@ -29,8 +29,8 @@
= link_to 'documentation', help_page_path("user/markdown", anchor: "wiki-specific-markdown")
.form-group
- = f.label :commit_message, class: 'control-label'
- .col-sm-10= f.text_field :message, class: 'form-control', rows: 18, value: commit_message
+ .col-sm-12= f.label :commit_message, class: 'control-label-full-width'
+ .col-sm-12= f.text_field :message, class: 'form-control', rows: 18, value: commit_message
.form-actions
- if @page && @page.persisted?
diff --git a/app/views/projects/wikis/_new.html.haml b/app/views/projects/wikis/_new.html.haml
index ba47574563d..1e553940593 100644
--- a/app/views/projects/wikis/_new.html.haml
+++ b/app/views/projects/wikis/_new.html.haml
@@ -1,21 +1,18 @@
-- @no_container = true
-
-%div{ class: container_class }
- #modal-new-wiki.modal
- .modal-dialog
- .modal-content
- .modal-header
- %a.close{ href: "#", "data-dismiss" => "modal" } ×
- %h3.page-title New Wiki Page
- .modal-body
- %form.new-wiki-page
- .form-group
- = label_tag :new_wiki_path do
- %span Page slug
- = text_field_tag :new_wiki_path, nil, placeholder: 'how-to-setup', class: 'form-control', required: true, :'data-wikis-path' => namespace_project_wikis_path(@project.namespace, @project), autofocus: true
- %span.new-wiki-page-slug-tip
- = icon('lightbulb-o')
- Tip: You can specify the full path for the new file.
- We will automatically create any missing directories.
- .form-actions
- = button_tag 'Create page', class: 'build-new-wiki btn btn-create'
+#modal-new-wiki.modal
+ .modal-dialog
+ .modal-content
+ .modal-header
+ %a.close{ href: "#", "data-dismiss" => "modal" } ×
+ %h3.page-title New Wiki Page
+ .modal-body
+ %form.new-wiki-page
+ .form-group
+ = label_tag :new_wiki_path do
+ %span Page slug
+ = text_field_tag :new_wiki_path, nil, placeholder: 'how-to-setup', class: 'form-control', required: true, :'data-wikis-path' => namespace_project_wikis_path(@project.namespace, @project), autofocus: true
+ %span.new-wiki-page-slug-tip
+ = icon('lightbulb-o')
+ Tip: You can specify the full path for the new file.
+ We will automatically create any missing directories.
+ .form-actions
+ = button_tag 'Create page', class: 'build-new-wiki btn btn-create'
diff --git a/app/views/projects/wikis/edit.html.haml b/app/views/projects/wikis/edit.html.haml
index b995d08cd02..fbe192a40ec 100644
--- a/app/views/projects/wikis/edit.html.haml
+++ b/app/views/projects/wikis/edit.html.haml
@@ -1,35 +1,34 @@
-- @no_container = true
+- @content_class = "limit-container-width limit-container-width-sm" unless fluid_layout
- page_title "Edit", @page.title.capitalize, "Wiki"
-%div{ class: container_class }
- .wiki-page-header.has-sidebar-toggle
- %button.btn.btn-default.sidebar-toggle.js-sidebar-wiki-toggle{ role: "button", type: "button" }
- = icon('angle-double-left')
+.wiki-page-header.has-sidebar-toggle
+ %button.btn.btn-default.sidebar-toggle.js-sidebar-wiki-toggle{ role: "button", type: "button" }
+ = icon('angle-double-left')
- .nav-text
- %h2.wiki-page-title
+ .nav-text
+ %h2.wiki-page-title
+ - if @page.persisted?
+ = link_to @page.title.capitalize, namespace_project_wiki_path(@project.namespace, @project, @page)
+ - else
+ = @page.title.capitalize
+ %span.light
+ &middot;
- if @page.persisted?
- = link_to @page.title.capitalize, namespace_project_wiki_path(@project.namespace, @project, @page)
+ Edit Page
- else
- = @page.title.capitalize
- %span.light
- &middot;
- - if @page.persisted?
- Edit Page
- - else
- Create Page
+ Create Page
- .nav-controls
- - if can?(current_user, :create_wiki, @project)
- = link_to '#modal-new-wiki', class: "add-new-wiki btn btn-new", "data-toggle" => "modal" do
- New page
- - if @page.persisted?
- = link_to namespace_project_wiki_history_path(@project.namespace, @project, @page), class: "btn" do
- Page history
- - if can?(current_user, :admin_wiki, @project)
- = link_to namespace_project_wiki_path(@project.namespace, @project, @page), data: { confirm: "Are you sure you want to delete this page?"}, method: :delete, class: "btn btn-danger" do
- Delete
+ .nav-controls
+ - if can?(current_user, :create_wiki, @project)
+ = link_to '#modal-new-wiki', class: "add-new-wiki btn btn-new", "data-toggle" => "modal" do
+ New page
+ - if @page.persisted?
+ = link_to namespace_project_wiki_history_path(@project.namespace, @project, @page), class: "btn" do
+ Page history
+ - if can?(current_user, :admin_wiki, @project)
+ = link_to namespace_project_wiki_path(@project.namespace, @project, @page), data: { confirm: "Are you sure you want to delete this page?"}, method: :delete, class: "btn btn-danger" do
+ Delete
- = render 'form'
+= render 'form'
= render 'sidebar'
diff --git a/app/views/projects/wikis/git_access.html.haml b/app/views/projects/wikis/git_access.html.haml
index 68862206248..e64dd6085fe 100644
--- a/app/views/projects/wikis/git_access.html.haml
+++ b/app/views/projects/wikis/git_access.html.haml
@@ -1,43 +1,42 @@
-- @no_container = true
+- @content_class = "limit-container-width limit-container-width-sm" unless fluid_layout
- page_title "Git Access", "Wiki"
-%div{ class: container_class }
- .wiki-page-header.has-sidebar-toggle
- %button.btn.btn-default.visible-xs.visible-sm.pull-right.sidebar-toggle.js-sidebar-wiki-toggle{ role: "button", type: "button" }
- = icon('angle-double-left')
+.wiki-page-header.has-sidebar-toggle
+ %button.btn.btn-default.visible-xs.visible-sm.pull-right.sidebar-toggle.js-sidebar-wiki-toggle{ role: "button", type: "button" }
+ = icon('angle-double-left')
- .git-access-header
- Clone repository
- %strong= @project_wiki.path_with_namespace
+ .git-access-header
+ Clone repository
+ %strong= @project_wiki.path_with_namespace
- = render "shared/clone_panel", project: @project_wiki
+ = render "shared/clone_panel", project: @project_wiki
- .wiki-git-access
- %h3 Install Gollum
- %pre.dark
- :preserve
- gem install gollum
- %p
- It is recommended to install
- %code github-markdown
- so that GFM features render locally:
- %pre.dark
- :preserve
- gem install github-markdown
+.wiki-git-access
+ %h3 Install Gollum
+ %pre.dark
+ :preserve
+ gem install gollum
+ %p
+ It is recommended to install
+ %code github-markdown
+ so that GFM features render locally:
+ %pre.dark
+ :preserve
+ gem install github-markdown
- %h3 Clone your wiki
- %pre.dark
- :preserve
- git clone #{ content_tag(:span, h(default_url_to_repo(@project_wiki)), class: 'clone')}
- cd #{h @project_wiki.path}
+ %h3 Clone your wiki
+ %pre.dark
+ :preserve
+ git clone #{ content_tag(:span, h(default_url_to_repo(@project_wiki)), class: 'clone')}
+ cd #{h @project_wiki.path}
- %h3 Start Gollum and edit locally
- %pre.dark
- :preserve
- gollum
- == Sinatra/1.3.5 has taken the stage on 4567 for development with backup from Thin
- >> Thin web server (v1.5.0 codename Knife)
- >> Maximum connections set to 1024
- >> Listening on 0.0.0.0:4567, CTRL+C to stop
+ %h3 Start Gollum and edit locally
+ %pre.dark
+ :preserve
+ gollum
+ == Sinatra/1.3.5 has taken the stage on 4567 for development with backup from Thin
+ >> Thin web server (v1.5.0 codename Knife)
+ >> Maximum connections set to 1024
+ >> Listening on 0.0.0.0:4567, CTRL+C to stop
= render 'sidebar'
diff --git a/app/views/projects/wikis/history.html.haml b/app/views/projects/wikis/history.html.haml
index dd7213622c1..0e47e2a5fa3 100644
--- a/app/views/projects/wikis/history.html.haml
+++ b/app/views/projects/wikis/history.html.haml
@@ -1,42 +1,41 @@
- page_title "History", @page.title.capitalize, "Wiki"
-%div{ class: container_class }
- .wiki-page-header.has-sidebar-toggle
- %button.btn.btn-default.sidebar-toggle.js-sidebar-wiki-toggle{ role: "button", type: "button" }
- = icon('angle-double-left')
+.wiki-page-header.has-sidebar-toggle
+ %button.btn.btn-default.sidebar-toggle.js-sidebar-wiki-toggle{ role: "button", type: "button" }
+ = icon('angle-double-left')
- .nav-text
- %h2.wiki-page-title
- = link_to @page.title.capitalize, namespace_project_wiki_path(@project.namespace, @project, @page)
- %span.light
- &middot;
- History
+ .nav-text
+ %h2.wiki-page-title
+ = link_to @page.title.capitalize, namespace_project_wiki_path(@project.namespace, @project, @page)
+ %span.light
+ &middot;
+ History
- .table-holder
- %table.table
- %thead
+.table-holder
+ %table.table
+ %thead
+ %tr
+ %th Page version
+ %th Author
+ %th Commit Message
+ %th Last updated
+ %th Format
+ %tbody
+ - @page.versions.each_with_index do |version, index|
+ - commit = version
%tr
- %th Page version
- %th Author
- %th Commit Message
- %th Last updated
- %th Format
- %tbody
- - @page.versions.each_with_index do |version, index|
- - commit = version
- %tr
- %td
- = link_to project_wiki_path_with_version(@project, @page,
- commit.id, index == 0) do
- = truncate_sha(commit.id)
- %td
- = commit.author.name
- %td
- = commit.message
- %td
- #{time_ago_with_tooltip(version.authored_date)}
- %td
- %strong
- = @page.page.wiki.page(@page.page.name, commit.id).try(:format)
+ %td
+ = link_to project_wiki_path_with_version(@project, @page,
+ commit.id, index == 0) do
+ = truncate_sha(commit.id)
+ %td
+ = commit.author.name
+ %td
+ = commit.message
+ %td
+ #{time_ago_with_tooltip(version.authored_date)}
+ %td
+ %strong
+ = @page.page.wiki.page(@page.page.name, commit.id).try(:format)
= render 'sidebar'
diff --git a/app/views/projects/wikis/show.html.haml b/app/views/projects/wikis/show.html.haml
index c00967546aa..f003ff6b63f 100644
--- a/app/views/projects/wikis/show.html.haml
+++ b/app/views/projects/wikis/show.html.haml
@@ -1,32 +1,31 @@
-- @no_container = true
+- @content_class = "limit-container-width limit-container-width-sm" unless fluid_layout
- page_title @page.title.capitalize, "Wiki"
-%div{ class: container_class }
- .wiki-page-header.has-sidebar-toggle
- %button.btn.btn-default.sidebar-toggle.js-sidebar-wiki-toggle{ role: "button", type: "button" }
- = icon('angle-double-left')
+.wiki-page-header.has-sidebar-toggle
+ %button.btn.btn-default.sidebar-toggle.js-sidebar-wiki-toggle{ role: "button", type: "button" }
+ = icon('angle-double-left')
- .wiki-breadcrumb
- %span= breadcrumb(@page.slug)
+ .wiki-breadcrumb
+ %span= breadcrumb(@page.slug)
- .nav-text
- %h2.wiki-page-title= @page.title.capitalize
- %span.wiki-last-edit-by
- Last edited by
- %strong
- #{@page.commit.author.name}
- #{time_ago_with_tooltip(@page.commit.authored_date)}
+ .nav-text
+ %h2.wiki-page-title= @page.title.capitalize
+ %span.wiki-last-edit-by
+ Last edited by
+ %strong
+ #{@page.commit.author.name}
+ #{time_ago_with_tooltip(@page.commit.authored_date)}
- .nav-controls
- = render 'main_links'
+ .nav-controls
+ = render 'main_links'
- - if @page.historical?
- .warning_message
- This is an old version of this page.
- You can view the #{link_to "most recent version", namespace_project_wiki_path(@project.namespace, @project, @page)} or browse the #{link_to "history", namespace_project_wiki_history_path(@project.namespace, @project, @page)}.
+- if @page.historical?
+ .warning_message
+ This is an old version of this page.
+ You can view the #{link_to "most recent version", namespace_project_wiki_path(@project.namespace, @project, @page)} or browse the #{link_to "history", namespace_project_wiki_history_path(@project.namespace, @project, @page)}.
- .wiki-holder.prepend-top-default.append-bottom-default
- .wiki
- = render_wiki_content(@page)
+.wiki-holder.prepend-top-default.append-bottom-default
+ .wiki
+ = render_wiki_content(@page)
= render 'sidebar'
diff --git a/app/views/shared/_clone_panel.html.haml b/app/views/shared/_clone_panel.html.haml
index 0aad4d0714f..75704eda361 100644
--- a/app/views/shared/_clone_panel.html.haml
+++ b/app/views/shared/_clone_panel.html.haml
@@ -19,7 +19,7 @@
= text_field_tag :project_clone, default_url_to_repo(project), class: "js-select-on-focus form-control", readonly: true, aria: { label: 'Project clone URL' }
.input-group-btn
- = clipboard_button(target: '#project_clone', title: _("Copy URL to clipboard"))
+ = clipboard_button(target: '#project_clone', title: _("Copy URL to clipboard"), class: "btn-default btn-clipboard")
:javascript
$('ul.clone-options-dropdown a').on('click',function(e){
diff --git a/app/views/shared/_commit_message_container.html.haml b/app/views/shared/_commit_message_container.html.haml
index 4b98ff88241..2329de9e11f 100644
--- a/app/views/shared/_commit_message_container.html.haml
+++ b/app/views/shared/_commit_message_container.html.haml
@@ -2,7 +2,7 @@
- nonce = SecureRandom.hex
- descriptions = local_assigns.slice(:message_with_description, :message_without_description)
= label_tag "commit_message-#{nonce}", class: 'control-label' do
- Commit message
+ #{ _('Commit message') }
.col-sm-10
.commit-message-container
.max-width-marker
diff --git a/app/views/shared/_new_commit_form.html.haml b/app/views/shared/_new_commit_form.html.haml
index 25a56f84ec5..0a4a24ae807 100644
--- a/app/views/shared/_new_commit_form.html.haml
+++ b/app/views/shared/_new_commit_form.html.haml
@@ -5,16 +5,12 @@
- else
- if can?(current_user, :push_code, @project)
.form-group.branch
- = label_tag 'branch_name', 'Target branch', class: 'control-label'
+ = label_tag 'branch_name', _('Target Branch'), class: 'control-label'
.col-sm-10
= text_field_tag 'branch_name', @branch_name || tree_edit_branch, required: true, class: "form-control js-branch-name ref-name"
.js-create-merge-request-container
- .checkbox
- - nonce = SecureRandom.hex
- = label_tag "create_merge_request-#{nonce}" do
- = check_box_tag 'create_merge_request', 1, true, class: 'js-create-merge-request', id: "create_merge_request-#{nonce}"
- Start a <strong>new merge request</strong> with these changes
+ = render 'shared/new_merge_request_checkbox'
- else
= hidden_field_tag 'branch_name', @branch_name || tree_edit_branch
= hidden_field_tag 'create_merge_request', 1
diff --git a/app/views/shared/_new_merge_request_checkbox.html.haml b/app/views/shared/_new_merge_request_checkbox.html.haml
new file mode 100644
index 00000000000..133c31f09c4
--- /dev/null
+++ b/app/views/shared/_new_merge_request_checkbox.html.haml
@@ -0,0 +1,8 @@
+.checkbox
+ - nonce = SecureRandom.hex
+ = label_tag "create_merge_request-#{nonce}" do
+ = check_box_tag 'create_merge_request', 1, true, class: 'js-create-merge-request', id: "create_merge_request-#{nonce}"
+ - translation_variables = { new_merge_request: "<strong>#{_('new merge request')}</strong>" }
+ - translation = _('Start a %{new_merge_request} with these changes') % translation_variables
+ #{ translation.html_safe }
+
diff --git a/app/views/shared/form_elements/_description.html.haml b/app/views/shared/form_elements/_description.html.haml
index 307d4919224..f65bb6a29e6 100644
--- a/app/views/shared/form_elements/_description.html.haml
+++ b/app/views/shared/form_elements/_description.html.haml
@@ -2,10 +2,10 @@
- model = local_assigns.fetch(:model)
- form = local_assigns.fetch(:form)
-- supports_slash_commands = model.new_record?
+- supports_quick_actions = model.new_record?
-- if supports_slash_commands
- - preview_url = preview_markdown_path(project, slash_commands_target_type: model.class.name)
+- if supports_quick_actions
+ - preview_url = preview_markdown_path(project, quick_actions_target_type: model.class.name)
- else
- preview_url = preview_markdown_path(project)
@@ -17,7 +17,7 @@
= render 'projects/zen', f: form, attr: :description,
classes: 'note-textarea',
placeholder: "Write a comment or drag your files here...",
- supports_slash_commands: supports_slash_commands
- = render 'shared/notes/hints', supports_slash_commands: supports_slash_commands
+ supports_quick_actions: supports_quick_actions
+ = render 'shared/notes/hints', supports_quick_actions: supports_quick_actions
.clearfix
.error-alert
diff --git a/app/views/shared/issuable/form/_merge_params.html.haml b/app/views/shared/issuable/form/_merge_params.html.haml
index 271150ed318..bfa91629e1e 100644
--- a/app/views/shared/issuable/form/_merge_params.html.haml
+++ b/app/views/shared/issuable/form/_merge_params.html.haml
@@ -3,7 +3,8 @@
- return unless issuable.is_a?(MergeRequest)
- return if issuable.closed_without_fork?
--# This check is duplicated below, to avoid conflicts with EE.
+-# This check is duplicated below to avoid CE -> EE merge conflicts.
+-# This comment and the following line should only exist in CE.
- return unless issuable.can_remove_source_branch?(current_user)
.form-group
diff --git a/app/views/shared/milestones/_issuables.html.haml b/app/views/shared/milestones/_issuables.html.haml
index 8af3bd597c5..7175e275f95 100644
--- a/app/views/shared/milestones/_issuables.html.haml
+++ b/app/views/shared/milestones/_issuables.html.haml
@@ -8,11 +8,11 @@
= title
- if show_counter
.counter
- = number_with_delimiter(issuables.size)
+ = number_with_delimiter(issuables.length)
- class_prefix = dom_class(issuables).pluralize
- %ul{ class: "well-list #{class_prefix}-sortable-list", id: "#{class_prefix}-list-#{id}", "data-state" => id }
+ %ul{ class: "well-list milestone-#{class_prefix}-list", id: "#{class_prefix}-list-#{id}" }
= render partial: 'shared/milestones/issuable',
- collection: issuables.order_position_asc,
+ collection: issuables,
as: :issuable,
locals: { show_project_name: show_project_name, show_full_project_name: show_full_project_name }
diff --git a/app/views/shared/milestones/_tabs.html.haml b/app/views/shared/milestones/_tabs.html.haml
index 6a6d817b344..4de8a6cb15f 100644
--- a/app/views/shared/milestones/_tabs.html.haml
+++ b/app/views/shared/milestones/_tabs.html.haml
@@ -31,12 +31,12 @@
.tab-content.milestone-content
- if milestone.is_a?(GlobalMilestone) || can?(current_user, :read_issue, @project)
.tab-pane.active#tab-issues{ data: { sort_endpoint: (sort_issues_namespace_project_milestone_path(@project.namespace, @project, @milestone) if @project && current_user) } }
- = render 'shared/milestones/issues_tab', issues: milestone.issues_visible_to_user(current_user).include_associations, show_project_name: show_project_name, show_full_project_name: show_full_project_name
- .tab-pane#tab-merge-requests{ data: { sort_endpoint: (sort_merge_requests_namespace_project_milestone_path(@project.namespace, @project, @milestone) if @project && current_user) } }
+ = render 'shared/milestones/issues_tab', issues: milestone.sorted_issues(current_user), show_project_name: show_project_name, show_full_project_name: show_full_project_name
+ .tab-pane#tab-merge-requests
-# loaded async
= render "shared/milestones/tab_loading"
- else
- .tab-pane.active#tab-merge-requests{ data: { sort_endpoint: (sort_merge_requests_namespace_project_milestone_path(@project.namespace, @project, @milestone) if @project && current_user) } }
+ .tab-pane.active#tab-merge-requests
-# loaded async
= render "shared/milestones/tab_loading"
.tab-pane#tab-participants
diff --git a/app/views/shared/notes/_form.html.haml b/app/views/shared/notes/_form.html.haml
index eaf50bc2115..c6b5dcc3647 100644
--- a/app/views/shared/notes/_form.html.haml
+++ b/app/views/shared/notes/_form.html.haml
@@ -1,6 +1,7 @@
-- supports_slash_commands = note_supports_slash_commands?(@note)
-- if supports_slash_commands
- - preview_url = preview_markdown_path(@project, slash_commands_target_type: @note.noteable_type, slash_commands_target_id: @note.noteable_id)
+- supports_autocomplete = local_assigns.fetch(:supports_autocomplete, true)
+- supports_quick_actions = note_supports_quick_actions?(@note)
+- if supports_quick_actions
+ - preview_url = preview_markdown_path(@project, quick_actions_target_type: @note.noteable_type, quick_actions_target_id: @note.noteable_id)
- else
- preview_url = preview_markdown_path(@project)
@@ -27,8 +28,9 @@
attr: :note,
classes: 'note-textarea js-note-text',
placeholder: "Write a comment or drag your files here...",
- supports_slash_commands: supports_slash_commands
- = render 'shared/notes/hints', supports_slash_commands: supports_slash_commands
+ supports_quick_actions: supports_quick_actions,
+ supports_autocomplete: supports_autocomplete
+ = render 'shared/notes/hints', supports_quick_actions: supports_quick_actions
.error-alert
.note-form-actions.clearfix
diff --git a/app/views/shared/notes/_hints.html.haml b/app/views/shared/notes/_hints.html.haml
index 7ce6130de60..bc1ac3d8ac2 100644
--- a/app/views/shared/notes/_hints.html.haml
+++ b/app/views/shared/notes/_hints.html.haml
@@ -1,10 +1,10 @@
-- supports_slash_commands = local_assigns.fetch(:supports_slash_commands, false)
+- supports_quick_actions = local_assigns.fetch(:supports_quick_actions, false)
.comment-toolbar.clearfix
.toolbar-text
= link_to 'Markdown', help_page_path('user/markdown'), target: '_blank', tabindex: -1
- - if supports_slash_commands
+ - if supports_quick_actions
and
- = link_to 'slash commands', help_page_path('user/project/slash_commands'), target: '_blank', tabindex: -1
+ = link_to 'quick actions', help_page_path('user/project/quick_actions'), target: '_blank', tabindex: -1
are
- else
is
diff --git a/app/views/shared/notes/_notes_with_form.html.haml b/app/views/shared/notes/_notes_with_form.html.haml
index 5902798dfd0..0cca8d875d2 100644
--- a/app/views/shared/notes/_notes_with_form.html.haml
+++ b/app/views/shared/notes/_notes_with_form.html.haml
@@ -12,7 +12,7 @@
%a.author_link{ href: user_path(current_user) }
= image_tag avatar_icon(current_user), alt: current_user.to_reference, class: 'avatar s40'
.timeline-content.timeline-content-form
- = render "shared/notes/form", view: diff_view
+ = render "shared/notes/form", view: diff_view, supports_autocomplete: autocomplete
- elsif !current_user
.disabled-comment.text-center.prepend-top-default
Please
diff --git a/changelogs/unreleased/12151-add-since-and-until-params-to-issuables.yml b/changelogs/unreleased/12151-add-since-and-until-params-to-issuables.yml
new file mode 100644
index 00000000000..2c915e62357
--- /dev/null
+++ b/changelogs/unreleased/12151-add-since-and-until-params-to-issuables.yml
@@ -0,0 +1,4 @@
+---
+title: Added "created_after" and "created_before" params to issuables
+merge_request: 12151
+author: Kyle Bishop @kybishop
diff --git a/changelogs/unreleased/12200-add-french-translation.yml b/changelogs/unreleased/12200-add-french-translation.yml
new file mode 100644
index 00000000000..f31d982e0b9
--- /dev/null
+++ b/changelogs/unreleased/12200-add-french-translation.yml
@@ -0,0 +1,4 @@
+---
+title: "Adding French translations"
+merge_request: 12200
+author : Erwan "Dremor" Georget
diff --git a/changelogs/unreleased/13336-multiple-broadcast-messages.yml b/changelogs/unreleased/13336-multiple-broadcast-messages.yml
new file mode 100644
index 00000000000..7dc73e1c6ea
--- /dev/null
+++ b/changelogs/unreleased/13336-multiple-broadcast-messages.yml
@@ -0,0 +1,4 @@
+---
+title: Display all current broadcast messages, not just the last one
+merge_request: 11113
+author: rickettm
diff --git a/changelogs/unreleased/23998-blame-age-map.yml b/changelogs/unreleased/23998-blame-age-map.yml
new file mode 100644
index 00000000000..26a38f0939c
--- /dev/null
+++ b/changelogs/unreleased/23998-blame-age-map.yml
@@ -0,0 +1,4 @@
+---
+title: Add blame view age mapping
+merge_request: 7198
+author: Jeff Stubler
diff --git a/changelogs/unreleased/25164-disable-fork-on-project-limit.yml b/changelogs/unreleased/25164-disable-fork-on-project-limit.yml
new file mode 100644
index 00000000000..9fa824b161d
--- /dev/null
+++ b/changelogs/unreleased/25164-disable-fork-on-project-limit.yml
@@ -0,0 +1,4 @@
+---
+title: Disable fork button on project limit
+merge_request: 12145
+author: Ivan Chernov
diff --git a/changelogs/unreleased/26212-upload-user-avatar-trough-api.yml b/changelogs/unreleased/26212-upload-user-avatar-trough-api.yml
new file mode 100644
index 00000000000..667454ae95d
--- /dev/null
+++ b/changelogs/unreleased/26212-upload-user-avatar-trough-api.yml
@@ -0,0 +1,4 @@
+---
+title: Accept image for avatar in user API
+merge_request: 12143
+author: Ivan Chernov
diff --git a/changelogs/unreleased/27070-rename-slash-commands-to-quick-actions.yml b/changelogs/unreleased/27070-rename-slash-commands-to-quick-actions.yml
new file mode 100644
index 00000000000..497239db808
--- /dev/null
+++ b/changelogs/unreleased/27070-rename-slash-commands-to-quick-actions.yml
@@ -0,0 +1,5 @@
+---
+title: Rename "Slash commands" to "Quick actions" and deprecate "chat commands" in favor
+ of "slash commands"
+merge_request:
+author:
diff --git a/changelogs/unreleased/27586-center-dropdown.yml b/changelogs/unreleased/27586-center-dropdown.yml
new file mode 100644
index 00000000000..4935f7504f7
--- /dev/null
+++ b/changelogs/unreleased/27586-center-dropdown.yml
@@ -0,0 +1,4 @@
+---
+title: Center dropdown for mini graph
+merge_request:
+author:
diff --git a/changelogs/unreleased/27697-make-arrow-icons-consistent-in-dropdown.yml b/changelogs/unreleased/27697-make-arrow-icons-consistent-in-dropdown.yml
new file mode 100644
index 00000000000..92b5b59f46f
--- /dev/null
+++ b/changelogs/unreleased/27697-make-arrow-icons-consistent-in-dropdown.yml
@@ -0,0 +1,4 @@
+---
+title: Use fa-chevron-down on dropdown arrows for consistency
+merge_request: 9659
+author: TM Lee
diff --git a/changelogs/unreleased/28139-use-color-input-broadcast-messages.yml b/changelogs/unreleased/28139-use-color-input-broadcast-messages.yml
new file mode 100644
index 00000000000..97ebabaff1c
--- /dev/null
+++ b/changelogs/unreleased/28139-use-color-input-broadcast-messages.yml
@@ -0,0 +1,4 @@
+---
+title: Use color inputs for broadcast messages
+merge_request:
+author:
diff --git a/changelogs/unreleased/30725-reset-user-limits-when-unchecking-external-user.yml b/changelogs/unreleased/30725-reset-user-limits-when-unchecking-external-user.yml
new file mode 100644
index 00000000000..3058404b3f8
--- /dev/null
+++ b/changelogs/unreleased/30725-reset-user-limits-when-unchecking-external-user.yml
@@ -0,0 +1,4 @@
+---
+title: Ensures default user limits when external user is unchecked
+merge_request: 12218
+author:
diff --git a/changelogs/unreleased/31415-responsive-pipelines-table-2.yml b/changelogs/unreleased/31415-responsive-pipelines-table-2.yml
new file mode 100644
index 00000000000..59402b85871
--- /dev/null
+++ b/changelogs/unreleased/31415-responsive-pipelines-table-2.yml
@@ -0,0 +1,4 @@
+---
+title: Create responsive mobile view for pipelines table
+merge_request:
+author:
diff --git a/changelogs/unreleased/31556-ci-coverage-paralel-rspec.yml b/changelogs/unreleased/31556-ci-coverage-paralel-rspec.yml
deleted file mode 100644
index 4137050a077..00000000000
--- a/changelogs/unreleased/31556-ci-coverage-paralel-rspec.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Fix the last coverage in trace log should be extracted
-merge_request: 11128
-author: dosuken123
diff --git a/changelogs/unreleased/32054-rails-should-use-timestamptz-database-type-for-postgresql.yml b/changelogs/unreleased/32054-rails-should-use-timestamptz-database-type-for-postgresql.yml
new file mode 100644
index 00000000000..7fc9e0a4f0e
--- /dev/null
+++ b/changelogs/unreleased/32054-rails-should-use-timestamptz-database-type-for-postgresql.yml
@@ -0,0 +1,4 @@
+---
+title: Add database helpers 'add_timestamps_with_timezone' and 'timestamps_with_timezone'
+merge_request: 11229
+author: @blackst0ne
diff --git a/changelogs/unreleased/32470-pag-links.yml b/changelogs/unreleased/32470-pag-links.yml
new file mode 100644
index 00000000000..d0fd284f3ee
--- /dev/null
+++ b/changelogs/unreleased/32470-pag-links.yml
@@ -0,0 +1,4 @@
+---
+title: more visual contrast in pagination widget
+merge_request:
+author:
diff --git a/changelogs/unreleased/32790-pipeline_schedules-pages-throwing-error-500.yml b/changelogs/unreleased/32790-pipeline_schedules-pages-throwing-error-500.yml
deleted file mode 100644
index a58f3a7429e..00000000000
--- a/changelogs/unreleased/32790-pipeline_schedules-pages-throwing-error-500.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Fix pipeline_schedules pages throwing error 500
-merge_request: 11706
-author: dosuken123
diff --git a/changelogs/unreleased/32995-issue-contents-dynamically-replaced-with-stale-version-after-saving-or-refreshing-relative-external_url-only.yml b/changelogs/unreleased/32995-issue-contents-dynamically-replaced-with-stale-version-after-saving-or-refreshing-relative-external_url-only.yml
deleted file mode 100644
index 5cd36a4e3e2..00000000000
--- a/changelogs/unreleased/32995-issue-contents-dynamically-replaced-with-stale-version-after-saving-or-refreshing-relative-external_url-only.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Fix incorrect ETag cache key when relative instance URL is used
-merge_request: 11964
-author:
diff --git a/changelogs/unreleased/33048-markdown-rendering-of-md-files-has-ceased-to-display-latex-equations.yml b/changelogs/unreleased/33048-markdown-rendering-of-md-files-has-ceased-to-display-latex-equations.yml
deleted file mode 100644
index 5648e013e75..00000000000
--- a/changelogs/unreleased/33048-markdown-rendering-of-md-files-has-ceased-to-display-latex-equations.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Fix math rendering on blob pages
-merge_request:
-author:
diff --git a/changelogs/unreleased/33445-document-delete-merge-branches-won-t-touch-protected-branches-docs.yml b/changelogs/unreleased/33445-document-delete-merge-branches-won-t-touch-protected-branches-docs.yml
new file mode 100644
index 00000000000..385f18e2560
--- /dev/null
+++ b/changelogs/unreleased/33445-document-delete-merge-branches-won-t-touch-protected-branches-docs.yml
@@ -0,0 +1,4 @@
+---
+title: Document the Delete Merged Branches functionality
+merge_request:
+author:
diff --git a/changelogs/unreleased/33461-display-user-id.yml b/changelogs/unreleased/33461-display-user-id.yml
new file mode 100644
index 00000000000..cba94625b07
--- /dev/null
+++ b/changelogs/unreleased/33461-display-user-id.yml
@@ -0,0 +1,4 @@
+---
+title: Display own user id in account settings page
+merge_request: 12141
+author: Riccardo Padovani
diff --git a/changelogs/unreleased/counters_cache_invalidation.yml b/changelogs/unreleased/counters_cache_invalidation.yml
deleted file mode 100644
index 1e78765ec10..00000000000
--- a/changelogs/unreleased/counters_cache_invalidation.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Invalidate cache for issue and MR counters more granularly
-merge_request:
-author:
diff --git a/changelogs/unreleased/disable-blocked-manual-actions.yml b/changelogs/unreleased/disable-blocked-manual-actions.yml
new file mode 100644
index 00000000000..a640f61a7dd
--- /dev/null
+++ b/changelogs/unreleased/disable-blocked-manual-actions.yml
@@ -0,0 +1,4 @@
+---
+title: disable blocked manual actions
+merge_request:
+author:
diff --git a/changelogs/unreleased/dm-diff-viewers.yml b/changelogs/unreleased/dm-diff-viewers.yml
new file mode 100644
index 00000000000..e5b1352c8f1
--- /dev/null
+++ b/changelogs/unreleased/dm-diff-viewers.yml
@@ -0,0 +1,4 @@
+---
+title: Implement diff viewers
+merge_request:
+author:
diff --git a/changelogs/unreleased/dm-fix-parser-cache.yml b/changelogs/unreleased/dm-fix-parser-cache.yml
new file mode 100644
index 00000000000..31c163b7272
--- /dev/null
+++ b/changelogs/unreleased/dm-fix-parser-cache.yml
@@ -0,0 +1,4 @@
+---
+title: Don't return nil for missing objects from parser cache
+merge_request:
+author:
diff --git a/changelogs/unreleased/dm-target-branch-slash-command-desc.yml b/changelogs/unreleased/dm-target-branch-slash-command-desc.yml
new file mode 100644
index 00000000000..768ddf0416e
--- /dev/null
+++ b/changelogs/unreleased/dm-target-branch-slash-command-desc.yml
@@ -0,0 +1,4 @@
+---
+title: Update /target_branch slash command description to be more consistent
+merge_request:
+author:
diff --git a/changelogs/unreleased/feature-add-support-for-services-configuration.yml b/changelogs/unreleased/feature-add-support-for-services-configuration.yml
new file mode 100644
index 00000000000..88a3eacd774
--- /dev/null
+++ b/changelogs/unreleased/feature-add-support-for-services-configuration.yml
@@ -0,0 +1,4 @@
+---
+title: Add support for image and services configuration in .gitlab-ci.yml
+merge_request: 8578
+author:
diff --git a/changelogs/unreleased/feature-unify-email-layouts.yml b/changelogs/unreleased/feature-unify-email-layouts.yml
new file mode 100644
index 00000000000..7a2e3f20b6b
--- /dev/null
+++ b/changelogs/unreleased/feature-unify-email-layouts.yml
@@ -0,0 +1,4 @@
+---
+title: Update the devise mail templates to match the design of the pipeline emails
+merge_request: 10483
+author: Alexis Reigel
diff --git a/changelogs/unreleased/fix-backup-restore-resume.yml b/changelogs/unreleased/fix-backup-restore-resume.yml
deleted file mode 100644
index b7dfd451f5d..00000000000
--- a/changelogs/unreleased/fix-backup-restore-resume.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Make backup task to continue on corrupt repositories
-merge_request: 11962
-author:
diff --git a/changelogs/unreleased/fix-gb-use-merge-ability-for-protected-manual-actions.yml b/changelogs/unreleased/fix-gb-use-merge-ability-for-protected-manual-actions.yml
deleted file mode 100644
index 43c18502cd6..00000000000
--- a/changelogs/unreleased/fix-gb-use-merge-ability-for-protected-manual-actions.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Respect merge, instead of push, permissions for protected actions
-merge_request: 11648
-author:
diff --git a/changelogs/unreleased/fix-github-clone-wiki.yml b/changelogs/unreleased/fix-github-clone-wiki.yml
new file mode 100644
index 00000000000..eadd90e1390
--- /dev/null
+++ b/changelogs/unreleased/fix-github-clone-wiki.yml
@@ -0,0 +1,4 @@
+---
+title: Github - Fix token interpolation when cloning wiki repository
+merge_request:
+author:
diff --git a/changelogs/unreleased/fix-missing-function-dropzone-input.yml b/changelogs/unreleased/fix-missing-function-dropzone-input.yml
new file mode 100644
index 00000000000..d9dfc76faaf
--- /dev/null
+++ b/changelogs/unreleased/fix-missing-function-dropzone-input.yml
@@ -0,0 +1,4 @@
+---
+title: Fix for cut & pasted images not working
+merge_request:
+author:
diff --git a/changelogs/unreleased/fix-overflow-slash-commands.yml b/changelogs/unreleased/fix-overflow-slash-commands.yml
new file mode 100644
index 00000000000..98ec399e8cb
--- /dev/null
+++ b/changelogs/unreleased/fix-overflow-slash-commands.yml
@@ -0,0 +1,4 @@
+---
+title: Fixed overflow on mobile screens for the slash commands
+merge_request:
+author:
diff --git a/changelogs/unreleased/fix-support-for-external-ci-services.yml b/changelogs/unreleased/fix-support-for-external-ci-services.yml
new file mode 100644
index 00000000000..eecb4519259
--- /dev/null
+++ b/changelogs/unreleased/fix-support-for-external-ci-services.yml
@@ -0,0 +1,4 @@
+---
+title: Fix support for external CI services
+merge_request: 11176
+author:
diff --git a/changelogs/unreleased/fix-terminals-support-for-kubernetes-service.yml b/changelogs/unreleased/fix-terminals-support-for-kubernetes-service.yml
deleted file mode 100644
index fb91da9510c..00000000000
--- a/changelogs/unreleased/fix-terminals-support-for-kubernetes-service.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Fix terminals support for Kubernetes Service
-merge_request:
-author:
diff --git a/changelogs/unreleased/help-landing-page-customizations.yml b/changelogs/unreleased/help-landing-page-customizations.yml
new file mode 100644
index 00000000000..58cab751ded
--- /dev/null
+++ b/changelogs/unreleased/help-landing-page-customizations.yml
@@ -0,0 +1,4 @@
+---
+title: Help landing page customizations
+merge_request: 11878
+author: Robin Bobbitt
diff --git a/changelogs/unreleased/instrument-merge-request-diff-load-commits.yml b/changelogs/unreleased/instrument-merge-request-diff-load-commits.yml
new file mode 100644
index 00000000000..916b182a48b
--- /dev/null
+++ b/changelogs/unreleased/instrument-merge-request-diff-load-commits.yml
@@ -0,0 +1,4 @@
+---
+title: Instrument MergeRequestDiff#load_commits
+merge_request:
+author:
diff --git a/changelogs/unreleased/issue_20900.yml b/changelogs/unreleased/issue_20900.yml
new file mode 100644
index 00000000000..e8cef6d2bce
--- /dev/null
+++ b/changelogs/unreleased/issue_20900.yml
@@ -0,0 +1,4 @@
+---
+title: Remove issues/merge requests drag n drop and sorting from milestone view
+merge_request:
+author:
diff --git a/changelogs/unreleased/issue_33205.yml b/changelogs/unreleased/issue_33205.yml
new file mode 100644
index 00000000000..54b442048d8
--- /dev/null
+++ b/changelogs/unreleased/issue_33205.yml
@@ -0,0 +1,4 @@
+---
+title: Fix API bug accepting wrong parameter to create merge request
+merge_request:
+author:
diff --git a/changelogs/unreleased/karma-headless-chrome.yml b/changelogs/unreleased/karma-headless-chrome.yml
new file mode 100644
index 00000000000..af3e9b3b0f9
--- /dev/null
+++ b/changelogs/unreleased/karma-headless-chrome.yml
@@ -0,0 +1,4 @@
+---
+title: Replace PhantomJS with headless Chrome for karma test suite
+merge_request: 12036
+author:
diff --git a/changelogs/unreleased/moved-submodules.yml b/changelogs/unreleased/moved-submodules.yml
new file mode 100644
index 00000000000..eee858717ed
--- /dev/null
+++ b/changelogs/unreleased/moved-submodules.yml
@@ -0,0 +1,4 @@
+---
+title: 'Handle renamed submodules in repository browser'
+merge_request: 10798
+author: David Turner
diff --git a/changelogs/unreleased/mr-widget-memory-usage-tech-debt-fix.yml b/changelogs/unreleased/mr-widget-memory-usage-tech-debt-fix.yml
new file mode 100644
index 00000000000..14b5493a246
--- /dev/null
+++ b/changelogs/unreleased/mr-widget-memory-usage-tech-debt-fix.yml
@@ -0,0 +1,4 @@
+---
+title: Changed utilities imports from ~ to relative paths
+merge_request:
+author:
diff --git a/changelogs/unreleased/reduce-sidekiq-wait-timings.yml b/changelogs/unreleased/reduce-sidekiq-wait-timings.yml
new file mode 100644
index 00000000000..4d23accc82e
--- /dev/null
+++ b/changelogs/unreleased/reduce-sidekiq-wait-timings.yml
@@ -0,0 +1,4 @@
+---
+title: Reduce time spent waiting for certain Sidekiq jobs to complete
+merge_request:
+author:
diff --git a/changelogs/unreleased/sh-fix-lfs-from-moving-across-filesystems.yml b/changelogs/unreleased/sh-fix-lfs-from-moving-across-filesystems.yml
deleted file mode 100644
index 161bce45601..00000000000
--- a/changelogs/unreleased/sh-fix-lfs-from-moving-across-filesystems.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Fix LFS timeouts when trying to save large files
-merge_request:
-author:
diff --git a/changelogs/unreleased/sh-fix-submodules-trailing-spaces.yml b/changelogs/unreleased/sh-fix-submodules-trailing-spaces.yml
deleted file mode 100644
index d633995d467..00000000000
--- a/changelogs/unreleased/sh-fix-submodules-trailing-spaces.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Strip trailing whitespaces in submodule URLs
-merge_request:
-author:
diff --git a/changelogs/unreleased/sh-recaptcha-fix-try2.yml b/changelogs/unreleased/sh-recaptcha-fix-try2.yml
deleted file mode 100644
index 94729252c6f..00000000000
--- a/changelogs/unreleased/sh-recaptcha-fix-try2.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Make sure reCAPTCHA configuration is loaded when spam checks are initiated
-merge_request:
-author:
diff --git a/changelogs/unreleased/speed-up-graphs.yml b/changelogs/unreleased/speed-up-graphs.yml
new file mode 100644
index 00000000000..7cb155af6fd
--- /dev/null
+++ b/changelogs/unreleased/speed-up-graphs.yml
@@ -0,0 +1,4 @@
+---
+title: Speed up used languages calculation on charts page
+merge_request:
+author:
diff --git a/changelogs/unreleased/zj-commit-status-sortable-name.yml b/changelogs/unreleased/zj-commit-status-sortable-name.yml
new file mode 100644
index 00000000000..1be9ac6380f
--- /dev/null
+++ b/changelogs/unreleased/zj-commit-status-sortable-name.yml
@@ -0,0 +1,4 @@
+---
+title: Handle nameless legacy jobs
+merge_request:
+author:
diff --git a/changelogs/unreleased/zj-drop-fk-if-exists.yml b/changelogs/unreleased/zj-drop-fk-if-exists.yml
deleted file mode 100644
index 237ba936de9..00000000000
--- a/changelogs/unreleased/zj-drop-fk-if-exists.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Remove foreigh key on ci_trigger_schedules only if it exists
-merge_request:
-author:
diff --git a/changelogs/unreleased/zj-raise-etag-route-regex-miss.yml b/changelogs/unreleased/zj-raise-etag-route-regex-miss.yml
new file mode 100644
index 00000000000..57a5f4e44c0
--- /dev/null
+++ b/changelogs/unreleased/zj-raise-etag-route-regex-miss.yml
@@ -0,0 +1,4 @@
+---
+title: Fix etag route not being a match for environments
+merge_request:
+author:
diff --git a/config.ru b/config.ru
index 89aba462f19..065ce59932f 100644
--- a/config.ru
+++ b/config.ru
@@ -15,9 +15,6 @@ if defined?(Unicorn)
end
end
-# set default directory for multiproces metrics gathering
-ENV['prometheus_multiproc_dir'] ||= 'tmp/prometheus_multiproc_dir'
-
require ::File.expand_path('../config/environment', __FILE__)
map ENV['RAILS_RELATIVE_URL_ROOT'] || "/" do
diff --git a/config/boot.rb b/config/boot.rb
index 17a71148370..db5ab918021 100644
--- a/config/boot.rb
+++ b/config/boot.rb
@@ -5,6 +5,9 @@ ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__)
require 'bundler/setup' if File.exist?(ENV['BUNDLE_GEMFILE'])
+# set default directory for multiproces metrics gathering
+ENV['prometheus_multiproc_dir'] ||= 'tmp/prometheus_multiproc_dir'
+
# Default Bootsnap configuration from https://github.com/Shopify/bootsnap#usage
require 'bootsnap'
Bootsnap.setup(
diff --git a/config/environments/production.rb b/config/environments/production.rb
index 82a19085b1d..c5cbfcf64cf 100644
--- a/config/environments/production.rb
+++ b/config/environments/production.rb
@@ -50,7 +50,7 @@ Rails.application.configure do
# config.logger = ActiveSupport::TaggedLogging.new(SyslogLogger.new)
# Enable serving of images, stylesheets, and JavaScripts from an asset server
- # config.action_controller.asset_host = "http://assets.example.com"
+ config.action_controller.asset_host = ENV['GITLAB_CDN_HOST'] if ENV['GITLAB_CDN_HOST'].present?
# Precompile additional assets (application.js, application.css, and all non-JS/CSS are already added)
# config.assets.precompile += %w( search.js )
diff --git a/config/initializers/8_metrics.rb b/config/initializers/8_metrics.rb
index 5e0eefdb154..508b886d6a0 100644
--- a/config/initializers/8_metrics.rb
+++ b/config/initializers/8_metrics.rb
@@ -113,6 +113,9 @@ def instrument_classes(instrumentation)
# This is a Rails scope so we have to instrument it manually.
instrumentation.instrument_method(Project, :visible_to_user)
+
+ # Needed for https://gitlab.com/gitlab-org/gitlab-ce/issues/30224#note_32306159
+ instrumentation.instrument_instance_method(MergeRequestDiff, :load_commits)
end
# rubocop:enable Metrics/AbcSize
diff --git a/config/initializers/active_record_data_types.rb b/config/initializers/active_record_data_types.rb
new file mode 100644
index 00000000000..beb97c6fce0
--- /dev/null
+++ b/config/initializers/active_record_data_types.rb
@@ -0,0 +1,24 @@
+# ActiveRecord custom data type for storing datetimes with timezone information.
+# See https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/11229
+
+if Gitlab::Database.postgresql?
+ require 'active_record/connection_adapters/postgresql_adapter'
+
+ module ActiveRecord
+ module ConnectionAdapters
+ class PostgreSQLAdapter
+ NATIVE_DATABASE_TYPES.merge!(datetime_with_timezone: { name: 'timestamptz' })
+ end
+ end
+ end
+elsif Gitlab::Database.mysql?
+ require 'active_record/connection_adapters/mysql2_adapter'
+
+ module ActiveRecord
+ module ConnectionAdapters
+ class AbstractMysqlAdapter
+ NATIVE_DATABASE_TYPES.merge!(datetime_with_timezone: { name: 'timestamp' })
+ end
+ end
+ end
+end
diff --git a/config/initializers/active_record_table_definition.rb b/config/initializers/active_record_table_definition.rb
new file mode 100644
index 00000000000..4f59e35f4da
--- /dev/null
+++ b/config/initializers/active_record_table_definition.rb
@@ -0,0 +1,24 @@
+# ActiveRecord custom method definitions with timezone information.
+# See https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/11229
+
+require 'active_record/connection_adapters/abstract/schema_definitions'
+
+# Appends columns `created_at` and `updated_at` to a table.
+#
+# It is used in table creation like:
+# create_table 'users' do |t|
+# t.timestamps_with_timezone
+# end
+module ActiveRecord
+ module ConnectionAdapters
+ class TableDefinition
+ def timestamps_with_timezone(**options)
+ options[:null] = false if options[:null].nil?
+
+ [:created_at, :updated_at].each do |column_name|
+ column(column_name, :datetime_with_timezone, options)
+ end
+ end
+ end
+ end
+end
diff --git a/config/initializers/rugged_use_gitlab_git_attributes.rb b/config/initializers/rugged_use_gitlab_git_attributes.rb
new file mode 100644
index 00000000000..7d652799786
--- /dev/null
+++ b/config/initializers/rugged_use_gitlab_git_attributes.rb
@@ -0,0 +1,25 @@
+# We don't want to ever call Rugged::Repository#fetch_attributes, because it has
+# a lot of I/O overhead:
+# <https://gitlab.com/gitlab-org/gitlab_git/commit/340e111e040ae847b614d35b4d3173ec48329015>
+#
+# While we don't do this from within the GitLab source itself, the Linguist gem
+# has a dependency on Rugged and uses the gitattributes file when calculating
+# repository-wide language statistics:
+# <https://github.com/github/linguist/blob/v4.7.0/lib/linguist/lazy_blob.rb#L33-L36>
+#
+# The options passed by Linguist are those assumed by Gitlab::Git::Attributes
+# anyway, and there is no great efficiency gain from just fetching the listed
+# attributes with our implementation, so we ignore the additional arguments.
+#
+module Rugged
+ class Repository
+ module UseGitlabGitAttributes
+ def fetch_attributes(name, *)
+ @attributes ||= Gitlab::Git::Attributes.new(path)
+ @attributes.attributes(name)
+ end
+ end
+
+ prepend UseGitlabGitAttributes
+ end
+end
diff --git a/config/karma.config.js b/config/karma.config.js
index 40c58e7771d..5911a9a7e10 100644
--- a/config/karma.config.js
+++ b/config/karma.config.js
@@ -21,7 +21,18 @@ module.exports = function(config) {
var karmaConfig = {
basePath: ROOT_PATH,
- browsers: ['PhantomJS'],
+ browsers: ['ChromeHeadlessCustom'],
+ customLaunchers: {
+ ChromeHeadlessCustom: {
+ base: 'ChromeHeadless',
+ displayName: 'Chrome',
+ flags: [
+ // chrome cannot run in sandboxed mode inside a docker container unless it is run with
+ // escalated kernel privileges (e.g. docker run --cap-add=CAP_SYS_ADMIN)
+ '--no-sandbox',
+ ],
+ }
+ },
frameworks: ['jasmine'],
files: [
{ pattern: 'spec/javascripts/test_bundle.js', watched: false },
@@ -43,6 +54,16 @@ module.exports = function(config) {
subdir: '.',
fixWebpackSourcePaths: true
};
+ karmaConfig.browserNoActivityTimeout = 60000; // 60 seconds
+ }
+
+ if (process.env.DEBUG) {
+ karmaConfig.logLevel = config.LOG_DEBUG;
+ process.env.CHROME_LOG_FILE = process.env.CHROME_LOG_FILE || 'chrome_debug.log';
+ }
+
+ if (process.env.CHROME_LOG_FILE) {
+ karmaConfig.customLaunchers.ChromeHeadlessCustom.flags.push('--enable-logging', '--v=1');
}
config.set(karmaConfig);
diff --git a/config/locales/en.yml b/config/locales/en.yml
index 9d47425950a..8932db138d9 100644
--- a/config/locales/en.yml
+++ b/config/locales/en.yml
@@ -2,17 +2,63 @@
# See https://github.com/svenfuchs/rails-i18n/tree/master/rails%2Flocale for starting points.
en:
- hello: "Hello world"
- errors:
- messages:
- label_already_exists_at_group_level: "already exists at group level for %{group}. Please choose another one."
- wrong_size: "is the wrong size (should be %{file_size})"
- size_too_small: "is too small (should be at least %{file_size})"
- size_too_big: "is too big (should be at most %{file_size})"
views:
pagination:
previous: "Prev"
next: "Next"
+ date:
+ abbr_day_names:
+ - Sun
+ - Mon
+ - Tue
+ - Wed
+ - Thu
+ - Fri
+ - Sat
+ abbr_month_names:
+ -
+ - Jan
+ - Feb
+ - Mar
+ - Apr
+ - May
+ - Jun
+ - Jul
+ - Aug
+ - Sep
+ - Oct
+ - Nov
+ - Dec
+ day_names:
+ - Sunday
+ - Monday
+ - Tuesday
+ - Wednesday
+ - Thursday
+ - Friday
+ - Saturday
+ formats:
+ default: "%Y-%m-%d"
+ long: "%B %d, %Y"
+ short: "%b %d"
+ month_names:
+ -
+ - January
+ - February
+ - March
+ - April
+ - May
+ - June
+ - July
+ - August
+ - September
+ - October
+ - November
+ - December
+ order:
+ - :year
+ - :month
+ - :day
datetime:
time_ago_in_words:
half_a_minute: "half a minute ago"
@@ -49,3 +95,158 @@ en:
almost_x_years:
one: "almost 1 year ago"
other: "almost %{count} years ago"
+ distance_in_words:
+ about_x_hours:
+ one: about 1 hour
+ other: about %{count} hours
+ about_x_months:
+ one: about 1 month
+ other: about %{count} months
+ about_x_years:
+ one: about 1 year
+ other: about %{count} years
+ almost_x_years:
+ one: almost 1 year
+ other: almost %{count} years
+ half_a_minute: half a minute
+ less_than_x_minutes:
+ one: less than a minute
+ other: less than %{count} minutes
+ less_than_x_seconds:
+ one: less than 1 second
+ other: less than %{count} seconds
+ over_x_years:
+ one: over 1 year
+ other: over %{count} years
+ x_days:
+ one: 1 day
+ other: "%{count} days"
+ x_minutes:
+ one: 1 minute
+ other: "%{count} minutes"
+ x_months:
+ one: 1 month
+ other: "%{count} months"
+ x_years:
+ one: 1 year
+ other: "%{count} years"
+ x_seconds:
+ one: 1 second
+ other: "%{count} seconds"
+ prompts:
+ day: Day
+ hour: Hour
+ minute: Minute
+ month: Month
+ second: Seconds
+ year: Year
+ errors:
+ format: "%{attribute} %{message}"
+ messages:
+ label_already_exists_at_group_level: "already exists at group level for %{group}. Please choose another one."
+ wrong_size: "is the wrong size (should be %{file_size})"
+ size_too_small: "is too small (should be at least %{file_size})"
+ size_too_big: "is too big (should be at most %{file_size})"
+ accepted: must be accepted
+ blank: can't be blank
+ present: must be blank
+ confirmation: doesn't match %{attribute}
+ empty: can't be empty
+ equal_to: must be equal to %{count}
+ even: must be even
+ exclusion: is reserved
+ greater_than: must be greater than %{count}
+ greater_than_or_equal_to: must be greater than or equal to %{count}
+ inclusion: is not included in the list
+ invalid: is invalid
+ less_than: must be less than %{count}
+ less_than_or_equal_to: must be less than or equal to %{count}
+ model_invalid: "Validation failed: %{errors}"
+ not_a_number: is not a number
+ not_an_integer: must be an integer
+ odd: must be odd
+ required: must exist
+ taken: has already been taken
+ too_long:
+ one: is too long (maximum is 1 character)
+ other: is too long (maximum is %{count} characters)
+ too_short:
+ one: is too short (minimum is 1 character)
+ other: is too short (minimum is %{count} characters)
+ wrong_length:
+ one: is the wrong length (should be 1 character)
+ other: is the wrong length (should be %{count} characters)
+ other_than: must be other than %{count}
+ template:
+ body: 'There were problems with the following fields:'
+ header:
+ one: 1 error prohibited this %{model} from being saved
+ other: "%{count} errors prohibited this %{model} from being saved"
+ helpers:
+ select:
+ prompt: Please select
+ submit:
+ create: Create %{model}
+ submit: Save %{model}
+ update: Update %{model}
+ number:
+ currency:
+ format:
+ delimiter: ","
+ format: "%u%n"
+ precision: 2
+ separator: "."
+ significant: false
+ strip_insignificant_zeros: false
+ unit: "$"
+ format:
+ delimiter: ","
+ precision: 3
+ separator: "."
+ significant: false
+ strip_insignificant_zeros: false
+ human:
+ decimal_units:
+ format: "%n %u"
+ units:
+ billion: Billion
+ million: Million
+ quadrillion: Quadrillion
+ thousand: Thousand
+ trillion: Trillion
+ unit: ''
+ format:
+ delimiter: ''
+ precision: 3
+ significant: true
+ strip_insignificant_zeros: true
+ storage_units:
+ format: "%n %u"
+ units:
+ byte:
+ one: Byte
+ other: Bytes
+ gb: GB
+ kb: KB
+ mb: MB
+ tb: TB
+ percentage:
+ format:
+ delimiter: ''
+ format: "%n%"
+ precision:
+ format:
+ delimiter: ''
+ support:
+ array:
+ last_word_connector: ", and "
+ two_words_connector: " and "
+ words_connector: ", "
+ time:
+ am: am
+ formats:
+ default: "%a, %d %b %Y %H:%M:%S %z"
+ long: "%B %d, %Y %H:%M"
+ short: "%d %b %H:%M"
+ timeago_tooltip: "%b %-d, %Y %-l:%M%P"
+ pm: pm
diff --git a/config/locales/es.yml b/config/locales/es.yml
index d71c6eb5047..fdc52b4ae11 100644
--- a/config/locales/es.yml
+++ b/config/locales/es.yml
@@ -251,4 +251,5 @@ es:
default: "%A, %d de %B de %Y %H:%M:%S %z"
long: "%d de %B de %Y %H:%M"
short: "%d de %b %H:%M"
+ timeago_tooltip: "%d de %B de %Y %H:%M"
pm: pm
diff --git a/config/webpack.config.js b/config/webpack.config.js
index 120f9d3193d..fb91ffef7e7 100644
--- a/config/webpack.config.js
+++ b/config/webpack.config.js
@@ -11,22 +11,13 @@ var WatchMissingNodeModulesPlugin = require('react-dev-utils/WatchMissingNodeMod
var ROOT_PATH = path.resolve(__dirname, '..');
var IS_PRODUCTION = process.env.NODE_ENV === 'production';
-var IS_DEV_SERVER = process.argv[1].indexOf('webpack-dev-server') !== -1;
+var IS_DEV_SERVER = process.argv.join(' ').indexOf('webpack-dev-server') !== -1;
var DEV_SERVER_HOST = process.env.DEV_SERVER_HOST || 'localhost';
var DEV_SERVER_PORT = parseInt(process.env.DEV_SERVER_PORT, 10) || 3808;
var DEV_SERVER_LIVERELOAD = process.env.DEV_SERVER_LIVERELOAD !== 'false';
var WEBPACK_REPORT = process.env.WEBPACK_REPORT;
var NO_COMPRESSION = process.env.NO_COMPRESSION;
-// optional dependency `node-zopfli` is unavailable on CentOS 6
-var ZOPFLI_AVAILABLE;
-try {
- require.resolve('node-zopfli');
- ZOPFLI_AVAILABLE = true;
-} catch(err) {
- ZOPFLI_AVAILABLE = false;
-}
-
var config = {
// because sqljs requires fs.
node: {
@@ -61,7 +52,7 @@ var config = {
network: './network/network_bundle.js',
notebook_viewer: './blob/notebook_viewer.js',
pdf_viewer: './blob/pdf_viewer.js',
- pipelines: './pipelines/index.js',
+ pipelines: './pipelines/pipelines_bundle.js',
pipelines_details: './pipelines/pipeline_details_bundle.js',
profile: './profile/profile_bundle.js',
protected_branches: './protected_branches/protected_branches_bundle.js',
@@ -233,12 +224,12 @@ if (IS_PRODUCTION) {
// zopfli requires a lot of compute time and is disabled in CI
if (!NO_COMPRESSION) {
- config.plugins.push(
- new CompressionPlugin({
- asset: '[path].gz[query]',
- algorithm: ZOPFLI_AVAILABLE ? 'zopfli' : 'gzip',
- })
- );
+ // gracefully fall back to gzip if `node-zopfli` is unavailable (e.g. in CentOS 6)
+ try {
+ config.plugins.push(new CompressionPlugin({ algorithm: 'zopfli' }));
+ } catch(err) {
+ config.plugins.push(new CompressionPlugin({ algorithm: 'gzip' }));
+ }
}
}
diff --git a/db/migrate/20160314114439_add_requested_at_to_members.rb b/db/migrate/20160314114439_add_requested_at_to_members.rb
index 273819d4cd8..76c8b8a1a24 100644
--- a/db/migrate/20160314114439_add_requested_at_to_members.rb
+++ b/db/migrate/20160314114439_add_requested_at_to_members.rb
@@ -1,3 +1,4 @@
+# rubocop:disable Migration/Datetime
class AddRequestedAtToMembers < ActiveRecord::Migration
def change
add_column :members, :requested_at, :datetime
diff --git a/db/migrate/20160415062917_create_personal_access_tokens.rb b/db/migrate/20160415062917_create_personal_access_tokens.rb
index ce0b33f32bd..c7b49870bf7 100644
--- a/db/migrate/20160415062917_create_personal_access_tokens.rb
+++ b/db/migrate/20160415062917_create_personal_access_tokens.rb
@@ -1,3 +1,5 @@
+# rubocop:disable Migration/Datetime
+# rubocop:disable Migration/Timestamps
class CreatePersonalAccessTokens < ActiveRecord::Migration
def change
create_table :personal_access_tokens do |t|
diff --git a/db/migrate/20160610204157_add_deployments.rb b/db/migrate/20160610204157_add_deployments.rb
index cb144ea8a6d..0e7e6e747a3 100644
--- a/db/migrate/20160610204157_add_deployments.rb
+++ b/db/migrate/20160610204157_add_deployments.rb
@@ -1,6 +1,7 @@
# See http://doc.gitlab.com/ce/development/migration_style_guide.html
# for more information on how to write migrations for GitLab.
+# rubocop:disable Migration/Datetime
class AddDeployments < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
diff --git a/db/migrate/20160610204158_add_environments.rb b/db/migrate/20160610204158_add_environments.rb
index e1c71d173c4..699cee2b246 100644
--- a/db/migrate/20160610204158_add_environments.rb
+++ b/db/migrate/20160610204158_add_environments.rb
@@ -1,6 +1,7 @@
# See http://doc.gitlab.com/ce/development/migration_style_guide.html
# for more information on how to write migrations for GitLab.
+# rubocop:disable Migration/Datetime
class AddEnvironments < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
diff --git a/db/migrate/20160705054938_add_protected_branches_push_access.rb b/db/migrate/20160705054938_add_protected_branches_push_access.rb
index f27295524e1..97aaaf9d2c8 100644
--- a/db/migrate/20160705054938_add_protected_branches_push_access.rb
+++ b/db/migrate/20160705054938_add_protected_branches_push_access.rb
@@ -1,6 +1,7 @@
# See http://doc.gitlab.com/ce/development/migration_style_guide.html
# for more information on how to write migrations for GitLab.
+# rubocop:disable Migration/Timestamps
class AddProtectedBranchesPushAccess < ActiveRecord::Migration
DOWNTIME = false
diff --git a/db/migrate/20160705054952_add_protected_branches_merge_access.rb b/db/migrate/20160705054952_add_protected_branches_merge_access.rb
index 32adfa266cd..51a52a5ac17 100644
--- a/db/migrate/20160705054952_add_protected_branches_merge_access.rb
+++ b/db/migrate/20160705054952_add_protected_branches_merge_access.rb
@@ -1,6 +1,7 @@
# See http://doc.gitlab.com/ce/development/migration_style_guide.html
# for more information on how to write migrations for GitLab.
+# rubocop:disable Migration/Timestamps
class AddProtectedBranchesMergeAccess < ActiveRecord::Migration
DOWNTIME = false
diff --git a/db/migrate/20160724205507_add_resolved_to_notes.rb b/db/migrate/20160724205507_add_resolved_to_notes.rb
index b8ebcdbd156..3aca272a3f7 100644
--- a/db/migrate/20160724205507_add_resolved_to_notes.rb
+++ b/db/migrate/20160724205507_add_resolved_to_notes.rb
@@ -1,3 +1,4 @@
+# rubocop:disable Migration/Datetime
class AddResolvedToNotes < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
diff --git a/db/migrate/20160727163552_create_user_agent_details.rb b/db/migrate/20160727163552_create_user_agent_details.rb
index ed4ccfedc0a..3eb36f8464f 100644
--- a/db/migrate/20160727163552_create_user_agent_details.rb
+++ b/db/migrate/20160727163552_create_user_agent_details.rb
@@ -1,3 +1,4 @@
+# rubocop:disable Migration/Timestamps
class CreateUserAgentDetails < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
diff --git a/db/migrate/20160727191041_create_boards.rb b/db/migrate/20160727191041_create_boards.rb
index 56afbd4e030..9ec8df1b8e8 100644
--- a/db/migrate/20160727191041_create_boards.rb
+++ b/db/migrate/20160727191041_create_boards.rb
@@ -1,3 +1,4 @@
+# rubocop:disable Migration/Timestamps
class CreateBoards < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
diff --git a/db/migrate/20160727193336_create_lists.rb b/db/migrate/20160727193336_create_lists.rb
index 61d501215f2..3fd95dc8cfc 100644
--- a/db/migrate/20160727193336_create_lists.rb
+++ b/db/migrate/20160727193336_create_lists.rb
@@ -1,3 +1,4 @@
+# rubocop:disable Migration/Timestamps
class CreateLists < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
diff --git a/db/migrate/20160805041956_add_deleted_at_to_namespaces.rb b/db/migrate/20160805041956_add_deleted_at_to_namespaces.rb
index 30d98a0124e..404c253e18b 100644
--- a/db/migrate/20160805041956_add_deleted_at_to_namespaces.rb
+++ b/db/migrate/20160805041956_add_deleted_at_to_namespaces.rb
@@ -1,3 +1,4 @@
+# rubocop:disable Migration/Datetime
# rubocop:disable RemoveIndex
class AddDeletedAtToNamespaces < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
diff --git a/db/migrate/20160824124900_add_table_issue_metrics.rb b/db/migrate/20160824124900_add_table_issue_metrics.rb
index e9bb79b3c62..30d35ef1db2 100644
--- a/db/migrate/20160824124900_add_table_issue_metrics.rb
+++ b/db/migrate/20160824124900_add_table_issue_metrics.rb
@@ -1,6 +1,8 @@
# See http://doc.gitlab.com/ce/development/migration_style_guide.html
# for more information on how to write migrations for GitLab.
+# rubocop:disable Migration/Datetime
+# rubocop:disable Migration/Timestamps
class AddTableIssueMetrics < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
diff --git a/db/migrate/20160825052008_add_table_merge_request_metrics.rb b/db/migrate/20160825052008_add_table_merge_request_metrics.rb
index e01cc5038b9..56b39634dfd 100644
--- a/db/migrate/20160825052008_add_table_merge_request_metrics.rb
+++ b/db/migrate/20160825052008_add_table_merge_request_metrics.rb
@@ -1,6 +1,8 @@
# See http://doc.gitlab.com/ce/development/migration_style_guide.html
# for more information on how to write migrations for GitLab.
+# rubocop:disable Migration/Datetime
+# rubocop:disable Migration/Timestamps
class AddTableMergeRequestMetrics < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
diff --git a/db/migrate/20160831214002_create_project_features.rb b/db/migrate/20160831214002_create_project_features.rb
index 343953826f0..7ac6c8ec654 100644
--- a/db/migrate/20160831214002_create_project_features.rb
+++ b/db/migrate/20160831214002_create_project_features.rb
@@ -1,3 +1,4 @@
+# rubocop:disable Migration/Timestamps
class CreateProjectFeatures < ActiveRecord::Migration
DOWNTIME = false
diff --git a/db/migrate/20160915042921_create_merge_requests_closing_issues.rb b/db/migrate/20160915042921_create_merge_requests_closing_issues.rb
index 94874a853da..10c5604bb5c 100644
--- a/db/migrate/20160915042921_create_merge_requests_closing_issues.rb
+++ b/db/migrate/20160915042921_create_merge_requests_closing_issues.rb
@@ -1,6 +1,7 @@
# See http://doc.gitlab.com/ce/development/migration_style_guide.html
# for more information on how to write migrations for GitLab.
+# rubocop:disable Migration/Timestamps
class CreateMergeRequestsClosingIssues < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
diff --git a/db/migrate/20161014173530_create_label_priorities.rb b/db/migrate/20161014173530_create_label_priorities.rb
index 2c22841c28a..28937c81e02 100644
--- a/db/migrate/20161014173530_create_label_priorities.rb
+++ b/db/migrate/20161014173530_create_label_priorities.rb
@@ -1,3 +1,4 @@
+# rubocop:disable Migration/Timestamps
class CreateLabelPriorities < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
diff --git a/db/migrate/20161113184239_create_user_chat_names_table.rb b/db/migrate/20161113184239_create_user_chat_names_table.rb
index 97b597654f7..62ccb599f2e 100644
--- a/db/migrate/20161113184239_create_user_chat_names_table.rb
+++ b/db/migrate/20161113184239_create_user_chat_names_table.rb
@@ -1,3 +1,5 @@
+# rubocop:disable Migration/Datetime
+# rubocop:disable Migration/Timestamps
class CreateUserChatNamesTable < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
diff --git a/db/migrate/20161124111402_add_routes_table.rb b/db/migrate/20161124111402_add_routes_table.rb
index a02e046a18e..f5241d906d1 100644
--- a/db/migrate/20161124111402_add_routes_table.rb
+++ b/db/migrate/20161124111402_add_routes_table.rb
@@ -1,6 +1,7 @@
# See http://doc.gitlab.com/ce/development/migration_style_guide.html
# for more information on how to write migrations for GitLab.
+# rubocop:disable Migration/Timestamps
class AddRoutesTable < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
diff --git a/db/migrate/20161221152132_add_last_used_at_to_key.rb b/db/migrate/20161221152132_add_last_used_at_to_key.rb
index fb2b15817de..86dc7870247 100644
--- a/db/migrate/20161221152132_add_last_used_at_to_key.rb
+++ b/db/migrate/20161221152132_add_last_used_at_to_key.rb
@@ -1,3 +1,4 @@
+# rubocop:disable Migration/Datetime
class AddLastUsedAtToKey < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
diff --git a/db/migrate/20161223034646_create_timelogs_ce.rb b/db/migrate/20161223034646_create_timelogs_ce.rb
index 66d9cd823fb..1e894cc9161 100644
--- a/db/migrate/20161223034646_create_timelogs_ce.rb
+++ b/db/migrate/20161223034646_create_timelogs_ce.rb
@@ -1,3 +1,4 @@
+# rubocop:disable Migration/Timestamps
class CreateTimelogsCe < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
diff --git a/db/migrate/20161228124936_change_expires_at_to_date_in_personal_access_tokens.rb b/db/migrate/20161228124936_change_expires_at_to_date_in_personal_access_tokens.rb
index af1bac897cc..16f7cc487ce 100644
--- a/db/migrate/20161228124936_change_expires_at_to_date_in_personal_access_tokens.rb
+++ b/db/migrate/20161228124936_change_expires_at_to_date_in_personal_access_tokens.rb
@@ -1,6 +1,7 @@
# See http://doc.gitlab.com/ce/development/migration_style_guide.html
# for more information on how to write migrations for GitLab.
+# rubocop:disable Migration/Datetime
class ChangeExpiresAtToDateInPersonalAccessTokens < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
diff --git a/db/migrate/20170120131253_create_chat_teams.rb b/db/migrate/20170120131253_create_chat_teams.rb
index 7995d383986..52208821911 100644
--- a/db/migrate/20170120131253_create_chat_teams.rb
+++ b/db/migrate/20170120131253_create_chat_teams.rb
@@ -1,3 +1,4 @@
+# rubocop:disable Migration/Timestamps
class CreateChatTeams < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
diff --git a/db/migrate/20170130221926_create_uploads.rb b/db/migrate/20170130221926_create_uploads.rb
index 6f06c5dd840..4d9fa0bb692 100644
--- a/db/migrate/20170130221926_create_uploads.rb
+++ b/db/migrate/20170130221926_create_uploads.rb
@@ -1,3 +1,4 @@
+# rubocop:disable Migration/Datetime
class CreateUploads < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
diff --git a/db/migrate/20170222143317_drop_ci_projects.rb b/db/migrate/20170222143317_drop_ci_projects.rb
index 4db8658f36f..9973e53501c 100644
--- a/db/migrate/20170222143317_drop_ci_projects.rb
+++ b/db/migrate/20170222143317_drop_ci_projects.rb
@@ -1,3 +1,4 @@
+# rubocop:disable Migration/Datetime
class DropCiProjects < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
diff --git a/db/migrate/20170301205639_remove_unused_ci_tables_and_columns.rb b/db/migrate/20170301205639_remove_unused_ci_tables_and_columns.rb
index 69dd15b8b4e..1a77d5934a3 100644
--- a/db/migrate/20170301205639_remove_unused_ci_tables_and_columns.rb
+++ b/db/migrate/20170301205639_remove_unused_ci_tables_and_columns.rb
@@ -1,3 +1,4 @@
+# rubocop:disable Migration/Datetime
class RemoveUnusedCiTablesAndColumns < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
diff --git a/db/migrate/20170309173138_create_protected_tags.rb b/db/migrate/20170309173138_create_protected_tags.rb
index 796f3c90344..4684c9964c4 100644
--- a/db/migrate/20170309173138_create_protected_tags.rb
+++ b/db/migrate/20170309173138_create_protected_tags.rb
@@ -1,3 +1,4 @@
+# rubocop:disable Migration/Timestamps
class CreateProtectedTags < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
diff --git a/db/migrate/20170314082049_create_system_note_metadata.rb b/db/migrate/20170314082049_create_system_note_metadata.rb
index dd1e6cf8172..fee47e96053 100644
--- a/db/migrate/20170314082049_create_system_note_metadata.rb
+++ b/db/migrate/20170314082049_create_system_note_metadata.rb
@@ -1,3 +1,4 @@
+# rubocop:disable Migration/Timestamps
class CreateSystemNoteMetadata < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
diff --git a/db/migrate/20170315194013_add_closed_at_to_issues.rb b/db/migrate/20170315194013_add_closed_at_to_issues.rb
index 1326118cc8d..34a1bd7ca8c 100644
--- a/db/migrate/20170315194013_add_closed_at_to_issues.rb
+++ b/db/migrate/20170315194013_add_closed_at_to_issues.rb
@@ -1,3 +1,4 @@
+# rubocop:disable Migration/Datetime
class AddClosedAtToIssues < ActiveRecord::Migration
DOWNTIME = false
diff --git a/db/migrate/20170322013926_create_container_repository.rb b/db/migrate/20170322013926_create_container_repository.rb
index 91540bc88bd..242f7b8d17d 100644
--- a/db/migrate/20170322013926_create_container_repository.rb
+++ b/db/migrate/20170322013926_create_container_repository.rb
@@ -1,3 +1,4 @@
+# rubocop:disable Migration/Timestamps
class CreateContainerRepository < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
diff --git a/db/migrate/20170329095907_create_ci_trigger_schedules.rb b/db/migrate/20170329095907_create_ci_trigger_schedules.rb
index cfcfa27ebb5..06a2010db23 100644
--- a/db/migrate/20170329095907_create_ci_trigger_schedules.rb
+++ b/db/migrate/20170329095907_create_ci_trigger_schedules.rb
@@ -1,3 +1,4 @@
+# rubocop:disable Migration/Datetime
class CreateCiTriggerSchedules < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
diff --git a/db/migrate/20170425112128_create_pipeline_schedules_table.rb b/db/migrate/20170425112128_create_pipeline_schedules_table.rb
index 3612a796ae8..57df47f5f42 100644
--- a/db/migrate/20170425112128_create_pipeline_schedules_table.rb
+++ b/db/migrate/20170425112128_create_pipeline_schedules_table.rb
@@ -1,3 +1,5 @@
+# rubocop:disable Migration/Datetime
+# rubocop:disable Migration/Timestamps
class CreatePipelineSchedulesTable < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
diff --git a/db/migrate/20170427215854_create_redirect_routes.rb b/db/migrate/20170427215854_create_redirect_routes.rb
index 2bf086b3e30..6db508e5db4 100644
--- a/db/migrate/20170427215854_create_redirect_routes.rb
+++ b/db/migrate/20170427215854_create_redirect_routes.rb
@@ -1,3 +1,4 @@
+# rubocop:disable Migration/Timestamps
class CreateRedirectRoutes < ActiveRecord::Migration
# Set this constant to true if this migration requires downtime.
DOWNTIME = false
diff --git a/db/migrate/20170503004125_add_last_repository_updated_at_to_projects.rb b/db/migrate/20170503004125_add_last_repository_updated_at_to_projects.rb
index 00c685cf342..2ea49f62742 100644
--- a/db/migrate/20170503004125_add_last_repository_updated_at_to_projects.rb
+++ b/db/migrate/20170503004125_add_last_repository_updated_at_to_projects.rb
@@ -1,3 +1,4 @@
+# rubocop:disable Migration/Datetime
class AddLastRepositoryUpdatedAtToProjects < ActiveRecord::Migration
DOWNTIME = false
diff --git a/db/migrate/20170523121229_create_conversational_development_index_metrics.rb b/db/migrate/20170523121229_create_conversational_development_index_metrics.rb
index 9f9ec526055..7026a867ae1 100644
--- a/db/migrate/20170523121229_create_conversational_development_index_metrics.rb
+++ b/db/migrate/20170523121229_create_conversational_development_index_metrics.rb
@@ -1,3 +1,4 @@
+# rubocop:disable Migration/Timestamps
class CreateConversationalDevelopmentIndexMetrics < ActiveRecord::Migration
DOWNTIME = false
diff --git a/db/migrate/20170525132202_create_pipeline_stages.rb b/db/migrate/20170525132202_create_pipeline_stages.rb
index 25656f2a2c2..825993aa41e 100644
--- a/db/migrate/20170525132202_create_pipeline_stages.rb
+++ b/db/migrate/20170525132202_create_pipeline_stages.rb
@@ -1,3 +1,4 @@
+# rubocop:disable Migration/Timestamps
class CreatePipelineStages < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
diff --git a/db/migrate/20170602154736_add_help_page_hide_commercial_content_to_application_settings.rb b/db/migrate/20170602154736_add_help_page_hide_commercial_content_to_application_settings.rb
new file mode 100644
index 00000000000..5e8b667b86d
--- /dev/null
+++ b/db/migrate/20170602154736_add_help_page_hide_commercial_content_to_application_settings.rb
@@ -0,0 +1,9 @@
+class AddHelpPageHideCommercialContentToApplicationSettings < ActiveRecord::Migration
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+
+ def change
+ add_column :application_settings, :help_page_hide_commercial_content, :boolean, default: false
+ end
+end
diff --git a/db/migrate/20170602154813_add_help_page_support_url_to_application_settings.rb b/db/migrate/20170602154813_add_help_page_support_url_to_application_settings.rb
new file mode 100644
index 00000000000..138fe9b2a37
--- /dev/null
+++ b/db/migrate/20170602154813_add_help_page_support_url_to_application_settings.rb
@@ -0,0 +1,9 @@
+class AddHelpPageSupportUrlToApplicationSettings < ActiveRecord::Migration
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+
+ def change
+ add_column :application_settings, :help_page_support_url, :string
+ end
+end
diff --git a/db/migrate/20170606154216_add_notification_setting_columns.rb b/db/migrate/20170606154216_add_notification_setting_columns.rb
new file mode 100644
index 00000000000..0a9b5da6583
--- /dev/null
+++ b/db/migrate/20170606154216_add_notification_setting_columns.rb
@@ -0,0 +1,26 @@
+class AddNotificationSettingColumns < ActiveRecord::Migration
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+
+ COLUMNS = [
+ :new_note,
+ :new_issue,
+ :reopen_issue,
+ :close_issue,
+ :reassign_issue,
+ :new_merge_request,
+ :reopen_merge_request,
+ :close_merge_request,
+ :reassign_merge_request,
+ :merge_merge_request,
+ :failed_pipeline,
+ :success_pipeline
+ ]
+
+ def change
+ COLUMNS.each do |column|
+ add_column(:notification_settings, column, :boolean)
+ end
+ end
+end
diff --git a/db/migrate/20170608171156_create_merge_request_diff_files.rb b/db/migrate/20170608171156_create_merge_request_diff_files.rb
new file mode 100644
index 00000000000..bf0c0d29adc
--- /dev/null
+++ b/db/migrate/20170608171156_create_merge_request_diff_files.rb
@@ -0,0 +1,22 @@
+class CreateMergeRequestDiffFiles < ActiveRecord::Migration
+ DOWNTIME = false
+
+ disable_ddl_transaction!
+
+ def change
+ create_table :merge_request_diff_files, id: false do |t|
+ t.belongs_to :merge_request_diff, null: false, foreign_key: { on_delete: :cascade }
+ t.integer :relative_order, null: false
+ t.boolean :new_file, null: false
+ t.boolean :renamed_file, null: false
+ t.boolean :deleted_file, null: false
+ t.boolean :too_large, null: false
+ t.string :a_mode, null: false
+ t.string :b_mode, null: false
+ t.text :new_path, null: false
+ t.text :old_path, null: false
+ t.text :diff, null: false
+ t.index [:merge_request_diff_id, :relative_order], name: 'index_merge_request_diff_files_on_mr_diff_id_and_order', unique: true
+ end
+ end
+end
diff --git a/db/migrate/20170614115405_merge_request_diff_file_limits_to_mysql.rb b/db/migrate/20170614115405_merge_request_diff_file_limits_to_mysql.rb
new file mode 100644
index 00000000000..4c1cf08aa06
--- /dev/null
+++ b/db/migrate/20170614115405_merge_request_diff_file_limits_to_mysql.rb
@@ -0,0 +1 @@
+require_relative 'merge_request_diff_file_limits_to_mysql'
diff --git a/db/migrate/merge_request_diff_file_limits_to_mysql.rb b/db/migrate/merge_request_diff_file_limits_to_mysql.rb
new file mode 100644
index 00000000000..3958380e4b9
--- /dev/null
+++ b/db/migrate/merge_request_diff_file_limits_to_mysql.rb
@@ -0,0 +1,12 @@
+class MergeRequestDiffFileLimitsToMysql < ActiveRecord::Migration
+ DOWNTIME = false
+
+ def up
+ return unless Gitlab::Database.mysql?
+
+ change_column :merge_request_diff_files, :diff, :text, limit: 2147483647
+ end
+
+ def down
+ end
+end
diff --git a/db/post_migrate/20170425130047_drop_ci_trigger_schedules_table.rb b/db/post_migrate/20170425130047_drop_ci_trigger_schedules_table.rb
index 24750c58ef0..159b533eaaa 100644
--- a/db/post_migrate/20170425130047_drop_ci_trigger_schedules_table.rb
+++ b/db/post_migrate/20170425130047_drop_ci_trigger_schedules_table.rb
@@ -1,3 +1,4 @@
+# rubocop:disable Migration/Datetime
class DropCiTriggerSchedulesTable < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
diff --git a/db/post_migrate/20170607121233_convert_custom_notification_settings_to_columns.rb b/db/post_migrate/20170607121233_convert_custom_notification_settings_to_columns.rb
new file mode 100644
index 00000000000..9abda6a1d73
--- /dev/null
+++ b/db/post_migrate/20170607121233_convert_custom_notification_settings_to_columns.rb
@@ -0,0 +1,55 @@
+class ConvertCustomNotificationSettingsToColumns < ActiveRecord::Migration
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+
+ disable_ddl_transaction!
+
+ class NotificationSetting < ActiveRecord::Base
+ self.table_name = 'notification_settings'
+
+ store :events, coder: JSON
+ end
+
+ EMAIL_EVENTS = [
+ :new_note,
+ :new_issue,
+ :reopen_issue,
+ :close_issue,
+ :reassign_issue,
+ :new_merge_request,
+ :reopen_merge_request,
+ :close_merge_request,
+ :reassign_merge_request,
+ :merge_merge_request,
+ :failed_pipeline,
+ :success_pipeline
+ ]
+
+ # We only need to migrate (up or down) rows where at least one of these
+ # settings is set.
+ def up
+ NotificationSetting.where("events LIKE '%true%'").find_each do |notification_setting|
+ EMAIL_EVENTS.each do |event|
+ notification_setting[event] = notification_setting.events[event]
+ end
+
+ notification_setting[:events] = nil
+ notification_setting.save!
+ end
+ end
+
+ def down
+ NotificationSetting.where(EMAIL_EVENTS.join(' OR ')).find_each do |notification_setting|
+ events = {}
+
+ EMAIL_EVENTS.each do |event|
+ events[event] = !!notification_setting.public_send(event)
+ notification_setting[event] = nil
+ end
+
+ notification_setting[:events] = events
+ notification_setting.save!
+ end
+ end
+end
diff --git a/db/post_migrate/20170609183112_remove_position_from_issuables.rb b/db/post_migrate/20170609183112_remove_position_from_issuables.rb
new file mode 100644
index 00000000000..4caaa2e83e8
--- /dev/null
+++ b/db/post_migrate/20170609183112_remove_position_from_issuables.rb
@@ -0,0 +1,8 @@
+class RemovePositionFromIssuables < ActiveRecord::Migration
+ DOWNTIME = false
+
+ def change
+ remove_column :issues, :position, :integer
+ remove_column :merge_requests, :position, :integer
+ end
+end
diff --git a/db/schema.rb b/db/schema.rb
index b93630a410d..f42827991aa 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -11,7 +11,8 @@
#
# It's strongly recommended that you check this file into your version control system.
-ActiveRecord::Schema.define(version: 20170606202615) do
+ActiveRecord::Schema.define(version: 20170614115405) do
+
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
enable_extension "pg_trgm"
@@ -123,6 +124,8 @@ ActiveRecord::Schema.define(version: 20170606202615) do
t.boolean "clientside_sentry_enabled", default: false, null: false
t.string "clientside_sentry_dsn"
t.boolean "prometheus_metrics_enabled", default: false, null: false
+ t.boolean "help_page_hide_commercial_content", default: false
+ t.string "help_page_support_url"
end
create_table "audit_events", force: :cascade do |t|
@@ -544,7 +547,6 @@ ActiveRecord::Schema.define(version: 20170606202615) do
t.integer "project_id"
t.datetime "created_at"
t.datetime "updated_at"
- t.integer "position", default: 0
t.string "branch_name"
t.text "description"
t.integer "milestone_id"
@@ -690,6 +692,22 @@ ActiveRecord::Schema.define(version: 20170606202615) do
add_index "members", ["source_id", "source_type"], name: "index_members_on_source_id_and_source_type", using: :btree
add_index "members", ["user_id"], name: "index_members_on_user_id", using: :btree
+ create_table "merge_request_diff_files", id: false, force: :cascade do |t|
+ t.integer "merge_request_diff_id", null: false
+ t.integer "relative_order", null: false
+ t.boolean "new_file", null: false
+ t.boolean "renamed_file", null: false
+ t.boolean "deleted_file", null: false
+ t.boolean "too_large", null: false
+ t.string "a_mode", null: false
+ t.string "b_mode", null: false
+ t.text "new_path", null: false
+ t.text "old_path", null: false
+ t.text "diff", null: false
+ end
+
+ add_index "merge_request_diff_files", ["merge_request_diff_id", "relative_order"], name: "index_merge_request_diff_files_on_mr_diff_id_and_order", unique: true, using: :btree
+
create_table "merge_request_diffs", force: :cascade do |t|
t.string "state"
t.text "st_commits"
@@ -735,7 +753,6 @@ ActiveRecord::Schema.define(version: 20170606202615) do
t.integer "target_project_id", null: false
t.integer "iid"
t.text "description"
- t.integer "position", default: 0
t.datetime "locked_at"
t.integer "updated_by_id"
t.text "merge_error"
@@ -875,6 +892,18 @@ ActiveRecord::Schema.define(version: 20170606202615) do
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.text "events"
+ t.boolean "new_note"
+ t.boolean "new_issue"
+ t.boolean "reopen_issue"
+ t.boolean "close_issue"
+ t.boolean "reassign_issue"
+ t.boolean "new_merge_request"
+ t.boolean "reopen_merge_request"
+ t.boolean "close_merge_request"
+ t.boolean "reassign_merge_request"
+ t.boolean "merge_merge_request"
+ t.boolean "failed_pipeline"
+ t.boolean "success_pipeline"
end
add_index "notification_settings", ["source_id", "source_type"], name: "index_notification_settings_on_source_id_and_source_type", using: :btree
@@ -1517,6 +1546,7 @@ ActiveRecord::Schema.define(version: 20170606202615) do
add_foreign_key "labels", "namespaces", column: "group_id", on_delete: :cascade
add_foreign_key "lists", "boards"
add_foreign_key "lists", "labels"
+ add_foreign_key "merge_request_diff_files", "merge_request_diffs", on_delete: :cascade
add_foreign_key "merge_request_metrics", "ci_pipelines", column: "pipeline_id", on_delete: :cascade
add_foreign_key "merge_request_metrics", "merge_requests", on_delete: :cascade
add_foreign_key "merge_requests_closing_issues", "issues", on_delete: :cascade
diff --git a/doc/README.md b/doc/README.md
index 9f12eed1471..ab8ea192a26 100644
--- a/doc/README.md
+++ b/doc/README.md
@@ -24,7 +24,7 @@ Shortcuts to GitLab's most visited docs:
- [GitLab Workflow](workflow/README.md): Enhance your workflow with the best of GitLab Workflow.
- See also [GitLab Workflow - an overview](https://about.gitlab.com/2016/10/25/gitlab-workflow-an-overview/).
- [GitLab Markdown](user/markdown.md): GitLab's advanced formatting system (GitLab Flavored Markdown).
-- [GitLab Slash Commands](user/project/slash_commands.md): Textual shortcuts for common actions on issues or merge requests that are usually done by clicking buttons or dropdowns in GitLab's UI.
+- [GitLab Quick Actions](user/project/quick_actions.md): Textual shortcuts for common actions on issues or merge requests that are usually done by clicking buttons or dropdowns in GitLab's UI.
### User account
@@ -59,6 +59,7 @@ Manage files and branches from the UI (user interface):
- Branches
- [Create a branch](user/project/repository/web_editor.md#create-a-new-branch)
- [Protected branches](user/project/protected_branches.md#protected-branches)
+ - [Delete merged branches](user/project/repository/branches/index.md#delete-merged-branches)
### Issues and Merge Requests (MRs)
diff --git a/doc/administration/environment_variables.md b/doc/administration/environment_variables.md
index b6676026d06..9bcd13a52f7 100644
--- a/doc/administration/environment_variables.md
+++ b/doc/administration/environment_variables.md
@@ -13,6 +13,7 @@ override certain values.
Variable | Type | Description
-------- | ---- | -----------
+`GITLAB_CDN_HOST` | string | Sets the hostname for a CDN to serve static assets (e.g. `mycdnsubdomain.fictional-cdn.com`)
`GITLAB_ROOT_PASSWORD` | string | Sets the password for the `root` user on installation
`GITLAB_HOST` | string | The full URL of the GitLab server (including `http://` or `https://`)
`RAILS_ENV` | string | The Rails environment; can be one of `production`, `development`, `staging` or `test`
@@ -58,6 +59,9 @@ to the naming scheme `GITLAB_#{name in 1_settings.rb in upper case}`.
## Omnibus configuration
+To set environment variables, follow [these
+instructions](https://docs.gitlab.com/omnibus/settings/environment-variables.html).
+
It's possible to preconfigure the GitLab docker image by adding the environment
variable `GITLAB_OMNIBUS_CONFIG` to the `docker run` command.
For more information see the ['preconfigure-docker-container' section in the Omnibus documentation](http://docs.gitlab.com/omnibus/docker/#preconfigure-docker-container).
diff --git a/doc/administration/job_artifacts.md b/doc/administration/job_artifacts.md
index 5599435564e..3587696225c 100644
--- a/doc/administration/job_artifacts.md
+++ b/doc/administration/job_artifacts.md
@@ -46,7 +46,10 @@ To disable artifacts site-wide, follow the steps below.
After a successful job, GitLab Runner uploads an archive containing the job
artifacts to GitLab.
-To change the location where the artifacts are stored, follow the steps below.
+### Using local storage
+
+To change the location where the artifacts are stored locally, follow the steps
+below.
---
@@ -82,6 +85,13 @@ _The artifacts are stored by default in
1. Save the file and [restart GitLab][] for the changes to take effect.
+### Using object storage
+
+In [GitLab Enterprise Edition Premium][eep] you can use an object storage like
+AWS S3 to store the artifacts.
+
+[Learn how to use the object storage option.][ee-os]
+
## Expiring artifacts
If an expiry date is used for the artifacts, they are marked for deletion
@@ -148,3 +158,5 @@ memory and disk I/O.
[reconfigure gitlab]: restart_gitlab.md "How to restart GitLab"
[restart gitlab]: restart_gitlab.md "How to restart GitLab"
[gitlab workhorse]: https://gitlab.com/gitlab-org/gitlab-workhorse "GitLab Workhorse repository"
+[ee-os]: https://docs.gitlab.com/ee/administration/job_artifacts.html#using-object-storage
+[eep]: https://about.gitlab.com/gitlab-ee/ "GitLab Enterprise Edition Premium"
diff --git a/doc/administration/raketasks/github_import.md b/doc/administration/raketasks/github_import.md
index affb4d17861..04c70c3644e 100644
--- a/doc/administration/raketasks/github_import.md
+++ b/doc/administration/raketasks/github_import.md
@@ -3,7 +3,7 @@
>**Note:**
>
> - [Introduced][ce-10308] in GitLab 9.1.
-> - You need a personal access token in order to retrieve and import GitHub
+> - You need a personal access token in order to retrieve and import GitHub
> projects. You can get it from: https://github.com/settings/tokens
> - You also need to pass an username as the second argument to the rake task
> which will become the owner of the project.
@@ -19,7 +19,7 @@ bundle exec rake import:github[access_token,root,foo/bar] RAILS_ENV=production
```
In this case, `access_token` is your GitHub personal access token, `root`
-is your GitLab username, and `foo/bar` is the new GitLab namespace/project that
+is your GitLab username, and `foo/bar` is the new GitLab namespace/project that
will get created from your GitHub project. Subgroups are also possible: `foo/foo/bar`.
diff --git a/doc/api/README.md b/doc/api/README.md
index 1241801a81c..4f189c16673 100644
--- a/doc/api/README.md
+++ b/doc/api/README.md
@@ -55,6 +55,11 @@ following locations:
- [V3 to V4](v3_to_v4.md)
- [Version](version.md)
+The following documentation is for the [internal CI API](ci/README.md):
+
+- [Builds](ci/builds.md)
+- [Runners](ci/runners.md)
+
## Road to GraphQL
Going forward, we will start on moving to
@@ -65,22 +70,20 @@ controller-specific endpoints. GraphQL has a number of benefits:
2. Callers of the API can request only what they need.
3. It is versioned by default.
-It will co-exist with the current V4 REST API. If we have a V5 API, this should be
-compatability layer on top of GraphQL.
-
-### Internal CI API
+It will co-exist with the current v4 REST API. If we have a v5 API, this should
+be a compatibility layer on top of GraphQL.
-The following documentation is for the [internal CI API](ci/README.md):
+## Authentication
-- [Builds](ci/builds.md)
-- [Runners](ci/runners.md)
+Most API requests require authentication via a session cookie or token. For
+those cases where it is not required, this will be mentioned in the documentation
+for each individual endpoint. For example, the [`/projects/:id` endpoint](projects.md).
-## Authentication
+There are three types of access tokens available:
-Most API requests require authentication via a session cookie or token. For those cases where it is not required, this will be mentioned in the documentation
-for each individual endpoint. For example, the [`/projects/:id` endpoint](projects.md).
-There are three types of tokens available: private tokens, OAuth 2 tokens, and personal
-access tokens.
+1. [OAuth2 tokens](#oauth2-tokens)
+1. [Private tokens](#private-tokens)
+1. [Personal access tokens](#personal-access-tokens)
If authentication information is invalid or omitted, an error message will be
returned with status code `401`:
@@ -91,20 +94,13 @@ returned with status code `401`:
}
```
-### Session Cookie
+### Session cookie
When signing in to GitLab as an ordinary user, a `_gitlab_session` cookie is
set. The API will use this cookie for authentication if it is present, but using
the API to generate a new session cookie is currently not supported.
-### Private Tokens
-
-You need to pass a `private_token` parameter via query string or header. If passed as a
-header, the header name must be `PRIVATE-TOKEN` (uppercase and with a dash instead of
-an underscore). You can find or reset your private token in your account page
-(`/profile/account`).
-
-### OAuth 2 Tokens
+### OAuth2 tokens
You can use an OAuth 2 token to authenticate with the API by passing it either in the
`access_token` parameter or in the `Authorization` header.
@@ -117,30 +113,31 @@ curl --header "Authorization: Bearer OAUTH-TOKEN" https://gitlab.example.com/api
Read more about [GitLab as an OAuth2 client](oauth2.md).
-### Personal Access Tokens
+### Private tokens
-> [Introduced][ce-3749] in GitLab 8.8.
+Private tokens provide full access to the GitLab API. Anyone with access to
+them can interact with GitLab as if they were you. You can find or reset your
+private token in your account page (`/profile/account`).
-You can create as many personal access tokens as you like from your GitLab
-profile (`/profile/personal_access_tokens`); perhaps one for each application
-that needs access to the GitLab API.
+For examples of usage, [read the basic usage section](#basic-usage).
-Once you have your token, pass it to the API using either the `private_token`
-parameter or the `PRIVATE-TOKEN` header.
+### Personal access tokens
-> [Introduced][ce-5951] in GitLab 8.15.
+Instead of using your private token which grants full access to your account,
+personal access tokens could be a better fit because of their granular
+permissions.
-Personal Access Tokens can be created with one or more scopes that allow various actions
-that a given token can perform. Although there are only two scopes available at the
-moment – `read_user` and `api` – the groundwork has been laid to add more scopes easily.
+Once you have your token, pass it to the API using either the `private_token`
+parameter or the `PRIVATE-TOKEN` header. For examples of usage,
+[read the basic usage section](#basic-usage).
-At any time you can revoke any personal access token by just clicking **Revoke**.
+[Read more about personal access tokens.][pat]
### Impersonation tokens
> [Introduced][ce-9099] in GitLab 9.0. Needs admin permissions.
-Impersonation tokens are a type of [Personal Access Token](#personal-access-tokens)
+Impersonation tokens are a type of [personal access token][pat]
that can only be created by an admin for a specific user.
They are a better alternative to using the user's password/private token
@@ -149,9 +146,11 @@ or private token, since the password/token can change over time. Impersonation
tokens are a great fit if you want to build applications or tools which
authenticate with the API as a specific user.
-For more information about the usage please refer to the
+For more information, refer to the
[users API](users.md#retrieve-user-impersonation-tokens) docs.
+For examples of usage, [read the basic usage section](#basic-usage).
+
### Sudo
> Needs admin permissions.
@@ -204,11 +203,16 @@ GET /projects?private_token=9koXpg98eAheJpvBs5tK&sudo=23
curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" --header "SUDO: 23" "https://gitlab.example.com/api/v4/projects"
```
-## Basic Usage
+## Basic usage
API requests should be prefixed with `api` and the API version. The API version
is defined in [`lib/api.rb`][lib-api-url].
+For endpoints that require [authentication](#authentication), you need to pass
+a `private_token` parameter via query string or header. If passed as a header,
+the header name must be `PRIVATE-TOKEN` (uppercase and with a dash instead of
+an underscore).
+
Example of a valid API request:
```
@@ -221,6 +225,12 @@ Example of a valid API request using cURL and authentication via header:
curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v4/projects"
```
+Example of a valid API request using cURL and authentication via a query string:
+
+```shell
+curl "https://gitlab.example.com/api/v4/projects?private_token=9koXpg98eAheJpvBs5tK"
+```
+
The API uses JSON to serialize data. You don't need to specify `.json` at the
end of an API URL.
@@ -436,3 +446,4 @@ programming languages. Visit the [GitLab website] for a complete list.
[ce-3749]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/3749
[ce-5951]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/5951
[ce-9099]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/9099
+[pat]: ../user/profile/personal_access_tokens.md
diff --git a/doc/api/branches.md b/doc/api/branches.md
index 325d0ea4ce3..dfaa7d6fab7 100644
--- a/doc/api/branches.md
+++ b/doc/api/branches.md
@@ -251,6 +251,8 @@ curl --request DELETE --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gi
Will delete all branches that are merged into the project's default branch.
+Protected branches will not be deleted as part of this operation.
+
```
DELETE /projects/:id/repository/merged_branches
```
diff --git a/doc/api/issues.md b/doc/api/issues.md
index 3f949ca5667..df5666bb7b6 100644
--- a/doc/api/issues.md
+++ b/doc/api/issues.md
@@ -221,7 +221,8 @@ GET /projects/:id/issues?search=issue+title+or+description
| `order_by` | string | no | Return requests ordered by `created_at` or `updated_at` fields. Default is `created_at` |
| `sort` | string | no | Return requests sorted in `asc` or `desc` order. Default is `desc` |
| `search` | string | no | Search project issues against their `title` and `description` |
-
+| `created_after` | datetime | no | Return issues created after the given time (inclusive) |
+| `created_before` | datetime | no | Return issues created before the given time (inclusive) |
```bash
curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/projects/4/issues
diff --git a/doc/api/merge_requests.md b/doc/api/merge_requests.md
index cb22b67f556..3dc808c196d 100644
--- a/doc/api/merge_requests.md
+++ b/doc/api/merge_requests.md
@@ -26,6 +26,8 @@ Parameters:
| `sort` | string | no | Return requests sorted in `asc` or `desc` order. Default is `desc` |
| `milestone` | string | no | Return merge requests for a specific milestone |
| `labels` | string | no | Return merge requests matching a comma separated list of labels |
+| `created_after` | datetime | no | Return merge requests created after the given time (inclusive) |
+| `created_before` | datetime | no | Return merge requests created before the given time (inclusive) |
```json
[
diff --git a/doc/api/oauth2.md b/doc/api/oauth2.md
index 46fe64d382e..07cb64cb373 100644
--- a/doc/api/oauth2.md
+++ b/doc/api/oauth2.md
@@ -134,4 +134,4 @@ access_token = client.password.get_token('user@example.com', 'secret')
puts access_token.token
```
-[personal access tokens]: ./README.md#personal-access-tokens \ No newline at end of file
+[personal access tokens]: ../user/profile/personal_access_tokens.md
diff --git a/doc/api/session.md b/doc/api/session.md
index 7dd504b67c5..f79eac11689 100644
--- a/doc/api/session.md
+++ b/doc/api/session.md
@@ -1,11 +1,9 @@
# Session API
-## Deprecation Notice
-
-1. Starting in GitLab 8.11, this feature has been *disabled* for users with two-factor authentication turned on.
-2. These users can access the API using [personal access tokens] instead.
-
----
+>**Deprecation notice:**
+Starting in GitLab 8.11, this feature has been **disabled** for users with
+[two-factor authentication][2fa] turned on. These users can access the API
+using [personal access tokens] instead.
You can login with both GitLab and LDAP credentials in order to obtain the
private token.
@@ -52,4 +50,5 @@ Example response:
}
```
-[personal access tokens]: ./README.md#personal-access-tokens
+[2fa]: ../user/profile/account/two_factor_authentication.md
+[personal access tokens]: ../user/profile/personal_access_tokens.md
diff --git a/doc/api/users.md b/doc/api/users.md
index f4167ba2605..b1ebd7b0c47 100644
--- a/doc/api/users.md
+++ b/doc/api/users.md
@@ -251,6 +251,7 @@ Parameters:
- `can_create_group` (optional) - User can create groups - true or false
- `confirm` (optional) - Require confirmation - true (default) or false
- `external` (optional) - Flags the user as external - true or false(default)
+- `avatar` (optional) - Image file for user's avatar
## User modification
@@ -279,6 +280,7 @@ Parameters:
- `admin` (optional) - User is admin - true or false (default)
- `can_create_group` (optional) - User can create groups - true or false
- `external` (optional) - Flags the user as external - true or false(default)
+- `avatar` (optional) - Image file for user's avatar
On password update, user will be forced to change it upon next login.
Note, at the moment this method does only return a `404` error,
@@ -804,7 +806,7 @@ Example response:
It creates a new impersonation token. Note that only administrators can do this.
You are only able to create impersonation tokens to impersonate the user and perform
-both API calls and Git reads and writes. The user will not see these tokens in his profile
+both API calls and Git reads and writes. The user will not see these tokens in their profile
settings page.
```
diff --git a/doc/ci/docker/using_docker_build.md b/doc/ci/docker/using_docker_build.md
index 408d46a756c..f7c2a0ef0ca 100644
--- a/doc/ci/docker/using_docker_build.md
+++ b/doc/ci/docker/using_docker_build.md
@@ -282,9 +282,9 @@ which can be avoided if a different driver is used, for example `overlay`.
> **Notes:**
- This feature requires GitLab 8.8 and GitLab Runner 1.2.
-- Starting from GitLab 8.12, if you have 2FA enabled in your account, you need
- to pass a personal access token instead of your password in order to login to
- GitLab's Container Registry.
+- Starting from GitLab 8.12, if you have [2FA] enabled in your account, you need
+ to pass a [personal access token][pat] instead of your password in order to
+ login to GitLab's Container Registry.
Once you've built a Docker image, you can push it up to the built-in
[GitLab Container Registry](../../user/project/container_registry.md). For example,
@@ -409,3 +409,5 @@ Some things you should be aware of when using the Container Registry:
[docker-in-docker]: https://blog.docker.com/2013/09/docker-can-now-run-within-docker/
[docker-cap]: https://docs.docker.com/engine/reference/run/#runtime-privilege-and-linux-capabilities
+[2fa]: ../../user/profile/account/two_factor_authentication.md
+[pat]: ../../user/profile/personal_access_tokens.md
diff --git a/doc/ci/docker/using_docker_images.md b/doc/ci/docker/using_docker_images.md
index 7709541ba9d..be4dea55c20 100644
--- a/doc/ci/docker/using_docker_images.md
+++ b/doc/ci/docker/using_docker_images.md
@@ -210,6 +210,18 @@ When the job is run, `tutum/wordpress` will be started and you will have
access to it from your build container under the hostnames `tutum-wordpress`
(requires GitLab Runner v1.1.0 or newer) and `tutum__wordpress`.
+When using a private registry, the image name also includes a hostname and port
+of the registry.
+
+```yaml
+services:
+- docker.example.com:5000/wordpress:latest
+```
+
+The service hostname will also include the registry hostname. Service will be
+available under hostnames `docker.example.com-wordpress` (requires GitLab Runner v1.1.0 or newer)
+and `docker.example.com__wordpress`.
+
*Note: hostname with underscores is not RFC valid and may cause problems in 3rd party applications.*
The alias hostnames for the service are made from the image name following these
diff --git a/doc/ci/examples/code_climate.md b/doc/ci/examples/code_climate.md
index a047e809788..5659a8c2a2a 100644
--- a/doc/ci/examples/code_climate.md
+++ b/doc/ci/examples/code_climate.md
@@ -27,7 +27,7 @@ download and analyze the report artifact in JSON format.
For GitLab [Enterprise Edition Starter][ee] users, this information can be automatically
extracted and shown right in the merge request widget. [Learn more on code quality
-diffs in merge requests](http://docs.gitlab.com/ee/user/project/merge_requests/code_quality_diff.md).
+diffs in merge requests](https://docs.gitlab.com/ee/user/project/merge_requests/code_quality_diff.html).
[cli]: https://github.com/codeclimate/codeclimate
[dind]: ../docker/using_docker_build.md#use-docker-in-docker-executor
diff --git a/doc/ci/runners/README.md b/doc/ci/runners/README.md
index 73aee3c3f56..76d746155eb 100644
--- a/doc/ci/runners/README.md
+++ b/doc/ci/runners/README.md
@@ -117,21 +117,21 @@ lowest number of jobs currently running on shared Runners.
We have following jobs in queue:
-- job 1 for project 1
-- job 2 for project 1
-- job 3 for project 1
-- job 4 for project 2
-- job 5 for project 2
-- job 6 for project 3
+- Job 1 for Project 1
+- Job 2 for Project 1
+- Job 3 for Project 1
+- Job 4 for Project 2
+- Job 5 for Project 2
+- Job 6 for Project 3
With the fair usage algorithm jobs are assigned in following order:
-1. We choose job 1, because project 1 doesn't run currently any jobs and has the lowest job number from projects that doesn't run jobs
-1. We choose job 4, because project 2 doesn't run currently any jobs and has the lowest job number from projects that doesn't run jobs
-1. We choose job 6, because project 3 doesn't run currently any jobs and has the lowest job number from projects that doesn't run jobs
-1. We choose job 2, because project 1 as other it runs 1 job
-1. We choose job 5, because project 2 runs 1 job, where project 1 runs 2 jobs now
-1. We choose job 3, because project 1 and runs 2 jobs
+1. Job 1 is chosen first, because it has the lowest job number from projects with no running jobs (i.e. all projects)
+1. Job 4 is next, because 4 is now the lowest job number from projects with no running jobs (Project 1 has a job running)
+1. Job 6 is next, because 6 is now the lowest job number from projects with no running jobs (Projects 1 and 2 have jobs running)
+1. Job 2 is next, because, of projects with the lowest number of jobs running (each has 1), it is the lowest job number
+1. Job 5 is next, because Project 1 now has 2 jobs running, and between Projects 2 and 3, Job 5 is the lowest remaining job number
+1. Lastly we choose Job 3... because it's the only job left
---
@@ -139,23 +139,23 @@ With the fair usage algorithm jobs are assigned in following order:
We have following jobs in queue:
-- job 1 for project 1
-- job 2 for project 1
-- job 3 for project 1
-- job 4 for project 2
-- job 5 for project 2
-- job 6 for project 3
+- Job 1 for project 1
+- Job 2 for project 1
+- Job 3 for project 1
+- Job 4 for project 2
+- Job 5 for project 2
+- Job 6 for project 3
With the fair usage algorithm jobs are assigned in following order:
-1. We choose job 1, because project 1 doesn't run currently any jobs and has the lowest job number from projects that doesn't run jobs
+1. Job 1 is chosen first, because it has the lowest job number from projects with no running jobs (i.e. all projects)
1. We finish job 1
-1. We choose job 2, because project 1 doesn't run currently any jobs and has the lowest job number from projects that doesn't run jobs
-1. We choose job 4, because project 2 doesn't run currently any jobs and has the lowest job number from projects that doesn't run jobs
+1. Job 2 is next, because, having finished Job 1, all projects have 0 jobs running again, and 2 is the lowest available job number
+1. Job 4 is next, because with Project 1 running a job, 4 is the lowest number from projects running no jobs (Projects 2 and 3)
1. We finish job 4
-1. We choose job 5, because project 2 doesn't run currently any jobs and has the lowest job number from projects that doesn't run jobs
-1. We choose job 6, because project 3 doesn't run currently any jobs
-1. We choose job 3, because project 1, 2 and 3 runs exactly one job now
+1. Job 5 is next, because having finished Job 4, Project 2 has no jobs running again
+1. Job 6 is next, because Project 3 is the only project left with no running jobs
+1. Lastly we choose Job 3... because, again, it's the only job left (who says 1 is the loneliest number?)
## Using shared Runners effectively
diff --git a/doc/development/limit_ee_conflicts.md b/doc/development/limit_ee_conflicts.md
index 51b4b398f2c..899be9eae4b 100644
--- a/doc/development/limit_ee_conflicts.md
+++ b/doc/development/limit_ee_conflicts.md
@@ -166,8 +166,8 @@ For instance this kind of thing:
= render 'projects/zen', f: form, attr: :description,
classes: 'note-textarea',
placeholder: "Write a comment or drag your files here...",
- supports_slash_commands: !issuable.persisted?
- = render 'projects/notes/hints', supports_slash_commands: !issuable.persisted?
+ supports_quick_actions: !issuable.persisted?
+ = render 'projects/notes/hints', supports_quick_actions: !issuable.persisted?
.clearfix
.error-alert
- if issuable.is_a?(Issue)
diff --git a/doc/development/migration_style_guide.md b/doc/development/migration_style_guide.md
index 77ba2a5fd87..161d2544169 100644
--- a/doc/development/migration_style_guide.md
+++ b/doc/development/migration_style_guide.md
@@ -122,7 +122,7 @@ limit can vary from installation to installation. As a result it's recommended
you do not use more than 32 threads in a single migration. Usually 4-8 threads
should be more than enough.
-## Removing indices
+## Removing indexes
When removing an index make sure to use the method `remove_concurrent_index` instead
of the regular `remove_index` method. The `remove_concurrent_index` method
@@ -142,7 +142,7 @@ class MyMigration < ActiveRecord::Migration
end
```
-## Adding indices
+## Adding indexes
If you need to add a unique index please keep in mind there is the possibility
of existing duplicates being present in the database. This means that should
@@ -222,6 +222,41 @@ add_column_with_default(:projects, :foo, :integer, default: 10, limit: 8)
add_column(:projects, :foo, :integer, default: 10, limit: 8)
```
+## Timestamp column type
+
+By default, Rails uses the `timestamp` data type that stores timestamp data without timezone information.
+The `timestamp` data type is used by calling either the `add_timestamps` or the `timestamps` method.
+Also Rails converts the `:datetime` data type to the `timestamp` one.
+
+Example:
+
+```ruby
+# timestamps
+create_table :users do |t|
+ t.timestamps
+end
+
+# add_timestamps
+def up
+ add_timestamps :users
+end
+
+# :datetime
+def up
+ add_column :users, :last_sign_in, :datetime
+end
+```
+
+Instead of using these methods one should use the following methods to store timestamps with timezones:
+
+* `add_timestamps_with_timezone`
+* `timestamps_with_timezone`
+
+This ensures all timestamps have a time zone specified. This in turn means existing timestamps won't
+suddenly use a different timezone when the system's timezone changes. It also makes it very clear which
+timezone was used in the first place.
+
+
## Testing
Make sure that your migration works with MySQL and PostgreSQL with data. An
diff --git a/doc/development/testing.md b/doc/development/testing.md
index 6d8b846d27f..cf3ea2ccfc2 100644
--- a/doc/development/testing.md
+++ b/doc/development/testing.md
@@ -25,7 +25,7 @@ records should use stubs/doubles as much as possible.
| --------- | ---------- | -------------- | ----- |
| `app/finders/` | `spec/finders/` | RSpec | |
| `app/helpers/` | `spec/helpers/` | RSpec | |
-| `app/db/{post_,}migrate/` | `spec/migrations/` | RSpec | |
+| `app/db/{post_,}migrate/` | `spec/migrations/` | RSpec | More details at [`spec/migrations/README.md`](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/spec/migrations/README.md). |
| `app/policies/` | `spec/policies/` | RSpec | |
| `app/presenters/` | `spec/presenters/` | RSpec | |
| `app/routing/` | `spec/routing/` | RSpec | |
diff --git a/doc/install/kubernetes/index.md b/doc/install/kubernetes/index.md
index 88c56a1d17c..5ea08869a9b 100644
--- a/doc/install/kubernetes/index.md
+++ b/doc/install/kubernetes/index.md
@@ -1,7 +1,7 @@
# Installing GitLab on Kubernetes
> Officially supported cloud providers are Google Container Service and Azure Container Service.
-> Officially supported schedulers are Kubernetes and Terraform.
+> Officially supported schedulers are Kubernetes, Terraform and Tectonic.
The easiest method to deploy GitLab in [Kubernetes](https://kubernetes.io/) is
to take advantage of the official GitLab Helm charts. [Helm] is a package
diff --git a/doc/integration/chat_commands.md b/doc/integration/chat_commands.md
index c878dc7e650..2856992ee25 100644
--- a/doc/integration/chat_commands.md
+++ b/doc/integration/chat_commands.md
@@ -1,14 +1 @@
-# Chat Commands
-
-Chat commands in Mattermost and Slack (also called Slack slash commands) allow you to control GitLab and view GitLab content right inside your chat client, without having to leave it. For Slack, this requires a [project service configuration](../user/project/integrations/slack_slash_commands.md). Simply type the command as a message in your chat client to activate it.
-
-Commands are scoped to a project, with a trigger term that is specified during configuration. (We suggest you use the project name as the trigger term for simplicty and clarity.) Taking the trigger term as `project-name`, the commands are:
-
-
-| Command | Effect |
-| ------- | ------ |
-| `/project-name help` | Shows all available chat commands |
-| `/project-name issue new <title> <shift+return> <description>` | Creates a new issue with title `<title>` and description `<description>` |
-| `/project-name issue show <id>` | Shows the issue with id `<id>` |
-| `/project-name issue search <query>` | Shows up to 5 issues matching `<query>` |
-| `/project-name deploy <from> to <to>` | Deploy from the `<from>` environment to the `<to>` environment | \ No newline at end of file
+This document was moved to [integration/slash_commands.md](slash_commands.md).
diff --git a/doc/integration/slash_commands.md b/doc/integration/slash_commands.md
new file mode 100644
index 00000000000..5d880ba785c
--- /dev/null
+++ b/doc/integration/slash_commands.md
@@ -0,0 +1,14 @@
+# Slash Commands
+
+Slash commands in Mattermost and Slack allow you to control GitLab and view GitLab content right inside your chat client, without having to leave it. For Slack, this requires a [project service configuration](../user/project/integrations/slack_slash_commands.md). Simply type the command as a message in your chat client to activate it.
+
+Commands are scoped to a project, with a trigger term that is specified during configuration. (We suggest you use the project name as the trigger term for simplicty and clarity.) Taking the trigger term as `project-name`, the commands are:
+
+
+| Command | Effect |
+| ------- | ------ |
+| `/project-name help` | Shows all available slash commands |
+| `/project-name issue new <title> <shift+return> <description>` | Creates a new issue with title `<title>` and description `<description>` |
+| `/project-name issue show <id>` | Shows the issue with id `<id>` |
+| `/project-name issue search <query>` | Shows up to 5 issues matching `<query>` |
+| `/project-name deploy <from> to <to>` | Deploy from the `<from>` environment to the `<to>` environment |
diff --git a/doc/update/9.3-to-9.4.md b/doc/update/9.3-to-9.4.md
new file mode 100644
index 00000000000..a712ce5a8b1
--- /dev/null
+++ b/doc/update/9.3-to-9.4.md
@@ -0,0 +1,317 @@
+# From 9.3 to 9.4
+
+Make sure you view this update guide from the tag (version) of GitLab you would
+like to install. In most cases this should be the highest numbered production
+tag (without rc in it). You can select the tag in the version dropdown at the
+top left corner of GitLab (below the menu bar).
+
+If the highest number stable branch is unclear please check the
+[GitLab Blog](https://about.gitlab.com/blog/archives.html) for installation
+guide links by version.
+
+### 1. Stop server
+
+```bash
+sudo service gitlab stop
+```
+
+### 2. Backup
+
+```bash
+cd /home/git/gitlab
+
+sudo -u git -H bundle exec rake gitlab:backup:create RAILS_ENV=production
+```
+
+### 3. Update Ruby
+
+NOTE: GitLab 9.0 and higher only support Ruby 2.3.x and dropped support for Ruby 2.1.x. Be
+sure to upgrade your interpreter if necessary.
+
+You can check which version you are running with `ruby -v`.
+
+Download and compile Ruby:
+
+```bash
+mkdir /tmp/ruby && cd /tmp/ruby
+curl --remote-name --progress https://cache.ruby-lang.org/pub/ruby/2.3/ruby-2.3.3.tar.gz
+echo '1014ee699071aa2ddd501907d18cbe15399c997d ruby-2.3.3.tar.gz' | shasum -c - && tar xzf ruby-2.3.3.tar.gz
+cd ruby-2.3.3
+./configure --disable-install-rdoc
+make
+sudo make install
+```
+
+Install Bundler:
+
+```bash
+sudo gem install bundler --no-ri --no-rdoc
+```
+
+### 4. Update Node
+
+GitLab now runs [webpack](http://webpack.js.org) to compile frontend assets and
+it has a minimum requirement of node v4.3.0.
+
+You can check which version you are running with `node -v`. If you are running
+a version older than `v4.3.0` you will need to update to a newer version. You
+can find instructions to install from community maintained packages or compile
+from source at the nodejs.org website.
+
+<https://nodejs.org/en/download/>
+
+
+Since 8.17, GitLab requires the use of yarn `>= v0.17.0` to manage
+JavaScript dependencies.
+
+```bash
+curl --location https://yarnpkg.com/install.sh | bash -
+```
+
+More information can be found on the [yarn website](https://yarnpkg.com/en/docs/install).
+
+### 5. Update Go
+
+NOTE: GitLab 9.4 and higher only supports Go 1.8.3 and dropped support for Go 1.5.x through 1.7.x. Be
+sure to upgrade your installation if necessary
+
+You can check which version you are running with `go version`.
+
+Download and install Go:
+
+```bash
+# Remove former Go installation folder
+sudo rm -rf /usr/local/go
+
+curl --remote-name --progress https://storage.googleapis.com/golang/go1.8.3.linux-amd64.tar.gz
+echo '1862f4c3d3907e59b04a757cfda0ea7aa9ef39274af99a784f5be843c80c6772 go1.8.3.linux-amd64.tar.gz' | shasum -a256 -c - && \
+ sudo tar -C /usr/local -xzf go1.8.3.linux-amd64.tar.gz
+sudo ln -sf /usr/local/go/bin/{go,godoc,gofmt} /usr/local/bin/
+rm go1.8.3.linux-amd64.tar.gz
+```
+
+### 6. Get latest code
+
+```bash
+cd /home/git/gitlab
+
+sudo -u git -H git fetch --all
+sudo -u git -H git checkout -- db/schema.rb # local changes will be restored automatically
+```
+
+For GitLab Community Edition:
+
+```bash
+cd /home/git/gitlab
+
+sudo -u git -H git checkout 9-4-stable
+```
+
+OR
+
+For GitLab Enterprise Edition:
+
+```bash
+cd /home/git/gitlab
+
+sudo -u git -H git checkout 9-4-stable-ee
+```
+
+### 5. Update gitlab-shell
+
+```bash
+cd /home/git/gitlab-shell
+
+sudo -u git -H git fetch --all --tags
+sudo -u git -H git checkout v$(</home/git/gitlab/GITLAB_SHELL_VERSION)
+sudo -u git -H bin/compile
+```
+
+### 6. Update gitlab-workhorse
+
+Install and compile gitlab-workhorse. This requires
+[Go 1.8](https://golang.org/dl) which should already be on your system from
+GitLab 8.1. GitLab-Workhorse uses [GNU Make](https://www.gnu.org/software/make/).
+If you are not using Linux you may have to run `gmake` instead of
+`make` below.
+
+```bash
+cd /home/git/gitlab-workhorse
+
+sudo -u git -H git fetch --all --tags
+sudo -u git -H git checkout v$(</home/git/gitlab/GITLAB_WORKHORSE_VERSION)
+sudo -u git -H make
+```
+
+### 7. Update Gitaly
+
+If you have not yet set up Gitaly then follow [Gitaly section of the installation
+guide](../install/installation.md#install-gitaly).
+
+#### Check Gitaly configuration
+
+Due to a bug in the `rake gitlab:gitaly:install` script your Gitaly
+configuration file may contain syntax errors. The block name
+`[[storages]]`, which may occur more than once in your `config.toml`
+file, should be `[[storage]]` instead.
+
+```shell
+cd /home/git/gitaly
+sudo -u git -H editor config.toml
+```
+
+#### Compile Gitaly
+
+```shell
+cd /home/git/gitaly
+sudo -u git -H git fetch --all --tags
+sudo -u git -H git checkout v$(</home/git/gitlab/GITALY_SERVER_VERSION)
+sudo -u git -H make
+```
+
+### 10. Update configuration files
+
+#### New configuration options for `gitlab.yml`
+
+There might be configuration options available for [`gitlab.yml`][yaml]. View them with the command below and apply them manually to your current `gitlab.yml`:
+
+```sh
+cd /home/git/gitlab
+
+git diff origin/9-3-stable:config/gitlab.yml.example origin/9-4-stable:config/gitlab.yml.example
+```
+
+#### Nginx configuration
+
+Ensure you're still up-to-date with the latest NGINX configuration changes:
+
+```sh
+cd /home/git/gitlab
+
+# For HTTPS configurations
+git diff origin/9-3-stable:lib/support/nginx/gitlab-ssl origin/9-4-stable:lib/support/nginx/gitlab-ssl
+
+# For HTTP configurations
+git diff origin/9-3-stable:lib/support/nginx/gitlab origin/9-4-stable:lib/support/nginx/gitlab
+```
+
+If you are using Strict-Transport-Security in your installation to continue using it you must enable it in your Nginx
+configuration as GitLab application no longer handles setting it.
+
+If you are using Apache instead of NGINX please see the updated [Apache templates].
+Also note that because Apache does not support upstreams behind Unix sockets you
+will need to let gitlab-workhorse listen on a TCP port. You can do this
+via [/etc/default/gitlab].
+
+[Apache templates]: https://gitlab.com/gitlab-org/gitlab-recipes/tree/master/web-server/apache
+[/etc/default/gitlab]: https://gitlab.com/gitlab-org/gitlab-ce/blob/9-4-stable/lib/support/init.d/gitlab.default.example#L38
+
+#### SMTP configuration
+
+If you're installing from source and use SMTP to deliver mail, you will need to add the following line
+to config/initializers/smtp_settings.rb:
+
+```ruby
+ActionMailer::Base.delivery_method = :smtp
+```
+
+See [smtp_settings.rb.sample] as an example.
+
+[smtp_settings.rb.sample]: https://gitlab.com/gitlab-org/gitlab-ce/blob/9-3-stable/config/initializers/smtp_settings.rb.sample#L13
+
+#### Init script
+
+There might be new configuration options available for [`gitlab.default.example`][gl-example]. View them with the command below and apply them manually to your current `/etc/default/gitlab`:
+
+```sh
+cd /home/git/gitlab
+
+git diff origin/9-3-stable:lib/support/init.d/gitlab.default.example origin/9-4-stable:lib/support/init.d/gitlab.default.example
+```
+
+Ensure you're still up-to-date with the latest init script changes:
+
+```bash
+cd /home/git/gitlab
+
+sudo cp lib/support/init.d/gitlab /etc/init.d/gitlab
+```
+
+For Ubuntu 16.04.1 LTS:
+
+```bash
+sudo systemctl daemon-reload
+```
+
+### 11. Install libs, migrations, etc.
+
+```bash
+cd /home/git/gitlab
+
+# MySQL installations (note: the line below states '--without postgres')
+sudo -u git -H bundle install --without postgres development test --deployment
+
+# PostgreSQL installations (note: the line below states '--without mysql')
+sudo -u git -H bundle install --without mysql development test --deployment
+
+# Optional: clean up old gems
+sudo -u git -H bundle clean
+
+# Run database migrations
+sudo -u git -H bundle exec rake db:migrate RAILS_ENV=production
+
+# Update node dependencies and recompile assets
+sudo -u git -H bundle exec rake yarn:install gitlab:assets:clean gitlab:assets:compile RAILS_ENV=production NODE_ENV=production
+
+# Clean up cache
+sudo -u git -H bundle exec rake cache:clear RAILS_ENV=production
+```
+
+**MySQL installations**: Run through the `MySQL strings limits` and `Tables and data conversion to utf8mb4` [tasks](../install/database_mysql.md).
+
+### 12. Start application
+
+```bash
+sudo service gitlab start
+sudo service nginx restart
+```
+
+### 13. Check application status
+
+Check if GitLab and its environment are configured correctly:
+
+```bash
+cd /home/git/gitlab
+
+sudo -u git -H bundle exec rake gitlab:env:info RAILS_ENV=production
+```
+
+To make sure you didn't miss anything run a more thorough check:
+
+```bash
+cd /home/git/gitlab
+
+sudo -u git -H bundle exec rake gitlab:check RAILS_ENV=production
+```
+
+If all items are green, then congratulations, the upgrade is complete!
+
+## Things went south? Revert to previous version (9.3)
+
+### 1. Revert the code to the previous version
+
+Follow the [upgrade guide from 9.2 to 9.3](9.2-to-9.3.md), except for the
+database migration (the backup is already migrated to the previous version).
+
+### 2. Restore from the backup
+
+```bash
+cd /home/git/gitlab
+
+sudo -u git -H bundle exec rake gitlab:backup:restore RAILS_ENV=production
+```
+
+If you have more than one backup `*.tar` file(s) please add `BACKUP=timestamp_of_backup` to the command above.
+
+[yaml]: https://gitlab.com/gitlab-org/gitlab-ce/blob/9-4-stable/config/gitlab.yml.example
+[gl-example]: https://gitlab.com/gitlab-org/gitlab-ce/blob/9-4-stable/lib/support/init.d/gitlab.default.example
diff --git a/doc/user/admin_area/monitoring/convdev.md b/doc/user/admin_area/monitoring/convdev.md
new file mode 100644
index 00000000000..3d93c7557a4
--- /dev/null
+++ b/doc/user/admin_area/monitoring/convdev.md
@@ -0,0 +1,29 @@
+# Conversational Development Index
+
+> [Introduced][ce-30469] in GitLab 9.3.
+
+Conversational Development Index (ConvDev) gives you an overview of your entire
+instance's feature usage, from idea to production. It looks at your usage in the
+past 30 days, averaged over the number of active users in that time period. It also
+provides a lead score per feature, which is calculated based on GitLab's analysis
+of top performing instances, based on [usage ping data][ping] that GitLab has
+collected. Your score is compared to the lead score, expressed as a percentage.
+The overall index score is an average over all your feature scores.
+
+![ConvDev index](img/convdev_index.png)
+
+The page also provides helpful links to articles and GitLab docs, to help you
+improve your scores.
+
+Your GitLab instance's usage ping must be activated in order to use this feature.
+Usage ping data is aggregated on GitLab's servers for analysis. Your usage
+information is **not sent** to any other GitLab instances.
+
+If you have just started using GitLab, it may take a few weeks for data to be
+collected before this feature is available.
+
+This feature is accessible only to a system admin, at
+**Admin area > Monitoring > ConvDev Index**.
+
+[ce-30469]: https://gitlab.com/gitlab-org/gitlab-ce/issues/30469
+[ping]: ../settings/usage_statistics.md#usage-ping
diff --git a/doc/user/admin_area/monitoring/img/convdev_index.png b/doc/user/admin_area/monitoring/img/convdev_index.png
new file mode 100644
index 00000000000..4e47ff2228d
--- /dev/null
+++ b/doc/user/admin_area/monitoring/img/convdev_index.png
Binary files differ
diff --git a/doc/user/admin_area/settings/usage_statistics.md b/doc/user/admin_area/settings/usage_statistics.md
index f3745d0efa7..d874688cc29 100644
--- a/doc/user/admin_area/settings/usage_statistics.md
+++ b/doc/user/admin_area/settings/usage_statistics.md
@@ -3,7 +3,8 @@
GitLab Inc. will periodically collect information about your instance in order
to perform various actions.
-All statistics are opt-out, you can disable them from the admin panel.
+All statistics are opt-out, you can enable/disable them from the admin panel
+under **Admin area > Settings > Usage statistics**.
## Version check
diff --git a/doc/user/discussions/index.md b/doc/user/discussions/index.md
index 59e343ebe51..8b1d299484c 100644
--- a/doc/user/discussions/index.md
+++ b/doc/user/discussions/index.md
@@ -10,7 +10,7 @@ You can leave a comment in the following places:
- commits
- commit diffs
-The comment area supports [Markdown] and [slash commands]. One can edit their
+The comment area supports [Markdown] and [quick actions]. One can edit their
own comment at any time, and anyone with [Master access level][permissions] or
higher can also edit a comment made by someone else.
@@ -146,5 +146,5 @@ comments in greater detail.
[discussion-view]: img/discussion_view.png
[discussions-resolved]: img/discussions_resolved.png
[markdown]: ../markdown.md
-[slash commands]: ../project/slash_commands.md
+[quick actions]: ../project/quick_actions.md
[permissions]: ../permissions.md
diff --git a/doc/user/permissions.md b/doc/user/permissions.md
index 3fda47b9e34..3d47e644ad2 100644
--- a/doc/user/permissions.md
+++ b/doc/user/permissions.md
@@ -89,6 +89,7 @@ group.
| Create project in group | | | | ✓ | ✓ |
| Manage group members | | | | | ✓ |
| Remove group | | | | | ✓ |
+| Manage group labels | | ✓ | ✓ | ✓ | ✓ |
## External Users
diff --git a/doc/user/profile/account/two_factor_authentication.md b/doc/user/profile/account/two_factor_authentication.md
index fb69d934ae1..590c3f862fb 100644
--- a/doc/user/profile/account/two_factor_authentication.md
+++ b/doc/user/profile/account/two_factor_authentication.md
@@ -125,23 +125,14 @@ applications and U2F devices.
## Personal access tokens
When 2FA is enabled, you can no longer use your normal account password to
-authenticate with Git over HTTPS on the command line, you must use a personal
-access token instead.
-
-1. Log in to your GitLab account.
-1. Go to your **Profile Settings**.
-1. Go to **Access Tokens**.
-1. Choose a name and expiry date for the token.
-1. Click on **Create Personal Access Token**.
-1. Save the personal access token somewhere safe.
-
-When using Git over HTTPS on the command line, enter the personal access token
-into the password field.
+authenticate with Git over HTTPS on the command line or when using
+[GitLab's API][api], you must use a [personal access token][pat] instead.
## Recovery options
To disable two-factor authentication on your account (for example, if you
have lost your code generation device) you can:
+
* [Use a saved recovery code](#use-a-saved-recovery-code)
* [Generate new recovery codes using SSH](#generate-new-recovery-codes-using-ssh)
* [Ask a GitLab administrator to disable two-factor authentication on your account](#ask-a-gitlab-administrator-to-disable-two-factor-authentication-on-your-account)
@@ -154,8 +145,9 @@ codes. If you saved these codes, you can use one of them to sign in.
To use a recovery code, enter your username/email and password on the GitLab
sign-in page. When prompted for a two-factor code, enter the recovery code.
-> **Note:** Once you use a recovery code, you cannot re-use it. You can still
- use the other recovery codes you saved.
+>**Note:**
+Once you use a recovery code, you cannot re-use it. You can still use the other
+recovery codes you saved.
### Generate new recovery codes using SSH
@@ -190,11 +182,14 @@ a new set of recovery codes with SSH.
two-factor code. Then, visit your Profile Settings and add a new device
so you do not lose access to your account again.
```
-3. Go to the GitLab sign-in page and enter your username/email and password. When prompted for a two-factor code, enter one of the recovery codes obtained
-from the command-line output.
-> **Note:** After signing in, visit your **Profile Settings -> Account** immediately to set up two-factor authentication with a new
- device.
+3. Go to the GitLab sign-in page and enter your username/email and password.
+ When prompted for a two-factor code, enter one of the recovery codes obtained
+ from the command-line output.
+
+>**Note:**
+After signing in, visit your **Profile settings > Account** immediately to set
+up two-factor authentication with a new device.
### Ask a GitLab administrator to disable two-factor authentication on your account
@@ -206,23 +201,23 @@ Sign in and re-enable two-factor authentication as soon as possible.
## Note to GitLab administrators
- You need to take special care to that 2FA keeps working after
-[restoring a GitLab backup](../../../raketasks/backup_restore.md).
-
+ [restoring a GitLab backup](../../../raketasks/backup_restore.md).
- To ensure 2FA authorizes correctly with TOTP server, you may want to ensure
-your GitLab server's time is synchronized via a service like NTP. Otherwise,
-you may have cases where authorization always fails because of time differences.
-
-[Google Authenticator]: https://support.google.com/accounts/answer/1066447?hl=en
-[FreeOTP]: https://freeotp.github.io/
-[YubiKey]: https://www.yubico.com/products/yubikey-hardware/
-
+ your GitLab server's time is synchronized via a service like NTP. Otherwise,
+ you may have cases where authorization always fails because of time differences.
- The GitLab U2F implementation does _not_ work when the GitLab instance is accessed from
-multiple hostnames, or FQDNs. Each U2F registration is linked to the _current hostname_ at
-the time of registration, and cannot be used for other hostnames/FQDNs.
+ multiple hostnames, or FQDNs. Each U2F registration is linked to the _current hostname_ at
+ the time of registration, and cannot be used for other hostnames/FQDNs.
For example, if a user is trying to access a GitLab instance from `first.host.xyz` and `second.host.xyz`:
- The user logs in via `first.host.xyz` and registers their U2F key.
- The user logs out and attempts to log in via `first.host.xyz` - U2F authentication suceeds.
- - The user logs out and attempts to log in via `second.host.xyz` - U2F authentication fails, because
+ - The user logs out and attempts to log in via `second.host.xyz` - U2F authentication fails, because
the U2F key has only been registered on `first.host.xyz`.
+
+[Google Authenticator]: https://support.google.com/accounts/answer/1066447?hl=en
+[FreeOTP]: https://freeotp.github.io/
+[YubiKey]: https://www.yubico.com/products/yubikey-hardware/
+[api]: ../../../api/README.md
+[pat]: ../personal_access_tokens.md
diff --git a/doc/user/profile/img/personal_access_tokens.png b/doc/user/profile/img/personal_access_tokens.png
new file mode 100644
index 00000000000..6aa63dbe342
--- /dev/null
+++ b/doc/user/profile/img/personal_access_tokens.png
Binary files differ
diff --git a/doc/user/profile/personal_access_tokens.md b/doc/user/profile/personal_access_tokens.md
new file mode 100644
index 00000000000..9488ce1ef30
--- /dev/null
+++ b/doc/user/profile/personal_access_tokens.md
@@ -0,0 +1,57 @@
+# Personal access tokens
+
+> [Introduced][ce-3749] in GitLab 8.8.
+
+Personal access tokens are useful if you need access to the [GitLab API][api].
+Instead of using your private token which grants full access to your account,
+personal access tokens could be a better fit because of their
+[granular permissions](#limiting-scopes-of-a-personal-access-token).
+
+You can also use them to authenticate against Git over HTTP. They are the only
+accepted method of authentication when you have
+[Two-Factor Authentication (2FA)][2fa] enabled.
+
+Once you have your token, [pass it to the API][usage] using either the
+`private_token` parameter or the `PRIVATE-TOKEN` header.
+
+## Creating a personal access token
+
+You can create as many personal access tokens as you like from your GitLab
+profile.
+
+1. Log in to your GitLab account.
+1. Go to your **Profile settings**.
+1. Go to **Access tokens**.
+1. Choose a name and optionally an expiry date for the token.
+1. Choose the [desired scopes](#limiting-scopes-of-a-personal-access-token).
+1. Click on **Create personal access token**.
+1. Save the personal access token somewhere safe. Once you leave or refresh
+ the page, you won't be able to access it again.
+
+![Personal access tokens page](img/personal_access_tokens.png)
+
+## Revoking a personal access token
+
+At any time, you can revoke any personal access token by just clicking the
+respective **Revoke** button under the 'Active personal access tokens' area.
+
+## Limiting scopes of a personal access token
+
+Personal access tokens can be created with one or more scopes that allow various
+actions that a given token can perform. The available scopes are depicted in
+the following table.
+
+| Scope | Description |
+| ----- | ----------- |
+|`read_user` | Allows access to the read-only endpoints under `/users`. Essentially, any of the `GET` requests in the [Users API][users] are allowed ([introduced][ce-5951] in GitLab 8.15). |
+| `api` | Grants complete access to the API (read/write) ([introduced][ce-5951] in GitLab 8.15). Required for accessing Git repositories over HTTP when 2FA is enabled. |
+| `read_registry` | Allows to read [container registry] images if a project is private and authorization is required ([introduced][ce-11845] in GitLab 9.3). |
+
+[2fa]: ../account/two_factor_authentication.md
+[api]: ../../api/README.md
+[ce-3749]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/3749
+[ce-5951]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/5951
+[ce-11845]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/11845
+[container registry]: ../project/container_registry.md
+[users]: ../../api/users.md
+[usage]: ../../api/README.md#basic-usage
diff --git a/doc/user/project/container_registry.md b/doc/user/project/container_registry.md
index 75ea911b9bc..629d69d8aea 100644
--- a/doc/user/project/container_registry.md
+++ b/doc/user/project/container_registry.md
@@ -8,8 +8,8 @@
Registry across your GitLab instance, visit the
[administrator documentation](../../administration/container_registry.md).
- Starting from GitLab 8.12, if you have 2FA enabled in your account, you need
- to pass a personal access token instead of your password in order to login to
- GitLab's Container Registry.
+ to pass a [personal access token][pat] instead of your password in order to
+ login to GitLab's Container Registry.
- Multiple level image names support was added in GitLab 9.1
With the Docker Container Registry integrated into GitLab, every project can
@@ -114,12 +114,11 @@ and [Using the GitLab Container Registry documentation](../../ci/docker/using_do
## Using with private projects
-If a project is private, credentials will need to be provided for authorization.
-The preferred way to do this, is by using personal access tokens, which can be
-created under `/profile/personal_access_tokens`. The minimal scope needed is:
-`read_registry`.
+> [Introduced][ce-11845] in GitLab 9.3.
-This feature was introduced in GitLab 9.3.
+If a project is private, credentials will need to be provided for authorization.
+The preferred way to do this, is by using [personal access tokens][pat].
+The minimal scope needed is `read_registry`.
## Troubleshooting the GitLab Container Registry
@@ -264,4 +263,6 @@ The solution: check the [IAM permissions again](https://docs.docker.com/registry
Once the right permissions were set, the error will go away.
[ce-4040]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/4040
+[ce-11845]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/11845
[docker-docs]: https://docs.docker.com/engine/userguide/intro/
+[pat]: ../profile/personal_access_tokens.md
diff --git a/doc/user/project/integrations/img/jira_service_page.png b/doc/user/project/integrations/img/jira_service_page.png
index c74351b57b8..e69376f74c4 100644
--- a/doc/user/project/integrations/img/jira_service_page.png
+++ b/doc/user/project/integrations/img/jira_service_page.png
Binary files differ
diff --git a/doc/user/project/integrations/img/merge_request_performance.png b/doc/user/project/integrations/img/merge_request_performance.png
index 93b2626fed7..eba6515a6ae 100644
--- a/doc/user/project/integrations/img/merge_request_performance.png
+++ b/doc/user/project/integrations/img/merge_request_performance.png
Binary files differ
diff --git a/doc/user/project/integrations/jira.md b/doc/user/project/integrations/jira.md
index a048260b033..cf03f2a9033 100644
--- a/doc/user/project/integrations/jira.md
+++ b/doc/user/project/integrations/jira.md
@@ -76,7 +76,7 @@ We have split this stage in steps so it is easier to follow.
![JIRA add user to group](img/jira_add_user_to_group.png)
----
+ ---
The JIRA configuration is over. Write down the new JIRA username and its
password as they will be needed when configuring GitLab in the next section.
@@ -98,14 +98,14 @@ in the table below.
| Field | Description |
| ----- | ----------- |
| `Web URL` | The base URL to the JIRA instance web interface which is being linked to this GitLab project. E.g., `https://jira.example.com`. |
-| `JIRA API URL` | The base URL to the JIRA instance API. Web URL value will be used if not set. E.g., `https://jira-api.example.com`. |
-| `Project key` | The short identifier for your JIRA project, all uppercase, e.g., `PROJ`. |
+| `JIRA API URL` | The base URL to the JIRA instance API. E.g., `https://jira-api.example.com`. This is optional. If not entered, the Web URL value be used. |
+| `Project key` | Put a JIRA project key (in uppercase), e.g. `MARS` in this field. This is only for testing the configuration settings. JIRA integration in GitLab works with _all_ JIRA projects in your JIRA instance. This field will be removed in a future release. |
| `Username` | The user name created in [configuring JIRA step](#configuring-jira). |
| `Password` |The password of the user created in [configuring JIRA step](#configuring-jira). |
| `JIRA issue transition` | This is the ID of a transition that moves issues to a closed state. You can find this number under JIRA workflow administration ([see screenshot](img/jira_workflow_screenshot.png)). **Closing JIRA issues via commits or Merge Requests won't work if you don't set the ID correctly.** |
After saving the configuration, your GitLab project will be able to interact
-with the linked JIRA project.
+with all JIRA projects in your JIRA instance.
![JIRA service page](img/jira_service_page.png)
diff --git a/doc/user/project/integrations/prometheus.md b/doc/user/project/integrations/prometheus.md
index d3fb5916dc6..86ceb14b965 100644
--- a/doc/user/project/integrations/prometheus.md
+++ b/doc/user/project/integrations/prometheus.md
@@ -167,15 +167,15 @@ environment which has had a successful deployment.
## Determining the performance impact of a merge
> [Introduced][ce-10408] in GitLab 9.2.
+> GitLab 9.3 added the [numeric comparison](https://gitlab.com/gitlab-org/gitlab-ce/issues/27439) of the 30 minute averages.
Developers can view the performance impact of their changes within the merge
-request workflow. When a source branch has been deployed to an environment, a
-sparkline will appear showing the average memory consumption of the app. The dot
+request workflow. When a source branch has been deployed to an environment, a sparkline and numeric comparison of the average memory consumption will appear. On the sparkline, a dot
indicates when the current changes were deployed, with up to 30 minutes of
-performance data displayed before and after. The sparkline will be updated after
+performance data displayed before and after. The comparison shows the difference between the 30 minute average before and after the deployment. This information is updated after
each commit has been deployed.
-Once merged and the target branch has been redeployed, the sparkline will switch
+Once merged and the target branch has been redeployed, the metrics will switch
to show the new environments this revision has been deployed to.
Performance data will be available for the duration it is persisted on the
diff --git a/doc/user/project/integrations/slack_slash_commands.md b/doc/user/project/integrations/slack_slash_commands.md
index 54e0ee611cb..c267da69bb3 100644
--- a/doc/user/project/integrations/slack_slash_commands.md
+++ b/doc/user/project/integrations/slack_slash_commands.md
@@ -2,7 +2,7 @@
> Introduced in GitLab 8.15
-Slack slash commands (also known as chat commmands) allow you to control GitLab and view content right inside Slack, without having to leave it. This requires configurations in both Slack and GitLab.
+Slack slash commands allow you to control GitLab and view content right inside Slack, without having to leave it. This requires configurations in both Slack and GitLab.
> Note: GitLab can also send events (e.g. issue created) to Slack as notifications. This is the separately configured [Slack Notifications Service](slack.md).
@@ -20,4 +20,4 @@ Slack slash commands (also known as chat commmands) allow you to control GitLab
## Usage
-You can now use the [Slack slash commands](../../../integration/chat_commands.md). \ No newline at end of file
+You can now use the [Slack slash commands](../../../integration/slash_commands.md).
diff --git a/doc/user/project/issue_board.md b/doc/user/project/issue_board.md
index 5aa8337b75d..ebea7062ecb 100644
--- a/doc/user/project/issue_board.md
+++ b/doc/user/project/issue_board.md
@@ -31,10 +31,11 @@ Below is a table of the definitions used for GitLab's Issue Board.
| **Card** | Every card represents an issue and it is shown under the list for which it has a label. The information you can see on a card consists of the issue number, the issue title, the assignee and the labels associated with it. You can drag cards around from one list to another. You can re-order cards within a list. |
There are two types of lists, the ones you create based on your labels, and
-one default:
+two defaults:
- Label list: a list based on a label. It shows all opened issues with that label.
-- **Done** (default): shows all closed issues. Always appears on the very right.
+- **Backlog** (default): shows all open issues that does not belong to one of lists. Always appears on the very left.
+- **Closed** (default): shows all closed issues. Always appears on the very right.
![GitLab Issue Board](img/issue_board.png)
diff --git a/doc/user/project/issues/confidential_issues.md b/doc/user/project/issues/confidential_issues.md
index 1760b182114..208be7d0ed5 100644
--- a/doc/user/project/issues/confidential_issues.md
+++ b/doc/user/project/issues/confidential_issues.md
@@ -43,9 +43,8 @@ next to the issues that are marked as confidential.
---
-Likewise, while inside the issue, you can see the eye-slash icon right next to
-the issue number, but there is also an indicator in the comment area that the
-issue you are commenting on is confidential.
+While inside the issue, you can see a persistent dark banner at the top of the
+screen.
![Confidential issue page](img/confidential_issues_issue_page.png)
diff --git a/doc/user/project/issues/img/confidential_issues_issue_page.png b/doc/user/project/issues/img/confidential_issues_issue_page.png
index f04ec8ff32b..91f7cc8d3ca 100755
--- a/doc/user/project/issues/img/confidential_issues_issue_page.png
+++ b/doc/user/project/issues/img/confidential_issues_issue_page.png
Binary files differ
diff --git a/doc/user/project/issues/issues_functionalities.md b/doc/user/project/issues/issues_functionalities.md
index ba843201e1a..294176e61f9 100644
--- a/doc/user/project/issues/issues_functionalities.md
+++ b/doc/user/project/issues/issues_functionalities.md
@@ -68,7 +68,7 @@ This feature is available only in [GitLab Enterprise Edition](https://about.gitl
- Spend: add the time spent on the implementation of that issue
> **Note:**
-both estimate and spend times are set via [GitLab Slash Commands](../slash_commands.md).
+both estimate and spend times are set via [GitLab Quick Actions](../quick_actions.md).
Learn more on the [Time Tracking documentation](https://docs.gitlab.com/ee/workflow/time_tracking.html).
@@ -147,7 +147,7 @@ or in the issue thread.
#### 15. Award emoji
-- Award an emoji to that issue.
+- Award an emoji to that issue.
> **Tip:**
Posting "+1" as comments in threads spam all
diff --git a/doc/user/project/new_ci_build_permissions_model.md b/doc/user/project/new_ci_build_permissions_model.md
index e9512497d6c..271adee7da1 100644
--- a/doc/user/project/new_ci_build_permissions_model.md
+++ b/doc/user/project/new_ci_build_permissions_model.md
@@ -212,9 +212,9 @@ Container Registries for private projects.
access token created explicitly for this purpose). This issue is resolved with
latest changes in GitLab Runner 1.8 which receives GitLab credentials with
build data.
-- Starting with GitLab 8.12, if you have 2FA enabled in your account, you need
- to pass a personal access token instead of your password in order to login to
- GitLab's Container Registry.
+- Starting from GitLab 8.12, if you have [2FA] enabled in your account, you need
+ to pass a [personal access token][pat] instead of your password in order to
+ login to GitLab's Container Registry.
Your jobs can access all container images that you would normally have access
to. The only implication is that you can push to the Container Registry of the
@@ -239,3 +239,5 @@ test:
[update-docs]: https://gitlab.com/gitlab-org/gitlab-ce/tree/master/doc/update
[workhorse]: https://gitlab.com/gitlab-org/gitlab-workhorse
[jobenv]: ../../ci/variables/README.md#predefined-variables-environment-variables
+[2fa]: ../profile/account/two_factor_authentication.md
+[pat]: ../profile/personal_access_tokens.md
diff --git a/doc/user/project/quick_actions.md b/doc/user/project/quick_actions.md
new file mode 100644
index 00000000000..19b51c83222
--- /dev/null
+++ b/doc/user/project/quick_actions.md
@@ -0,0 +1,39 @@
+# GitLab quick actions
+
+Quick actions are textual shortcuts for common actions on issues or merge
+requests that are usually done by clicking buttons or dropdowns in GitLab's UI.
+You can enter these commands while creating a new issue or merge request, and
+in comments. Each command should be on a separate line in order to be properly
+detected and executed. The commands are removed from the issue, merge request or
+comment body before it is saved and will not be visible to anyone else.
+
+Below is a list of all of the available commands and descriptions about what they
+do.
+
+| Command | Action |
+|:---------------------------|:-------------|
+| `/close` | Close the issue or merge request |
+| `/reopen` | Reopen the issue or merge request |
+| `/merge` | Merge (when pipeline succeeds) |
+| `/title <New title>` | Change title |
+| `/assign @username` | Assign |
+| `/unassign` | Remove assignee |
+| `/milestone %milestone` | Set milestone |
+| `/remove_milestone` | Remove milestone |
+| `/label ~foo ~"bar baz"` | Add label(s) |
+| `/unlabel ~foo ~"bar baz"` | Remove all or specific label(s) |
+| `/relabel ~foo ~"bar baz"` | Replace all label(s) |
+| `/todo` | Add a todo |
+| `/done` | Mark todo as done |
+| `/subscribe` | Subscribe |
+| `/unsubscribe` | Unsubscribe |
+| <code>/due &lt;in 2 days &#124; this Friday &#124; December 31st&gt;</code> | Set due date |
+| `/remove_due_date` | Remove due date |
+| `/wip` | Toggle the Work In Progress status |
+| <code>/estimate &lt;1w 3d 2h 14m&gt;</code> | Set time estimate |
+| `/remove_estimate` | Remove estimated time |
+| <code>/spend &lt;1h 30m &#124; -1h 5m&gt;</code> | Add or subtract spent time |
+| `/remove_time_spent` | Remove time spent |
+| `/target_branch <Branch Name>` | Set target branch for current merge request |
+| `/award :emoji:` | Toggle award for :emoji: |
+| `/board_move ~column` | Move issue to column on the board |
diff --git a/doc/user/project/repository/branches/img/delete_merged_branches.png b/doc/user/project/repository/branches/img/delete_merged_branches.png
new file mode 100644
index 00000000000..1856a624f74
--- /dev/null
+++ b/doc/user/project/repository/branches/img/delete_merged_branches.png
Binary files differ
diff --git a/doc/user/project/repository/branches/index.md b/doc/user/project/repository/branches/index.md
new file mode 100644
index 00000000000..1948627ee79
--- /dev/null
+++ b/doc/user/project/repository/branches/index.md
@@ -0,0 +1,17 @@
+# Branches
+
+## Delete merged branches
+
+> [Introduced][ce-6449] in GitLab 8.14.
+
+![Delete merged branches](img/delete_merged_branches.png)
+
+This feature allows merged branches to be deleted in bulk. Only branches that
+have been merged and [are not protected][protected] will be deleted as part of
+this operation.
+
+It's particularly useful to clean up old branches that were not deleting
+automatically when a merge request was merged.
+
+[ce-6449]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/6449 "Add button to delete all merged branches"
+[protected]: ../../protected_branches.md
diff --git a/doc/user/project/settings/import_export.md b/doc/user/project/settings/import_export.md
index 58d2fd76c61..35960ade3d4 100644
--- a/doc/user/project/settings/import_export.md
+++ b/doc/user/project/settings/import_export.md
@@ -27,14 +27,15 @@ with all their related data and be moved into a new GitLab instance.
| GitLab version | Import/Export version |
| -------- | -------- |
-| 9.2.0 to current | 0.1.7 |
-| 8.17.0 | 0.1.6 |
-| 8.13.0 | 0.1.5 |
-| 8.12.0 | 0.1.4 |
-| 8.10.3 | 0.1.3 |
-| 8.10.0 | 0.1.2 |
-| 8.9.5 | 0.1.1 |
-| 8.9.0 | 0.1.0 |
+| 9.4.0 to current | 0.1.8 |
+| 9.2.0 | 0.1.7 |
+| 8.17.0 | 0.1.6 |
+| 8.13.0 | 0.1.5 |
+| 8.12.0 | 0.1.4 |
+| 8.10.3 | 0.1.3 |
+| 8.10.0 | 0.1.2 |
+| 8.9.5 | 0.1.1 |
+| 8.9.0 | 0.1.0 |
> The table reflects what GitLab version we updated the Import/Export version at.
> For instance, 8.10.3 and 8.11 will have the same Import/Export version (0.1.3)
diff --git a/doc/user/project/slash_commands.md b/doc/user/project/slash_commands.md
index 08452ca75cd..e9103a3f49c 100644
--- a/doc/user/project/slash_commands.md
+++ b/doc/user/project/slash_commands.md
@@ -1,39 +1 @@
-# GitLab slash commands
-
-Slash commands are textual shortcuts for common actions on issues or merge
-requests that are usually done by clicking buttons or dropdowns in GitLab's UI.
-You can enter these commands while creating a new issue or merge request, and
-in comments. Each command should be on a separate line in order to be properly
-detected and executed. The commands are removed from the issue, merge request or
-comment body before it is saved and will not be visible to anyone else.
-
-Below is a list of all of the available commands and descriptions about what they
-do.
-
-| Command | Action |
-|:---------------------------|:-------------|
-| `/close` | Close the issue or merge request |
-| `/reopen` | Reopen the issue or merge request |
-| `/merge` | Merge (when pipeline succeeds) |
-| `/title <New title>` | Change title |
-| `/assign @username` | Assign |
-| `/unassign` | Remove assignee |
-| `/milestone %milestone` | Set milestone |
-| `/remove_milestone` | Remove milestone |
-| `/label ~foo ~"bar baz"` | Add label(s) |
-| `/unlabel ~foo ~"bar baz"` | Remove all or specific label(s) |
-| `/relabel ~foo ~"bar baz"` | Replace all label(s) |
-| `/todo` | Add a todo |
-| `/done` | Mark todo as done |
-| `/subscribe` | Subscribe |
-| `/unsubscribe` | Unsubscribe |
-| <code>/due &lt;in 2 days &#124; this Friday &#124; December 31st&gt;</code> | Set due date |
-| `/remove_due_date` | Remove due date |
-| `/wip` | Toggle the Work In Progress status |
-| <code>/estimate &lt;1w 3d 2h 14m&gt;</code> | Set time estimate |
-| `/remove_estimate` | Remove estimated time |
-| <code>/spend &lt;1h 30m &#124; -1h 5m&gt;</code> | Add or subtract spent time |
-| `/remove_time_spent` | Remove time spent |
-| `/target_branch <Branch Name>` | Set target branch for current merge request |
-| `/award :emoji:` | Toggle award for :emoji: |
-| `/board_move ~column` | Move issue to column on the board |
+This document was moved to [user/project/quick_actions.md](quick_actions.md).
diff --git a/doc/workflow/README.md b/doc/workflow/README.md
index 604c7d5cefb..54d4028a50a 100644
--- a/doc/workflow/README.md
+++ b/doc/workflow/README.md
@@ -21,7 +21,7 @@
- [Project users](add-user/add-user.md)
- [Protected branches](../user/project/protected_branches.md)
- [Protected tags](../user/project/protected_tags.md)
-- [Slash commands](../user/project/slash_commands.md)
+- [Quick Actions](../user/project/quick_actions.md)
- [Sharing a project with a group](share_with_group.md)
- [Share projects with other groups](share_projects_with_other_groups.md)
- [Time tracking](time_tracking.md)
diff --git a/doc/workflow/time_tracking.md b/doc/workflow/time_tracking.md
index de12994c516..bfe87bb2ceb 100644
--- a/doc/workflow/time_tracking.md
+++ b/doc/workflow/time_tracking.md
@@ -21,13 +21,13 @@ below.
## How to enter data
-Time Tracking uses two [slash commands] that GitLab introduced with this new
+Time Tracking uses two [quick actions] that GitLab introduced with this new
feature: `/spend` and `/estimate`.
-Slash commands can be used in the body of an issue or a merge request, but also
+Quick actions can be used in the body of an issue or a merge request, but also
in a comment in both an issue or a merge request.
-Below is an example of how you can use those new slash commands inside a comment.
+Below is an example of how you can use those new quick actions inside a comment.
![Time tracking example in a comment](time-tracking/time-tracking-example.png)
@@ -70,4 +70,4 @@ The following time units are available:
Default conversion rates are 1w = 5d and 1d = 8h.
[landing]: https://about.gitlab.com/features/time-tracking
-[slash-commands]: ../user/project/slash_commands.md
+[quick actions]: ../user/project/quick_actions.md
diff --git a/features/steps/project/issues/award_emoji.rb b/features/steps/project/issues/award_emoji.rb
index dfd0bc13305..2324edda975 100644
--- a/features/steps/project/issues/award_emoji.rb
+++ b/features/steps/project/issues/award_emoji.rb
@@ -34,8 +34,8 @@ class Spinach::Features::AwardEmoji < Spinach::FeatureSteps
page.within '.awards' do
expect do
page.find('.js-emoji-btn.active').click
- sleep 0.3
- end.to change{ page.all(".award-control.js-emoji-btn").size }.from(3).to(2)
+ wait_for_requests
+ end.to change { page.all(".award-control.js-emoji-btn").size }.from(3).to(2)
end
end
diff --git a/lib/api/entities.rb b/lib/api/entities.rb
index a836df3dc81..412443a2405 100644
--- a/lib/api/entities.rb
+++ b/lib/api/entities.rb
@@ -603,6 +603,9 @@ module API
expose :plantuml_url
expose :terminal_max_session_time
expose :polling_interval_multiplier
+ expose :help_page_hide_commercial_content
+ expose :help_page_text
+ expose :help_page_support_url
end
class Release < Grape::Entity
@@ -804,7 +807,11 @@ module API
end
class Image < Grape::Entity
- expose :name
+ expose :name, :entrypoint
+ end
+
+ class Service < Image
+ expose :alias, :command
end
class Artifacts < Grape::Entity
@@ -848,7 +855,7 @@ module API
expose :variables
expose :steps, using: Step
expose :image, using: Image
- expose :services, using: Image
+ expose :services, using: Service
expose :artifacts, using: Artifacts
expose :cache, using: Cache
expose :credentials, using: Credentials
diff --git a/lib/api/helpers/internal_helpers.rb b/lib/api/helpers/internal_helpers.rb
index d3732d67622..5e9cf5e68b1 100644
--- a/lib/api/helpers/internal_helpers.rb
+++ b/lib/api/helpers/internal_helpers.rb
@@ -10,6 +10,10 @@ module API
set_project unless defined?(@project)
@project
end
+
+ def redirected_path
+ @redirected_path
+ end
def ssh_authentication_abilities
[
@@ -38,8 +42,9 @@ module API
def set_project
if params[:gl_repository]
@project, @wiki = Gitlab::GlRepository.parse(params[:gl_repository])
+ @redirected_path = nil
else
- @project, @wiki = Gitlab::RepoPath.parse(params[:project])
+ @project, @wiki, @redirected_path = Gitlab::RepoPath.parse(params[:project])
end
end
diff --git a/lib/api/internal.rb b/lib/api/internal.rb
index 38631953014..9ec418edea4 100644
--- a/lib/api/internal.rb
+++ b/lib/api/internal.rb
@@ -34,7 +34,7 @@ module API
access_checker_klass = wiki? ? Gitlab::GitAccessWiki : Gitlab::GitAccess
access_checker = access_checker_klass
- .new(actor, project, protocol, authentication_abilities: ssh_authentication_abilities)
+ .new(actor, project, protocol, authentication_abilities: ssh_authentication_abilities, redirected_path: redirected_path)
begin
access_checker.check(params[:action], params[:changes])
@@ -86,8 +86,16 @@ module API
}
end
+ get "/broadcast_messages" do
+ if messages = BroadcastMessage.current
+ present messages, with: Entities::BroadcastMessage
+ else
+ []
+ end
+ end
+
get "/broadcast_message" do
- if message = BroadcastMessage.current
+ if message = BroadcastMessage.current.last
present message, with: Entities::BroadcastMessage
else
{}
diff --git a/lib/api/issues.rb b/lib/api/issues.rb
index 78db960ae28..09dca0dff8b 100644
--- a/lib/api/issues.rb
+++ b/lib/api/issues.rb
@@ -27,6 +27,8 @@ module API
optional :milestone, type: String, desc: 'Return issues for a specific milestone'
optional :iids, type: Array[Integer], desc: 'The IID array of issues'
optional :search, type: String, desc: 'Search issues for text present in the title or description'
+ optional :created_after, type: DateTime, desc: 'Return issues created after the specified time'
+ optional :created_before, type: DateTime, desc: 'Return issues created before the specified time'
use :pagination
end
diff --git a/lib/api/merge_requests.rb b/lib/api/merge_requests.rb
index 710deba5ae3..1118fc7465b 100644
--- a/lib/api/merge_requests.rb
+++ b/lib/api/merge_requests.rb
@@ -72,6 +72,8 @@ module API
optional :iids, type: Array[Integer], desc: 'The IID array of merge requests'
optional :milestone, type: String, desc: 'Return merge requests for a specific milestone'
optional :labels, type: String, desc: 'Comma-separated list of label names'
+ optional :created_after, type: DateTime, desc: 'Return merge requests created after the specified time'
+ optional :created_before, type: DateTime, desc: 'Return merge requests created before the specified time'
use :pagination
end
get ":id/merge_requests" do
@@ -97,7 +99,7 @@ module API
authorize! :create_merge_request, user_project
mr_params = declared_params(include_missing: false)
- mr_params[:force_remove_source_branch] = mr_params.delete(:remove_source_branch) if mr_params[:remove_source_branch].present?
+ mr_params[:force_remove_source_branch] = mr_params.delete(:remove_source_branch)
merge_request = ::MergeRequests::CreateService.new(user_project, current_user, mr_params).execute
diff --git a/lib/api/milestones.rb b/lib/api/milestones.rb
index a3ea619a2fb..3541d3c95fb 100644
--- a/lib/api/milestones.rb
+++ b/lib/api/milestones.rb
@@ -117,7 +117,7 @@ module API
finder_params = {
project_id: user_project.id,
milestone_title: milestone.title,
- sort: 'position_asc'
+ sort: 'label_priority'
}
issues = IssuesFinder.new(current_user, finder_params).execute
@@ -140,7 +140,7 @@ module API
finder_params = {
project_id: user_project.id,
milestone_title: milestone.title,
- sort: 'position_asc'
+ sort: 'label_priority'
}
merge_requests = MergeRequestsFinder.new(current_user, finder_params).execute
diff --git a/lib/api/services.rb b/lib/api/services.rb
index 47bd9940f77..7488f95a9b7 100644
--- a/lib/api/services.rb
+++ b/lib/api/services.rb
@@ -685,7 +685,7 @@ module API
trigger_services.each do |service_slug, settings|
helpers do
- def chat_command_service(project, service_slug, params)
+ def slash_command_service(project, service_slug, params)
project.services.active.where(template: false).find do |service|
service.try(:token) == params[:token] && service.to_param == service_slug.underscore
end
@@ -710,7 +710,7 @@ module API
# This is not accurate, but done to prevent leakage of the project names
not_found!('Service') unless project
- service = chat_command_service(project, service_slug, params)
+ service = slash_command_service(project, service_slug, params)
result = service.try(:trigger, params)
if result
diff --git a/lib/api/settings.rb b/lib/api/settings.rb
index 25027c3b114..d598f9a62a2 100644
--- a/lib/api/settings.rb
+++ b/lib/api/settings.rb
@@ -39,7 +39,9 @@ module API
:email_author_in_body,
:enabled_git_access_protocol,
:gravatar_enabled,
+ :help_page_hide_commercial_content,
:help_page_text,
+ :help_page_support_url,
:home_page_url,
:housekeeping_enabled,
:html_emails_enabled,
@@ -101,7 +103,9 @@ module API
optional :home_page_url, type: String, desc: 'We will redirect non-logged in users to this page'
optional :after_sign_out_path, type: String, desc: 'We will redirect users to this page after they sign out'
optional :sign_in_text, type: String, desc: 'The sign in text of the GitLab application'
+ optional :help_page_hide_commercial_content, type: Boolean, desc: 'Hide marketing-related entries from help'
optional :help_page_text, type: String, desc: 'Custom text displayed on the help page'
+ optional :help_page_support_url, type: String, desc: 'Alternate support URL for help page'
optional :shared_runners_enabled, type: Boolean, desc: 'Enable shared runners for new projects'
given shared_runners_enabled: ->(val) { val } do
requires :shared_runners_text, type: String, desc: 'Shared runners text '
diff --git a/lib/api/users.rb b/lib/api/users.rb
index dda64715ee1..7257ecb5b67 100644
--- a/lib/api/users.rb
+++ b/lib/api/users.rb
@@ -29,6 +29,7 @@ module API
optional :can_create_group, type: Boolean, desc: 'Flag indicating the user can create groups'
optional :skip_confirmation, type: Boolean, default: false, desc: 'Flag indicating the account is confirmed'
optional :external, type: Boolean, desc: 'Flag indicating the user is an external user'
+ optional :avatar, type: File, desc: 'Avatar image for user'
all_or_none_of :extern_uid, :provider
end
end
diff --git a/lib/api/v3/services.rb b/lib/api/v3/services.rb
index 118c6df6549..2d13d6fabfd 100644
--- a/lib/api/v3/services.rb
+++ b/lib/api/v3/services.rb
@@ -608,7 +608,7 @@ module API
trigger_services.each do |service_slug, settings|
helpers do
- def chat_command_service(project, service_slug, params)
+ def slash_command_service(project, service_slug, params)
project.services.active.where(template: false).find do |service|
service.try(:token) == params[:token] && service.to_param == service_slug.underscore
end
@@ -633,7 +633,7 @@ module API
# This is not accurate, but done to prevent leakage of the project names
not_found!('Service') unless project
- service = chat_command_service(project, service_slug, params)
+ service = slash_command_service(project, service_slug, params)
result = service.try(:trigger, params)
if result
diff --git a/lib/banzai/reference_parser/base_parser.rb b/lib/banzai/reference_parser/base_parser.rb
index 1e2536231d8..279fca8d043 100644
--- a/lib/banzai/reference_parser/base_parser.rb
+++ b/lib/banzai/reference_parser/base_parser.rb
@@ -171,7 +171,7 @@ module Banzai
collection.where(id: to_query).each { |row| cache[row.id] = row }
end
- cache.values_at(*ids)
+ cache.values_at(*ids).compact
else
collection.where(id: ids)
end
diff --git a/lib/ci/api/entities.rb b/lib/ci/api/entities.rb
index 792ff628b09..6b82b2b4f13 100644
--- a/lib/ci/api/entities.rb
+++ b/lib/ci/api/entities.rb
@@ -45,7 +45,21 @@ module Ci
expose :artifacts_expire_at, if: ->(build, _) { build.artifacts? }
expose :options do |model|
- model.options
+ # This part ensures that output of old API is still the same after adding support
+ # for extended docker configuration options, used by new API
+ #
+ # I'm leaving this here, not in the model, because it should be removed at the same time
+ # when old API will be removed (planned for August 2017).
+ model.options.dup.tap do |options|
+ options[:image] = options[:image][:name] if options[:image].is_a?(Hash)
+ options[:services].map! do |service|
+ if service.is_a?(Hash)
+ service[:name]
+ else
+ service
+ end
+ end
+ end
end
expose :timeout do |model|
diff --git a/lib/github/import.rb b/lib/github/import.rb
index 9c7eb965f93..b20614b3060 100644
--- a/lib/github/import.rb
+++ b/lib/github/import.rb
@@ -92,7 +92,7 @@ module Github
end
def fetch_wiki_repository
- wiki_url = "https://{options.fetch(:token)}@github.com/#{repo}.wiki.git"
+ wiki_url = "https://#{options.fetch(:token)}@github.com/#{repo}.wiki.git"
wiki_path = "#{project.path_with_namespace}.wiki"
unless project.wiki.repository_exists?
diff --git a/lib/gitlab/ci/build/image.rb b/lib/gitlab/ci/build/image.rb
index c62aeb60fa9..b88b2e36d53 100644
--- a/lib/gitlab/ci/build/image.rb
+++ b/lib/gitlab/ci/build/image.rb
@@ -2,7 +2,7 @@ module Gitlab
module Ci
module Build
class Image
- attr_reader :name
+ attr_reader :alias, :command, :entrypoint, :name
class << self
def from_image(job)
@@ -21,7 +21,14 @@ module Gitlab
end
def initialize(image)
- @name = image
+ if image.is_a?(String)
+ @name = image
+ elsif image.is_a?(Hash)
+ @alias = image[:alias]
+ @command = image[:command]
+ @entrypoint = image[:entrypoint]
+ @name = image[:name]
+ end
end
def valid?
diff --git a/lib/gitlab/ci/config/entry/image.rb b/lib/gitlab/ci/config/entry/image.rb
index b5050257688..897dcff8012 100644
--- a/lib/gitlab/ci/config/entry/image.rb
+++ b/lib/gitlab/ci/config/entry/image.rb
@@ -8,8 +8,36 @@ module Gitlab
class Image < Node
include Validatable
+ ALLOWED_KEYS = %i[name entrypoint].freeze
+
validations do
- validates :config, type: String
+ validates :config, hash_or_string: true
+ validates :config, allowed_keys: ALLOWED_KEYS
+
+ validates :name, type: String, presence: true
+ validates :entrypoint, type: String, allow_nil: true
+ end
+
+ def hash?
+ @config.is_a?(Hash)
+ end
+
+ def string?
+ @config.is_a?(String)
+ end
+
+ def name
+ value[:name]
+ end
+
+ def entrypoint
+ value[:entrypoint]
+ end
+
+ def value
+ return { name: @config } if string?
+ return @config if hash?
+ {}
end
end
end
diff --git a/lib/gitlab/ci/config/entry/service.rb b/lib/gitlab/ci/config/entry/service.rb
new file mode 100644
index 00000000000..b52faf48b58
--- /dev/null
+++ b/lib/gitlab/ci/config/entry/service.rb
@@ -0,0 +1,34 @@
+module Gitlab
+ module Ci
+ class Config
+ module Entry
+ ##
+ # Entry that represents a configuration of Docker service.
+ #
+ class Service < Image
+ include Validatable
+
+ ALLOWED_KEYS = %i[name entrypoint command alias].freeze
+
+ validations do
+ validates :config, hash_or_string: true
+ validates :config, allowed_keys: ALLOWED_KEYS
+
+ validates :name, type: String, presence: true
+ validates :entrypoint, type: String, allow_nil: true
+ validates :command, type: String, allow_nil: true
+ validates :alias, type: String, allow_nil: true
+ end
+
+ def alias
+ value[:alias]
+ end
+
+ def command
+ value[:command]
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/ci/config/entry/services.rb b/lib/gitlab/ci/config/entry/services.rb
index 84f8ab780f5..0066894e069 100644
--- a/lib/gitlab/ci/config/entry/services.rb
+++ b/lib/gitlab/ci/config/entry/services.rb
@@ -9,7 +9,30 @@ module Gitlab
include Validatable
validations do
- validates :config, array_of_strings: true
+ validates :config, type: Array
+ end
+
+ def compose!(deps = nil)
+ super do
+ @entries = []
+ @config.each do |config|
+ @entries << Entry::Factory.new(Entry::Service)
+ .value(config || {})
+ .create!
+ end
+
+ @entries.each do |entry|
+ entry.compose!(deps)
+ end
+ end
+ end
+
+ def value
+ @entries.map(&:value)
+ end
+
+ def descendants
+ @entries
end
end
end
diff --git a/lib/gitlab/ci/config/entry/validators.rb b/lib/gitlab/ci/config/entry/validators.rb
index bd7428b1272..b2ca3c881e4 100644
--- a/lib/gitlab/ci/config/entry/validators.rb
+++ b/lib/gitlab/ci/config/entry/validators.rb
@@ -44,6 +44,14 @@ module Gitlab
end
end
+ class HashOrStringValidator < ActiveModel::EachValidator
+ def validate_each(record, attribute, value)
+ unless value.is_a?(Hash) || value.is_a?(String)
+ record.errors.add(attribute, 'should be a hash or a string')
+ end
+ end
+ end
+
class KeyValidator < ActiveModel::EachValidator
include LegacyValidationHelpers
diff --git a/lib/gitlab/ci/status/external/common.rb b/lib/gitlab/ci/status/external/common.rb
index 4969a350862..9307545b5b1 100644
--- a/lib/gitlab/ci/status/external/common.rb
+++ b/lib/gitlab/ci/status/external/common.rb
@@ -3,6 +3,10 @@ module Gitlab
module Status
module External
module Common
+ def label
+ subject.description
+ end
+
def has_details?
subject.target_url.present? &&
can?(user, :read_commit_status, subject)
diff --git a/lib/gitlab/current_settings.rb b/lib/gitlab/current_settings.rb
index 48735fd197d..284e6ad55a5 100644
--- a/lib/gitlab/current_settings.rb
+++ b/lib/gitlab/current_settings.rb
@@ -10,43 +10,49 @@ module Gitlab
delegate :sidekiq_throttling_enabled?, to: :current_application_settings
- def fake_application_settings
- OpenStruct.new(::ApplicationSetting.defaults)
+ def fake_application_settings(defaults = ApplicationSetting.defaults)
+ FakeApplicationSettings.new(defaults)
end
private
def ensure_application_settings!
- unless ENV['IN_MEMORY_APPLICATION_SETTINGS'] == 'true'
- settings = retrieve_settings_from_database?
- end
+ return in_memory_application_settings if ENV['IN_MEMORY_APPLICATION_SETTINGS'] == 'true'
- settings || in_memory_application_settings
+ cached_application_settings || uncached_application_settings
end
- def retrieve_settings_from_database?
- settings = retrieve_settings_from_database_cache?
- return settings if settings.present?
-
- return fake_application_settings unless connect_to_db?
-
+ def cached_application_settings
begin
- db_settings = ::ApplicationSetting.current
- # In case Redis isn't running or the Redis UNIX socket file is not available
+ ApplicationSetting.cached
rescue ::Redis::BaseError, ::Errno::ENOENT
- db_settings = ::ApplicationSetting.last
+ # In case Redis isn't running or the Redis UNIX socket file is not available
end
- db_settings || ::ApplicationSetting.create_from_defaults
end
- def retrieve_settings_from_database_cache?
+ def uncached_application_settings
+ return fake_application_settings unless connect_to_db?
+
+ # This loads from the database into the cache, so handle Redis errors
begin
- settings = ApplicationSetting.cached
+ db_settings = ApplicationSetting.current
rescue ::Redis::BaseError, ::Errno::ENOENT
# In case Redis isn't running or the Redis UNIX socket file is not available
- settings = nil
end
- settings
+
+ # If there are pending migrations, it's possible there are columns that
+ # need to be added to the application settings. To prevent Rake tasks
+ # and other callers from failing, use any loaded settings and return
+ # defaults for missing columns.
+ if ActiveRecord::Migrator.needs_migration?
+ defaults = ApplicationSetting.defaults
+ defaults.merge!(db_settings.attributes.symbolize_keys) if db_settings.present?
+ return fake_application_settings(defaults)
+ end
+
+ return db_settings if db_settings.present?
+
+ ApplicationSetting.create_from_defaults || in_memory_application_settings
end
def in_memory_application_settings
@@ -62,8 +68,7 @@ module Gitlab
active_db_connection = ActiveRecord::Base.connection.active? rescue false
active_db_connection &&
- ActiveRecord::Base.connection.table_exists?('application_settings') &&
- !ActiveRecord::Migrator.needs_migration?
+ ActiveRecord::Base.connection.table_exists?('application_settings')
rescue ActiveRecord::NoDatabaseError
false
end
diff --git a/lib/gitlab/database.rb b/lib/gitlab/database.rb
index d0bd1299671..0d5a7cf0694 100644
--- a/lib/gitlab/database.rb
+++ b/lib/gitlab/database.rb
@@ -83,6 +83,22 @@ module Gitlab
end
end
+ def self.bulk_insert(table, rows)
+ return if rows.empty?
+
+ keys = rows.first.keys
+ columns = keys.map { |key| connection.quote_column_name(key) }
+
+ tuples = rows.map do |row|
+ row.values_at(*keys).map { |value| connection.quote(value) }
+ end
+
+ connection.execute <<-EOF.strip_heredoc
+ INSERT INTO #{table} (#{columns.join(', ')})
+ VALUES #{tuples.map { |tuple| "(#{tuple.join(', ')})" }.join(', ')}
+ EOF
+ end
+
# pool_size - The size of the DB pool.
# host - An optional host name to use instead of the default one.
def self.create_connection_pool(pool_size, host = nil)
diff --git a/lib/gitlab/database/migration_helpers.rb b/lib/gitlab/database/migration_helpers.rb
index a412bb6dbd2..9181202a091 100644
--- a/lib/gitlab/database/migration_helpers.rb
+++ b/lib/gitlab/database/migration_helpers.rb
@@ -1,6 +1,39 @@
module Gitlab
module Database
module MigrationHelpers
+ # Adds `created_at` and `updated_at` columns with timezone information.
+ #
+ # This method is an improved version of Rails' built-in method `add_timestamps`.
+ #
+ # Available options are:
+ # default - The default value for the column.
+ # null - When set to `true` the column will allow NULL values.
+ # The default is to not allow NULL values.
+ def add_timestamps_with_timezone(table_name, options = {})
+ options[:null] = false if options[:null].nil?
+
+ [:created_at, :updated_at].each do |column_name|
+ if options[:default] && transaction_open?
+ raise '`add_timestamps_with_timezone` with default value cannot be run inside a transaction. ' \
+ 'You can disable transactions by calling `disable_ddl_transaction!` ' \
+ 'in the body of your migration class'
+ end
+
+ # If default value is presented, use `add_column_with_default` method instead.
+ if options[:default]
+ add_column_with_default(
+ table_name,
+ column_name,
+ :datetime_with_timezone,
+ default: options[:default],
+ allow_null: options[:null]
+ )
+ else
+ add_column(table_name, column_name, :datetime_with_timezone, options)
+ end
+ end
+ end
+
# Creates a new index, concurrently when supported
#
# On PostgreSQL this method creates an index concurrently, on MySQL this
@@ -200,6 +233,12 @@ module Gitlab
# Update in batches of 5% until we run out of any rows to update.
batch_size = ((total / 100.0) * 5.0).ceil
+ max_size = 1000
+
+ # The upper limit is 1000 to ensure we don't lock too many rows. For
+ # example, for "merge_requests" even 1% of the table is around 35 000
+ # rows for GitLab.com.
+ batch_size = max_size if batch_size > max_size
start_arel = table.project(table[:id]).order(table[:id].asc).take(1)
start_arel = yield table, start_arel if block_given?
diff --git a/lib/gitlab/diff/file.rb b/lib/gitlab/diff/file.rb
index 4212a0dbe2e..d2863a4da71 100644
--- a/lib/gitlab/diff/file.rb
+++ b/lib/gitlab/diff/file.rb
@@ -5,7 +5,20 @@ module Gitlab
delegate :new_file?, :deleted_file?, :renamed_file?,
:old_path, :new_path, :a_mode, :b_mode, :mode_changed?,
- :submodule?, :too_large?, :collapsed?, to: :diff, prefix: false
+ :submodule?, :expanded?, :too_large?, :collapsed?, :line_count, to: :diff, prefix: false
+
+ # Finding a viewer for a diff file happens based only on extension and whether the
+ # diff file blobs are binary or text, which means 1 diff file should only be matched by 1 viewer,
+ # and the order of these viewers doesn't really matter.
+ #
+ # However, when the diff file blobs are LFS pointers, we cannot know for sure whether the
+ # file being pointed to is binary or text. In this case, we match only on
+ # extension, preferring binary viewers over text ones if both exist, since the
+ # large files referred to in "Large File Storage" are much more likely to be
+ # binary than text.
+ RICH_VIEWERS = [
+ DiffViewer::Image
+ ].sort_by { |v| v.binary? ? 0 : 1 }.freeze
def initialize(diff, repository:, diff_refs: nil, fallback_diff_refs: nil)
@diff = diff
@@ -177,6 +190,100 @@ module Gitlab
def text?
!binary?
end
+
+ def external_storage_error?
+ old_blob&.external_storage_error? || new_blob&.external_storage_error?
+ end
+
+ def stored_externally?
+ old_blob&.stored_externally? || new_blob&.stored_externally?
+ end
+
+ def external_storage
+ old_blob&.external_storage || new_blob&.external_storage
+ end
+
+ def content_changed?
+ old_blob && new_blob && old_blob.id != new_blob.id
+ end
+
+ def different_type?
+ old_blob && new_blob && old_blob.binary? != new_blob.binary?
+ end
+
+ def size
+ [old_blob&.size, new_blob&.size].compact.sum
+ end
+
+ def raw_size
+ [old_blob&.raw_size, new_blob&.raw_size].compact.sum
+ end
+
+ def raw_binary?
+ old_blob&.raw_binary? || new_blob&.raw_binary?
+ end
+
+ def raw_text?
+ !raw_binary? && !different_type?
+ end
+
+ def simple_viewer
+ @simple_viewer ||= simple_viewer_class.new(self)
+ end
+
+ def rich_viewer
+ return @rich_viewer if defined?(@rich_viewer)
+
+ @rich_viewer = rich_viewer_class&.new(self)
+ end
+
+ def rendered_as_text?(ignore_errors: true)
+ simple_viewer.is_a?(DiffViewer::Text) && (ignore_errors || simple_viewer.render_error.nil?)
+ end
+
+ private
+
+ def simple_viewer_class
+ return DiffViewer::NotDiffable unless diffable?
+
+ if content_changed?
+ if raw_text?
+ DiffViewer::Text
+ else
+ DiffViewer::NoPreview
+ end
+ elsif new_file?
+ if raw_text?
+ DiffViewer::Text
+ else
+ DiffViewer::Added
+ end
+ elsif deleted_file?
+ if raw_text?
+ DiffViewer::Text
+ else
+ DiffViewer::Deleted
+ end
+ elsif renamed_file?
+ DiffViewer::Renamed
+ elsif mode_changed?
+ DiffViewer::ModeChanged
+ end
+ end
+
+ def rich_viewer_class
+ viewer_class_from(RICH_VIEWERS)
+ end
+
+ def viewer_class_from(classes)
+ return unless diffable?
+ return if different_type? || external_storage_error?
+ return unless new_file? || deleted_file? || content_changed?
+
+ verify_binary = !stored_externally?
+
+ classes.find { |viewer_class| viewer_class.can_render?(self, verify_binary: verify_binary) }
+ end
end
end
end
diff --git a/lib/gitlab/diff/line.rb b/lib/gitlab/diff/line.rb
index bd52ae47e9f..2d89ccfc354 100644
--- a/lib/gitlab/diff/line.rb
+++ b/lib/gitlab/diff/line.rb
@@ -42,25 +42,25 @@ module Gitlab
end
def added?
- type == 'new' || type == 'new-nonewline'
+ %w[new new-nonewline].include?(type)
end
def removed?
- type == 'old' || type == 'old-nonewline'
- end
-
- def rich_text
- @parent_file.highlight_lines! if @parent_file && !@rich_text
-
- @rich_text
+ %w[old old-nonewline].include?(type)
end
def meta?
- type == 'match'
+ %w[match new-nonewline old-nonewline].include?(type)
end
def discussable?
- !['match', 'new-nonewline', 'old-nonewline'].include?(type)
+ !meta?
+ end
+
+ def rich_text
+ @parent_file.highlight_lines! if @parent_file && !@rich_text
+
+ @rich_text
end
def as_json(opts = nil)
diff --git a/lib/gitlab/diff/parallel_diff.rb b/lib/gitlab/diff/parallel_diff.rb
index 481536a380b..0cb26fa45c8 100644
--- a/lib/gitlab/diff/parallel_diff.rb
+++ b/lib/gitlab/diff/parallel_diff.rb
@@ -14,16 +14,7 @@ module Gitlab
lines = []
highlighted_diff_lines = diff_file.highlighted_diff_lines
highlighted_diff_lines.each do |line|
- if line.meta? || line.unchanged?
- # line in the right panel is the same as in the left one
- lines << {
- left: line,
- right: line
- }
-
- free_right_index = nil
- i += 1
- elsif line.removed?
+ if line.removed?
lines << {
left: line,
right: nil
@@ -51,6 +42,15 @@ module Gitlab
free_right_index = nil
i += 1
end
+ elsif line.meta? || line.unchanged?
+ # line in the right panel is the same as in the left one
+ lines << {
+ left: line,
+ right: line
+ }
+
+ free_right_index = nil
+ i += 1
end
end
diff --git a/lib/gitlab/etag_caching/middleware.rb b/lib/gitlab/etag_caching/middleware.rb
index 7f884183bb1..1d6f5bb5e1c 100644
--- a/lib/gitlab/etag_caching/middleware.rb
+++ b/lib/gitlab/etag_caching/middleware.rb
@@ -7,7 +7,7 @@ module Gitlab
def call(env)
request = Rack::Request.new(env)
- route = Gitlab::EtagCaching::Router.match(request)
+ route = Gitlab::EtagCaching::Router.match(request.path_info)
return @app.call(env) unless route
track_event(:etag_caching_middleware_used, route)
diff --git a/lib/gitlab/etag_caching/router.rb b/lib/gitlab/etag_caching/router.rb
index dccc66b3918..75167a6b088 100644
--- a/lib/gitlab/etag_caching/router.rb
+++ b/lib/gitlab/etag_caching/router.rb
@@ -53,8 +53,8 @@ module Gitlab
)
].freeze
- def self.match(request)
- ROUTES.find { |route| route.regexp.match(request.path_info) }
+ def self.match(path)
+ ROUTES.find { |route| route.regexp.match(path) }
end
end
end
diff --git a/lib/gitlab/etag_caching/store.rb b/lib/gitlab/etag_caching/store.rb
index 0039fc01c8f..072fcfc65e6 100644
--- a/lib/gitlab/etag_caching/store.rb
+++ b/lib/gitlab/etag_caching/store.rb
@@ -25,6 +25,8 @@ module Gitlab
end
def redis_key(key)
+ raise 'Invalid key' if !Rails.env.production? && !Gitlab::EtagCaching::Router.match(key)
+
"#{REDIS_NAMESPACE}#{key}"
end
end
diff --git a/lib/gitlab/fake_application_settings.rb b/lib/gitlab/fake_application_settings.rb
new file mode 100644
index 00000000000..bb14a8cd9e7
--- /dev/null
+++ b/lib/gitlab/fake_application_settings.rb
@@ -0,0 +1,27 @@
+# This class extends an OpenStruct object by adding predicate methods to mimic
+# ActiveRecord access. We rely on the initial values being true or false to
+# determine whether to define a predicate method because for a newly-added
+# column that has not been migrated yet, there is no way to determine the
+# column type without parsing db/schema.rb.
+module Gitlab
+ class FakeApplicationSettings < OpenStruct
+ def initialize(options = {})
+ super
+
+ FakeApplicationSettings.define_predicate_methods(options)
+ end
+
+ # Mimic ActiveRecord predicate methods for boolean values
+ def self.define_predicate_methods(options)
+ options.each do |key, value|
+ next if key.to_s.end_with?('?')
+ next unless [true, false].include?(value)
+
+ define_method "#{key}?" do
+ actual_key = key.to_s.chomp('?')
+ self[actual_key]
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/git/diff.rb b/lib/gitlab/git/diff.rb
index 88ad760bea3..f825568f194 100644
--- a/lib/gitlab/git/diff.rb
+++ b/lib/gitlab/git/diff.rb
@@ -16,9 +16,11 @@ module Gitlab
alias_method :renamed_file?, :renamed_file
attr_accessor :expanded
+ attr_writer :too_large
- # We need this accessor because of `to_hash` and `init_from_hash`
- attr_accessor :too_large
+ alias_method :expanded?, :expanded
+
+ SERIALIZE_KEYS = %i(diff new_path old_path a_mode b_mode new_file renamed_file deleted_file too_large).freeze
class << self
# The maximum size of a diff to display.
@@ -229,16 +231,10 @@ module Gitlab
end
end
- def serialize_keys
- @serialize_keys ||= %i(diff new_path old_path a_mode b_mode new_file renamed_file deleted_file too_large)
- end
-
def to_hash
hash = {}
- keys = serialize_keys
-
- keys.each do |key|
+ SERIALIZE_KEYS.each do |key|
hash[key] = send(key)
end
@@ -265,6 +261,9 @@ module Gitlab
end
end
+ # This is used by `to_hash` and `init_from_hash`.
+ alias_method :too_large, :too_large?
+
def too_large!
@diff = ''
@line_count = 0
@@ -313,7 +312,7 @@ module Gitlab
def init_from_hash(hash)
raw_diff = hash.symbolize_keys
- serialize_keys.each do |key|
+ SERIALIZE_KEYS.each do |key|
send(:"#{key}=", raw_diff[key.to_sym])
end
end
diff --git a/lib/gitlab/git/diff_collection.rb b/lib/gitlab/git/diff_collection.rb
index 334e06a6eca..555894907cc 100644
--- a/lib/gitlab/git/diff_collection.rb
+++ b/lib/gitlab/git/diff_collection.rb
@@ -97,7 +97,7 @@ module Gitlab
diff = Gitlab::Git::Diff.new(raw, expanded: expanded)
- if !expanded && over_safe_limits?(i)
+ if !expanded && over_safe_limits?(i) && diff.line_count > 0
diff.collapse!
end
diff --git a/lib/gitlab/git/gitmodules_parser.rb b/lib/gitlab/git/gitmodules_parser.rb
new file mode 100644
index 00000000000..f4e3b5e5129
--- /dev/null
+++ b/lib/gitlab/git/gitmodules_parser.rb
@@ -0,0 +1,77 @@
+module Gitlab
+ module Git
+ class GitmodulesParser
+ def initialize(content)
+ @content = content
+ end
+
+ # Parses the contents of a .gitmodules file and returns a hash of
+ # submodule information, indexed by path.
+ def parse
+ reindex_by_path(get_submodules_by_name)
+ end
+
+ private
+
+ class State
+ def initialize
+ @result = {}
+ @current_submodule = nil
+ end
+
+ def start_section(section)
+ # In some .gitmodules files (e.g. nodegit's), a header
+ # with the same name appears multiple times; we want to
+ # accumulate the configs across these
+ @current_submodule = @result[section] || { 'name' => section }
+ @result[section] = @current_submodule
+ end
+
+ def set_attribute(attr, value)
+ @current_submodule[attr] = value
+ end
+
+ def section_started?
+ !@current_submodule.nil?
+ end
+
+ def submodules_by_name
+ @result
+ end
+ end
+
+ def get_submodules_by_name
+ iterator = State.new
+
+ @content.split("\n").each_with_object(iterator) do |text, iterator|
+ next if text =~ /^\s*#/
+
+ if text =~ /\A\[submodule "(?<name>[^"]+)"\]\z/
+ iterator.start_section($~[:name])
+ else
+ next unless iterator.section_started?
+
+ next unless text =~ /\A\s*(?<key>\w+)\s*=\s*(?<value>.*)\z/
+
+ value = $~[:value].chomp
+ iterator.set_attribute($~[:key], value)
+ end
+ end
+
+ iterator.submodules_by_name
+ end
+
+ def reindex_by_path(submodules_by_name)
+ # Convert from an indexed by name to an array indexed by path
+ # If a submodule doesn't have a path, it is considered bogus
+ # and is ignored
+ submodules_by_name.each_with_object({}) do |(name, data), results|
+ path = data.delete 'path'
+ next unless path
+
+ results[path] = data
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/git/repository.rb b/lib/gitlab/git/repository.rb
index 85695d0a4df..c1f942f931a 100644
--- a/lib/gitlab/git/repository.rb
+++ b/lib/gitlab/git/repository.rb
@@ -617,9 +617,9 @@ module Gitlab
#
# Ex.
# {
- # "rack" => {
+ # "current_path/rack" => {
+ # "name" => "original_path/rack",
# "id" => "c67be4624545b4263184c4a0e8f887efd0a66320",
- # "path" => "rack",
# "url" => "git://github.com/chneukirchen/rack.git"
# },
# "encoding" => {
@@ -637,7 +637,8 @@ module Gitlab
return {}
end
- parse_gitmodules(commit, content)
+ parser = GitmodulesParser.new(content)
+ fill_submodule_ids(commit, parser.parse)
end
# Return total commits count accessible from passed ref
@@ -998,42 +999,19 @@ module Gitlab
end
end
- # Parses the contents of a .gitmodules file and returns a hash of
- # submodule information.
- def parse_gitmodules(commit, content)
- modules = {}
-
- name = nil
- content.each_line do |line|
- case line.strip
- when /\A\[submodule "(?<name>[^"]+)"\]\z/ # Submodule header
- name = $~[:name]
- modules[name] = {}
- when /\A(?<key>\w+)\s*=\s*(?<value>.*)\z/ # Key/value pair
- key = $~[:key]
- value = $~[:value].chomp
-
- next unless name && modules[name]
-
- modules[name][key] = value
-
- if key == 'path'
- begin
- modules[name]['id'] = blob_content(commit, value)
- rescue InvalidBlobName
- # The current entry is invalid
- modules.delete(name)
- name = nil
- end
- end
- when /\A#/ # Comment
- next
- else # Invalid line
- name = nil
+ # Fill in the 'id' field of a submodule hash from its values
+ # as-of +commit+. Return a Hash consisting only of entries
+ # from the submodule hash for which the 'id' field is filled.
+ def fill_submodule_ids(commit, submodule_data)
+ submodule_data.each do |path, data|
+ id = begin
+ blob_content(commit, path)
+ rescue InvalidBlobName
+ nil
end
+ data['id'] = id
end
-
- modules
+ submodule_data.select { |path, data| data['id'] }
end
# Returns true if +commit+ introduced changes to +path+, using commit
diff --git a/lib/gitlab/git_access.rb b/lib/gitlab/git_access.rb
index 0a19d24eb20..0b62911958d 100644
--- a/lib/gitlab/git_access.rb
+++ b/lib/gitlab/git_access.rb
@@ -22,12 +22,13 @@ module Gitlab
PUSH_COMMANDS = %w{ git-receive-pack }.freeze
ALL_COMMANDS = DOWNLOAD_COMMANDS + PUSH_COMMANDS
- attr_reader :actor, :project, :protocol, :authentication_abilities
+ attr_reader :actor, :project, :protocol, :authentication_abilities, :redirected_path
- def initialize(actor, project, protocol, authentication_abilities:)
+ def initialize(actor, project, protocol, authentication_abilities:, redirected_path: nil)
@actor = actor
@project = project
@protocol = protocol
+ @redirected_path = redirected_path
@authentication_abilities = authentication_abilities
end
@@ -35,6 +36,7 @@ module Gitlab
check_protocol!
check_active_user!
check_project_accessibility!
+ check_project_moved!
check_command_disabled!(cmd)
check_command_existence!(cmd)
check_repository_existence!
@@ -87,6 +89,21 @@ module Gitlab
end
end
+ def check_project_moved!
+ if redirected_path
+ url = protocol == 'ssh' ? project.ssh_url_to_repo : project.http_url_to_repo
+ message = <<-MESSAGE.strip_heredoc
+ Project '#{redirected_path}' was moved to '#{project.full_path}'.
+
+ Please update your Git remote and try again:
+
+ git remote set-url origin #{url}
+ MESSAGE
+
+ raise NotFoundError, message
+ end
+ end
+
def check_command_disabled!(cmd)
if upload_pack?(cmd)
check_upload_pack_disabled!
diff --git a/lib/gitlab/gitaly_client/util.rb b/lib/gitlab/gitaly_client/util.rb
index 86d055d3533..f5a4c5493ef 100644
--- a/lib/gitlab/gitaly_client/util.rb
+++ b/lib/gitlab/gitaly_client/util.rb
@@ -4,7 +4,6 @@ module Gitlab
class << self
def repository(repository_storage, relative_path)
Gitaly::Repository.new(
- path: File.join(Gitlab.config.repositories.storages[repository_storage]['path'], relative_path),
storage_name: repository_storage,
relative_path: relative_path
)
diff --git a/lib/gitlab/group_hierarchy.rb b/lib/gitlab/group_hierarchy.rb
index e9d5d52cabb..357c076e874 100644
--- a/lib/gitlab/group_hierarchy.rb
+++ b/lib/gitlab/group_hierarchy.rb
@@ -3,33 +3,38 @@ module Gitlab
#
# This class uses recursive CTEs and as a result will only work on PostgreSQL.
class GroupHierarchy
- attr_reader :base, :model
-
- # base - An instance of ActiveRecord::Relation for which to get parent or
- # child groups.
- def initialize(base)
- @base = base
- @model = base.model
+ attr_reader :ancestors_base, :descendants_base, :model
+
+ # ancestors_base - An instance of ActiveRecord::Relation for which to
+ # get parent groups.
+ # descendants_base - An instance of ActiveRecord::Relation for which to
+ # get child groups. If omitted, ancestors_base is used.
+ def initialize(ancestors_base, descendants_base = ancestors_base)
+ raise ArgumentError.new("Model of ancestors_base does not match model of descendants_base") if ancestors_base.model != descendants_base.model
+
+ @ancestors_base = ancestors_base
+ @descendants_base = descendants_base
+ @model = ancestors_base.model
end
- # Returns a relation that includes the base set of groups and all their
- # ancestors (recursively).
+ # Returns a relation that includes the ancestors_base set of groups
+ # and all their ancestors (recursively).
def base_and_ancestors
- return model.none unless Group.supports_nested_groups?
+ return ancestors_base unless Group.supports_nested_groups?
base_and_ancestors_cte.apply_to(model.all)
end
- # Returns a relation that includes the base set of groups and all their
- # descendants (recursively).
+ # Returns a relation that includes the descendants_base set of groups
+ # and all their descendants (recursively).
def base_and_descendants
- return model.none unless Group.supports_nested_groups?
+ return descendants_base unless Group.supports_nested_groups?
base_and_descendants_cte.apply_to(model.all)
end
- # Returns a relation that includes the base groups, their ancestors, and the
- # descendants of the base groups.
+ # Returns a relation that includes the base groups, their ancestors,
+ # and the descendants of the base groups.
#
# The resulting query will roughly look like the following:
#
@@ -48,8 +53,10 @@ module Gitlab
#
# Using this approach allows us to further add criteria to the relation with
# Rails thinking it's selecting data the usual way.
+ #
+ # If nested groups are not supported, ancestors_base is returned.
def all_groups
- return base unless Group.supports_nested_groups?
+ return ancestors_base unless Group.supports_nested_groups?
ancestors = base_and_ancestors_cte
descendants = base_and_descendants_cte
@@ -72,7 +79,7 @@ module Gitlab
def base_and_ancestors_cte
cte = SQL::RecursiveCTE.new(:base_and_ancestors)
- cte << base.except(:order)
+ cte << ancestors_base.except(:order)
# Recursively get all the ancestors of the base set.
cte << model.
@@ -86,7 +93,7 @@ module Gitlab
def base_and_descendants_cte
cte = SQL::RecursiveCTE.new(:base_and_descendants)
- cte << base.except(:order)
+ cte << descendants_base.except(:order)
# Recursively get all the descendants of the base set.
cte << model.
diff --git a/lib/gitlab/i18n.rb b/lib/gitlab/i18n.rb
index 328dd17e452..a5ad2f952d3 100644
--- a/lib/gitlab/i18n.rb
+++ b/lib/gitlab/i18n.rb
@@ -6,6 +6,7 @@ module Gitlab
'en' => 'English',
'es' => 'Español',
'de' => 'Deutsch',
+ 'fr' => 'Français',
'pt_BR' => 'Português(Brasil)',
'zh_CN' => '简体中文',
'zh_HK' => 'ç¹é«”中文(香港)',
diff --git a/lib/gitlab/import_export.rb b/lib/gitlab/import_export.rb
index 27d5a9198b6..3470a09eaf0 100644
--- a/lib/gitlab/import_export.rb
+++ b/lib/gitlab/import_export.rb
@@ -3,7 +3,7 @@ module Gitlab
extend self
# For every version update, the version history in import_export.md has to be kept up to date.
- VERSION = '0.1.7'.freeze
+ VERSION = '0.1.8'.freeze
FILENAME_LIMIT = 50
def export_path(relative_path:)
diff --git a/lib/gitlab/import_export/import_export.yml b/lib/gitlab/import_export/import_export.yml
index ff2b1d08c3c..72183e8aad4 100644
--- a/lib/gitlab/import_export/import_export.yml
+++ b/lib/gitlab/import_export/import_export.yml
@@ -26,7 +26,8 @@ project_tree:
- notes:
- :author
- :events
- - :merge_request_diff
+ - merge_request_diff:
+ - :merge_request_diff_files
- :events
- :timelogs
- label_links:
@@ -92,6 +93,8 @@ excluded_attributes:
- :expired_at
merge_request_diff:
- :st_diffs
+ merge_request_diff_files:
+ - :diff
issues:
- :milestone_id
merge_requests:
@@ -113,6 +116,8 @@ methods:
- :type
merge_request_diff:
- :utf8_st_diffs
+ merge_request_diff_files:
+ - :utf8_diff
merge_requests:
- :diff_head_sha
project:
diff --git a/lib/gitlab/import_export/json_hash_builder.rb b/lib/gitlab/import_export/json_hash_builder.rb
index 48c09dafcb6..b48f63bcd7e 100644
--- a/lib/gitlab/import_export/json_hash_builder.rb
+++ b/lib/gitlab/import_export/json_hash_builder.rb
@@ -83,7 +83,9 @@ module Gitlab
# +value+ existing model to be included in the hash
# +json_config_hash+ the original hash containing the root model
def add_model_value(current_key, value, json_config_hash)
- @attributes_finder.parse(value) { |hash| value = { value => hash } }
+ @attributes_finder.parse(value) do |hash|
+ value = { value => hash } unless value.is_a?(Hash)
+ end
add_to_array(current_key, json_config_hash, value)
end
diff --git a/lib/gitlab/import_export/relation_factory.rb b/lib/gitlab/import_export/relation_factory.rb
index 695852526cb..20580459046 100644
--- a/lib/gitlab/import_export/relation_factory.rb
+++ b/lib/gitlab/import_export/relation_factory.rb
@@ -71,6 +71,7 @@ module Gitlab
@relation_hash['data'].deep_symbolize_keys! if @relation_name == :events && @relation_hash['data']
set_st_diff_commits if @relation_name == :merge_request_diff
+ set_diff if @relation_name == :merge_request_diff_files
end
def update_user_references
@@ -202,6 +203,10 @@ module Gitlab
HashUtil.deep_symbolize_array_with_date!(@relation_hash['st_commits'])
end
+ def set_diff
+ @relation_hash['diff'] = @relation_hash.delete('utf8_diff')
+ end
+
def existing_or_new_object
# Only find existing records to avoid mapping tables such as milestones
# Otherwise always create the record, skipping the extra SELECT clause.
diff --git a/lib/gitlab/job_waiter.rb b/lib/gitlab/job_waiter.rb
index 8db91d25a4b..208f0e1bbea 100644
--- a/lib/gitlab/job_waiter.rb
+++ b/lib/gitlab/job_waiter.rb
@@ -14,7 +14,7 @@ module Gitlab
# timeout - The maximum amount of seconds to block the caller for. This
# ensures we don't indefinitely block a caller in case a job takes
# long to process, or is never processed.
- def wait(timeout = 60)
+ def wait(timeout = 10)
start = Time.current
while (Time.current - start) <= timeout
diff --git a/lib/gitlab/kubernetes.rb b/lib/gitlab/kubernetes.rb
index 4a6091488c8..c56c1a4322f 100644
--- a/lib/gitlab/kubernetes.rb
+++ b/lib/gitlab/kubernetes.rb
@@ -8,13 +8,13 @@ module Gitlab
)
# Filters an array of pods (as returned by the kubernetes API) by their labels
- def filter_pods(pods, labels = {})
- pods.select do |pod|
- metadata = pod.fetch("metadata", {})
- pod_labels = metadata.fetch("labels", nil)
- next unless pod_labels
+ def filter_by_label(items, labels = {})
+ items.select do |item|
+ metadata = item.fetch("metadata", {})
+ item_labels = metadata.fetch("labels", nil)
+ next unless item_labels
- labels.all? { |k, v| pod_labels[k.to_s] == v }
+ labels.all? { |k, v| item_labels[k.to_s] == v }
end
end
diff --git a/lib/gitlab/slash_commands/command_definition.rb b/lib/gitlab/quick_actions/command_definition.rb
index caab8856014..3937d9c153a 100644
--- a/lib/gitlab/slash_commands/command_definition.rb
+++ b/lib/gitlab/quick_actions/command_definition.rb
@@ -1,5 +1,5 @@
module Gitlab
- module SlashCommands
+ module QuickActions
class CommandDefinition
attr_accessor :name, :aliases, :description, :explanation, :params,
:condition_block, :parse_params_block, :action_block
diff --git a/lib/gitlab/slash_commands/dsl.rb b/lib/gitlab/quick_actions/dsl.rb
index 1b5b4566d81..a4a97236ffc 100644
--- a/lib/gitlab/slash_commands/dsl.rb
+++ b/lib/gitlab/quick_actions/dsl.rb
@@ -1,5 +1,5 @@
module Gitlab
- module SlashCommands
+ module QuickActions
module Dsl
extend ActiveSupport::Concern
@@ -14,7 +14,7 @@ module Gitlab
end
class_methods do
- # Allows to give a description to the next slash command.
+ # Allows to give a description to the next quick action.
# This description is shown in the autocomplete menu.
# It accepts a block that will be evaluated with the context given to
# `CommandDefintion#to_h`.
@@ -31,7 +31,7 @@ module Gitlab
@description = block_given? ? block : text
end
- # Allows to define params for the next slash command.
+ # Allows to define params for the next quick action.
# These params are shown in the autocomplete menu.
#
# Example:
diff --git a/lib/gitlab/slash_commands/extractor.rb b/lib/gitlab/quick_actions/extractor.rb
index 6dbb467d70d..09576be7156 100644
--- a/lib/gitlab/slash_commands/extractor.rb
+++ b/lib/gitlab/quick_actions/extractor.rb
@@ -1,10 +1,10 @@
module Gitlab
- module SlashCommands
+ module QuickActions
# This class takes an array of commands that should be extracted from a
# given text.
#
# ```
- # extractor = Gitlab::SlashCommands::Extractor.new([:open, :assign, :labels])
+ # extractor = Gitlab::QuickActions::Extractor.new([:open, :assign, :labels])
# ```
class Extractor
attr_reader :command_definitions
@@ -24,7 +24,7 @@ module Gitlab
#
# Usage:
# ```
- # extractor = Gitlab::SlashCommands::Extractor.new([:open, :assign, :labels])
+ # extractor = Gitlab::QuickActions::Extractor.new([:open, :assign, :labels])
# msg = %(hello\n/labels ~foo ~"bar baz"\nworld)
# commands = extractor.extract_commands(msg) #=> [['labels', '~foo ~"bar baz"']]
# msg #=> "hello\nworld"
diff --git a/lib/gitlab/repo_path.rb b/lib/gitlab/repo_path.rb
index 878e03f61d7..3591fa9145e 100644
--- a/lib/gitlab/repo_path.rb
+++ b/lib/gitlab/repo_path.rb
@@ -3,16 +3,18 @@ module Gitlab
NotFoundError = Class.new(StandardError)
def self.parse(repo_path)
+ wiki = false
project_path = strip_storage_path(repo_path.sub(/\.git\z/, ''), fail_on_not_found: false)
- project = Project.find_by_full_path(project_path)
- if project_path.end_with?('.wiki') && !project
- project = Project.find_by_full_path(project_path.chomp('.wiki'))
+ project, was_redirected = find_project(project_path)
+
+ if project_path.end_with?('.wiki') && project.nil?
+ project, was_redirected = find_project(project_path.chomp('.wiki'))
wiki = true
- else
- wiki = false
end
- [project, wiki]
+ redirected_path = project_path if was_redirected
+
+ [project, wiki, redirected_path]
end
def self.strip_storage_path(repo_path, fail_on_not_found: true)
@@ -30,5 +32,12 @@ module Gitlab
result.sub(/\A\/*/, '')
end
+
+ def self.find_project(project_path)
+ project = Project.find_by_full_path(project_path, follow_redirects: true)
+ was_redirected = project && project.full_path.casecmp(project_path) != 0
+
+ [project, was_redirected]
+ end
end
end
diff --git a/lib/gitlab/chat_commands/base_command.rb b/lib/gitlab/slash_commands/base_command.rb
index 25da8474e95..cc3c9a50555 100644
--- a/lib/gitlab/chat_commands/base_command.rb
+++ b/lib/gitlab/slash_commands/base_command.rb
@@ -1,5 +1,5 @@
module Gitlab
- module ChatCommands
+ module SlashCommands
class BaseCommand
QUERY_LIMIT = 5
diff --git a/lib/gitlab/chat_commands/command.rb b/lib/gitlab/slash_commands/command.rb
index 3e0c30c33b7..a78408b0519 100644
--- a/lib/gitlab/chat_commands/command.rb
+++ b/lib/gitlab/slash_commands/command.rb
@@ -1,11 +1,11 @@
module Gitlab
- module ChatCommands
+ module SlashCommands
class Command < BaseCommand
COMMANDS = [
- Gitlab::ChatCommands::IssueShow,
- Gitlab::ChatCommands::IssueNew,
- Gitlab::ChatCommands::IssueSearch,
- Gitlab::ChatCommands::Deploy
+ Gitlab::SlashCommands::IssueShow,
+ Gitlab::SlashCommands::IssueNew,
+ Gitlab::SlashCommands::IssueSearch,
+ Gitlab::SlashCommands::Deploy
].freeze
def execute
@@ -15,10 +15,10 @@ module Gitlab
if command.allowed?(project, current_user)
command.new(project, current_user, params).execute(match)
else
- Gitlab::ChatCommands::Presenters::Access.new.access_denied
+ Gitlab::SlashCommands::Presenters::Access.new.access_denied
end
else
- Gitlab::ChatCommands::Help.new(project, current_user, params).execute(available_commands, params[:text])
+ Gitlab::SlashCommands::Help.new(project, current_user, params).execute(available_commands, params[:text])
end
end
diff --git a/lib/gitlab/chat_commands/deploy.rb b/lib/gitlab/slash_commands/deploy.rb
index 458d90f84e8..e71eb15d604 100644
--- a/lib/gitlab/chat_commands/deploy.rb
+++ b/lib/gitlab/slash_commands/deploy.rb
@@ -1,5 +1,5 @@
module Gitlab
- module ChatCommands
+ module SlashCommands
class Deploy < BaseCommand
def self.match(text)
/\Adeploy\s+(?<from>\S+.*)\s+to+\s+(?<to>\S+.*)\z/.match(text)
@@ -24,12 +24,12 @@ module Gitlab
actions = find_actions(from, to)
if actions.none?
- Gitlab::ChatCommands::Presenters::Deploy.new(nil).no_actions
+ Gitlab::SlashCommands::Presenters::Deploy.new(nil).no_actions
elsif actions.one?
action = play!(from, to, actions.first)
- Gitlab::ChatCommands::Presenters::Deploy.new(action).present(from, to)
+ Gitlab::SlashCommands::Presenters::Deploy.new(action).present(from, to)
else
- Gitlab::ChatCommands::Presenters::Deploy.new(actions).too_many_actions
+ Gitlab::SlashCommands::Presenters::Deploy.new(actions).too_many_actions
end
end
diff --git a/lib/gitlab/chat_commands/help.rb b/lib/gitlab/slash_commands/help.rb
index 6c0e4d304a4..81f3707e03e 100644
--- a/lib/gitlab/chat_commands/help.rb
+++ b/lib/gitlab/slash_commands/help.rb
@@ -1,5 +1,5 @@
module Gitlab
- module ChatCommands
+ module SlashCommands
class Help < BaseCommand
# This class has to be used last, as it always matches. It has to match
# because other commands were not triggered and we want to show the help
@@ -17,7 +17,7 @@ module Gitlab
end
def execute(commands, text)
- Gitlab::ChatCommands::Presenters::Help.new(commands).present(trigger, text)
+ Gitlab::SlashCommands::Presenters::Help.new(commands).present(trigger, text)
end
def trigger
diff --git a/lib/gitlab/chat_commands/issue_command.rb b/lib/gitlab/slash_commands/issue_command.rb
index 84de3e44c70..87ea19b8806 100644
--- a/lib/gitlab/chat_commands/issue_command.rb
+++ b/lib/gitlab/slash_commands/issue_command.rb
@@ -1,5 +1,5 @@
module Gitlab
- module ChatCommands
+ module SlashCommands
class IssueCommand < BaseCommand
def self.available?(project)
project.issues_enabled? && project.default_issues_tracker?
diff --git a/lib/gitlab/chat_commands/issue_new.rb b/lib/gitlab/slash_commands/issue_new.rb
index 016054ecd46..25f965e843d 100644
--- a/lib/gitlab/chat_commands/issue_new.rb
+++ b/lib/gitlab/slash_commands/issue_new.rb
@@ -1,5 +1,5 @@
module Gitlab
- module ChatCommands
+ module SlashCommands
class IssueNew < IssueCommand
def self.match(text)
# we can not match \n with the dot by passing the m modifier as than
@@ -35,7 +35,7 @@ module Gitlab
end
def presenter(issue)
- Gitlab::ChatCommands::Presenters::IssueNew.new(issue)
+ Gitlab::SlashCommands::Presenters::IssueNew.new(issue)
end
end
end
diff --git a/lib/gitlab/chat_commands/issue_search.rb b/lib/gitlab/slash_commands/issue_search.rb
index 3491b53093e..acba84b54b4 100644
--- a/lib/gitlab/chat_commands/issue_search.rb
+++ b/lib/gitlab/slash_commands/issue_search.rb
@@ -1,5 +1,5 @@
module Gitlab
- module ChatCommands
+ module SlashCommands
class IssueSearch < IssueCommand
def self.match(text)
/\Aissue\s+search\s+(?<query>.*)/.match(text)
diff --git a/lib/gitlab/chat_commands/issue_show.rb b/lib/gitlab/slash_commands/issue_show.rb
index d6013f4d10c..ffa5184e5cb 100644
--- a/lib/gitlab/chat_commands/issue_show.rb
+++ b/lib/gitlab/slash_commands/issue_show.rb
@@ -1,5 +1,5 @@
module Gitlab
- module ChatCommands
+ module SlashCommands
class IssueShow < IssueCommand
def self.match(text)
/\Aissue\s+show\s+#{Issue.reference_prefix}?(?<iid>\d+)/.match(text)
@@ -13,9 +13,9 @@ module Gitlab
issue = find_by_iid(match[:iid])
if issue
- Gitlab::ChatCommands::Presenters::IssueShow.new(issue).present
+ Gitlab::SlashCommands::Presenters::IssueShow.new(issue).present
else
- Gitlab::ChatCommands::Presenters::Access.new.not_found
+ Gitlab::SlashCommands::Presenters::Access.new.not_found
end
end
end
diff --git a/lib/gitlab/chat_commands/presenters/access.rb b/lib/gitlab/slash_commands/presenters/access.rb
index 92f4fa17f78..1a817eb735b 100644
--- a/lib/gitlab/chat_commands/presenters/access.rb
+++ b/lib/gitlab/slash_commands/presenters/access.rb
@@ -1,5 +1,5 @@
module Gitlab
- module ChatCommands
+ module SlashCommands
module Presenters
class Access < Presenters::Base
def access_denied
diff --git a/lib/gitlab/chat_commands/presenters/base.rb b/lib/gitlab/slash_commands/presenters/base.rb
index 05994bee79d..27696436574 100644
--- a/lib/gitlab/chat_commands/presenters/base.rb
+++ b/lib/gitlab/slash_commands/presenters/base.rb
@@ -1,5 +1,5 @@
module Gitlab
- module ChatCommands
+ module SlashCommands
module Presenters
class Base
include Gitlab::Routing.url_helpers
diff --git a/lib/gitlab/chat_commands/presenters/deploy.rb b/lib/gitlab/slash_commands/presenters/deploy.rb
index 863d0bf99ca..b8dc77bd37b 100644
--- a/lib/gitlab/chat_commands/presenters/deploy.rb
+++ b/lib/gitlab/slash_commands/presenters/deploy.rb
@@ -1,5 +1,5 @@
module Gitlab
- module ChatCommands
+ module SlashCommands
module Presenters
class Deploy < Presenters::Base
def present(from, to)
diff --git a/lib/gitlab/chat_commands/presenters/help.rb b/lib/gitlab/slash_commands/presenters/help.rb
index cd47b7f4c6a..ea611a4d629 100644
--- a/lib/gitlab/chat_commands/presenters/help.rb
+++ b/lib/gitlab/slash_commands/presenters/help.rb
@@ -1,5 +1,5 @@
module Gitlab
- module ChatCommands
+ module SlashCommands
module Presenters
class Help < Presenters::Base
def present(trigger, text)
diff --git a/lib/gitlab/chat_commands/presenters/issue_base.rb b/lib/gitlab/slash_commands/presenters/issue_base.rb
index 25bc82994ba..341f2aabdd0 100644
--- a/lib/gitlab/chat_commands/presenters/issue_base.rb
+++ b/lib/gitlab/slash_commands/presenters/issue_base.rb
@@ -1,5 +1,5 @@
module Gitlab
- module ChatCommands
+ module SlashCommands
module Presenters
module IssueBase
def color(issuable)
diff --git a/lib/gitlab/chat_commands/presenters/issue_new.rb b/lib/gitlab/slash_commands/presenters/issue_new.rb
index 3674ba25641..86490a39cc1 100644
--- a/lib/gitlab/chat_commands/presenters/issue_new.rb
+++ b/lib/gitlab/slash_commands/presenters/issue_new.rb
@@ -1,5 +1,5 @@
module Gitlab
- module ChatCommands
+ module SlashCommands
module Presenters
class IssueNew < Presenters::Base
include Presenters::IssueBase
diff --git a/lib/gitlab/chat_commands/presenters/issue_search.rb b/lib/gitlab/slash_commands/presenters/issue_search.rb
index 73788cf9662..4e27d668685 100644
--- a/lib/gitlab/chat_commands/presenters/issue_search.rb
+++ b/lib/gitlab/slash_commands/presenters/issue_search.rb
@@ -1,5 +1,5 @@
module Gitlab
- module ChatCommands
+ module SlashCommands
module Presenters
class IssueSearch < Presenters::Base
include Presenters::IssueBase
diff --git a/lib/gitlab/chat_commands/presenters/issue_show.rb b/lib/gitlab/slash_commands/presenters/issue_show.rb
index bd784ad241e..c99316df667 100644
--- a/lib/gitlab/chat_commands/presenters/issue_show.rb
+++ b/lib/gitlab/slash_commands/presenters/issue_show.rb
@@ -1,5 +1,5 @@
module Gitlab
- module ChatCommands
+ module SlashCommands
module Presenters
class IssueShow < Presenters::Base
include Presenters::IssueBase
diff --git a/lib/gitlab/chat_commands/result.rb b/lib/gitlab/slash_commands/result.rb
index 324d7ef43a3..7021b4b01b2 100644
--- a/lib/gitlab/chat_commands/result.rb
+++ b/lib/gitlab/slash_commands/result.rb
@@ -1,5 +1,5 @@
module Gitlab
- module ChatCommands
+ module SlashCommands
Result = Struct.new(:type, :message)
end
end
diff --git a/lib/tasks/gitlab/gitaly.rake b/lib/tasks/gitlab/gitaly.rake
index 3c5bc0146a1..e88111c3725 100644
--- a/lib/tasks/gitlab/gitaly.rake
+++ b/lib/tasks/gitlab/gitaly.rake
@@ -30,11 +30,7 @@ namespace :gitlab do
puts "# Gitaly storage configuration generated from #{Gitlab.config.source} on #{Time.current.to_s(:long)}"
puts "# This is in TOML format suitable for use in Gitaly's config.toml file."
- config = Gitlab.config.repositories.storages.map do |key, val|
- { name: key, path: val['path'] }
- end
-
- puts TOML.dump(storage: config)
+ puts gitaly_configuration_toml
end
private
@@ -42,10 +38,10 @@ namespace :gitlab do
# We cannot create config.toml files for all possible Gitaly configuations.
# For instance, if Gitaly is running on another machine then it makes no
# sense to write a config.toml file on the current machine. This method will
- # only write a config.toml file in the most common and simplest case: the
- # case where we have exactly one Gitaly process and we are sure it is
- # running locally because it uses a Unix socket.
- def create_gitaly_configuration
+ # only generate a configuration for the most common and simplest case: when
+ # we have exactly one Gitaly process and we are sure it is running locally
+ # because it uses a Unix socket.
+ def gitaly_configuration_toml
storages = []
address = nil
@@ -63,8 +59,12 @@ namespace :gitlab do
storages << { name: key, path: val['path'] }
end
+ TOML.dump(socket_path: address.sub(%r{\Aunix:}, ''), storage: storages)
+ end
+
+ def create_gitaly_configuration
File.open("config.toml", "w") do |f|
- f.puts TOML.dump(socket_path: address.sub(%r{\Aunix:}, ''), storages: storages)
+ f.puts gitaly_configuration_toml
end
rescue ArgumentError => e
puts "Skipping config.toml generation:"
diff --git a/lib/tasks/migrate/add_limits_mysql.rake b/lib/tasks/migrate/add_limits_mysql.rake
index 761f275d42a..151f42a2222 100644
--- a/lib/tasks/migrate/add_limits_mysql.rake
+++ b/lib/tasks/migrate/add_limits_mysql.rake
@@ -1,9 +1,11 @@
require Rails.root.join('db/migrate/limits_to_mysql')
require Rails.root.join('db/migrate/markdown_cache_limits_to_mysql')
+require Rails.root.join('db/migrate/merge_request_diff_file_limits_to_mysql')
desc "GitLab | Add limits to strings in mysql database"
task add_limits_mysql: :environment do
puts "Adding limits to schema.rb for mysql"
LimitsToMysql.new.up
MarkdownCacheLimitsToMysql.new.up
+ MergeRequestDiffFileLimitsToMysql.new.up
end
diff --git a/locale/bg/gitlab.po b/locale/bg/gitlab.po
index e6caf83252d..43a5de65c43 100644
--- a/locale/bg/gitlab.po
+++ b/locale/bg/gitlab.po
@@ -250,6 +250,9 @@ msgstr "ИÑкате ли да видите данните? Помолете аÐ
msgid "We don't have enough data to show this stage."
msgstr "ÐÑма доÑтатъчно данни за този етап."
+msgid "You have reached your project limit"
+msgstr ""
+
msgid "You need permission."
msgstr "Ðуждаете Ñе от разрешение."
diff --git a/locale/de/gitlab.po b/locale/de/gitlab.po
index 9a660571db9..ea864091b10 100644
--- a/locale/de/gitlab.po
+++ b/locale/de/gitlab.po
@@ -291,6 +291,9 @@ msgstr "Um diese Daten einsehen zu können, wenden Sie sich bitte an Ihren Admin
msgid "We don't have enough data to show this stage."
msgstr "Es liegen nicht genügend Daten vor, um diese Phase anzuzeigen."
+msgid "You have reached your project limit"
+msgstr ""
+
msgid "You need permission."
msgstr "Sie benötigen Zugriffsrechte."
diff --git a/locale/en/gitlab.po b/locale/en/gitlab.po
index 4e44731fc5a..afb8fb3176f 100644
--- a/locale/en/gitlab.po
+++ b/locale/en/gitlab.po
@@ -17,23 +17,217 @@ msgstr ""
"Plural-Forms: nplurals=2; plural=n != 1;\n"
"\n"
+msgid "%{commit_author_link} committed %{commit_timeago}"
+msgstr ""
+
+msgid "About auto deploy"
+msgstr ""
+
+msgid "Active"
+msgstr ""
+
+msgid "Activity"
+msgstr ""
+
+msgid "Add Changelog"
+msgstr ""
+
+msgid "Add Contribution guide"
+msgstr ""
+
+msgid "Add License"
+msgstr ""
+
+msgid "Add an SSH key to your profile to pull or push via SSH."
+msgstr ""
+
+msgid "Add new directory"
+msgstr ""
+
+msgid "Archived project! Repository is read-only"
+msgstr ""
+
msgid "Are you sure you want to delete this pipeline schedule?"
msgstr ""
+msgid "Attach a file by drag &amp; drop or %{upload_link}"
+msgstr ""
+
+msgid "Branch"
+msgid_plural "Branches"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "Branch <strong>%{branch_name}</strong> was created. To set up auto deploy, choose a GitLab CI Yaml template and commit your changes. %{link_to_autodeploy_doc}"
+msgstr ""
+
+msgid "Branches"
+msgstr ""
+
+msgid "Browse files"
+msgstr ""
+
msgid "ByAuthor|by"
msgstr ""
+msgid "CI configuration"
+msgstr ""
+
msgid "Cancel"
msgstr ""
+msgid "ChangeTypeActionLabel|Pick into branch"
+msgstr ""
+
+msgid "ChangeTypeActionLabel|Revert in branch"
+msgstr ""
+
+msgid "ChangeTypeAction|Cherry-pick"
+msgstr ""
+
+msgid "ChangeTypeAction|Revert"
+msgstr ""
+
+msgid "Changelog"
+msgstr ""
+
+msgid "Charts"
+msgstr ""
+
+msgid "Cherry-pick this commit"
+msgstr ""
+
+msgid "Cherry-pick this merge request"
+msgstr ""
+
+msgid "CiStatusLabel|canceled"
+msgstr ""
+
+msgid "CiStatusLabel|created"
+msgstr ""
+
+msgid "CiStatusLabel|failed"
+msgstr ""
+
+msgid "CiStatusLabel|manual action"
+msgstr ""
+
+msgid "CiStatusLabel|passed"
+msgstr ""
+
+msgid "CiStatusLabel|passed with warnings"
+msgstr ""
+
+msgid "CiStatusLabel|pending"
+msgstr ""
+
+msgid "CiStatusLabel|skipped"
+msgstr ""
+
+msgid "CiStatusLabel|waiting for manual action"
+msgstr ""
+
+msgid "CiStatusText|blocked"
+msgstr ""
+
+msgid "CiStatusText|canceled"
+msgstr ""
+
+msgid "CiStatusText|created"
+msgstr ""
+
+msgid "CiStatusText|failed"
+msgstr ""
+
+msgid "CiStatusText|manual"
+msgstr ""
+
+msgid "CiStatusText|passed"
+msgstr ""
+
+msgid "CiStatusText|pending"
+msgstr ""
+
+msgid "CiStatusText|skipped"
+msgstr ""
+
+msgid "CiStatus|running"
+msgstr ""
+
msgid "Commit"
msgid_plural "Commits"
msgstr[0] ""
msgstr[1] ""
+msgid "Commit message"
+msgstr ""
+
+msgid "CommitBoxTitle|Commit"
+msgstr ""
+
+msgid "CommitMessage|Add %{file_name}"
+msgstr ""
+
+msgid "Commits"
+msgstr ""
+
+msgid "Commits|History"
+msgstr ""
+
+msgid "Committed by"
+msgstr ""
+
+msgid "Compare"
+msgstr ""
+
+msgid "Contribution guide"
+msgstr ""
+
+msgid "Contributors"
+msgstr ""
+
+msgid "Copy URL to clipboard"
+msgstr ""
+
+msgid "Copy commit SHA to clipboard"
+msgstr ""
+
+msgid "Create New Directory"
+msgstr ""
+
+msgid "Create directory"
+msgstr ""
+
+msgid "Create empty bare repository"
+msgstr ""
+
+msgid "Create merge request"
+msgstr ""
+
+msgid "Create new..."
+msgstr ""
+
+msgid "CreateNewFork|Fork"
+msgstr ""
+
+msgid "CreateTag|Tag"
+msgstr ""
+
msgid "Cron Timezone"
msgstr ""
+msgid "Cron syntax"
+msgstr ""
+
+msgid "Custom notification events"
+msgstr ""
+
+msgid "Custom notification levels are the same as participating levels. With custom notification levels you will also receive notifications for select events. To find out more, check out %{notification_link}."
+msgstr ""
+
+msgid "Cycle Analytics"
+msgstr ""
+
msgid "Cycle Analytics gives an overview of how much time it takes to go from idea to production in your project."
msgstr ""
@@ -58,6 +252,9 @@ msgstr ""
msgid "CycleAnalyticsStage|Test"
msgstr ""
+msgid "Define a custom pattern with cron syntax"
+msgstr ""
+
msgid "Delete"
msgstr ""
@@ -69,19 +266,67 @@ msgstr[1] ""
msgid "Description"
msgstr ""
+msgid "Directory name"
+msgstr ""
+
+msgid "Don't show again"
+msgstr ""
+
+msgid "Download"
+msgstr ""
+
+msgid "Download tar"
+msgstr ""
+
+msgid "Download tar.bz2"
+msgstr ""
+
+msgid "Download tar.gz"
+msgstr ""
+
+msgid "Download zip"
+msgstr ""
+
+msgid "DownloadArtifacts|Download"
+msgstr ""
+
+msgid "DownloadCommit|Email Patches"
+msgstr ""
+
+msgid "DownloadCommit|Plain Diff"
+msgstr ""
+
+msgid "DownloadSource|Download"
+msgstr ""
+
msgid "Edit"
msgstr ""
msgid "Edit Pipeline Schedule %{id}"
msgstr ""
+msgid "Every day (at 4:00am)"
+msgstr ""
+
+msgid "Every month (on the 1st at 4:00am)"
+msgstr ""
+
+msgid "Every week (Sundays at 4:00am)"
+msgstr ""
+
msgid "Failed to change the owner"
msgstr ""
msgid "Failed to remove the pipeline schedule"
msgstr ""
-msgid "Filter"
+msgid "Files"
+msgstr ""
+
+msgid "Find by path"
+msgstr ""
+
+msgid "Find file"
msgstr ""
msgid "FirstPushedBy|First"
@@ -90,18 +335,47 @@ msgstr ""
msgid "FirstPushedBy|pushed by"
msgstr ""
+msgid "Fork"
+msgid_plural "Forks"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "ForkedFromProjectPath|Forked from"
+msgstr ""
+
msgid "From issue creation until deploy to production"
msgstr ""
msgid "From merge request merge until deploy to production"
msgstr ""
+msgid "Go to your fork"
+msgstr ""
+
+msgid "GoToYourFork|Fork"
+msgstr ""
+
+msgid "Home"
+msgstr ""
+
+msgid "Housekeeping successfully started"
+msgstr ""
+
+msgid "Import repository"
+msgstr ""
+
msgid "Interval Pattern"
msgstr ""
msgid "Introducing Cycle Analytics"
msgstr ""
+msgid "LFSStatus|Disabled"
+msgstr ""
+
+msgid "LFSStatus|Enabled"
+msgstr ""
+
msgid "Last %d day"
msgid_plural "Last %d days"
msgstr[0] ""
@@ -110,6 +384,24 @@ msgstr[1] ""
msgid "Last Pipeline"
msgstr ""
+msgid "Last Update"
+msgstr ""
+
+msgid "Last commit"
+msgstr ""
+
+msgid "Learn more in the"
+msgstr ""
+
+msgid "Learn more in the|pipeline schedules documentation"
+msgstr ""
+
+msgid "Leave group"
+msgstr ""
+
+msgid "Leave project"
+msgstr ""
+
msgid "Limited to showing %d event at most"
msgid_plural "Limited to showing %d events at most"
msgstr[0] ""
@@ -118,6 +410,9 @@ msgstr[1] ""
msgid "Median"
msgstr ""
+msgid "MissingSSHKeyWarningLink|add an SSH key"
+msgstr ""
+
msgid "New Issue"
msgid_plural "New Issues"
msgstr[0] ""
@@ -126,6 +421,33 @@ msgstr[1] ""
msgid "New Pipeline Schedule"
msgstr ""
+msgid "New branch"
+msgstr ""
+
+msgid "New directory"
+msgstr ""
+
+msgid "New file"
+msgstr ""
+
+msgid "New issue"
+msgstr ""
+
+msgid "New merge request"
+msgstr ""
+
+msgid "New schedule"
+msgstr ""
+
+msgid "New snippet"
+msgstr ""
+
+msgid "New tag"
+msgstr ""
+
+msgid "No repository"
+msgstr ""
+
msgid "No schedules"
msgstr ""
@@ -135,12 +457,75 @@ msgstr ""
msgid "Not enough data"
msgstr ""
+msgid "Notification events"
+msgstr ""
+
+msgid "NotificationEvent|Close issue"
+msgstr ""
+
+msgid "NotificationEvent|Close merge request"
+msgstr ""
+
+msgid "NotificationEvent|Failed pipeline"
+msgstr ""
+
+msgid "NotificationEvent|Merge merge request"
+msgstr ""
+
+msgid "NotificationEvent|New issue"
+msgstr ""
+
+msgid "NotificationEvent|New merge request"
+msgstr ""
+
+msgid "NotificationEvent|New note"
+msgstr ""
+
+msgid "NotificationEvent|Reassign issue"
+msgstr ""
+
+msgid "NotificationEvent|Reassign merge request"
+msgstr ""
+
+msgid "NotificationEvent|Reopen issue"
+msgstr ""
+
+msgid "NotificationEvent|Successful pipeline"
+msgstr ""
+
+msgid "NotificationLevel|Custom"
+msgstr ""
+
+msgid "NotificationLevel|Disabled"
+msgstr ""
+
+msgid "NotificationLevel|Global"
+msgstr ""
+
+msgid "NotificationLevel|On mention"
+msgstr ""
+
+msgid "NotificationLevel|Participate"
+msgstr ""
+
+msgid "NotificationLevel|Watch"
+msgstr ""
+
+msgid "OfSearchInADropdown|Filter"
+msgstr ""
+
msgid "OpenedNDaysAgo|Opened"
msgstr ""
+msgid "Options"
+msgstr ""
+
msgid "Owner"
msgstr ""
+msgid "Pipeline"
+msgstr ""
+
msgid "Pipeline Health"
msgstr ""
@@ -177,12 +562,78 @@ msgstr ""
msgid "PipelineSchedules|Target"
msgstr ""
+msgid "PipelineSheduleIntervalPattern|Custom"
+msgstr ""
+
+msgid "Pipeline|with stage"
+msgstr ""
+
+msgid "Pipeline|with stages"
+msgstr ""
+
+msgid "Project '%{project_name}' queued for deletion."
+msgstr ""
+
+msgid "Project '%{project_name}' was successfully created."
+msgstr ""
+
+msgid "Project '%{project_name}' was successfully updated."
+msgstr ""
+
+msgid "Project '%{project_name}' will be deleted."
+msgstr ""
+
+msgid "Project access must be granted explicitly to each user."
+msgstr ""
+
+msgid "Project export could not be deleted."
+msgstr ""
+
+msgid "Project export has been deleted."
+msgstr ""
+
+msgid "Project export link has expired. Please generate a new export from your project settings."
+msgstr ""
+
+msgid "Project export started. A download link will be sent by email."
+msgstr ""
+
+msgid "Project home"
+msgstr ""
+
+msgid "ProjectFeature|Disabled"
+msgstr ""
+
+msgid "ProjectFeature|Everyone with access"
+msgstr ""
+
+msgid "ProjectFeature|Only team members"
+msgstr ""
+
+msgid "ProjectFileTree|Name"
+msgstr ""
+
+msgid "ProjectLastActivity|Never"
+msgstr ""
+
msgid "ProjectLifecycle|Stage"
msgstr ""
+msgid "ProjectNetworkGraph|Graph"
+msgstr ""
+
msgid "Read more"
msgstr ""
+msgid "Readme"
+msgstr ""
+
+msgid "RefSwitcher|Branches"
+msgstr ""
+
+msgid "RefSwitcher|Tags"
+msgstr ""
+
msgid "Related Commits"
msgstr ""
@@ -201,23 +652,85 @@ msgstr ""
msgid "Related Merged Requests"
msgstr ""
+msgid "Remind later"
+msgstr ""
+
+msgid "Remove project"
+msgstr ""
+
+msgid "Request Access"
+msgstr ""
+
+msgid "Revert this commit"
+msgstr ""
+
+msgid "Revert this merge request"
+msgstr ""
+
msgid "Save pipeline schedule"
msgstr ""
msgid "Schedule a new pipeline"
msgstr ""
+msgid "Scheduling Pipelines"
+msgstr ""
+
+msgid "Search branches and tags"
+msgstr ""
+
+msgid "Select Archive Format"
+msgstr ""
+
msgid "Select a timezone"
msgstr ""
msgid "Select target branch"
msgstr ""
+msgid "Set a password on your account to pull or push via %{protocol}"
+msgstr ""
+
+msgid "Set up CI"
+msgstr ""
+
+msgid "Set up Koding"
+msgstr ""
+
+msgid "Set up auto deploy"
+msgstr ""
+
+msgid "SetPasswordToCloneLink|set a password"
+msgstr ""
+
msgid "Showing %d event"
msgid_plural "Showing %d events"
msgstr[0] ""
msgstr[1] ""
+msgid "Source code"
+msgstr ""
+
+msgid "StarProject|Star"
+msgstr ""
+
+msgid "Start a %{new_merge_request} with these changes"
+msgstr ""
+
+msgid "Start a <strong>new merge request</strong> with these changes"
+msgstr ""
+
+msgid "Switch branch/tag"
+msgstr ""
+
+msgid "Tag"
+msgid_plural "Tags"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "Tags"
+msgstr ""
+
msgid "Target Branch"
msgstr ""
@@ -227,18 +740,33 @@ msgstr ""
msgid "The collection of events added to the data gathered for that stage."
msgstr ""
+msgid "The fork relationship has been removed."
+msgstr ""
+
msgid "The issue stage shows the time it takes from creating an issue to assigning the issue to a milestone, or add the issue to a list on your Issue Board. Begin creating issues to see data for this stage."
msgstr ""
msgid "The phase of the development lifecycle."
msgstr ""
+msgid "The pipelines schedule runs pipelines in the future, repeatedly, for specific branches or tags. Those scheduled pipelines will inherit limited project access based on their associated user."
+msgstr ""
+
msgid "The planning stage shows the time from the previous step to pushing your first commit. This time will be added automatically once you push your first commit."
msgstr ""
msgid "The production stage shows the total time it takes between creating an issue and deploying the code to production. The data will be automatically added once you have completed the full idea to production cycle."
msgstr ""
+msgid "The project can be accessed by any logged in user."
+msgstr ""
+
+msgid "The project can be accessed without any authentication."
+msgstr ""
+
+msgid "The repository for this project does not exist."
+msgstr ""
+
msgid "The review stage shows the time from creating the merge request to merging it. The data will automatically be added after you merge your first merge request."
msgstr ""
@@ -254,6 +782,9 @@ msgstr ""
msgid "The value lying at the midpoint of a series of observed values. E.g., between 3, 5, 9, the median is 5. Between 3, 5, 7, 8, the median is (5+7)/2 = 6."
msgstr ""
+msgid "This means you can not push code until you create an empty repository or import existing one."
+msgstr ""
+
msgid "Time before an issue gets scheduled"
msgstr ""
@@ -266,6 +797,129 @@ msgstr ""
msgid "Time until first merge request"
msgstr ""
+msgid "Timeago|%s days ago"
+msgstr ""
+
+msgid "Timeago|%s days remaining"
+msgstr ""
+
+msgid "Timeago|%s hours remaining"
+msgstr ""
+
+msgid "Timeago|%s minutes ago"
+msgstr ""
+
+msgid "Timeago|%s minutes remaining"
+msgstr ""
+
+msgid "Timeago|%s months ago"
+msgstr ""
+
+msgid "Timeago|%s months remaining"
+msgstr ""
+
+msgid "Timeago|%s seconds remaining"
+msgstr ""
+
+msgid "Timeago|%s weeks ago"
+msgstr ""
+
+msgid "Timeago|%s weeks remaining"
+msgstr ""
+
+msgid "Timeago|%s years ago"
+msgstr ""
+
+msgid "Timeago|%s years remaining"
+msgstr ""
+
+msgid "Timeago|1 day remaining"
+msgstr ""
+
+msgid "Timeago|1 hour remaining"
+msgstr ""
+
+msgid "Timeago|1 minute remaining"
+msgstr ""
+
+msgid "Timeago|1 month remaining"
+msgstr ""
+
+msgid "Timeago|1 week remaining"
+msgstr ""
+
+msgid "Timeago|1 year remaining"
+msgstr ""
+
+msgid "Timeago|Past due"
+msgstr ""
+
+msgid "Timeago|a day ago"
+msgstr ""
+
+msgid "Timeago|a month ago"
+msgstr ""
+
+msgid "Timeago|a week ago"
+msgstr ""
+
+msgid "Timeago|a while"
+msgstr ""
+
+msgid "Timeago|a year ago"
+msgstr ""
+
+msgid "Timeago|about %s hours ago"
+msgstr ""
+
+msgid "Timeago|about a minute ago"
+msgstr ""
+
+msgid "Timeago|about an hour ago"
+msgstr ""
+
+msgid "Timeago|in %s days"
+msgstr ""
+
+msgid "Timeago|in %s hours"
+msgstr ""
+
+msgid "Timeago|in %s minutes"
+msgstr ""
+
+msgid "Timeago|in %s months"
+msgstr ""
+
+msgid "Timeago|in %s seconds"
+msgstr ""
+
+msgid "Timeago|in %s weeks"
+msgstr ""
+
+msgid "Timeago|in %s years"
+msgstr ""
+
+msgid "Timeago|in 1 day"
+msgstr ""
+
+msgid "Timeago|in 1 hour"
+msgstr ""
+
+msgid "Timeago|in 1 minute"
+msgstr ""
+
+msgid "Timeago|in 1 month"
+msgstr ""
+
+msgid "Timeago|in 1 week"
+msgstr ""
+
+msgid "Timeago|in 1 year"
+msgstr ""
+
+msgid "Timeago|less than a minute ago"
+msgstr ""
+
msgid "Time|hr"
msgid_plural "Time|hrs"
msgstr[0] ""
@@ -285,16 +939,96 @@ msgstr ""
msgid "Total test time for all commits/merges"
msgstr ""
+msgid "Unstar"
+msgstr ""
+
+msgid "Upload New File"
+msgstr ""
+
+msgid "Upload file"
+msgstr ""
+
+msgid "Use your global notification setting"
+msgstr ""
+
+msgid "VisibilityLevel|Internal"
+msgstr ""
+
+msgid "VisibilityLevel|Private"
+msgstr ""
+
+msgid "VisibilityLevel|Public"
+msgstr ""
+
msgid "Want to see the data? Please ask an administrator for access."
msgstr ""
msgid "We don't have enough data to show this stage."
msgstr ""
+msgid "Withdraw Access Request"
+msgstr ""
+
+msgid ""
+"You are going to remove %{project_name_with_namespace}.\n"
+"Removed project CANNOT be restored!\n"
+"Are you ABSOLUTELY sure?"
+msgstr ""
+
+msgid "You are going to remove the fork relationship to source project %{forked_from_project}. Are you ABSOLUTELY sure?"
+msgstr ""
+
+msgid "You are going to transfer %{project_name_with_namespace} to another owner. Are you ABSOLUTELY sure?"
+msgstr ""
+
+msgid "You can only add files when you are on a branch"
+msgstr ""
+
+msgid "You have reached your project limit"
+msgstr ""
+
+msgid "You must sign in to star a project"
+msgstr ""
+
msgid "You need permission."
msgstr ""
+msgid "You will not get any notifications via email"
+msgstr ""
+
+msgid "You will only receive notifications for the events you choose"
+msgstr ""
+
+msgid "You will only receive notifications for threads you have participated in"
+msgstr ""
+
+msgid "You will receive notifications for any activity"
+msgstr ""
+
+msgid "You will receive notifications only for comments in which you were @mentioned"
+msgstr ""
+
+msgid "You won't be able to pull or push project code via %{protocol} until you %{set_password_link} on your account"
+msgstr ""
+
+msgid "You won't be able to pull or push project code via SSH until you %{add_ssh_key_link} to your profile"
+msgstr ""
+
+msgid "Your name"
+msgstr ""
+
msgid "day"
msgid_plural "days"
msgstr[0] ""
msgstr[1] ""
+
+msgid "new merge request"
+msgstr ""
+
+msgid "notification emails"
+msgstr ""
+
+msgid "parent"
+msgid_plural "parents"
+msgstr[0] ""
+msgstr[1] ""
diff --git a/locale/es/gitlab.po b/locale/es/gitlab.po
index 78d28d69885..6946a256308 100644
--- a/locale/es/gitlab.po
+++ b/locale/es/gitlab.po
@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: gitlab 1.0.0\n"
"Report-Msgid-Bugs-To: \n"
-"PO-Revision-Date: 2017-06-07 12:29-0500\n"
+"PO-Revision-Date: 2017-06-15 21:59-0500\n"
"Language-Team: Spanish\n"
"Language: es\n"
"MIME-Version: 1.0\n"
@@ -17,9 +17,15 @@ msgstr ""
"Last-Translator: Bob Van Landuyt <bob@gitlab.com>\n"
"X-Generator: Poedit 2.0.2\n"
+msgid "%{commit_author_link} committed %{commit_timeago}"
+msgstr "%{commit_author_link} cambió %{commit_timeago}"
+
msgid "About auto deploy"
msgstr "Acerca del auto despliegue"
+msgid "Active"
+msgstr "Activo"
+
msgid "Activity"
msgstr "Actividad"
@@ -39,7 +45,13 @@ msgid "Add new directory"
msgstr "Agregar nuevo directorio"
msgid "Archived project! Repository is read-only"
-msgstr "¡Proyecto archivado! El repositorio es de sólo lectura"
+msgstr "¡Proyecto archivado! El repositorio es de solo lectura"
+
+msgid "Are you sure you want to delete this pipeline schedule?"
+msgstr "¿Estás seguro que deseas eliminar esta programación del pipeline?"
+
+msgid "Attach a file by drag &amp; drop or %{upload_link}"
+msgstr "Adjunte un archivo arrastrando &amp; soltando o %{upload_link}"
msgid "Branch"
msgid_plural "Branches"
@@ -52,18 +64,42 @@ msgstr "La rama <strong>%{branch_name}</strong> fue creada. Para configurar el a
msgid "Branches"
msgstr "Ramas"
+msgid "Browse files"
+msgstr "Examinar los archivos"
+
msgid "ByAuthor|by"
msgstr "por"
msgid "CI configuration"
msgstr "Configuración de CI"
+msgid "Cancel"
+msgstr "Cancelar"
+
+msgid "ChangeTypeActionLabel|Pick into branch"
+msgstr "Escoger en la rama"
+
+msgid "ChangeTypeActionLabel|Revert in branch"
+msgstr "Revertir en la rama"
+
+msgid "ChangeTypeAction|Cherry-pick"
+msgstr "Cherry-pick"
+
+msgid "ChangeTypeAction|Revert"
+msgstr "Revertir"
+
msgid "Changelog"
msgstr "Changelog"
msgid "Charts"
msgstr "Gráficos"
+msgid "Cherry-pick this commit"
+msgstr "Escoger este cambio"
+
+msgid "Cherry-pick this merge request"
+msgstr "Escoger esta solicitud de fusión"
+
msgid "CiStatusLabel|canceled"
msgstr "cancelado"
@@ -71,7 +107,7 @@ msgid "CiStatusLabel|created"
msgstr "creado"
msgid "CiStatusLabel|failed"
-msgstr "fallado"
+msgstr "fallido"
msgid "CiStatusLabel|manual action"
msgstr "acción manual"
@@ -123,6 +159,12 @@ msgid_plural "Commits"
msgstr[0] "Cambio"
msgstr[1] "Cambios"
+msgid "Commit message"
+msgstr "Mensaje del cambio"
+
+msgid "CommitBoxTitle|Commit"
+msgstr "Cambio"
+
msgid "CommitMessage|Add %{file_name}"
msgstr "Agregar %{file_name}"
@@ -132,6 +174,9 @@ msgstr "Cambios"
msgid "Commits|History"
msgstr "Historial"
+msgid "Committed by"
+msgstr "Enviado por"
+
msgid "Compare"
msgstr "Comparar"
@@ -159,9 +204,21 @@ msgstr "Crear repositorio vacío"
msgid "Create merge request"
msgstr "Crear solicitud de fusión"
+msgid "Create new..."
+msgstr "Crear nuevo..."
+
msgid "CreateNewFork|Fork"
msgstr "Bifurcar"
+msgid "CreateTag|Tag"
+msgstr "Etiqueta"
+
+msgid "Cron Timezone"
+msgstr "Zona horaria del Cron"
+
+msgid "Cron syntax"
+msgstr "Sintaxis de Cron"
+
msgid "Custom notification events"
msgstr "Eventos de notificaciones personalizadas"
@@ -195,17 +252,29 @@ msgstr "Puesta en escena"
msgid "CycleAnalyticsStage|Test"
msgstr "Pruebas"
+msgid "Define a custom pattern with cron syntax"
+msgstr "Definir un patrón personalizado con la sintaxis de cron"
+
+msgid "Delete"
+msgstr "Eliminar"
+
msgid "Deploy"
msgid_plural "Deploys"
msgstr[0] "Despliegue"
msgstr[1] "Despliegues"
+msgid "Description"
+msgstr "Descripción"
+
msgid "Directory name"
msgstr "Nombre del directorio"
msgid "Don't show again"
msgstr "No mostrar de nuevo"
+msgid "Download"
+msgstr "Descargar"
+
msgid "Download tar"
msgstr "Descargar tar"
@@ -221,9 +290,36 @@ msgstr "Descargar zip"
msgid "DownloadArtifacts|Download"
msgstr "Descargar"
+msgid "DownloadCommit|Email Patches"
+msgstr "Parches por correo electrónico"
+
+msgid "DownloadCommit|Plain Diff"
+msgstr "Diferencias en texto plano"
+
msgid "DownloadSource|Download"
msgstr "Descargar"
+msgid "Edit"
+msgstr "Editar"
+
+msgid "Edit Pipeline Schedule %{id}"
+msgstr "Editar Programación del Pipeline %{id}"
+
+msgid "Every day (at 4:00am)"
+msgstr "Todos los días (a las 4:00 am)"
+
+msgid "Every month (on the 1st at 4:00am)"
+msgstr "Todos los meses (el día 1 a las 4:00 am)"
+
+msgid "Every week (Sundays at 4:00am)"
+msgstr "Todas las semanas (domingos a las 4:00 am)"
+
+msgid "Failed to change the owner"
+msgstr "Error al cambiar el propietario"
+
+msgid "Failed to remove the pipeline schedule"
+msgstr "Error al eliminar la programación del pipeline"
+
msgid "Files"
msgstr "Archivos"
@@ -239,12 +335,14 @@ msgstr "Primer"
msgid "FirstPushedBy|pushed by"
msgstr "enviado por"
+msgid "Fork"
+msgid_plural "Forks"
+msgstr[0] "Bifurcación"
+msgstr[1] "Bifurcaciones"
+
msgid "ForkedFromProjectPath|Forked from"
msgstr "Bifurcado de"
-msgid "Forks"
-msgstr "Bifurcaciones"
-
msgid "From issue creation until deploy to production"
msgstr "Desde la creación de la incidencia hasta el despliegue a producción"
@@ -266,6 +364,9 @@ msgstr "Servicio de limpieza iniciado con éxito"
msgid "Import repository"
msgstr "Importar repositorio"
+msgid "Interval Pattern"
+msgstr "Patrón de intervalo"
+
msgid "Introducing Cycle Analytics"
msgstr "Introducción a Cycle Analytics"
@@ -280,12 +381,21 @@ msgid_plural "Last %d days"
msgstr[0] "Último %d día"
msgstr[1] "Últimos %d días"
+msgid "Last Pipeline"
+msgstr "Último Pipeline"
+
msgid "Last Update"
msgstr "Última actualización"
msgid "Last commit"
msgstr "Último cambio"
+msgid "Learn more in the"
+msgstr "Más información en la"
+
+msgid "Learn more in the|pipeline schedules documentation"
+msgstr "documentación sobre la programación de pipelines"
+
msgid "Leave group"
msgstr "Abandonar grupo"
@@ -308,6 +418,9 @@ msgid_plural "New Issues"
msgstr[0] "Nueva incidencia"
msgstr[1] "Nuevas incidencias"
+msgid "New Pipeline Schedule"
+msgstr "Nueva Programación del Pipeline"
+
msgid "New branch"
msgstr "Nueva rama"
@@ -323,6 +436,9 @@ msgstr "Nueva incidencia"
msgid "New merge request"
msgstr "Nueva solicitud de fusión"
+msgid "New schedule"
+msgstr "Nueva programación"
+
msgid "New snippet"
msgstr "Nuevo fragmento de código"
@@ -332,6 +448,9 @@ msgstr "Nueva etiqueta"
msgid "No repository"
msgstr "No hay repositorio"
+msgid "No schedules"
+msgstr "No hay programaciones"
+
msgid "Not available"
msgstr "No disponible"
@@ -392,12 +511,66 @@ msgstr "Participación"
msgid "NotificationLevel|Watch"
msgstr "Vigilancia"
+msgid "OfSearchInADropdown|Filter"
+msgstr "Filtrar"
+
msgid "OpenedNDaysAgo|Opened"
msgstr "Abierto"
+msgid "Options"
+msgstr "Opciones"
+
+msgid "Owner"
+msgstr "Propietario"
+
+msgid "Pipeline"
+msgstr "Pipeline"
+
msgid "Pipeline Health"
msgstr "Estado del Pipeline"
+msgid "Pipeline Schedule"
+msgstr "Programación del Pipeline"
+
+msgid "Pipeline Schedules"
+msgstr "Programaciones de los Pipelines"
+
+msgid "PipelineSchedules|Activated"
+msgstr "Activado"
+
+msgid "PipelineSchedules|Active"
+msgstr "Activos"
+
+msgid "PipelineSchedules|All"
+msgstr "Todos"
+
+msgid "PipelineSchedules|Inactive"
+msgstr "Inactivos"
+
+msgid "PipelineSchedules|Next Run"
+msgstr "Próxima Ejecución"
+
+msgid "PipelineSchedules|None"
+msgstr "Ninguno"
+
+msgid "PipelineSchedules|Provide a short description for this pipeline"
+msgstr "Proporcione una breve descripción para este pipeline"
+
+msgid "PipelineSchedules|Take ownership"
+msgstr "Tomar posesión"
+
+msgid "PipelineSchedules|Target"
+msgstr "Destino"
+
+msgid "PipelineSheduleIntervalPattern|Custom"
+msgstr "Personalizado"
+
+msgid "Pipeline|with stage"
+msgstr "con etapa"
+
+msgid "Pipeline|with stages"
+msgstr "con etapas"
+
msgid "Project '%{project_name}' queued for deletion."
msgstr "Proyecto ‘%{project_name}’ en cola para eliminación."
@@ -453,7 +626,7 @@ msgid "Read more"
msgstr "Leer más"
msgid "Readme"
-msgstr "Readme"
+msgstr "Léeme"
msgid "RefSwitcher|Branches"
msgstr "Ramas"
@@ -488,14 +661,35 @@ msgstr "Eliminar proyecto"
msgid "Request Access"
msgstr "Solicitar acceso"
+msgid "Revert this commit"
+msgstr "Revertir este cambio"
+
+msgid "Revert this merge request"
+msgstr "Revertir esta solicitud de fusión"
+
+msgid "Save pipeline schedule"
+msgstr "Guardar programación del pipeline"
+
+msgid "Schedule a new pipeline"
+msgstr "Programar un nuevo pipeline"
+
+msgid "Scheduling Pipelines"
+msgstr "Programación de Pipelines"
+
msgid "Search branches and tags"
msgstr "Buscar ramas y etiquetas"
msgid "Select Archive Format"
msgstr "Seleccionar formato de archivo"
+msgid "Select a timezone"
+msgstr "Selecciona una zona horaria"
+
+msgid "Select target branch"
+msgstr "Selecciona una rama de destino"
+
msgid "Set a password on your account to pull or push via %{protocol}"
-msgstr "Establezca una contraseña en su cuenta para actualizar o enviar a través de% {protocol}"
+msgstr "Establezca una contraseña en su cuenta para actualizar o enviar a través de %{protocol}"
msgid "Set up CI"
msgstr "Configurar CI"
@@ -520,6 +714,9 @@ msgstr "Código fuente"
msgid "StarProject|Star"
msgstr "Destacar"
+msgid "Start a %{new_merge_request} with these changes"
+msgstr "Iniciar una %{new_merge_request} con estos cambios"
+
msgid "Switch branch/tag"
msgstr "Cambiar rama/etiqueta"
@@ -531,6 +728,9 @@ msgstr[1] "Etiquetas"
msgid "Tags"
msgstr "Etiquetas"
+msgid "Target Branch"
+msgstr "Rama de destino"
+
msgid "The coding stage shows the time from the first commit to creating the merge request. The data will automatically be added here once you create your first merge request."
msgstr "La etapa de desarrollo muestra el tiempo desde el primer cambio hasta la creación de la solicitud de fusión. Los datos serán automáticamente incorporados aquí una vez creada tu primera solicitud de fusión."
@@ -546,6 +746,9 @@ msgstr "La etapa de incidencia muestra el tiempo que toma desde la creación de
msgid "The phase of the development lifecycle."
msgstr "La etapa del ciclo de vida de desarrollo."
+msgid "The pipelines schedule runs pipelines in the future, repeatedly, for specific branches or tags. Those scheduled pipelines will inherit limited project access based on their associated user."
+msgstr "La programación de pipelines ejecuta pipelines en el futuro, repetidamente, para ramas o etiquetas específicas. Los pipelines programados heredarán acceso limitado al proyecto basado en su usuario asociado."
+
msgid "The planning stage shows the time from the previous step to pushing your first commit. This time will be added automatically once you push your first commit."
msgstr "La etapa de planificación muestra el tiempo desde el paso anterior hasta el envío de tu primera confirmación. Este tiempo se añadirá automáticamente una vez que usted envíe el primer cambio."
@@ -652,16 +855,16 @@ msgid "Timeago|a day ago"
msgstr "hace un día"
msgid "Timeago|a month ago"
-msgstr "hace 1 mes"
+msgstr "hace un mes"
msgid "Timeago|a week ago"
-msgstr "hace 1 semana"
+msgstr "hace una semana"
msgid "Timeago|a while"
msgstr "hace un momento"
msgid "Timeago|a year ago"
-msgstr "hace 1 año"
+msgstr "hace un año"
msgid "Timeago|about %s hours ago"
msgstr "hace alrededor de %s horas"
@@ -772,18 +975,21 @@ msgstr ""
"¡El proyecto eliminado NO puede ser restaurado!\n"
"¿Estás TOTALMENTE seguro?"
-msgid "You are going to remove the fork relationship to source project %{forked_from_project}. Are you ABSOLUTELY sure?"
+msgid "You are going to remove the fork relationship to source project %{forked_from_project}. Are you ABSOLUTELY sure?"
msgstr "Vas a eliminar el enlace de la bifurcación con el proyecto original %{forked_from_project}. ¿Estás TOTALMENTE seguro?"
msgid "You are going to transfer %{project_name_with_namespace} to another owner. Are you ABSOLUTELY sure?"
msgstr "Vas a transferir %{project_name_with_namespace} a otro propietario. ¿Estás TOTALMENTE seguro?"
msgid "You can only add files when you are on a branch"
-msgstr "Sólo puede agregar archivos cuando estas en una rama"
+msgstr "Solo puedes agregar archivos cuando estás en una rama"
msgid "You must sign in to star a project"
msgstr "Debes iniciar sesión para destacar un proyecto"
+msgid "You have reached your project limit"
+msgstr ""
+
msgid "You need permission."
msgstr "Necesitas permisos."
@@ -797,10 +1003,10 @@ msgid "You will only receive notifications for threads you have participated in"
msgstr "Solo recibirás notificaciones de los temas en los que has participado"
msgid "You will receive notifications for any activity"
-msgstr "Recibirás notificaciones para cualquier actividad"
+msgstr "Recibirás notificaciones por cualquier actividad"
msgid "You will receive notifications only for comments in which you were @mentioned"
-msgstr "Recibirás notificaciones sólo para los comentarios en los que se te mencionó"
+msgstr "Recibirás notificaciones solo para los comentarios en los que se te mencionó"
msgid "You won't be able to pull or push project code via %{protocol} until you %{set_password_link} on your account"
msgstr "No podrás actualizar o enviar código al proyecto a través de %{protocol} hasta que %{set_password_link} en tu cuenta"
@@ -811,13 +1017,18 @@ msgstr "No podrás actualizar o enviar código al proyecto a través de SSH hast
msgid "Your name"
msgstr "Tu nombre"
-msgid "committed"
-msgstr "cambió"
-
msgid "day"
msgid_plural "days"
msgstr[0] "día"
msgstr[1] "días"
+msgid "new merge request"
+msgstr "nueva solicitud de fusión"
+
msgid "notification emails"
msgstr "correos electrónicos de notificación"
+
+msgid "parent"
+msgid_plural "parents"
+msgstr[0] "padre"
+msgstr[1] "padres"
diff --git a/locale/fr/gitlab.po b/locale/fr/gitlab.po
new file mode 100644
index 00000000000..2000fa433b4
--- /dev/null
+++ b/locale/fr/gitlab.po
@@ -0,0 +1,207 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the gitlab package.
+# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
+# Dremor <egeorget@opmbx.org>, 2017. #zanata
+msgid ""
+msgstr ""
+"Project-Id-Version: gitlab 1.0.0\n"
+"Report-Msgid-Bugs-To: \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"PO-Revision-Date: 2017-06-14 04:21-0400\n"
+"Last-Translator: Dremor <egeorget@opmbx.org>\n"
+"Language-Team: French (https://www.transifex.com/gitlab-fr/teams/75145/fr/)\n"
+"Language: fr\n"
+"Plural-Forms: nplurals=2; plural=(n > 1);\n"
+"X-Generator: Zanata 3.9.6\n"
+
+msgid "ByAuthor|by"
+msgstr "par"
+
+msgid "Commit"
+msgid_plural "Commits"
+msgstr[0] "Validation"
+msgstr[1] "Validations"
+
+msgid "Cycle Analytics gives an overview of how much time it takes to go from idea to production in your project."
+msgstr "L’analyseur de cycle permet d’avoir une vue d’ensemble du temps nécessaire pour aller d’une idée à sa mise en production pour votre projet."
+
+msgid "CycleAnalyticsStage|Code"
+msgstr "Code"
+
+msgid "CycleAnalyticsStage|Issue"
+msgstr "Incident"
+
+msgid "CycleAnalyticsStage|Plan"
+msgstr "Planification"
+
+msgid "CycleAnalyticsStage|Production"
+msgstr "Production"
+
+msgid "CycleAnalyticsStage|Review"
+msgstr "Examen"
+
+msgid "CycleAnalyticsStage|Staging"
+msgstr "Pré-production"
+
+msgid "CycleAnalyticsStage|Test"
+msgstr "Test"
+
+msgid "Deploy"
+msgid_plural "Deploys"
+msgstr[0] "Déploiement"
+msgstr[1] "Déploiements"
+
+msgid "FirstPushedBy|First"
+msgstr "En premier"
+
+msgid "FirstPushedBy|pushed by"
+msgstr "poussé par"
+
+msgid "From issue creation until deploy to production"
+msgstr "Depuis la création de l'incident jusqu'au déploiement en production"
+
+msgid "From merge request merge until deploy to production"
+msgstr "Depuis la fusion de la demande de fusion jusqu'au déploiement en production"
+
+msgid "Introducing Cycle Analytics"
+msgstr "Introduction à l'analyseur de cycle"
+
+msgid "Last %d day"
+msgid_plural "Last %d days"
+msgstr[0] "Le dernier %d jour"
+msgstr[1] "Les derniers %d jours"
+
+msgid "Limited to showing %d event at most"
+msgid_plural "Limited to showing %d events at most"
+msgstr[0] "Limiter l'affichage au plus à %d évènement"
+msgstr[1] "Limiter l'affichage au plus à %d évènements"
+
+msgid "Median"
+msgstr "Médian"
+
+msgid "New Issue"
+msgid_plural "New Issues"
+msgstr[0] "Nouvel incident"
+msgstr[1] "Nouveaux incidents"
+
+msgid "Not available"
+msgstr "Indisponible"
+
+msgid "Not enough data"
+msgstr "Données insuffisantes"
+
+msgid "OpenedNDaysAgo|Opened"
+msgstr "Ouvert"
+
+msgid "Pipeline Health"
+msgstr "Santé du Pipeline"
+
+msgid "ProjectLifecycle|Stage"
+msgstr "Étape"
+
+msgid "Read more"
+msgstr "Lire plus"
+
+msgid "Related Commits"
+msgstr "Validations liés"
+
+msgid "Related Deployed Jobs"
+msgstr "Tâches de déploiement liés"
+
+msgid "Related Issues"
+msgstr "Incidents liés"
+
+msgid "Related Jobs"
+msgstr "Tâches liées"
+
+msgid "Related Merge Requests"
+msgstr "Demandes de fusion liées"
+
+msgid "Related Merged Requests"
+msgstr "Demandes fusionnées liées"
+
+msgid "Showing %d event"
+msgid_plural "Showing %d events"
+msgstr[0] "Affichage de %d évènement"
+msgstr[1] "Affichage de %d évènements"
+
+msgid "The coding stage shows the time from the first commit to creating the merge request. The data will automatically be added here once you create your first merge request."
+msgstr "L’étape de développement montre le temps entre la première validation et la création de la demande de fusion. Les données seront automatiquement ajoutées ici une fois que vous aurez créé votre première demande de fusion."
+
+msgid "The collection of events added to the data gathered for that stage."
+msgstr "L’ensemble d’évènements ajoutés aux données récupérées pour cette étape."
+
+msgid "The issue stage shows the time it takes from creating an issue to assigning the issue to a milestone, or add the issue to a list on your Issue Board. Begin creating issues to see data for this stage."
+msgstr "L'étape des incidents montre le temps nécessaire entre la création d'un incident et son assignation à un jalon, ou son ajout à une liste d'un tableau d'incident. Débutez à créer des incidents pour voir des données pour cette étape."
+
+msgid "The phase of the development lifecycle."
+msgstr "Les étapes du cycle de développement."
+
+msgid "The planning stage shows the time from the previous step to pushing your first commit. This time will be added automatically once you push your first commit."
+msgstr "L’étape de planification montre le temps entre l’étape précédente et l’envoi de votre première validation. Ce temps sera automatiquement ajouté quand vous pousserez votre première validation."
+
+msgid "The production stage shows the total time it takes between creating an issue and deploying the code to production. The data will be automatically added once you have completed the full idea to production cycle."
+msgstr "L’étape de mise en production montre le temps nécessaire entre la création d’un incident et le déploiement du code en production. Les données seront automatiquement ajoutées une fois que vous aurez complété le cycle complet, depuis l’idée jusqu’à la mise en production."
+
+msgid "The review stage shows the time from creating the merge request to merging it. The data will automatically be added after you merge your first merge request."
+msgstr "L’étape d’évaluation montre le temps entre la création de la demande de fusion et la fusion effective de celle-ci. Ces données seront automatiquement ajoutées après que vous ayez fusionné votre première demande de fusion."
+
+msgid "The staging stage shows the time between merging the MR and deploying code to the production environment. The data will be automatically added once you deploy to production for the first time."
+msgstr "L’étape de pré-production indique le temps entre la fusion de la RF et le déploiement du code dans l’environnent de production. Les données seront automatiquement ajoutées une fois que vous déploierez en production pour la première fois."
+
+msgid "The testing stage shows the time GitLab CI takes to run every pipeline for the related merge request. The data will automatically be added after your first pipeline finishes running."
+msgstr "L’étape de test montre le temps que le CI de GitLab met pour exécuter chaque pipeline liés à la demande de fusion. Les données seront automatiquement ajoutées après que votre premier pipeline s’achèvera."
+
+msgid "The time taken by each data entry gathered by that stage."
+msgstr "Le temps pris par chaque entrée récoltée durant cette étape."
+
+msgid "The value lying at the midpoint of a series of observed values. E.g., between 3, 5, 9, the median is 5. Between 3, 5, 7, 8, the median is (5+7)/2 = 6."
+msgstr "La valeur située au point médian d’une série de valeur observée. C.à.d., entre 3, 5, 9, le médian est 5. Entre 3, 5, 7, 8, le médian est (5+7)/2 = 6."
+
+msgid "Time before an issue gets scheduled"
+msgstr "Temps avant qu’un incident ne soit planifié"
+
+msgid "Time before an issue starts implementation"
+msgstr "Temps avant que résolution ne débute"
+
+msgid "Time between merge request creation and merge/close"
+msgstr "Temps entre la création d'une demande de fusion et sa fusion/clôture"
+
+msgid "Time until first merge request"
+msgstr "Temps jusqu’à la première demande de fusion"
+
+msgid "Time|hr"
+msgid_plural "Time|hrs"
+msgstr[0] "hr"
+msgstr[1] "hrs"
+
+msgid "Time|min"
+msgid_plural "Time|mins"
+msgstr[0] "min"
+msgstr[1] "mins"
+
+msgid "Time|s"
+msgstr "s"
+
+msgid "Total Time"
+msgstr "Temps total"
+
+msgid "Total test time for all commits/merges"
+msgstr "Temps total de test pour toutes les validations/fusions"
+
+msgid "Want to see the data? Please ask an administrator for access."
+msgstr "Vous voulez voir les données ? Merci de contacter un administrateur pour en obtenir l’accès."
+
+msgid "We don't have enough data to show this stage."
+msgstr "Nous n'avons pas suffisamment de données pour afficher cette étape."
+
+msgid "You need permission."
+msgstr "Vous avez besoin d’une autorisation."
+
+msgid "day"
+msgid_plural "days"
+msgstr[0] "jour"
+msgstr[1] "jours"
diff --git a/locale/fr/gitlab.po.time_stamp b/locale/fr/gitlab.po.time_stamp
new file mode 100644
index 00000000000..e69de29bb2d
--- /dev/null
+++ b/locale/fr/gitlab.po.time_stamp
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index 050f6c446c1..2d8076f5567 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -8,8 +8,8 @@ msgid ""
msgstr ""
"Project-Id-Version: gitlab 1.0.0\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2017-06-07 21:22+0200\n"
-"PO-Revision-Date: 2017-06-07 21:22+0200\n"
+"POT-Creation-Date: 2017-06-15 21:59-0500\n"
+"PO-Revision-Date: 2017-06-15 21:59-0500\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"Language: \n"
@@ -18,23 +18,217 @@ msgstr ""
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\n"
+msgid "%{commit_author_link} committed %{commit_timeago}"
+msgstr ""
+
+msgid "About auto deploy"
+msgstr ""
+
+msgid "Active"
+msgstr ""
+
+msgid "Activity"
+msgstr ""
+
+msgid "Add Changelog"
+msgstr ""
+
+msgid "Add Contribution guide"
+msgstr ""
+
+msgid "Add License"
+msgstr ""
+
+msgid "Add an SSH key to your profile to pull or push via SSH."
+msgstr ""
+
+msgid "Add new directory"
+msgstr ""
+
+msgid "Archived project! Repository is read-only"
+msgstr ""
+
msgid "Are you sure you want to delete this pipeline schedule?"
msgstr ""
+msgid "Attach a file by drag &amp; drop or %{upload_link}"
+msgstr ""
+
+msgid "Branch"
+msgid_plural "Branches"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "Branch <strong>%{branch_name}</strong> was created. To set up auto deploy, choose a GitLab CI Yaml template and commit your changes. %{link_to_autodeploy_doc}"
+msgstr ""
+
+msgid "Branches"
+msgstr ""
+
+msgid "Browse files"
+msgstr ""
+
msgid "ByAuthor|by"
msgstr ""
+msgid "CI configuration"
+msgstr ""
+
msgid "Cancel"
msgstr ""
+msgid "ChangeTypeActionLabel|Pick into branch"
+msgstr ""
+
+msgid "ChangeTypeActionLabel|Revert in branch"
+msgstr ""
+
+msgid "ChangeTypeAction|Cherry-pick"
+msgstr ""
+
+msgid "ChangeTypeAction|Revert"
+msgstr ""
+
+msgid "Changelog"
+msgstr ""
+
+msgid "Charts"
+msgstr ""
+
+msgid "Cherry-pick this commit"
+msgstr ""
+
+msgid "Cherry-pick this merge request"
+msgstr ""
+
+msgid "CiStatusLabel|canceled"
+msgstr ""
+
+msgid "CiStatusLabel|created"
+msgstr ""
+
+msgid "CiStatusLabel|failed"
+msgstr ""
+
+msgid "CiStatusLabel|manual action"
+msgstr ""
+
+msgid "CiStatusLabel|passed"
+msgstr ""
+
+msgid "CiStatusLabel|passed with warnings"
+msgstr ""
+
+msgid "CiStatusLabel|pending"
+msgstr ""
+
+msgid "CiStatusLabel|skipped"
+msgstr ""
+
+msgid "CiStatusLabel|waiting for manual action"
+msgstr ""
+
+msgid "CiStatusText|blocked"
+msgstr ""
+
+msgid "CiStatusText|canceled"
+msgstr ""
+
+msgid "CiStatusText|created"
+msgstr ""
+
+msgid "CiStatusText|failed"
+msgstr ""
+
+msgid "CiStatusText|manual"
+msgstr ""
+
+msgid "CiStatusText|passed"
+msgstr ""
+
+msgid "CiStatusText|pending"
+msgstr ""
+
+msgid "CiStatusText|skipped"
+msgstr ""
+
+msgid "CiStatus|running"
+msgstr ""
+
msgid "Commit"
msgid_plural "Commits"
msgstr[0] ""
msgstr[1] ""
+msgid "Commit message"
+msgstr ""
+
+msgid "CommitBoxTitle|Commit"
+msgstr ""
+
+msgid "CommitMessage|Add %{file_name}"
+msgstr ""
+
+msgid "Commits"
+msgstr ""
+
+msgid "Commits|History"
+msgstr ""
+
+msgid "Committed by"
+msgstr ""
+
+msgid "Compare"
+msgstr ""
+
+msgid "Contribution guide"
+msgstr ""
+
+msgid "Contributors"
+msgstr ""
+
+msgid "Copy URL to clipboard"
+msgstr ""
+
+msgid "Copy commit SHA to clipboard"
+msgstr ""
+
+msgid "Create New Directory"
+msgstr ""
+
+msgid "Create directory"
+msgstr ""
+
+msgid "Create empty bare repository"
+msgstr ""
+
+msgid "Create merge request"
+msgstr ""
+
+msgid "Create new..."
+msgstr ""
+
+msgid "CreateNewFork|Fork"
+msgstr ""
+
+msgid "CreateTag|Tag"
+msgstr ""
+
msgid "Cron Timezone"
msgstr ""
+msgid "Cron syntax"
+msgstr ""
+
+msgid "Custom notification events"
+msgstr ""
+
+msgid "Custom notification levels are the same as participating levels. With custom notification levels you will also receive notifications for select events. To find out more, check out %{notification_link}."
+msgstr ""
+
+msgid "Cycle Analytics"
+msgstr ""
+
msgid "Cycle Analytics gives an overview of how much time it takes to go from idea to production in your project."
msgstr ""
@@ -59,6 +253,9 @@ msgstr ""
msgid "CycleAnalyticsStage|Test"
msgstr ""
+msgid "Define a custom pattern with cron syntax"
+msgstr ""
+
msgid "Delete"
msgstr ""
@@ -70,19 +267,67 @@ msgstr[1] ""
msgid "Description"
msgstr ""
+msgid "Directory name"
+msgstr ""
+
+msgid "Don't show again"
+msgstr ""
+
+msgid "Download"
+msgstr ""
+
+msgid "Download tar"
+msgstr ""
+
+msgid "Download tar.bz2"
+msgstr ""
+
+msgid "Download tar.gz"
+msgstr ""
+
+msgid "Download zip"
+msgstr ""
+
+msgid "DownloadArtifacts|Download"
+msgstr ""
+
+msgid "DownloadCommit|Email Patches"
+msgstr ""
+
+msgid "DownloadCommit|Plain Diff"
+msgstr ""
+
+msgid "DownloadSource|Download"
+msgstr ""
+
msgid "Edit"
msgstr ""
msgid "Edit Pipeline Schedule %{id}"
msgstr ""
+msgid "Every day (at 4:00am)"
+msgstr ""
+
+msgid "Every month (on the 1st at 4:00am)"
+msgstr ""
+
+msgid "Every week (Sundays at 4:00am)"
+msgstr ""
+
msgid "Failed to change the owner"
msgstr ""
msgid "Failed to remove the pipeline schedule"
msgstr ""
-msgid "Filter"
+msgid "Files"
+msgstr ""
+
+msgid "Find by path"
+msgstr ""
+
+msgid "Find file"
msgstr ""
msgid "FirstPushedBy|First"
@@ -91,18 +336,47 @@ msgstr ""
msgid "FirstPushedBy|pushed by"
msgstr ""
+msgid "Fork"
+msgid_plural "Forks"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "ForkedFromProjectPath|Forked from"
+msgstr ""
+
msgid "From issue creation until deploy to production"
msgstr ""
msgid "From merge request merge until deploy to production"
msgstr ""
+msgid "Go to your fork"
+msgstr ""
+
+msgid "GoToYourFork|Fork"
+msgstr ""
+
+msgid "Home"
+msgstr ""
+
+msgid "Housekeeping successfully started"
+msgstr ""
+
+msgid "Import repository"
+msgstr ""
+
msgid "Interval Pattern"
msgstr ""
msgid "Introducing Cycle Analytics"
msgstr ""
+msgid "LFSStatus|Disabled"
+msgstr ""
+
+msgid "LFSStatus|Enabled"
+msgstr ""
+
msgid "Last %d day"
msgid_plural "Last %d days"
msgstr[0] ""
@@ -111,6 +385,24 @@ msgstr[1] ""
msgid "Last Pipeline"
msgstr ""
+msgid "Last Update"
+msgstr ""
+
+msgid "Last commit"
+msgstr ""
+
+msgid "Learn more in the"
+msgstr ""
+
+msgid "Learn more in the|pipeline schedules documentation"
+msgstr ""
+
+msgid "Leave group"
+msgstr ""
+
+msgid "Leave project"
+msgstr ""
+
msgid "Limited to showing %d event at most"
msgid_plural "Limited to showing %d events at most"
msgstr[0] ""
@@ -119,6 +411,9 @@ msgstr[1] ""
msgid "Median"
msgstr ""
+msgid "MissingSSHKeyWarningLink|add an SSH key"
+msgstr ""
+
msgid "New Issue"
msgid_plural "New Issues"
msgstr[0] ""
@@ -127,6 +422,33 @@ msgstr[1] ""
msgid "New Pipeline Schedule"
msgstr ""
+msgid "New branch"
+msgstr ""
+
+msgid "New directory"
+msgstr ""
+
+msgid "New file"
+msgstr ""
+
+msgid "New issue"
+msgstr ""
+
+msgid "New merge request"
+msgstr ""
+
+msgid "New schedule"
+msgstr ""
+
+msgid "New snippet"
+msgstr ""
+
+msgid "New tag"
+msgstr ""
+
+msgid "No repository"
+msgstr ""
+
msgid "No schedules"
msgstr ""
@@ -136,12 +458,75 @@ msgstr ""
msgid "Not enough data"
msgstr ""
+msgid "Notification events"
+msgstr ""
+
+msgid "NotificationEvent|Close issue"
+msgstr ""
+
+msgid "NotificationEvent|Close merge request"
+msgstr ""
+
+msgid "NotificationEvent|Failed pipeline"
+msgstr ""
+
+msgid "NotificationEvent|Merge merge request"
+msgstr ""
+
+msgid "NotificationEvent|New issue"
+msgstr ""
+
+msgid "NotificationEvent|New merge request"
+msgstr ""
+
+msgid "NotificationEvent|New note"
+msgstr ""
+
+msgid "NotificationEvent|Reassign issue"
+msgstr ""
+
+msgid "NotificationEvent|Reassign merge request"
+msgstr ""
+
+msgid "NotificationEvent|Reopen issue"
+msgstr ""
+
+msgid "NotificationEvent|Successful pipeline"
+msgstr ""
+
+msgid "NotificationLevel|Custom"
+msgstr ""
+
+msgid "NotificationLevel|Disabled"
+msgstr ""
+
+msgid "NotificationLevel|Global"
+msgstr ""
+
+msgid "NotificationLevel|On mention"
+msgstr ""
+
+msgid "NotificationLevel|Participate"
+msgstr ""
+
+msgid "NotificationLevel|Watch"
+msgstr ""
+
+msgid "OfSearchInADropdown|Filter"
+msgstr ""
+
msgid "OpenedNDaysAgo|Opened"
msgstr ""
+msgid "Options"
+msgstr ""
+
msgid "Owner"
msgstr ""
+msgid "Pipeline"
+msgstr ""
+
msgid "Pipeline Health"
msgstr ""
@@ -178,12 +563,78 @@ msgstr ""
msgid "PipelineSchedules|Target"
msgstr ""
+msgid "PipelineSheduleIntervalPattern|Custom"
+msgstr ""
+
+msgid "Pipeline|with stage"
+msgstr ""
+
+msgid "Pipeline|with stages"
+msgstr ""
+
+msgid "Project '%{project_name}' queued for deletion."
+msgstr ""
+
+msgid "Project '%{project_name}' was successfully created."
+msgstr ""
+
+msgid "Project '%{project_name}' was successfully updated."
+msgstr ""
+
+msgid "Project '%{project_name}' will be deleted."
+msgstr ""
+
+msgid "Project access must be granted explicitly to each user."
+msgstr ""
+
+msgid "Project export could not be deleted."
+msgstr ""
+
+msgid "Project export has been deleted."
+msgstr ""
+
+msgid "Project export link has expired. Please generate a new export from your project settings."
+msgstr ""
+
+msgid "Project export started. A download link will be sent by email."
+msgstr ""
+
+msgid "Project home"
+msgstr ""
+
+msgid "ProjectFeature|Disabled"
+msgstr ""
+
+msgid "ProjectFeature|Everyone with access"
+msgstr ""
+
+msgid "ProjectFeature|Only team members"
+msgstr ""
+
+msgid "ProjectFileTree|Name"
+msgstr ""
+
+msgid "ProjectLastActivity|Never"
+msgstr ""
+
msgid "ProjectLifecycle|Stage"
msgstr ""
+msgid "ProjectNetworkGraph|Graph"
+msgstr ""
+
msgid "Read more"
msgstr ""
+msgid "Readme"
+msgstr ""
+
+msgid "RefSwitcher|Branches"
+msgstr ""
+
+msgid "RefSwitcher|Tags"
+msgstr ""
+
msgid "Related Commits"
msgstr ""
@@ -202,23 +653,82 @@ msgstr ""
msgid "Related Merged Requests"
msgstr ""
+msgid "Remind later"
+msgstr ""
+
+msgid "Remove project"
+msgstr ""
+
+msgid "Request Access"
+msgstr ""
+
+msgid "Revert this commit"
+msgstr ""
+
+msgid "Revert this merge request"
+msgstr ""
+
msgid "Save pipeline schedule"
msgstr ""
msgid "Schedule a new pipeline"
msgstr ""
+msgid "Scheduling Pipelines"
+msgstr ""
+
+msgid "Search branches and tags"
+msgstr ""
+
+msgid "Select Archive Format"
+msgstr ""
+
msgid "Select a timezone"
msgstr ""
msgid "Select target branch"
msgstr ""
+msgid "Set a password on your account to pull or push via %{protocol}"
+msgstr ""
+
+msgid "Set up CI"
+msgstr ""
+
+msgid "Set up Koding"
+msgstr ""
+
+msgid "Set up auto deploy"
+msgstr ""
+
+msgid "SetPasswordToCloneLink|set a password"
+msgstr ""
+
msgid "Showing %d event"
msgid_plural "Showing %d events"
msgstr[0] ""
msgstr[1] ""
+msgid "Source code"
+msgstr ""
+
+msgid "StarProject|Star"
+msgstr ""
+
+msgid "Start a %{new_merge_request} with these changes"
+msgstr ""
+
+msgid "Switch branch/tag"
+msgstr ""
+
+msgid "Tag"
+msgid_plural "Tags"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "Tags"
+msgstr ""
+
msgid "Target Branch"
msgstr ""
@@ -228,18 +738,33 @@ msgstr ""
msgid "The collection of events added to the data gathered for that stage."
msgstr ""
+msgid "The fork relationship has been removed."
+msgstr ""
+
msgid "The issue stage shows the time it takes from creating an issue to assigning the issue to a milestone, or add the issue to a list on your Issue Board. Begin creating issues to see data for this stage."
msgstr ""
msgid "The phase of the development lifecycle."
msgstr ""
+msgid "The pipelines schedule runs pipelines in the future, repeatedly, for specific branches or tags. Those scheduled pipelines will inherit limited project access based on their associated user."
+msgstr ""
+
msgid "The planning stage shows the time from the previous step to pushing your first commit. This time will be added automatically once you push your first commit."
msgstr ""
msgid "The production stage shows the total time it takes between creating an issue and deploying the code to production. The data will be automatically added once you have completed the full idea to production cycle."
msgstr ""
+msgid "The project can be accessed by any logged in user."
+msgstr ""
+
+msgid "The project can be accessed without any authentication."
+msgstr ""
+
+msgid "The repository for this project does not exist."
+msgstr ""
+
msgid "The review stage shows the time from creating the merge request to merging it. The data will automatically be added after you merge your first merge request."
msgstr ""
@@ -255,6 +780,9 @@ msgstr ""
msgid "The value lying at the midpoint of a series of observed values. E.g., between 3, 5, 9, the median is 5. Between 3, 5, 7, 8, the median is (5+7)/2 = 6."
msgstr ""
+msgid "This means you can not push code until you create an empty repository or import existing one."
+msgstr ""
+
msgid "Time before an issue gets scheduled"
msgstr ""
@@ -267,6 +795,129 @@ msgstr ""
msgid "Time until first merge request"
msgstr ""
+msgid "Timeago|%s days ago"
+msgstr ""
+
+msgid "Timeago|%s days remaining"
+msgstr ""
+
+msgid "Timeago|%s hours remaining"
+msgstr ""
+
+msgid "Timeago|%s minutes ago"
+msgstr ""
+
+msgid "Timeago|%s minutes remaining"
+msgstr ""
+
+msgid "Timeago|%s months ago"
+msgstr ""
+
+msgid "Timeago|%s months remaining"
+msgstr ""
+
+msgid "Timeago|%s seconds remaining"
+msgstr ""
+
+msgid "Timeago|%s weeks ago"
+msgstr ""
+
+msgid "Timeago|%s weeks remaining"
+msgstr ""
+
+msgid "Timeago|%s years ago"
+msgstr ""
+
+msgid "Timeago|%s years remaining"
+msgstr ""
+
+msgid "Timeago|1 day remaining"
+msgstr ""
+
+msgid "Timeago|1 hour remaining"
+msgstr ""
+
+msgid "Timeago|1 minute remaining"
+msgstr ""
+
+msgid "Timeago|1 month remaining"
+msgstr ""
+
+msgid "Timeago|1 week remaining"
+msgstr ""
+
+msgid "Timeago|1 year remaining"
+msgstr ""
+
+msgid "Timeago|Past due"
+msgstr ""
+
+msgid "Timeago|a day ago"
+msgstr ""
+
+msgid "Timeago|a month ago"
+msgstr ""
+
+msgid "Timeago|a week ago"
+msgstr ""
+
+msgid "Timeago|a while"
+msgstr ""
+
+msgid "Timeago|a year ago"
+msgstr ""
+
+msgid "Timeago|about %s hours ago"
+msgstr ""
+
+msgid "Timeago|about a minute ago"
+msgstr ""
+
+msgid "Timeago|about an hour ago"
+msgstr ""
+
+msgid "Timeago|in %s days"
+msgstr ""
+
+msgid "Timeago|in %s hours"
+msgstr ""
+
+msgid "Timeago|in %s minutes"
+msgstr ""
+
+msgid "Timeago|in %s months"
+msgstr ""
+
+msgid "Timeago|in %s seconds"
+msgstr ""
+
+msgid "Timeago|in %s weeks"
+msgstr ""
+
+msgid "Timeago|in %s years"
+msgstr ""
+
+msgid "Timeago|in 1 day"
+msgstr ""
+
+msgid "Timeago|in 1 hour"
+msgstr ""
+
+msgid "Timeago|in 1 minute"
+msgstr ""
+
+msgid "Timeago|in 1 month"
+msgstr ""
+
+msgid "Timeago|in 1 week"
+msgstr ""
+
+msgid "Timeago|in 1 year"
+msgstr ""
+
+msgid "Timeago|less than a minute ago"
+msgstr ""
+
msgid "Time|hr"
msgid_plural "Time|hrs"
msgstr[0] ""
@@ -286,16 +937,96 @@ msgstr ""
msgid "Total test time for all commits/merges"
msgstr ""
+msgid "Unstar"
+msgstr ""
+
+msgid "Upload New File"
+msgstr ""
+
+msgid "Upload file"
+msgstr ""
+
+msgid "Use your global notification setting"
+msgstr ""
+
+msgid "VisibilityLevel|Internal"
+msgstr ""
+
+msgid "VisibilityLevel|Private"
+msgstr ""
+
+msgid "VisibilityLevel|Public"
+msgstr ""
+
msgid "Want to see the data? Please ask an administrator for access."
msgstr ""
msgid "We don't have enough data to show this stage."
msgstr ""
+msgid "Withdraw Access Request"
+msgstr ""
+
+msgid ""
+"You are going to remove %{project_name_with_namespace}.\n"
+"Removed project CANNOT be restored!\n"
+"Are you ABSOLUTELY sure?"
+msgstr ""
+
+msgid "You are going to remove the fork relationship to source project %{forked_from_project}. Are you ABSOLUTELY sure?"
+msgstr ""
+
+msgid "You are going to transfer %{project_name_with_namespace} to another owner. Are you ABSOLUTELY sure?"
+msgstr ""
+
+msgid "You can only add files when you are on a branch"
+msgstr ""
+
+msgid "You have reached your project limit"
+msgstr ""
+
+msgid "You must sign in to star a project"
+msgstr ""
+
msgid "You need permission."
msgstr ""
+msgid "You will not get any notifications via email"
+msgstr ""
+
+msgid "You will only receive notifications for the events you choose"
+msgstr ""
+
+msgid "You will only receive notifications for threads you have participated in"
+msgstr ""
+
+msgid "You will receive notifications for any activity"
+msgstr ""
+
+msgid "You will receive notifications only for comments in which you were @mentioned"
+msgstr ""
+
+msgid "You won't be able to pull or push project code via %{protocol} until you %{set_password_link} on your account"
+msgstr ""
+
+msgid "You won't be able to pull or push project code via SSH until you %{add_ssh_key_link} to your profile"
+msgstr ""
+
+msgid "Your name"
+msgstr ""
+
msgid "day"
msgid_plural "days"
msgstr[0] ""
msgstr[1] ""
+
+msgid "new merge request"
+msgstr ""
+
+msgid "notification emails"
+msgstr ""
+
+msgid "parent"
+msgid_plural "parents"
+msgstr[0] ""
+msgstr[1] ""
diff --git a/locale/pt_BR/gitlab.po b/locale/pt_BR/gitlab.po
index 5ad41f92b64..fe6d51c36ac 100644
--- a/locale/pt_BR/gitlab.po
+++ b/locale/pt_BR/gitlab.po
@@ -250,6 +250,9 @@ msgstr "Precisa visualizar os dados? Solicite acesso ao administrador."
msgid "We don't have enough data to show this stage."
msgstr "Não temos dados suficientes para mostrar esta fase."
+msgid "You have reached your project limit"
+msgstr ""
+
msgid "You need permission."
msgstr "Você precisa de permissão."
diff --git a/locale/zh_CN/gitlab.po b/locale/zh_CN/gitlab.po
index 11434460207..8d994ff3c4f 100644
--- a/locale/zh_CN/gitlab.po
+++ b/locale/zh_CN/gitlab.po
@@ -283,6 +283,9 @@ msgstr "æƒé™ä¸è¶³ã€‚如需查看相关数æ®ï¼Œè¯·å‘管ç†å‘˜ç”³è¯·æƒé™ã€‚
msgid "We don't have enough data to show this stage."
msgstr "该阶段的数æ®ä¸è¶³ï¼Œæ— æ³•æ˜¾ç¤ºã€‚"
+msgid "You have reached your project limit"
+msgstr ""
+
msgid "You need permission."
msgstr "您需è¦ç›¸å…³çš„æƒé™ã€‚"
diff --git a/locale/zh_HK/gitlab.po b/locale/zh_HK/gitlab.po
index 8277c8d8e27..f4f70d80d34 100644
--- a/locale/zh_HK/gitlab.po
+++ b/locale/zh_HK/gitlab.po
@@ -1,18 +1,20 @@
# Huang Tao <htve@outlook.com>, 2017. #zanata
+# Victor Wu <anonymous@domain.com>, 2017.
+# Hazel Yang <anonymous@domain.com>, 2017.
msgid ""
msgstr ""
"Project-Id-Version: gitlab 1.0.0\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2017-06-15 14:57+0200\n"
+"POT-Creation-Date: 2017-06-15 21:59-0500\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"PO-Revision-Date: 2017-06-15 10:34-0400\n"
+"PO-Revision-Date: 2017-06-19 09:57-0400\n"
"Last-Translator: Huang Tao <htve@outlook.com>\n"
"Language-Team: Chinese (Hong Kong) (https://translate.zanata.org/project/view/GitLab)\n"
"Language: zh-HK\n"
-"Plural-Forms: nplurals=1; plural=0;\n"
"X-Generator: Zanata 3.9.6\n"
+"Plural-Forms: nplurals=1; plural=0\n"
msgid "%{commit_author_link} committed %{commit_timeago}"
msgstr "ç”± %{commit_author_link} æ交於 %{commit_timeago}"
@@ -299,7 +301,7 @@ msgid "DownloadCommit|Email Patches"
msgstr "é›»å­éƒµä»¶è£œä¸"
msgid "DownloadCommit|Plain Diff"
-msgstr "Diff 文件"
+msgstr "差異文件"
msgid "DownloadSource|Download"
msgstr "下載"
@@ -311,13 +313,13 @@ msgid "Edit Pipeline Schedule %{id}"
msgstr "編輯 %{id} æµæ°´ç·šè¨ˆåŠƒ"
msgid "Every day (at 4:00am)"
-msgstr "æ¯æ—¥åŸ·è¡Œï¼ˆæ·©æ™¨4點)"
+msgstr "æ¯æ—¥åŸ·è¡Œï¼ˆæ·©æ™¨ 4 點)"
msgid "Every month (on the 1st at 4:00am)"
-msgstr "æ¯æœˆåŸ·è¡Œï¼ˆæ¯æœˆ1日淩晨4點)"
+msgstr "æ¯æœˆåŸ·è¡Œï¼ˆæ¯æœˆ 1 日淩晨 4 點)"
msgid "Every week (Sundays at 4:00am)"
-msgstr "æ¯é€±åŸ·è¡Œï¼ˆå‘¨æ—¥æ·©æ™¨4點)"
+msgstr "æ¯é€±åŸ·è¡Œï¼ˆå‘¨æ—¥æ·©æ™¨ 4 點)"
msgid "Failed to change the owner"
msgstr "無法變更所有者"
@@ -369,7 +371,7 @@ msgid "Import repository"
msgstr "導入存儲庫"
msgid "Interval Pattern"
-msgstr "é‡è¤‡é€±æœŸ"
+msgstr "循環週期"
msgid "Introducing Cycle Analytics"
msgstr "週期分æžç°¡ä»‹"
@@ -489,7 +491,7 @@ msgid "NotificationEvent|Reassign merge request"
msgstr "é‡æ–°æŒ‡æ´¾åˆä½µè«‹æ±‚"
msgid "NotificationEvent|Reopen issue"
-msgstr "é‡æ–°æ‰“é–‹è­°é¡Œ"
+msgstr "é‡å•Ÿè­°é¡Œ"
msgid "NotificationEvent|Successful pipeline"
msgstr "æµæ°´ç·šæˆåŠŸå®Œæˆ"
@@ -662,7 +664,7 @@ msgid "Remove project"
msgstr "刪除項目"
msgid "Request Access"
-msgstr "申請訪å•"
+msgstr "申請權é™"
msgid "Revert this commit"
msgstr "還原此æ交"
@@ -716,12 +718,9 @@ msgstr "æºä»£ç¢¼"
msgid "StarProject|Star"
msgstr "星標"
-msgid "Start a %{new_merge_request} with these changes"
+msgid "Start a %{new_merge_request} with these changes"
msgstr "由此更改 %{new_merge_request}"
-msgid "Start a <strong>new merge request</strong> with these changes"
-msgstr "由此更改創建<strong>æ–°åˆä½µè«‹æ±‚</strong>"
-
msgid "Switch branch/tag"
msgstr "切æ›åˆ†æ”¯/標籤"
@@ -751,7 +750,7 @@ msgid ""
"The issue stage shows the time it takes from creating an issue to assigning "
"the issue to a milestone, or add the issue to a list on your Issue Board. "
"Begin creating issues to see data for this stage."
-msgstr "議題階段概述了從創建議題到將議題添加到è£ç¨‹ç¢‘或議題看æ¿çš„時間。創建第壹個議題後,數據將自動添加到此處.。"
+msgstr "議題階段概述了從創建議題到將議題添加到è£ç¨‹ç¢‘或議題看æ¿æ‰€èŠ±è²»çš„時間。創建第壹個議題後,數據將自動添加到此處.。"
msgid "The phase of the development lifecycle."
msgstr "項目生命週期中的å„個階段。"
@@ -828,7 +827,7 @@ msgid "Time until first merge request"
msgstr "創建第壹個åˆä½µè«‹æ±‚之å‰çš„時間"
msgid "Timeago|%s days ago"
-msgstr "%s 天å‰"
+msgstr " %s 天å‰"
msgid "Timeago|%s days remaining"
msgstr "剩餘 %s 天"
@@ -837,13 +836,13 @@ msgid "Timeago|%s hours remaining"
msgstr "剩餘 %s å°æ™‚"
msgid "Timeago|%s minutes ago"
-msgstr "%s 分é˜å‰"
+msgstr " %s 分é˜å‰"
msgid "Timeago|%s minutes remaining"
msgstr "剩餘 %s 分é˜"
msgid "Timeago|%s months ago"
-msgstr "%s 個月å‰"
+msgstr " %s 個月å‰"
msgid "Timeago|%s months remaining"
msgstr "剩餘 %s 月"
@@ -852,13 +851,13 @@ msgid "Timeago|%s seconds remaining"
msgstr "剩餘 %s 秒"
msgid "Timeago|%s weeks ago"
-msgstr "%s 星期å‰"
+msgstr " %s 星期å‰"
msgid "Timeago|%s weeks remaining"
msgstr "剩餘 %s 星期"
msgid "Timeago|%s years ago"
-msgstr "%s å¹´å‰"
+msgstr " %s å¹´å‰"
msgid "Timeago|%s years remaining"
msgstr "剩餘 %s 年"
@@ -885,19 +884,19 @@ msgid "Timeago|Past due"
msgstr "逾期"
msgid "Timeago|a day ago"
-msgstr "1 天å‰"
+msgstr " 1 天å‰"
msgid "Timeago|a month ago"
-msgstr "1 個月å‰"
+msgstr " 1 個月å‰"
msgid "Timeago|a week ago"
-msgstr "1 星期å‰"
+msgstr " 1 星期å‰"
msgid "Timeago|a while"
-msgstr "剛剛"
+msgstr " 剛剛"
msgid "Timeago|a year ago"
-msgstr "1 å¹´å‰"
+msgstr " 1 å¹´å‰"
msgid "Timeago|about %s hours ago"
msgstr "ç´„ %s å°æ™‚å‰"
@@ -995,7 +994,7 @@ msgid "We don't have enough data to show this stage."
msgstr "該階段的數據ä¸è¶³ï¼Œç„¡æ³•é¡¯ç¤ºã€‚"
msgid "Withdraw Access Request"
-msgstr "å–消訪å•è«‹æ±‚"
+msgstr "å–消權é™ç”³è¯·"
msgid ""
"You are going to remove %{project_name_with_namespace}.\n"
@@ -1018,6 +1017,9 @@ msgstr "å³å°‡ %{project_name_with_namespace} 轉義給å¦å£¹å€‹æ‰€æœ‰è€…。確å
msgid "You can only add files when you are on a branch"
msgstr "åªèƒ½åœ¨åˆ†æ”¯ä¸Šæ·»åŠ æ–‡ä»¶"
+msgid "You have reached your project limit"
+msgstr "您已é”到項目數é‡é™åˆ¶"
+
msgid "You must sign in to star a project"
msgstr "必須登錄æ‰èƒ½å°é …目加星標"
diff --git a/locale/zh_TW/gitlab.po b/locale/zh_TW/gitlab.po
index e40723a9d8d..5130572d7ed 100644
--- a/locale/zh_TW/gitlab.po
+++ b/locale/zh_TW/gitlab.po
@@ -283,6 +283,9 @@ msgstr "權é™ä¸è¶³ã€‚如需查看相關資料,請å‘管ç†å“¡ç”³è«‹æ¬Šé™ã€‚
msgid "We don't have enough data to show this stage."
msgstr "因該階段的資料ä¸è¶³è€Œç„¡æ³•é¡¯ç¤ºç›¸é—œè³‡è¨Š"
+msgid "You have reached your project limit"
+msgstr ""
+
msgid "You need permission."
msgstr "您需è¦ç›¸é—œçš„權é™ã€‚"
diff --git a/package.json b/package.json
index 29165fd4182..045f07ee2f9 100644
--- a/package.json
+++ b/package.json
@@ -72,13 +72,13 @@
"eslint-plugin-jasmine": "^2.1.0",
"eslint-plugin-promise": "^3.5.0",
"istanbul": "^0.4.5",
- "jasmine-core": "^2.5.2",
+ "jasmine-core": "^2.6.3",
"jasmine-jquery": "^2.1.1",
- "karma": "^1.4.1",
+ "karma": "^1.7.0",
+ "karma-chrome-launcher": "^2.1.1",
"karma-coverage-istanbul-reporter": "^0.2.0",
"karma-jasmine": "^1.1.0",
"karma-mocha-reporter": "^2.2.2",
- "karma-phantomjs-launcher": "^1.0.2",
"karma-sourcemap-loader": "^0.3.7",
"karma-webpack": "^2.0.2",
"nodemon": "^1.11.0",
diff --git a/rubocop/cop/migration/add_timestamps.rb b/rubocop/cop/migration/add_timestamps.rb
new file mode 100644
index 00000000000..08ddd91e54d
--- /dev/null
+++ b/rubocop/cop/migration/add_timestamps.rb
@@ -0,0 +1,25 @@
+require_relative '../../migration_helpers'
+
+module RuboCop
+ module Cop
+ module Migration
+ # Cop that checks if 'add_timestamps' method is called with timezone information.
+ class AddTimestamps < RuboCop::Cop::Cop
+ include MigrationHelpers
+
+ MSG = 'Do not use `add_timestamps`, use `add_timestamps_with_timezone` instead'.freeze
+
+ # Check methods.
+ def on_send(node)
+ return unless in_migration?(node)
+
+ add_offense(node, :selector) if method_name(node) == :add_timestamps
+ end
+
+ def method_name(node)
+ node.children[1]
+ end
+ end
+ end
+ end
+end
diff --git a/rubocop/cop/migration/datetime.rb b/rubocop/cop/migration/datetime.rb
new file mode 100644
index 00000000000..651935dd53e
--- /dev/null
+++ b/rubocop/cop/migration/datetime.rb
@@ -0,0 +1,36 @@
+require_relative '../../migration_helpers'
+
+module RuboCop
+ module Cop
+ module Migration
+ # Cop that checks if datetime data type is added with timezone information.
+ class Datetime < RuboCop::Cop::Cop
+ include MigrationHelpers
+
+ MSG = 'Do not use the `datetime` data type, use `datetime_with_timezone` instead'.freeze
+
+ # Check methods in table creation.
+ def on_def(node)
+ return unless in_migration?(node)
+
+ node.each_descendant(:send) do |send_node|
+ add_offense(send_node, :selector) if method_name(send_node) == :datetime
+ end
+ end
+
+ # Check methods.
+ def on_send(node)
+ return unless in_migration?(node)
+
+ node.each_descendant do |descendant|
+ add_offense(node, :expression) if descendant.type == :sym && descendant.children.last == :datetime
+ end
+ end
+
+ def method_name(node)
+ node.children[1]
+ end
+ end
+ end
+ end
+end
diff --git a/rubocop/cop/migration/timestamps.rb b/rubocop/cop/migration/timestamps.rb
new file mode 100644
index 00000000000..71a9420cc3b
--- /dev/null
+++ b/rubocop/cop/migration/timestamps.rb
@@ -0,0 +1,27 @@
+require_relative '../../migration_helpers'
+
+module RuboCop
+ module Cop
+ module Migration
+ # Cop that checks if 'timestamps' method is called with timezone information.
+ class Timestamps < RuboCop::Cop::Cop
+ include MigrationHelpers
+
+ MSG = 'Do not use `timestamps`, use `timestamps_with_timezone` instead'.freeze
+
+ # Check methods in table creation.
+ def on_def(node)
+ return unless in_migration?(node)
+
+ node.each_descendant(:send) do |send_node|
+ add_offense(send_node, :selector) if method_name(send_node) == :timestamps
+ end
+ end
+
+ def method_name(node)
+ node.children[1]
+ end
+ end
+ end
+ end
+end
diff --git a/rubocop/cop/rspec/single_line_hook.rb b/rubocop/cop/rspec/single_line_hook.rb
new file mode 100644
index 00000000000..be611054323
--- /dev/null
+++ b/rubocop/cop/rspec/single_line_hook.rb
@@ -0,0 +1,38 @@
+require 'rubocop-rspec'
+
+module RuboCop
+ module Cop
+ module RSpec
+ # This cop checks for single-line hook blocks
+ #
+ # @example
+ #
+ # # bad
+ # before { do_something }
+ # after(:each) { undo_something }
+ #
+ # # good
+ # before do
+ # do_something
+ # end
+ #
+ # after(:each) do
+ # undo_something
+ # end
+ class SingleLineHook < Cop
+ MESSAGE = "Don't use single-line hook blocks.".freeze
+
+ def_node_search :rspec_hook?, <<~PATTERN
+ (send nil {:after :around :before} ...)
+ PATTERN
+
+ def on_block(node)
+ return unless rspec_hook?(node)
+ return unless node.single_line?
+
+ add_offense(node, :expression, MESSAGE)
+ end
+ end
+ end
+ end
+end
diff --git a/rubocop/rubocop.rb b/rubocop/rubocop.rb
index dae30969abf..55d7708fa8c 100644
--- a/rubocop/rubocop.rb
+++ b/rubocop/rubocop.rb
@@ -8,7 +8,11 @@ require_relative 'cop/migration/add_column_with_default_to_large_table'
require_relative 'cop/migration/add_concurrent_foreign_key'
require_relative 'cop/migration/add_concurrent_index'
require_relative 'cop/migration/add_index'
+require_relative 'cop/migration/add_timestamps'
+require_relative 'cop/migration/datetime'
require_relative 'cop/migration/remove_concurrent_index'
require_relative 'cop/migration/remove_index'
require_relative 'cop/migration/reversible_add_column_with_default'
+require_relative 'cop/migration/timestamps'
require_relative 'cop/migration/update_column_in_batches'
+require_relative 'cop/rspec/single_line_hook'
diff --git a/spec/controllers/admin/identities_controller_spec.rb b/spec/controllers/admin/identities_controller_spec.rb
index c131d22a30a..a29853bf8df 100644
--- a/spec/controllers/admin/identities_controller_spec.rb
+++ b/spec/controllers/admin/identities_controller_spec.rb
@@ -2,7 +2,10 @@ require 'spec_helper'
describe Admin::IdentitiesController do
let(:admin) { create(:admin) }
- before { sign_in(admin) }
+
+ before do
+ sign_in(admin)
+ end
describe 'UPDATE identity' do
let(:user) { create(:omniauth_user, provider: 'ldapmain', extern_uid: 'uid=myuser,ou=people,dc=example,dc=com') }
diff --git a/spec/controllers/admin/services_controller_spec.rb b/spec/controllers/admin/services_controller_spec.rb
index c94616d8508..4ca0cfc74e9 100644
--- a/spec/controllers/admin/services_controller_spec.rb
+++ b/spec/controllers/admin/services_controller_spec.rb
@@ -3,7 +3,9 @@ require 'spec_helper'
describe Admin::ServicesController do
let(:admin) { create(:admin) }
- before { sign_in(admin) }
+ before do
+ sign_in(admin)
+ end
describe 'GET #edit' do
let!(:project) { create(:empty_project) }
diff --git a/spec/controllers/autocomplete_controller_spec.rb b/spec/controllers/autocomplete_controller_spec.rb
index 4c3a5ec49ef..b40f647644d 100644
--- a/spec/controllers/autocomplete_controller_spec.rb
+++ b/spec/controllers/autocomplete_controller_spec.rb
@@ -200,7 +200,9 @@ describe AutocompleteController do
end
context 'skip_users parameter included' do
- before { sign_in(user) }
+ before do
+ sign_in(user)
+ end
it 'skips the user IDs passed' do
get(:users, skip_users: [user, user2].map(&:id))
diff --git a/spec/controllers/groups/group_members_controller_spec.rb b/spec/controllers/groups/group_members_controller_spec.rb
index ed4ad7b600e..cce53f6697c 100644
--- a/spec/controllers/groups/group_members_controller_spec.rb
+++ b/spec/controllers/groups/group_members_controller_spec.rb
@@ -16,10 +16,14 @@ describe Groups::GroupMembersController do
describe 'POST create' do
let(:group_user) { create(:user) }
- before { sign_in(user) }
+ before do
+ sign_in(user)
+ end
context 'when user does not have enough rights' do
- before { group.add_developer(user) }
+ before do
+ group.add_developer(user)
+ end
it 'returns 403' do
post :create, group_id: group,
@@ -32,7 +36,9 @@ describe Groups::GroupMembersController do
end
context 'when user has enough rights' do
- before { group.add_owner(user) }
+ before do
+ group.add_owner(user)
+ end
it 'adds user to members' do
post :create, group_id: group,
@@ -59,7 +65,9 @@ describe Groups::GroupMembersController do
describe 'DELETE destroy' do
let(:member) { create(:group_member, :developer, group: group) }
- before { sign_in(user) }
+ before do
+ sign_in(user)
+ end
context 'when member is not found' do
it 'returns 403' do
@@ -71,7 +79,9 @@ describe Groups::GroupMembersController do
context 'when member is found' do
context 'when user does not have enough rights' do
- before { group.add_developer(user) }
+ before do
+ group.add_developer(user)
+ end
it 'returns 403' do
delete :destroy, group_id: group, id: member
@@ -82,7 +92,9 @@ describe Groups::GroupMembersController do
end
context 'when user has enough rights' do
- before { group.add_owner(user) }
+ before do
+ group.add_owner(user)
+ end
it '[HTML] removes user from members' do
delete :destroy, group_id: group, id: member
@@ -103,7 +115,9 @@ describe Groups::GroupMembersController do
end
describe 'DELETE leave' do
- before { sign_in(user) }
+ before do
+ sign_in(user)
+ end
context 'when member is not found' do
it 'returns 404' do
@@ -115,7 +129,9 @@ describe Groups::GroupMembersController do
context 'when member is found' do
context 'and is not an owner' do
- before { group.add_developer(user) }
+ before do
+ group.add_developer(user)
+ end
it 'removes user from members' do
delete :leave, group_id: group
@@ -134,7 +150,9 @@ describe Groups::GroupMembersController do
end
context 'and is an owner' do
- before { group.add_owner(user) }
+ before do
+ group.add_owner(user)
+ end
it 'cannot removes himself from the group' do
delete :leave, group_id: group
@@ -144,7 +162,9 @@ describe Groups::GroupMembersController do
end
context 'and is a requester' do
- before { group.request_access(user) }
+ before do
+ group.request_access(user)
+ end
it 'removes user from members' do
delete :leave, group_id: group
@@ -159,7 +179,9 @@ describe Groups::GroupMembersController do
end
describe 'POST request_access' do
- before { sign_in(user) }
+ before do
+ sign_in(user)
+ end
it 'creates a new GroupMember that is not a team member' do
post :request_access, group_id: group
@@ -174,7 +196,9 @@ describe Groups::GroupMembersController do
describe 'POST approve_access_request' do
let(:member) { create(:group_member, :access_request, group: group) }
- before { sign_in(user) }
+ before do
+ sign_in(user)
+ end
context 'when member is not found' do
it 'returns 403' do
@@ -186,7 +210,9 @@ describe Groups::GroupMembersController do
context 'when member is found' do
context 'when user does not have enough rights' do
- before { group.add_developer(user) }
+ before do
+ group.add_developer(user)
+ end
it 'returns 403' do
post :approve_access_request, group_id: group, id: member
@@ -197,7 +223,9 @@ describe Groups::GroupMembersController do
end
context 'when user has enough rights' do
- before { group.add_owner(user) }
+ before do
+ group.add_owner(user)
+ end
it 'adds user to members' do
post :approve_access_request, group_id: group, id: member
diff --git a/spec/controllers/groups_controller_spec.rb b/spec/controllers/groups_controller_spec.rb
index b0b24b1de1b..c4092303a67 100644
--- a/spec/controllers/groups_controller_spec.rb
+++ b/spec/controllers/groups_controller_spec.rb
@@ -2,7 +2,7 @@ require 'rails_helper'
describe GroupsController do
let(:user) { create(:user) }
- let(:group) { create(:group) }
+ let(:group) { create(:group, :public) }
let(:project) { create(:empty_project, namespace: group) }
let!(:group_member) { create(:group_member, group: group, user: user) }
@@ -35,14 +35,15 @@ describe GroupsController do
sign_in(user)
end
- it 'shows the public subgroups' do
+ it 'shows all subgroups' do
get :subgroups, id: group.to_param
- expect(assigns(:nested_groups)).to contain_exactly(public_subgroup)
+ expect(assigns(:nested_groups)).to contain_exactly(public_subgroup, private_subgroup)
end
- context 'being member' do
+ context 'being member of private subgroup' do
it 'shows public and private subgroups the user is member of' do
+ group_member.destroy!
private_subgroup.add_guest(user)
get :subgroups, id: group.to_param
diff --git a/spec/controllers/notification_settings_controller_spec.rb b/spec/controllers/notification_settings_controller_spec.rb
index 9e3a31e1a6b..6b690407ce3 100644
--- a/spec/controllers/notification_settings_controller_spec.rb
+++ b/spec/controllers/notification_settings_controller_spec.rb
@@ -58,7 +58,10 @@ describe NotificationSettingsController do
expect(response.status).to eq 200
expect(notification_setting.level).to eq("custom")
- expect(notification_setting.events).to eq(custom_events)
+
+ custom_events.each do |event, value|
+ expect(notification_setting.event_enabled?(event)).to eq(value)
+ end
end
end
end
@@ -86,7 +89,10 @@ describe NotificationSettingsController do
expect(response.status).to eq 200
expect(notification_setting.level).to eq("custom")
- expect(notification_setting.events).to eq(custom_events)
+
+ custom_events.each do |event, value|
+ expect(notification_setting.event_enabled?(event)).to eq(value)
+ end
end
end
end
@@ -94,7 +100,10 @@ describe NotificationSettingsController do
context 'not authorized' do
let(:private_project) { create(:empty_project, :private) }
- before { sign_in(user) }
+
+ before do
+ sign_in(user)
+ end
it 'returns 404' do
post :create,
@@ -120,7 +129,9 @@ describe NotificationSettingsController do
end
context 'when authorized' do
- before{ sign_in(user) }
+ before do
+ sign_in(user)
+ end
it 'returns success' do
put :update,
@@ -152,7 +163,9 @@ describe NotificationSettingsController do
context 'not authorized' do
let(:other_user) { create(:user) }
- before { sign_in(other_user) }
+ before do
+ sign_in(other_user)
+ end
it 'returns 404' do
put :update,
diff --git a/spec/controllers/profiles/personal_access_tokens_controller_spec.rb b/spec/controllers/profiles/personal_access_tokens_controller_spec.rb
index 98a43e278b2..ed08a4c1bf2 100644
--- a/spec/controllers/profiles/personal_access_tokens_controller_spec.rb
+++ b/spec/controllers/profiles/personal_access_tokens_controller_spec.rb
@@ -4,7 +4,9 @@ describe Profiles::PersonalAccessTokensController do
let(:user) { create(:user) }
let(:token_attributes) { attributes_for(:personal_access_token) }
- before { sign_in(user) }
+ before do
+ sign_in(user)
+ end
describe '#create' do
def created_token
@@ -38,7 +40,9 @@ describe Profiles::PersonalAccessTokensController do
let!(:inactive_personal_access_token) { create(:personal_access_token, :revoked, user: user) }
let!(:impersonation_personal_access_token) { create(:personal_access_token, :impersonation, user: user) }
- before { get :index }
+ before do
+ get :index
+ end
it "retrieves active personal access tokens" do
expect(assigns(:active_personal_access_tokens)).to include(active_personal_access_token)
diff --git a/spec/controllers/projects/commit_controller_spec.rb b/spec/controllers/projects/commit_controller_spec.rb
index 69e4706dc71..7fb08df1950 100644
--- a/spec/controllers/projects/commit_controller_spec.rb
+++ b/spec/controllers/projects/commit_controller_spec.rb
@@ -281,7 +281,9 @@ describe Projects::CommitController do
end
context 'when the path does not exist in the diff' do
- before { diff_for_path(id: commit.id, old_path: existing_path.succ, new_path: existing_path.succ) }
+ before do
+ diff_for_path(id: commit.id, old_path: existing_path.succ, new_path: existing_path.succ)
+ end
it 'returns a 404' do
expect(response).to have_http_status(404)
@@ -302,7 +304,9 @@ describe Projects::CommitController do
end
context 'when the commit does not exist' do
- before { diff_for_path(id: commit.id.succ, old_path: existing_path, new_path: existing_path) }
+ before do
+ diff_for_path(id: commit.id.succ, old_path: existing_path, new_path: existing_path)
+ end
it 'returns a 404' do
expect(response).to have_http_status(404)
diff --git a/spec/controllers/projects/compare_controller_spec.rb b/spec/controllers/projects/compare_controller_spec.rb
index 15ac4e0925a..8f4694c9854 100644
--- a/spec/controllers/projects/compare_controller_spec.rb
+++ b/spec/controllers/projects/compare_controller_spec.rb
@@ -128,7 +128,9 @@ describe Projects::CompareController do
end
context 'when the path does not exist in the diff' do
- before { diff_for_path(from: ref_from, to: ref_to, old_path: existing_path.succ, new_path: existing_path.succ) }
+ before do
+ diff_for_path(from: ref_from, to: ref_to, old_path: existing_path.succ, new_path: existing_path.succ)
+ end
it 'returns a 404' do
expect(response).to have_http_status(404)
@@ -149,7 +151,9 @@ describe Projects::CompareController do
end
context 'when the from ref does not exist' do
- before { diff_for_path(from: ref_from.succ, to: ref_to, old_path: existing_path, new_path: existing_path) }
+ before do
+ diff_for_path(from: ref_from.succ, to: ref_to, old_path: existing_path, new_path: existing_path)
+ end
it 'returns a 404' do
expect(response).to have_http_status(404)
@@ -157,7 +161,9 @@ describe Projects::CompareController do
end
context 'when the to ref does not exist' do
- before { diff_for_path(from: ref_from, to: ref_to.succ, old_path: existing_path, new_path: existing_path) }
+ before do
+ diff_for_path(from: ref_from, to: ref_to.succ, old_path: existing_path, new_path: existing_path)
+ end
it 'returns a 404' do
expect(response).to have_http_status(404)
diff --git a/spec/controllers/projects/forks_controller_spec.rb b/spec/controllers/projects/forks_controller_spec.rb
index 8282d79298f..dc8290c438e 100644
--- a/spec/controllers/projects/forks_controller_spec.rb
+++ b/spec/controllers/projects/forks_controller_spec.rb
@@ -14,7 +14,9 @@ describe Projects::ForksController do
end
context 'when fork is public' do
- before { forked_project.update_attribute(:visibility_level, Project::PUBLIC) }
+ before do
+ forked_project.update_attribute(:visibility_level, Project::PUBLIC)
+ end
it 'is visible for non logged in users' do
get_forks
@@ -35,7 +37,9 @@ describe Projects::ForksController do
end
context 'when user is logged in' do
- before { sign_in(project.creator) }
+ before do
+ sign_in(project.creator)
+ end
context 'when user is not a Project member neither a group member' do
it 'does not see the Project listed' do
@@ -46,7 +50,9 @@ describe Projects::ForksController do
end
context 'when user is a member of the Project' do
- before { forked_project.team << [project.creator, :developer] }
+ before do
+ forked_project.team << [project.creator, :developer]
+ end
it 'sees the project listed' do
get_forks
@@ -56,7 +62,9 @@ describe Projects::ForksController do
end
context 'when user is a member of the Group' do
- before { forked_project.group.add_developer(project.creator) }
+ before do
+ forked_project.group.add_developer(project.creator)
+ end
it 'sees the project listed' do
get_forks
diff --git a/spec/controllers/projects/group_links_controller_spec.rb b/spec/controllers/projects/group_links_controller_spec.rb
index ca4a8e871c0..b5435357f53 100644
--- a/spec/controllers/projects/group_links_controller_spec.rb
+++ b/spec/controllers/projects/group_links_controller_spec.rb
@@ -22,7 +22,10 @@ describe Projects::GroupLinksController do
end
context 'when user has access to group he want to link project to' do
- before { group.add_developer(user) }
+ before do
+ group.add_developer(user)
+ end
+
include_context 'link project to group'
it 'links project with selected group' do
diff --git a/spec/controllers/projects/issues_controller_spec.rb b/spec/controllers/projects/issues_controller_spec.rb
index b65e9e0dfc0..eed3c3a9098 100644
--- a/spec/controllers/projects/issues_controller_spec.rb
+++ b/spec/controllers/projects/issues_controller_spec.rb
@@ -212,7 +212,9 @@ describe Projects::IssuesController do
let(:another_project) { create(:empty_project, :private) }
context 'when user has access to move issue' do
- before { another_project.team << [user, :reporter] }
+ before do
+ another_project.team << [user, :reporter]
+ end
it 'moves issue to another project' do
move_issue
@@ -250,14 +252,18 @@ describe Projects::IssuesController do
end
context 'when an issue is identified as spam' do
- before { allow_any_instance_of(AkismetService).to receive(:is_spam?).and_return(true) }
+ before do
+ allow_any_instance_of(AkismetService).to receive(:is_spam?).and_return(true)
+ end
context 'when captcha is not verified' do
def update_spam_issue
update_issue(title: 'Spam Title', description: 'Spam lives here')
end
- before { allow_any_instance_of(described_class).to receive(:verify_recaptcha).and_return(false) }
+ before do
+ allow_any_instance_of(described_class).to receive(:verify_recaptcha).and_return(false)
+ end
it 'rejects an issue recognized as a spam' do
expect(Gitlab::Recaptcha).to receive(:load_configurations!).and_return(true)
@@ -620,14 +626,18 @@ describe Projects::IssuesController do
end
context 'when an issue is identified as spam' do
- before { allow_any_instance_of(AkismetService).to receive(:is_spam?).and_return(true) }
+ before do
+ allow_any_instance_of(AkismetService).to receive(:is_spam?).and_return(true)
+ end
context 'when captcha is not verified' do
def post_spam_issue
post_new_issue(title: 'Spam Title', description: 'Spam lives here')
end
- before { allow_any_instance_of(described_class).to receive(:verify_recaptcha).and_return(false) }
+ before do
+ allow_any_instance_of(described_class).to receive(:verify_recaptcha).and_return(false)
+ end
it 'rejects an issue recognized as a spam' do
expect { post_spam_issue }.not_to change(Issue, :count)
@@ -692,7 +702,7 @@ describe Projects::IssuesController do
end
end
- context 'when description has slash commands' do
+ context 'when description has quick actions' do
before do
sign_in(user)
end
@@ -739,7 +749,10 @@ describe Projects::IssuesController do
describe "DELETE #destroy" do
context "when the user is a developer" do
- before { sign_in(user) }
+ before do
+ sign_in(user)
+ end
+
it "rejects a developer to destroy an issue" do
delete :destroy, namespace_id: project.namespace, project_id: project, id: issue.iid
expect(response).to have_http_status(404)
@@ -751,7 +764,9 @@ describe Projects::IssuesController do
let(:namespace) { create(:namespace, owner: owner) }
let(:project) { create(:empty_project, namespace: namespace) }
- before { sign_in(owner) }
+ before do
+ sign_in(owner)
+ end
it "deletes the issue" do
delete :destroy, namespace_id: project.namespace, project_id: project, id: issue.iid
diff --git a/spec/controllers/projects/jobs_controller_spec.rb b/spec/controllers/projects/jobs_controller_spec.rb
index 4a737587899..472e5fc51a0 100644
--- a/spec/controllers/projects/jobs_controller_spec.rb
+++ b/spec/controllers/projects/jobs_controller_spec.rb
@@ -28,7 +28,7 @@ describe Projects::JobsController do
get_index(scope: 'running')
end
- it 'has only running builds' do
+ it 'has only running jobs' do
expect(response).to have_http_status(:ok)
expect(assigns(:builds).first.status).to eq('running')
end
@@ -41,7 +41,7 @@ describe Projects::JobsController do
get_index(scope: 'finished')
end
- it 'has only finished builds' do
+ it 'has only finished jobs' do
expect(response).to have_http_status(:ok)
expect(assigns(:builds).first.status).to eq('success')
end
@@ -67,7 +67,7 @@ describe Projects::JobsController do
context 'number of queries' do
before do
Ci::Build::AVAILABLE_STATUSES.each do |status|
- create_build(status, status)
+ create_job(status, status)
end
end
@@ -76,7 +76,7 @@ describe Projects::JobsController do
expect(recorded.count).to be_within(5).of(7)
end
- def create_build(name, status)
+ def create_job(name, status)
pipeline = create(:ci_pipeline, project: project)
create(:ci_build, :tags, :triggered, :artifacts,
pipeline: pipeline, name: name, status: status)
@@ -94,21 +94,21 @@ describe Projects::JobsController do
end
describe 'GET show' do
- let!(:build) { create(:ci_build, :failed, pipeline: pipeline) }
+ let!(:job) { create(:ci_build, :failed, pipeline: pipeline) }
context 'when requesting HTML' do
- context 'when build exists' do
+ context 'when job exists' do
before do
- get_show(id: build.id)
+ get_show(id: job.id)
end
- it 'has a build' do
+ it 'has a job' do
expect(response).to have_http_status(:ok)
- expect(assigns(:build).id).to eq(build.id)
+ expect(assigns(:build).id).to eq(job.id)
end
end
- context 'when build does not exist' do
+ context 'when job does not exist' do
before do
get_show(id: 1234)
end
@@ -128,12 +128,12 @@ describe Projects::JobsController do
allow_any_instance_of(Ci::Build).to receive(:merge_request).and_return(merge_request)
- get_show(id: build.id, format: :json)
+ get_show(id: job.id, format: :json)
end
it 'exposes needed information' do
expect(response).to have_http_status(:ok)
- expect(json_response['raw_path']).to match(/builds\/\d+\/raw\z/)
+ expect(json_response['raw_path']).to match(/jobs\/\d+\/raw\z/)
expect(json_response.dig('merge_request', 'path')).to match(/merge_requests\/\d+\z/)
expect(json_response['new_issue_path'])
.to include('/issues/new')
@@ -155,35 +155,35 @@ describe Projects::JobsController do
get_trace
end
- context 'when build has a trace' do
- let(:build) { create(:ci_build, :trace, pipeline: pipeline) }
+ context 'when job has a trace' do
+ let(:job) { create(:ci_build, :trace, pipeline: pipeline) }
it 'returns a trace' do
expect(response).to have_http_status(:ok)
- expect(json_response['id']).to eq build.id
- expect(json_response['status']).to eq build.status
+ expect(json_response['id']).to eq job.id
+ expect(json_response['status']).to eq job.status
expect(json_response['html']).to eq('BUILD TRACE')
end
end
- context 'when build has no traces' do
- let(:build) { create(:ci_build, pipeline: pipeline) }
+ context 'when job has no traces' do
+ let(:job) { create(:ci_build, pipeline: pipeline) }
it 'returns no traces' do
expect(response).to have_http_status(:ok)
- expect(json_response['id']).to eq build.id
- expect(json_response['status']).to eq build.status
+ expect(json_response['id']).to eq job.id
+ expect(json_response['status']).to eq job.status
expect(json_response['html']).to be_nil
end
end
- context 'when build has a trace with ANSI sequence and Unicode' do
- let(:build) { create(:ci_build, :unicode_trace, pipeline: pipeline) }
+ context 'when job has a trace with ANSI sequence and Unicode' do
+ let(:job) { create(:ci_build, :unicode_trace, pipeline: pipeline) }
it 'returns a trace with Unicode' do
expect(response).to have_http_status(:ok)
- expect(json_response['id']).to eq build.id
- expect(json_response['status']).to eq build.status
+ expect(json_response['id']).to eq job.id
+ expect(json_response['status']).to eq job.status
expect(json_response['html']).to include("ヾ(´༎ຶД༎ຶ`)ノ")
end
end
@@ -191,23 +191,23 @@ describe Projects::JobsController do
def get_trace
get :trace, namespace_id: project.namespace,
project_id: project,
- id: build.id,
+ id: job.id,
format: :json
end
end
describe 'GET status.json' do
- let(:build) { create(:ci_build, pipeline: pipeline) }
- let(:status) { build.detailed_status(double('user')) }
+ let(:job) { create(:ci_build, pipeline: pipeline) }
+ let(:status) { job.detailed_status(double('user')) }
before do
get :status, namespace_id: project.namespace,
project_id: project,
- id: build.id,
+ id: job.id,
format: :json
end
- it 'return a detailed build status in json' do
+ it 'return a detailed job status in json' do
expect(response).to have_http_status(:ok)
expect(json_response['text']).to eq status.text
expect(json_response['label']).to eq status.label
@@ -224,17 +224,17 @@ describe Projects::JobsController do
post_retry
end
- context 'when build is retryable' do
- let(:build) { create(:ci_build, :retryable, pipeline: pipeline) }
+ context 'when job is retryable' do
+ let(:job) { create(:ci_build, :retryable, pipeline: pipeline) }
- it 'redirects to the retried build page' do
+ it 'redirects to the retried job page' do
expect(response).to have_http_status(:found)
expect(response).to redirect_to(namespace_project_job_path(id: Ci::Build.last.id))
end
end
- context 'when build is not retryable' do
- let(:build) { create(:ci_build, pipeline: pipeline) }
+ context 'when job is not retryable' do
+ let(:job) { create(:ci_build, pipeline: pipeline) }
it 'renders unprocessable_entity' do
expect(response).to have_http_status(:unprocessable_entity)
@@ -244,7 +244,7 @@ describe Projects::JobsController do
def post_retry
post :retry, namespace_id: project.namespace,
project_id: project,
- id: build.id
+ id: job.id
end
end
@@ -260,21 +260,21 @@ describe Projects::JobsController do
post_play
end
- context 'when build is playable' do
- let(:build) { create(:ci_build, :playable, pipeline: pipeline) }
+ context 'when job is playable' do
+ let(:job) { create(:ci_build, :playable, pipeline: pipeline) }
- it 'redirects to the played build page' do
+ it 'redirects to the played job page' do
expect(response).to have_http_status(:found)
- expect(response).to redirect_to(namespace_project_job_path(id: build.id))
+ expect(response).to redirect_to(namespace_project_job_path(id: job.id))
end
it 'transits to pending' do
- expect(build.reload).to be_pending
+ expect(job.reload).to be_pending
end
end
- context 'when build is not playable' do
- let(:build) { create(:ci_build, pipeline: pipeline) }
+ context 'when job is not playable' do
+ let(:job) { create(:ci_build, pipeline: pipeline) }
it 'renders unprocessable_entity' do
expect(response).to have_http_status(:unprocessable_entity)
@@ -284,7 +284,7 @@ describe Projects::JobsController do
def post_play
post :play, namespace_id: project.namespace,
project_id: project,
- id: build.id
+ id: job.id
end
end
@@ -296,21 +296,21 @@ describe Projects::JobsController do
post_cancel
end
- context 'when build is cancelable' do
- let(:build) { create(:ci_build, :cancelable, pipeline: pipeline) }
+ context 'when job is cancelable' do
+ let(:job) { create(:ci_build, :cancelable, pipeline: pipeline) }
- it 'redirects to the canceled build page' do
+ it 'redirects to the canceled job page' do
expect(response).to have_http_status(:found)
- expect(response).to redirect_to(namespace_project_job_path(id: build.id))
+ expect(response).to redirect_to(namespace_project_job_path(id: job.id))
end
it 'transits to canceled' do
- expect(build.reload).to be_canceled
+ expect(job.reload).to be_canceled
end
end
- context 'when build is not cancelable' do
- let(:build) { create(:ci_build, :canceled, pipeline: pipeline) }
+ context 'when job is not cancelable' do
+ let(:job) { create(:ci_build, :canceled, pipeline: pipeline) }
it 'returns unprocessable_entity' do
expect(response).to have_http_status(:unprocessable_entity)
@@ -320,7 +320,7 @@ describe Projects::JobsController do
def post_cancel
post :cancel, namespace_id: project.namespace,
project_id: project,
- id: build.id
+ id: job.id
end
end
@@ -330,7 +330,7 @@ describe Projects::JobsController do
sign_in(user)
end
- context 'when builds are cancelable' do
+ context 'when jobs are cancelable' do
before do
create_list(:ci_build, 2, :cancelable, pipeline: pipeline)
@@ -347,7 +347,7 @@ describe Projects::JobsController do
end
end
- context 'when builds are not cancelable' do
+ context 'when jobs are not cancelable' do
before do
create_list(:ci_build, 2, :canceled, pipeline: pipeline)
@@ -374,26 +374,26 @@ describe Projects::JobsController do
post_erase
end
- context 'when build is erasable' do
- let(:build) { create(:ci_build, :erasable, :trace, pipeline: pipeline) }
+ context 'when job is erasable' do
+ let(:job) { create(:ci_build, :erasable, :trace, pipeline: pipeline) }
- it 'redirects to the erased build page' do
+ it 'redirects to the erased job page' do
expect(response).to have_http_status(:found)
- expect(response).to redirect_to(namespace_project_job_path(id: build.id))
+ expect(response).to redirect_to(namespace_project_job_path(id: job.id))
end
it 'erases artifacts' do
- expect(build.artifacts_file.exists?).to be_falsey
- expect(build.artifacts_metadata.exists?).to be_falsey
+ expect(job.artifacts_file.exists?).to be_falsey
+ expect(job.artifacts_metadata.exists?).to be_falsey
end
it 'erases trace' do
- expect(build.trace.exist?).to be_falsey
+ expect(job.trace.exist?).to be_falsey
end
end
- context 'when build is not erasable' do
- let(:build) { create(:ci_build, :erased, pipeline: pipeline) }
+ context 'when job is not erasable' do
+ let(:job) { create(:ci_build, :erased, pipeline: pipeline) }
it 'returns unprocessable_entity' do
expect(response).to have_http_status(:unprocessable_entity)
@@ -403,7 +403,7 @@ describe Projects::JobsController do
def post_erase
post :erase, namespace_id: project.namespace,
project_id: project,
- id: build.id
+ id: job.id
end
end
@@ -412,8 +412,8 @@ describe Projects::JobsController do
get_raw
end
- context 'when build has a trace file' do
- let(:build) { create(:ci_build, :trace, pipeline: pipeline) }
+ context 'when job has a trace file' do
+ let(:job) { create(:ci_build, :trace, pipeline: pipeline) }
it 'send a trace file' do
expect(response).to have_http_status(:ok)
@@ -422,8 +422,8 @@ describe Projects::JobsController do
end
end
- context 'when build does not have a trace file' do
- let(:build) { create(:ci_build, pipeline: pipeline) }
+ context 'when job does not have a trace file' do
+ let(:job) { create(:ci_build, pipeline: pipeline) }
it 'returns not_found' do
expect(response).to have_http_status(:not_found)
@@ -433,7 +433,7 @@ describe Projects::JobsController do
def get_raw
post :raw, namespace_id: project.namespace,
project_id: project,
- id: build.id
+ id: job.id
end
end
end
diff --git a/spec/controllers/projects/merge_requests_controller_spec.rb b/spec/controllers/projects/merge_requests_controller_spec.rb
index 6e1c91738db..d8a3a510f97 100644
--- a/spec/controllers/projects/merge_requests_controller_spec.rb
+++ b/spec/controllers/projects/merge_requests_controller_spec.rb
@@ -19,7 +19,10 @@ describe Projects::MergeRequestsController do
render_views
let(:fork_project) { create(:forked_project_with_submodules) }
- before { fork_project.team << [user, :master] }
+
+ before do
+ fork_project.team << [user, :master]
+ end
context 'when rendering HTML response' do
it 'renders new merge request widget template' do
@@ -328,7 +331,9 @@ describe Projects::MergeRequestsController do
end
context 'when the sha parameter does not match the source SHA' do
- before { post :merge, base_params.merge(sha: 'foo') }
+ before do
+ post :merge, base_params.merge(sha: 'foo')
+ end
it 'returns :sha_mismatch' do
expect(json_response).to eq('status' => 'sha_mismatch')
@@ -473,7 +478,9 @@ describe Projects::MergeRequestsController do
let(:namespace) { create(:namespace, owner: owner) }
let(:project) { create(:project, namespace: namespace) }
- before { sign_in owner }
+ before do
+ sign_in owner
+ end
it "deletes the merge request" do
delete :destroy, namespace_id: project.namespace, project_id: project, id: merge_request.iid
@@ -505,7 +512,9 @@ describe Projects::MergeRequestsController do
context 'with default params' do
context 'as html' do
- before { go(format: 'html') }
+ before do
+ go(format: 'html')
+ end
it 'renders the diff template' do
expect(response).to render_template('diffs')
@@ -513,7 +522,9 @@ describe Projects::MergeRequestsController do
end
context 'as json' do
- before { go(format: 'json') }
+ before do
+ go(format: 'json')
+ end
it 'renders the diffs template to a string' do
expect(response).to render_template('projects/merge_requests/show/_diffs')
@@ -544,7 +555,9 @@ describe Projects::MergeRequestsController do
context 'with ignore_whitespace_change' do
context 'as html' do
- before { go(format: 'html', w: 1) }
+ before do
+ go(format: 'html', w: 1)
+ end
it 'renders the diff template' do
expect(response).to render_template('diffs')
@@ -552,7 +565,9 @@ describe Projects::MergeRequestsController do
end
context 'as json' do
- before { go(format: 'json', w: 1) }
+ before do
+ go(format: 'json', w: 1)
+ end
it 'renders the diffs template to a string' do
expect(response).to render_template('projects/merge_requests/show/_diffs')
@@ -562,7 +577,9 @@ describe Projects::MergeRequestsController do
end
context 'with view' do
- before { go(view: 'parallel') }
+ before do
+ go(view: 'parallel')
+ end
it 'saves the preferred diff view in a cookie' do
expect(response.cookies['diff_view']).to eq('parallel')
@@ -605,7 +622,9 @@ describe Projects::MergeRequestsController do
end
context 'when the path does not exist in the diff' do
- before { diff_for_path(id: merge_request.iid, old_path: 'files/ruby/nopen.rb', new_path: 'files/ruby/nopen.rb') }
+ before do
+ diff_for_path(id: merge_request.iid, old_path: 'files/ruby/nopen.rb', new_path: 'files/ruby/nopen.rb')
+ end
it 'returns a 404' do
expect(response).to have_http_status(404)
@@ -626,7 +645,9 @@ describe Projects::MergeRequestsController do
end
context 'when the merge request does not exist' do
- before { diff_for_path(id: merge_request.iid.succ, old_path: existing_path, new_path: existing_path) }
+ before do
+ diff_for_path(id: merge_request.iid.succ, old_path: existing_path, new_path: existing_path)
+ end
it 'returns a 404' do
expect(response).to have_http_status(404)
@@ -670,7 +691,9 @@ describe Projects::MergeRequestsController do
context 'when the source branch is in a different project to the target' do
let(:other_project) { create(:project) }
- before { other_project.team << [user, :master] }
+ before do
+ other_project.team << [user, :master]
+ end
context 'when the path exists in the diff' do
it 'disables diff notes' do
@@ -690,7 +713,9 @@ describe Projects::MergeRequestsController do
end
context 'when the path does not exist in the diff' do
- before { diff_for_path(old_path: 'files/ruby/nopen.rb', new_path: 'files/ruby/nopen.rb', merge_request: { source_project: other_project, source_branch: 'feature', target_branch: 'master' }) }
+ before do
+ diff_for_path(old_path: 'files/ruby/nopen.rb', new_path: 'files/ruby/nopen.rb', merge_request: { source_project: other_project, source_branch: 'feature', target_branch: 'master' })
+ end
it 'returns a 404' do
expect(response).to have_http_status(404)
@@ -913,7 +938,9 @@ describe Projects::MergeRequestsController do
end
context 'when the file does not exist cannot be resolved in the UI' do
- before { conflict_for_path('files/ruby/regexp.rb') }
+ before do
+ conflict_for_path('files/ruby/regexp.rb')
+ end
it 'returns a 404 status code' do
expect(response).to have_http_status(:not_found)
@@ -923,7 +950,9 @@ describe Projects::MergeRequestsController do
context 'with an existing file' do
let(:path) { 'files/ruby/regex.rb' }
- before { conflict_for_path(path) }
+ before do
+ conflict_for_path(path)
+ end
it 'returns a 200 status code' do
expect(response).to have_http_status(:ok)
@@ -1195,7 +1224,9 @@ describe Projects::MergeRequestsController do
end
context 'when head_pipeline does not exist' do
- before { get_pipeline_status }
+ before do
+ get_pipeline_status
+ end
it 'return empty' do
expect(response).to have_http_status(:ok)
diff --git a/spec/controllers/projects/pipelines_controller_spec.rb b/spec/controllers/projects/pipelines_controller_spec.rb
index 954f89e3854..734532668d3 100644
--- a/spec/controllers/projects/pipelines_controller_spec.rb
+++ b/spec/controllers/projects/pipelines_controller_spec.rb
@@ -5,9 +5,12 @@ describe Projects::PipelinesController do
let(:user) { create(:user) }
let(:project) { create(:empty_project, :public) }
+ let(:feature) { ProjectFeature::DISABLED }
before do
project.add_developer(user)
+ project.project_feature.update(
+ builds_access_level: feature)
sign_in(user)
end
@@ -153,16 +156,26 @@ describe Projects::PipelinesController do
format: :json
end
- it 'retries a pipeline without returning any content' do
- expect(response).to have_http_status(:no_content)
- expect(build.reload).to be_retried
+ context 'when builds are enabled' do
+ let(:feature) { ProjectFeature::ENABLED }
+
+ it 'retries a pipeline without returning any content' do
+ expect(response).to have_http_status(:no_content)
+ expect(build.reload).to be_retried
+ end
+ end
+
+ context 'when builds are disabled' do
+ it 'fails to retry pipeline' do
+ expect(response).to have_http_status(:not_found)
+ end
end
end
describe 'POST cancel.json' do
let!(:pipeline) { create(:ci_pipeline, project: project) }
let!(:build) { create(:ci_build, :running, pipeline: pipeline) }
-
+
before do
post :cancel, namespace_id: project.namespace,
project_id: project,
@@ -170,9 +183,19 @@ describe Projects::PipelinesController do
format: :json
end
- it 'cancels a pipeline without returning any content' do
- expect(response).to have_http_status(:no_content)
- expect(pipeline.reload).to be_canceled
+ context 'when builds are enabled' do
+ let(:feature) { ProjectFeature::ENABLED }
+
+ it 'cancels a pipeline without returning any content' do
+ expect(response).to have_http_status(:no_content)
+ expect(pipeline.reload).to be_canceled
+ end
+ end
+
+ context 'when builds are disabled' do
+ it 'fails to retry pipeline' do
+ expect(response).to have_http_status(:not_found)
+ end
end
end
end
diff --git a/spec/controllers/projects/project_members_controller_spec.rb b/spec/controllers/projects/project_members_controller_spec.rb
index 2294d5df581..f2b59ba82ca 100644
--- a/spec/controllers/projects/project_members_controller_spec.rb
+++ b/spec/controllers/projects/project_members_controller_spec.rb
@@ -16,10 +16,14 @@ describe Projects::ProjectMembersController do
describe 'POST create' do
let(:project_user) { create(:user) }
- before { sign_in(user) }
+ before do
+ sign_in(user)
+ end
context 'when user does not have enough rights' do
- before { project.team << [user, :developer] }
+ before do
+ project.team << [user, :developer]
+ end
it 'returns 404' do
post :create, namespace_id: project.namespace,
@@ -33,7 +37,9 @@ describe Projects::ProjectMembersController do
end
context 'when user has enough rights' do
- before { project.team << [user, :master] }
+ before do
+ project.team << [user, :master]
+ end
it 'adds user to members' do
expect_any_instance_of(Members::CreateService).to receive(:execute).and_return(status: :success)
@@ -64,7 +70,9 @@ describe Projects::ProjectMembersController do
describe 'DELETE destroy' do
let(:member) { create(:project_member, :developer, project: project) }
- before { sign_in(user) }
+ before do
+ sign_in(user)
+ end
context 'when member is not found' do
it 'returns 404' do
@@ -78,7 +86,9 @@ describe Projects::ProjectMembersController do
context 'when member is found' do
context 'when user does not have enough rights' do
- before { project.team << [user, :developer] }
+ before do
+ project.team << [user, :developer]
+ end
it 'returns 404' do
delete :destroy, namespace_id: project.namespace,
@@ -91,7 +101,9 @@ describe Projects::ProjectMembersController do
end
context 'when user has enough rights' do
- before { project.team << [user, :master] }
+ before do
+ project.team << [user, :master]
+ end
it '[HTML] removes user from members' do
delete :destroy, namespace_id: project.namespace,
@@ -117,7 +129,9 @@ describe Projects::ProjectMembersController do
end
describe 'DELETE leave' do
- before { sign_in(user) }
+ before do
+ sign_in(user)
+ end
context 'when member is not found' do
it 'returns 404' do
@@ -130,7 +144,9 @@ describe Projects::ProjectMembersController do
context 'when member is found' do
context 'and is not an owner' do
- before { project.team << [user, :developer] }
+ before do
+ project.team << [user, :developer]
+ end
it 'removes user from members' do
delete :leave, namespace_id: project.namespace,
@@ -145,7 +161,9 @@ describe Projects::ProjectMembersController do
context 'and is an owner' do
let(:project) { create(:empty_project, namespace: user.namespace) }
- before { project.team << [user, :master] }
+ before do
+ project.team << [user, :master]
+ end
it 'cannot remove himself from the project' do
delete :leave, namespace_id: project.namespace,
@@ -156,7 +174,9 @@ describe Projects::ProjectMembersController do
end
context 'and is a requester' do
- before { project.request_access(user) }
+ before do
+ project.request_access(user)
+ end
it 'removes user from members' do
delete :leave, namespace_id: project.namespace,
@@ -172,7 +192,9 @@ describe Projects::ProjectMembersController do
end
describe 'POST request_access' do
- before { sign_in(user) }
+ before do
+ sign_in(user)
+ end
it 'creates a new ProjectMember that is not a team member' do
post :request_access, namespace_id: project.namespace,
@@ -190,7 +212,9 @@ describe Projects::ProjectMembersController do
describe 'POST approve' do
let(:member) { create(:project_member, :access_request, project: project) }
- before { sign_in(user) }
+ before do
+ sign_in(user)
+ end
context 'when member is not found' do
it 'returns 404' do
@@ -204,7 +228,9 @@ describe Projects::ProjectMembersController do
context 'when member is found' do
context 'when user does not have enough rights' do
- before { project.team << [user, :developer] }
+ before do
+ project.team << [user, :developer]
+ end
it 'returns 404' do
post :approve_access_request, namespace_id: project.namespace,
@@ -217,7 +243,9 @@ describe Projects::ProjectMembersController do
end
context 'when user has enough rights' do
- before { project.team << [user, :master] }
+ before do
+ project.team << [user, :master]
+ end
it 'adds user to members' do
post :approve_access_request, namespace_id: project.namespace,
@@ -252,7 +280,10 @@ describe Projects::ProjectMembersController do
end
context 'when user can access source project members' do
- before { another_project.team << [user, :guest] }
+ before do
+ another_project.team << [user, :guest]
+ end
+
include_context 'import applied'
it 'imports source project members' do
diff --git a/spec/controllers/projects/snippets_controller_spec.rb b/spec/controllers/projects/snippets_controller_spec.rb
index 8c23c46798e..2434f822c6f 100644
--- a/spec/controllers/projects/snippets_controller_spec.rb
+++ b/spec/controllers/projects/snippets_controller_spec.rb
@@ -46,7 +46,9 @@ describe Projects::SnippetsController do
end
context 'when signed in as the author' do
- before { sign_in(user) }
+ before do
+ sign_in(user)
+ end
it 'renders the snippet' do
get :index, namespace_id: project.namespace, project_id: project
@@ -57,7 +59,9 @@ describe Projects::SnippetsController do
end
context 'when signed in as a project member' do
- before { sign_in(user2) }
+ before do
+ sign_in(user2)
+ end
it 'renders the snippet' do
get :index, namespace_id: project.namespace, project_id: project
@@ -317,7 +321,9 @@ describe Projects::SnippetsController do
end
context 'when signed in as the author' do
- before { sign_in(user) }
+ before do
+ sign_in(user)
+ end
it 'renders the snippet' do
get action, namespace_id: project.namespace, project_id: project, id: project_snippet.to_param
@@ -328,7 +334,9 @@ describe Projects::SnippetsController do
end
context 'when signed in as a project member' do
- before { sign_in(user2) }
+ before do
+ sign_in(user2)
+ end
it 'renders the snippet' do
get action, namespace_id: project.namespace, project_id: project, id: project_snippet.to_param
@@ -349,7 +357,9 @@ describe Projects::SnippetsController do
end
context 'when signed in' do
- before { sign_in(user) }
+ before do
+ sign_in(user)
+ end
it 'responds with status 404' do
get action, namespace_id: project.namespace, project_id: project, id: 42
diff --git a/spec/controllers/projects/tags_controller_spec.rb b/spec/controllers/projects/tags_controller_spec.rb
index fc97bac64cd..c48f41ca12e 100644
--- a/spec/controllers/projects/tags_controller_spec.rb
+++ b/spec/controllers/projects/tags_controller_spec.rb
@@ -6,7 +6,9 @@ describe Projects::TagsController do
let!(:invalid_release) { create(:release, project: project, tag: 'does-not-exist') }
describe 'GET index' do
- before { get :index, namespace_id: project.namespace.to_param, project_id: project }
+ before do
+ get :index, namespace_id: project.namespace.to_param, project_id: project
+ end
it 'returns the tags for the page' do
expect(assigns(:tags).map(&:name)).to eq(['v1.1.0', 'v1.0.0'])
@@ -19,7 +21,9 @@ describe Projects::TagsController do
end
describe 'GET show' do
- before { get :show, namespace_id: project.namespace.to_param, project_id: project, id: id }
+ before do
+ get :show, namespace_id: project.namespace.to_param, project_id: project, id: id
+ end
context "valid tag" do
let(:id) { 'v1.0.0' }
diff --git a/spec/controllers/projects_controller_spec.rb b/spec/controllers/projects_controller_spec.rb
index 4f6fc6691be..240a81367d0 100644
--- a/spec/controllers/projects_controller_spec.rb
+++ b/spec/controllers/projects_controller_spec.rb
@@ -29,7 +29,9 @@ describe ProjectsController do
describe "GET show" do
context "user not project member" do
- before { sign_in(user) }
+ before do
+ sign_in(user)
+ end
context "user does not have access to project" do
let(:private_project) { create(:empty_project, :private) }
@@ -108,7 +110,9 @@ describe ProjectsController do
context "project with empty repo" do
let(:empty_project) { create(:project_empty_repo, :public) }
- before { sign_in(user) }
+ before do
+ sign_in(user)
+ end
User.project_views.keys.each do |project_view|
context "with #{project_view} view set" do
@@ -128,7 +132,9 @@ describe ProjectsController do
context "project with broken repo" do
let(:empty_project) { create(:project_broken_repo, :public) }
- before { sign_in(user) }
+ before do
+ sign_in(user)
+ end
User.project_views.keys.each do |project_view|
context "with #{project_view} view set" do
diff --git a/spec/controllers/search_controller_spec.rb b/spec/controllers/search_controller_spec.rb
index 3173aae664c..a3708ad0908 100644
--- a/spec/controllers/search_controller_spec.rb
+++ b/spec/controllers/search_controller_spec.rb
@@ -18,7 +18,9 @@ describe SearchController do
context 'on restricted projects' do
context 'when signed out' do
- before { sign_out(user) }
+ before do
+ sign_out(user)
+ end
it "doesn't expose comments on issues" do
project = create(:empty_project, :public, :issues_private)
diff --git a/spec/controllers/sent_notifications_controller_spec.rb b/spec/controllers/sent_notifications_controller_spec.rb
index 954fc2eaf21..0cc8a3b68eb 100644
--- a/spec/controllers/sent_notifications_controller_spec.rb
+++ b/spec/controllers/sent_notifications_controller_spec.rb
@@ -14,7 +14,9 @@ describe SentNotificationsController, type: :controller do
describe 'GET unsubscribe' do
context 'when the user is not logged in' do
context 'when the force param is passed' do
- before { get(:unsubscribe, id: sent_notification.reply_key, force: true) }
+ before do
+ get(:unsubscribe, id: sent_notification.reply_key, force: true)
+ end
it 'unsubscribes the user' do
expect(issue.subscribed?(user, project)).to be_falsey
@@ -30,7 +32,9 @@ describe SentNotificationsController, type: :controller do
end
context 'when the force param is not passed' do
- before { get(:unsubscribe, id: sent_notification.reply_key) }
+ before do
+ get(:unsubscribe, id: sent_notification.reply_key)
+ end
it 'does not unsubscribe the user' do
expect(issue.subscribed?(user, project)).to be_truthy
@@ -47,10 +51,14 @@ describe SentNotificationsController, type: :controller do
end
context 'when the user is logged in' do
- before { sign_in(user) }
+ before do
+ sign_in(user)
+ end
context 'when the ID passed does not exist' do
- before { get(:unsubscribe, id: sent_notification.reply_key.reverse) }
+ before do
+ get(:unsubscribe, id: sent_notification.reply_key.reverse)
+ end
it 'does not unsubscribe the user' do
expect(issue.subscribed?(user, project)).to be_truthy
@@ -66,7 +74,9 @@ describe SentNotificationsController, type: :controller do
end
context 'when the force param is passed' do
- before { get(:unsubscribe, id: sent_notification.reply_key, force: true) }
+ before do
+ get(:unsubscribe, id: sent_notification.reply_key, force: true)
+ end
it 'unsubscribes the user' do
expect(issue.subscribed?(user, project)).to be_falsey
@@ -89,7 +99,10 @@ describe SentNotificationsController, type: :controller do
end
end
let(:sent_notification) { create(:sent_notification, project: project, noteable: merge_request, recipient: user) }
- before { get(:unsubscribe, id: sent_notification.reply_key) }
+
+ before do
+ get(:unsubscribe, id: sent_notification.reply_key)
+ end
it 'unsubscribes the user' do
expect(merge_request.subscribed?(user, project)).to be_falsey
diff --git a/spec/controllers/sessions_controller_spec.rb b/spec/controllers/sessions_controller_spec.rb
index e87e24a33a1..03f4b0ba343 100644
--- a/spec/controllers/sessions_controller_spec.rb
+++ b/spec/controllers/sessions_controller_spec.rb
@@ -142,7 +142,9 @@ describe SessionsController do
end
context 'when OTP is invalid' do
- before { authenticate_2fa(otp_attempt: 'invalid') }
+ before do
+ authenticate_2fa(otp_attempt: 'invalid')
+ end
it 'does not authenticate' do
expect(subject.current_user).not_to eq user
@@ -169,7 +171,9 @@ describe SessionsController do
end
context 'when OTP is invalid' do
- before { authenticate_2fa(otp_attempt: 'invalid') }
+ before do
+ authenticate_2fa(otp_attempt: 'invalid')
+ end
it 'does not authenticate' do
expect(subject.current_user).not_to eq user
diff --git a/spec/controllers/snippets_controller_spec.rb b/spec/controllers/snippets_controller_spec.rb
index 9073c39f562..b1339b2a185 100644
--- a/spec/controllers/snippets_controller_spec.rb
+++ b/spec/controllers/snippets_controller_spec.rb
@@ -437,7 +437,9 @@ describe SnippetsController do
end
context 'when signed in user is the author' do
- before { get :raw, id: personal_snippet.to_param }
+ before do
+ get :raw, id: personal_snippet.to_param
+ end
it 'responds with status 200' do
expect(assigns(:snippet)).to eq(personal_snippet)
diff --git a/spec/controllers/users_controller_spec.rb b/spec/controllers/users_controller_spec.rb
index d33e2ba1e53..842d82cdbe9 100644
--- a/spec/controllers/users_controller_spec.rb
+++ b/spec/controllers/users_controller_spec.rb
@@ -43,7 +43,9 @@ describe UsersController do
end
context 'when logged in' do
- before { sign_in(user) }
+ before do
+ sign_in(user)
+ end
it 'renders show' do
get :show, username: user.username
@@ -62,7 +64,9 @@ describe UsersController do
end
context 'when logged in' do
- before { sign_in(user) }
+ before do
+ sign_in(user)
+ end
it 'renders 404' do
get :show, username: 'nonexistent'
diff --git a/spec/factories/application_settings.rb b/spec/factories/application_settings.rb
new file mode 100644
index 00000000000..aef65e724c2
--- /dev/null
+++ b/spec/factories/application_settings.rb
@@ -0,0 +1,4 @@
+FactoryGirl.define do
+ factory :application_setting do
+ end
+end
diff --git a/spec/factories/ci/builds.rb b/spec/factories/ci/builds.rb
index 0bb5a86d9b9..0cc498f0ce9 100644
--- a/spec/factories/ci/builds.rb
+++ b/spec/factories/ci/builds.rb
@@ -194,8 +194,8 @@ FactoryGirl.define do
trait :extended_options do
options do
{
- image: 'ruby:2.1',
- services: ['postgres'],
+ image: { name: 'ruby:2.1', entrypoint: '/bin/sh' },
+ services: ['postgres', { name: 'docker:dind', entrypoint: '/bin/sh', command: 'sleep 30', alias: 'docker' }],
after_script: %w(ls date),
artifacts: {
name: 'artifacts_file',
diff --git a/spec/features/abuse_report_spec.rb b/spec/features/abuse_report_spec.rb
index 1e11fb756b2..5e6cd64c5c1 100644
--- a/spec/features/abuse_report_spec.rb
+++ b/spec/features/abuse_report_spec.rb
@@ -4,7 +4,7 @@ feature 'Abuse reports', feature: true do
let(:another_user) { create(:user) }
before do
- login_as :user
+ gitlab_sign_in :user
end
scenario 'Report abuse' do
diff --git a/spec/features/admin/admin_abuse_reports_spec.rb b/spec/features/admin/admin_abuse_reports_spec.rb
index 340884fc986..3a6e356b0b0 100644
--- a/spec/features/admin/admin_abuse_reports_spec.rb
+++ b/spec/features/admin/admin_abuse_reports_spec.rb
@@ -5,7 +5,7 @@ describe "Admin::AbuseReports", feature: true, js: true do
context 'as an admin' do
before do
- login_as :admin
+ gitlab_sign_in :admin
end
describe 'if a user has been reported for abuse' do
diff --git a/spec/features/admin/admin_active_tab_spec.rb b/spec/features/admin/admin_active_tab_spec.rb
index 16064d60ce2..c74336d8221 100644
--- a/spec/features/admin/admin_active_tab_spec.rb
+++ b/spec/features/admin/admin_active_tab_spec.rb
@@ -2,7 +2,7 @@ require 'spec_helper'
RSpec.describe 'admin active tab' do
before do
- login_as :admin
+ gitlab_sign_in :admin
end
shared_examples 'page has active tab' do |title|
diff --git a/spec/features/admin/admin_appearance_spec.rb b/spec/features/admin/admin_appearance_spec.rb
index 595366ce352..d8fd4319328 100644
--- a/spec/features/admin/admin_appearance_spec.rb
+++ b/spec/features/admin/admin_appearance_spec.rb
@@ -4,7 +4,7 @@ feature 'Admin Appearance', feature: true do
let!(:appearance) { create(:appearance) }
scenario 'Create new appearance' do
- login_as :admin
+ gitlab_sign_in :admin
visit admin_appearances_path
fill_in 'appearance_title', with: 'MyCompany'
@@ -20,7 +20,7 @@ feature 'Admin Appearance', feature: true do
end
scenario 'Preview appearance' do
- login_as :admin
+ gitlab_sign_in :admin
visit admin_appearances_path
click_link "Preview"
@@ -34,7 +34,7 @@ feature 'Admin Appearance', feature: true do
end
scenario 'Appearance logo' do
- login_as :admin
+ gitlab_sign_in :admin
visit admin_appearances_path
attach_file(:appearance_logo, logo_fixture)
@@ -46,7 +46,7 @@ feature 'Admin Appearance', feature: true do
end
scenario 'Header logos' do
- login_as :admin
+ gitlab_sign_in :admin
visit admin_appearances_path
attach_file(:appearance_header_logo, logo_fixture)
diff --git a/spec/features/admin/admin_broadcast_messages_spec.rb b/spec/features/admin/admin_broadcast_messages_spec.rb
index d6c63f66a9b..da063bf7b74 100644
--- a/spec/features/admin/admin_broadcast_messages_spec.rb
+++ b/spec/features/admin/admin_broadcast_messages_spec.rb
@@ -2,7 +2,7 @@ require 'spec_helper'
feature 'Admin Broadcast Messages', feature: true do
before do
- login_as :admin
+ gitlab_sign_in :admin
create(:broadcast_message, :expired, message: 'Migration to new server')
visit admin_broadcast_messages_path
end
diff --git a/spec/features/admin/admin_browse_spam_logs_spec.rb b/spec/features/admin/admin_browse_spam_logs_spec.rb
index bee57472270..d9c4fc686b1 100644
--- a/spec/features/admin/admin_browse_spam_logs_spec.rb
+++ b/spec/features/admin/admin_browse_spam_logs_spec.rb
@@ -4,7 +4,7 @@ describe 'Admin browse spam logs' do
let!(:spam_log) { create(:spam_log, description: 'abcde ' * 20) }
before do
- login_as :admin
+ gitlab_sign_in :admin
end
scenario 'Browse spam logs' do
diff --git a/spec/features/admin/admin_browses_logs_spec.rb b/spec/features/admin/admin_browses_logs_spec.rb
index d880f3f07db..c734a2ef16d 100644
--- a/spec/features/admin/admin_browses_logs_spec.rb
+++ b/spec/features/admin/admin_browses_logs_spec.rb
@@ -2,7 +2,7 @@ require 'spec_helper'
describe 'Admin browses logs' do
before do
- login_as :admin
+ gitlab_sign_in :admin
end
it 'shows available log files' do
diff --git a/spec/features/admin/admin_builds_spec.rb b/spec/features/admin/admin_builds_spec.rb
index 999ce3611b5..e767081f3e5 100644
--- a/spec/features/admin/admin_builds_spec.rb
+++ b/spec/features/admin/admin_builds_spec.rb
@@ -2,7 +2,7 @@ require 'spec_helper'
describe 'Admin Builds' do
before do
- login_as :admin
+ gitlab_sign_in :admin
end
describe 'GET /admin/builds' do
diff --git a/spec/features/admin/admin_cohorts_spec.rb b/spec/features/admin/admin_cohorts_spec.rb
index dd14ffdb2ce..952e5475213 100644
--- a/spec/features/admin/admin_cohorts_spec.rb
+++ b/spec/features/admin/admin_cohorts_spec.rb
@@ -2,7 +2,7 @@ require 'rails_helper'
feature 'Admin cohorts page', feature: true do
before do
- login_as :admin
+ gitlab_sign_in :admin
end
scenario 'See users count per month' do
diff --git a/spec/features/admin/admin_conversational_development_index_spec.rb b/spec/features/admin/admin_conversational_development_index_spec.rb
index 739ab907a29..b484677a6df 100644
--- a/spec/features/admin/admin_conversational_development_index_spec.rb
+++ b/spec/features/admin/admin_conversational_development_index_spec.rb
@@ -2,7 +2,7 @@ require 'spec_helper'
describe 'Admin Conversational Development Index' do
before do
- login_as :admin
+ gitlab_sign_in :admin
end
context 'when usage ping is disabled' do
diff --git a/spec/features/admin/admin_deploy_keys_spec.rb b/spec/features/admin/admin_deploy_keys_spec.rb
index 5f5fa4e932a..81cddd03f80 100644
--- a/spec/features/admin/admin_deploy_keys_spec.rb
+++ b/spec/features/admin/admin_deploy_keys_spec.rb
@@ -5,7 +5,7 @@ RSpec.describe 'admin deploy keys', type: :feature do
let!(:another_deploy_key) { create(:another_deploy_key, public: true) }
before do
- login_as(:admin)
+ gitlab_sign_in(:admin)
end
it 'show all public deploy keys' do
diff --git a/spec/features/admin/admin_disables_git_access_protocol_spec.rb b/spec/features/admin/admin_disables_git_access_protocol_spec.rb
index e8e080ce3e2..679bf63e0fd 100644
--- a/spec/features/admin/admin_disables_git_access_protocol_spec.rb
+++ b/spec/features/admin/admin_disables_git_access_protocol_spec.rb
@@ -8,7 +8,7 @@ feature 'Admin disables Git access protocol', feature: true do
background do
stub_env('IN_MEMORY_APPLICATION_SETTINGS', 'false')
- login_as(admin)
+ gitlab_sign_in(admin)
end
context 'with HTTP disabled' do
diff --git a/spec/features/admin/admin_disables_two_factor_spec.rb b/spec/features/admin/admin_disables_two_factor_spec.rb
index 71be66303d2..5437da29979 100644
--- a/spec/features/admin/admin_disables_two_factor_spec.rb
+++ b/spec/features/admin/admin_disables_two_factor_spec.rb
@@ -2,7 +2,7 @@ require 'rails_helper'
feature 'Admin disables 2FA for a user', feature: true do
scenario 'successfully', js: true do
- login_as(:admin)
+ gitlab_sign_in(:admin)
user = create(:user, :two_factor)
edit_user(user)
@@ -17,7 +17,7 @@ feature 'Admin disables 2FA for a user', feature: true do
end
scenario 'for a user without 2FA enabled' do
- login_as(:admin)
+ gitlab_sign_in(:admin)
user = create(:user)
edit_user(user)
diff --git a/spec/features/admin/admin_groups_spec.rb b/spec/features/admin/admin_groups_spec.rb
index cf9d7bca255..8b0fafc5f07 100644
--- a/spec/features/admin/admin_groups_spec.rb
+++ b/spec/features/admin/admin_groups_spec.rb
@@ -6,7 +6,7 @@ feature 'Admin Groups', feature: true do
let(:internal) { Gitlab::VisibilityLevel::INTERNAL }
let(:user) { create :user }
let!(:group) { create :group }
- let!(:current_user) { login_as :admin }
+ let!(:current_user) { gitlab_sign_in :admin }
before do
stub_application_setting(default_group_visibility: internal)
diff --git a/spec/features/admin/admin_health_check_spec.rb b/spec/features/admin/admin_health_check_spec.rb
index 523afa2318f..75093aa4167 100644
--- a/spec/features/admin/admin_health_check_spec.rb
+++ b/spec/features/admin/admin_health_check_spec.rb
@@ -5,7 +5,7 @@ feature "Admin Health Check", feature: true do
before do
stub_env('IN_MEMORY_APPLICATION_SETTINGS', 'false')
- login_as :admin
+ gitlab_sign_in :admin
end
describe '#show' do
diff --git a/spec/features/admin/admin_hook_logs_spec.rb b/spec/features/admin/admin_hook_logs_spec.rb
index 5b67f4de6ac..ec80c420c79 100644
--- a/spec/features/admin/admin_hook_logs_spec.rb
+++ b/spec/features/admin/admin_hook_logs_spec.rb
@@ -6,7 +6,7 @@ feature 'Admin::HookLogs', feature: true do
let(:hook_log) { create(:web_hook_log, web_hook: system_hook, internal_error_message: 'some error') }
before do
- login_as :admin
+ gitlab_sign_in :admin
end
scenario 'show list of hook logs' do
diff --git a/spec/features/admin/admin_hooks_spec.rb b/spec/features/admin/admin_hooks_spec.rb
index 80f7ec43c06..c07c21bd6a1 100644
--- a/spec/features/admin/admin_hooks_spec.rb
+++ b/spec/features/admin/admin_hooks_spec.rb
@@ -3,7 +3,7 @@ require 'spec_helper'
describe 'Admin::Hooks', feature: true do
before do
@project = create(:project)
- login_as :admin
+ gitlab_sign_in :admin
@system_hook = create(:system_hook)
end
diff --git a/spec/features/admin/admin_labels_spec.rb b/spec/features/admin/admin_labels_spec.rb
index a9251db13e5..bb40918bd22 100644
--- a/spec/features/admin/admin_labels_spec.rb
+++ b/spec/features/admin/admin_labels_spec.rb
@@ -5,7 +5,7 @@ RSpec.describe 'admin issues labels' do
let!(:feature_label) { Label.create(title: 'feature', template: true) }
before do
- login_as :admin
+ gitlab_sign_in :admin
end
describe 'list' do
diff --git a/spec/features/admin/admin_manage_applications_spec.rb b/spec/features/admin/admin_manage_applications_spec.rb
index 0079125889b..ae41267e5fc 100644
--- a/spec/features/admin/admin_manage_applications_spec.rb
+++ b/spec/features/admin/admin_manage_applications_spec.rb
@@ -2,7 +2,7 @@ require 'spec_helper'
RSpec.describe 'admin manage applications', feature: true do
before do
- login_as :admin
+ gitlab_sign_in :admin
end
it do
diff --git a/spec/features/admin/admin_projects_spec.rb b/spec/features/admin/admin_projects_spec.rb
index 9d205104ebe..ad8f1d496f2 100644
--- a/spec/features/admin/admin_projects_spec.rb
+++ b/spec/features/admin/admin_projects_spec.rb
@@ -6,7 +6,7 @@ describe "Admin::Projects", feature: true do
let(:user) { create :user }
let!(:project) { create(:project) }
let!(:current_user) do
- login_as :admin
+ gitlab_sign_in :admin
end
describe "GET /admin/projects" do
diff --git a/spec/features/admin/admin_requests_profiles_spec.rb b/spec/features/admin/admin_requests_profiles_spec.rb
index e8ecb70306b..2bfe401521b 100644
--- a/spec/features/admin/admin_requests_profiles_spec.rb
+++ b/spec/features/admin/admin_requests_profiles_spec.rb
@@ -3,7 +3,7 @@ require 'spec_helper'
describe 'Admin::RequestsProfilesController', feature: true do
before do
FileUtils.mkdir_p(Gitlab::RequestProfiler::PROFILES_DIR)
- login_as(:admin)
+ gitlab_sign_in(:admin)
end
after do
diff --git a/spec/features/admin/admin_runners_spec.rb b/spec/features/admin/admin_runners_spec.rb
index 5dcc7d35d82..5b3323fed13 100644
--- a/spec/features/admin/admin_runners_spec.rb
+++ b/spec/features/admin/admin_runners_spec.rb
@@ -5,7 +5,7 @@ describe "Admin Runners" do
before do
stub_env('IN_MEMORY_APPLICATION_SETTINGS', 'false')
- login_as :admin
+ gitlab_sign_in :admin
end
describe "Runners page" do
@@ -134,7 +134,10 @@ describe "Admin Runners" do
describe 'runners registration token' do
let!(:token) { current_application_settings.runners_registration_token }
- before { visit admin_runners_path }
+
+ before do
+ visit admin_runners_path
+ end
it 'has a registration token' do
expect(page).to have_content("Registration token is #{token}")
diff --git a/spec/features/admin/admin_settings_spec.rb b/spec/features/admin/admin_settings_spec.rb
index 5099441dce2..2d6565e6d3b 100644
--- a/spec/features/admin/admin_settings_spec.rb
+++ b/spec/features/admin/admin_settings_spec.rb
@@ -5,7 +5,7 @@ feature 'Admin updates settings', feature: true do
before do
stub_env('IN_MEMORY_APPLICATION_SETTINGS', 'false')
- login_as :admin
+ gitlab_sign_in :admin
visit admin_application_settings_path
end
@@ -20,10 +20,15 @@ feature 'Admin updates settings', feature: true do
uncheck 'Gravatar enabled'
fill_in 'Home page URL', with: 'https://about.gitlab.com/'
fill_in 'Help page text', with: 'Example text'
+ check 'Hide marketing-related entries from help'
+ fill_in 'Support page URL', with: 'http://example.com/help'
click_button 'Save'
expect(current_application_settings.gravatar_enabled).to be_falsey
expect(current_application_settings.home_page_url).to eq "https://about.gitlab.com/"
+ expect(current_application_settings.help_page_text).to eq "Example text"
+ expect(current_application_settings.help_page_hide_commercial_content).to be_truthy
+ expect(current_application_settings.help_page_support_url).to eq "http://example.com/help"
expect(page).to have_content "Application settings saved successfully"
end
diff --git a/spec/features/admin/admin_system_info_spec.rb b/spec/features/admin/admin_system_info_spec.rb
index 15482347886..4efc7f0eb48 100644
--- a/spec/features/admin/admin_system_info_spec.rb
+++ b/spec/features/admin/admin_system_info_spec.rb
@@ -2,7 +2,7 @@ require 'spec_helper'
describe 'Admin System Info' do
before do
- login_as :admin
+ gitlab_sign_in :admin
end
describe 'GET /admin/system_info' do
diff --git a/spec/features/admin/admin_users_impersonation_tokens_spec.rb b/spec/features/admin/admin_users_impersonation_tokens_spec.rb
index 0fb4baeb71c..231c094c91d 100644
--- a/spec/features/admin/admin_users_impersonation_tokens_spec.rb
+++ b/spec/features/admin/admin_users_impersonation_tokens_spec.rb
@@ -12,7 +12,9 @@ describe 'Admin > Users > Impersonation Tokens', feature: true, js: true do
find(".table.inactive-tokens")
end
- before { login_as(admin) }
+ before do
+ gitlab_sign_in(admin)
+ end
describe "token creation" do
it "allows creation of a token" do
diff --git a/spec/features/admin/admin_users_spec.rb b/spec/features/admin/admin_users_spec.rb
index 301a47169a4..2d5f0987ea2 100644
--- a/spec/features/admin/admin_users_spec.rb
+++ b/spec/features/admin/admin_users_spec.rb
@@ -5,7 +5,7 @@ describe "Admin::Users", feature: true do
create(:omniauth_user, provider: 'twitter', extern_uid: '123456')
end
- let!(:current_user) { login_as :admin }
+ let!(:current_user) { gitlab_sign_in :admin }
describe "GET /admin/users" do
before do
@@ -124,7 +124,10 @@ describe "Admin::Users", feature: true do
describe 'Impersonation' do
let(:another_user) { create(:user) }
- before { visit admin_user_path(another_user) }
+
+ before do
+ visit admin_user_path(another_user)
+ end
context 'before impersonating' do
it 'shows impersonate button for other users' do
@@ -149,7 +152,9 @@ describe "Admin::Users", feature: true do
end
context 'when impersonating' do
- before { click_link 'Impersonate' }
+ before do
+ click_link 'Impersonate'
+ end
it 'logs in as the user when impersonate is clicked' do
expect(page.find(:css, '.header-user .profile-link')['data-user']).to eql(another_user.username)
diff --git a/spec/features/admin/admin_uses_repository_checks_spec.rb b/spec/features/admin/admin_uses_repository_checks_spec.rb
index ab5c42365fe..91d70435db8 100644
--- a/spec/features/admin/admin_uses_repository_checks_spec.rb
+++ b/spec/features/admin/admin_uses_repository_checks_spec.rb
@@ -5,7 +5,7 @@ feature 'Admin uses repository checks', feature: true do
before do
stub_env('IN_MEMORY_APPLICATION_SETTINGS', 'false')
- login_as :admin
+ gitlab_sign_in :admin
end
scenario 'to trigger a single check' do
diff --git a/spec/features/atom/issues_spec.rb b/spec/features/atom/issues_spec.rb
index a61231ea254..c8ef4533b98 100644
--- a/spec/features/atom/issues_spec.rb
+++ b/spec/features/atom/issues_spec.rb
@@ -15,7 +15,7 @@ describe 'Issues Feed', feature: true do
context 'when authenticated' do
it 'renders atom feed' do
- login_with user
+ gitlab_sign_in user
visit namespace_project_issues_path(project.namespace, project, :atom)
expect(response_headers['Content-Type']).
diff --git a/spec/features/auto_deploy_spec.rb b/spec/features/auto_deploy_spec.rb
index 1cf7396bbac..74f5f70702a 100644
--- a/spec/features/auto_deploy_spec.rb
+++ b/spec/features/auto_deploy_spec.rb
@@ -7,7 +7,7 @@ describe 'Auto deploy' do
before do
create :kubernetes_service, project: project
project.team << [user, :master]
- login_as user
+ gitlab_sign_in user
end
context 'when no deployment service is active' do
diff --git a/spec/features/boards/add_issues_modal_spec.rb b/spec/features/boards/add_issues_modal_spec.rb
index 2b8edac4f10..ba58af22841 100644
--- a/spec/features/boards/add_issues_modal_spec.rb
+++ b/spec/features/boards/add_issues_modal_spec.rb
@@ -14,7 +14,7 @@ describe 'Issue Boards add issue modal', :feature, :js do
before do
project.team << [user, :master]
- login_as(user)
+ gitlab_sign_in(user)
visit namespace_project_board_path(project.namespace, project, board)
wait_for_requests
diff --git a/spec/features/boards/boards_spec.rb b/spec/features/boards/boards_spec.rb
index c80453b8227..87fc31d414c 100644
--- a/spec/features/boards/boards_spec.rb
+++ b/spec/features/boards/boards_spec.rb
@@ -12,7 +12,7 @@ describe 'Issue Boards', feature: true, js: true do
project.team << [user, :master]
project.team << [user2, :master]
- login_as(user)
+ gitlab_sign_in(user)
end
context 'no lists' do
@@ -247,13 +247,13 @@ describe 'Issue Boards', feature: true, js: true do
end
it 'issue moves from closed' do
- drag(list_from_index: 3, list_to_index: 2)
-
- expect(find('.board:nth-child(3)')).to have_content(issue8.title)
+ drag(list_from_index: 2, list_to_index: 3)
wait_for_board_cards(2, 8)
- wait_for_board_cards(3, 3)
- wait_for_board_cards(4, 0)
+ wait_for_board_cards(3, 1)
+ wait_for_board_cards(4, 2)
+
+ expect(find('.board:nth-child(4)')).to have_content(issue8.title)
end
context 'issue card' do
@@ -519,7 +519,7 @@ describe 'Issue Boards', feature: true, js: true do
context 'signed out user' do
before do
- logout
+ gitlab_sign_out
visit namespace_project_board_path(project.namespace, project, board)
wait_for_requests
end
@@ -542,8 +542,8 @@ describe 'Issue Boards', feature: true, js: true do
before do
project.team << [user_guest, :guest]
- logout
- login_as(user_guest)
+ gitlab_sign_out
+ gitlab_sign_in(user_guest)
visit namespace_project_board_path(project.namespace, project, board)
wait_for_requests
end
diff --git a/spec/features/boards/issue_ordering_spec.rb b/spec/features/boards/issue_ordering_spec.rb
index 1c289993e28..1e620061e5e 100644
--- a/spec/features/boards/issue_ordering_spec.rb
+++ b/spec/features/boards/issue_ordering_spec.rb
@@ -15,7 +15,7 @@ describe 'Issue Boards', :feature, :js do
before do
project.team << [user, :master]
- login_as(user)
+ gitlab_sign_in(user)
end
context 'un-ordered issues' do
diff --git a/spec/features/boards/keyboard_shortcut_spec.rb b/spec/features/boards/keyboard_shortcut_spec.rb
index c2167ba12cd..ed3b38e6a7e 100644
--- a/spec/features/boards/keyboard_shortcut_spec.rb
+++ b/spec/features/boards/keyboard_shortcut_spec.rb
@@ -6,7 +6,7 @@ describe 'Issue Boards shortcut', feature: true, js: true do
before do
create(:board, project: project)
- login_as :admin
+ gitlab_sign_in :admin
visit namespace_project_path(project.namespace, project)
end
diff --git a/spec/features/boards/modal_filter_spec.rb b/spec/features/boards/modal_filter_spec.rb
index b6de6143354..8899e1ef5e5 100644
--- a/spec/features/boards/modal_filter_spec.rb
+++ b/spec/features/boards/modal_filter_spec.rb
@@ -12,7 +12,7 @@ describe 'Issue Boards add issue modal filtering', :feature, :js do
before do
project.team << [user, :master]
- login_as(user)
+ gitlab_sign_in(user)
end
it 'shows empty state when no results found' do
diff --git a/spec/features/boards/new_issue_spec.rb b/spec/features/boards/new_issue_spec.rb
index 056224dc436..77cd87d6601 100644
--- a/spec/features/boards/new_issue_spec.rb
+++ b/spec/features/boards/new_issue_spec.rb
@@ -10,7 +10,7 @@ describe 'Issue Boards new issue', feature: true, js: true do
before do
project.team << [user, :master]
- login_as(user)
+ gitlab_sign_in(user)
visit namespace_project_board_path(project.namespace, project, board)
wait_for_requests
@@ -19,18 +19,18 @@ describe 'Issue Boards new issue', feature: true, js: true do
end
it 'displays new issue button' do
- expect(first('.board')).to have_selector('.board-issue-count-holder .btn', count: 1)
+ expect(first('.board')).to have_selector('.issue-count-badge-add-button', count: 1)
end
it 'does not display new issue button in closed list' do
page.within('.board:nth-child(3)') do
- expect(page).not_to have_selector('.board-issue-count-holder .btn')
+ expect(page).not_to have_selector('.issue-count-badge-add-button')
end
end
it 'shows form when clicking button' do
page.within(first('.board')) do
- find('.board-issue-count-holder .btn').click
+ find('.issue-count-badge-add-button').click
expect(page).to have_selector('.board-new-issue-form')
end
@@ -38,7 +38,7 @@ describe 'Issue Boards new issue', feature: true, js: true do
it 'hides form when clicking cancel' do
page.within(first('.board')) do
- find('.board-issue-count-holder .btn').click
+ find('.issue-count-badge-add-button').click
expect(page).to have_selector('.board-new-issue-form')
@@ -50,7 +50,7 @@ describe 'Issue Boards new issue', feature: true, js: true do
it 'creates new issue' do
page.within(first('.board')) do
- find('.board-issue-count-holder .btn').click
+ find('.issue-count-badge-add-button').click
end
page.within(first('.board-new-issue-form')) do
@@ -60,14 +60,14 @@ describe 'Issue Boards new issue', feature: true, js: true do
wait_for_requests
- page.within(first('.board .board-issue-count')) do
+ page.within(first('.board .issue-count-badge-count')) do
expect(page).to have_content('1')
end
end
it 'shows sidebar when creating new issue' do
page.within(first('.board')) do
- find('.board-issue-count-holder .btn').click
+ find('.issue-count-badge-add-button').click
end
page.within(first('.board-new-issue-form')) do
@@ -88,7 +88,7 @@ describe 'Issue Boards new issue', feature: true, js: true do
end
it 'does not display new issue button' do
- expect(page).to have_selector('.board-issue-count-holder .btn', count: 0)
+ expect(page).to have_selector('.issue-count-badge-add-button', count: 0)
end
end
end
diff --git a/spec/features/boards/sidebar_spec.rb b/spec/features/boards/sidebar_spec.rb
index 235e4899707..301c243febd 100644
--- a/spec/features/boards/sidebar_spec.rb
+++ b/spec/features/boards/sidebar_spec.rb
@@ -20,7 +20,7 @@ describe 'Issue Boards', feature: true, js: true do
project.team << [user, :master]
- login_as(user)
+ gitlab_sign_in(user)
visit namespace_project_board_path(project.namespace, project, board)
wait_for_requests
diff --git a/spec/features/boards/sub_group_project_spec.rb b/spec/features/boards/sub_group_project_spec.rb
index 4cd05010a93..d57ae6a71e7 100644
--- a/spec/features/boards/sub_group_project_spec.rb
+++ b/spec/features/boards/sub_group_project_spec.rb
@@ -13,7 +13,7 @@ describe 'Sub-group project issue boards', :feature, :js do
before do
project.add_master(user)
- login_as(user)
+ gitlab_sign_in(user)
visit namespace_project_board_path(project.namespace, project, board)
wait_for_requests
diff --git a/spec/features/calendar_spec.rb b/spec/features/calendar_spec.rb
index 1b6d8439f92..b2e72fc7dee 100644
--- a/spec/features/calendar_spec.rb
+++ b/spec/features/calendar_spec.rb
@@ -68,7 +68,7 @@ feature 'Contributions Calendar', :feature, :js do
end
before do
- login_as user
+ gitlab_sign_in user
end
describe 'calendar day selection' do
diff --git a/spec/features/ci_lint_spec.rb b/spec/features/ci_lint_spec.rb
index 3ebc432206a..de16ee3e567 100644
--- a/spec/features/ci_lint_spec.rb
+++ b/spec/features/ci_lint_spec.rb
@@ -2,7 +2,7 @@ require 'spec_helper'
describe 'CI Lint', js: true do
before do
- login_as :user
+ gitlab_sign_in :user
end
describe 'YAML parsing' do
diff --git a/spec/features/commits_spec.rb b/spec/features/commits_spec.rb
index 2772f05982a..ab2d85371bf 100644
--- a/spec/features/commits_spec.rb
+++ b/spec/features/commits_spec.rb
@@ -7,7 +7,7 @@ describe 'Commits' do
describe 'CI' do
before do
- login_as :user
+ gitlab_sign_in :user
stub_ci_pipeline_to_return_yaml_file
end
@@ -191,7 +191,7 @@ describe 'Commits' do
before do
project.team << [user, :master]
- login_with(user)
+ gitlab_sign_in(user)
visit namespace_project_commits_path(project.namespace, project, branch_name)
end
diff --git a/spec/features/container_registry_spec.rb b/spec/features/container_registry_spec.rb
index fa7adbe71ea..80d16539d5a 100644
--- a/spec/features/container_registry_spec.rb
+++ b/spec/features/container_registry_spec.rb
@@ -9,7 +9,7 @@ describe "Container Registry" do
end
before do
- login_as(user)
+ gitlab_sign_in(user)
project.add_developer(user)
stub_container_registry_config(enabled: true)
stub_container_registry_tags(repository: :any, tags: [])
diff --git a/spec/features/copy_as_gfm_spec.rb b/spec/features/copy_as_gfm_spec.rb
index 740f60c05cc..005c88f6bab 100644
--- a/spec/features/copy_as_gfm_spec.rb
+++ b/spec/features/copy_as_gfm_spec.rb
@@ -6,7 +6,7 @@ describe 'Copy as GFM', feature: true, js: true do
include ActionView::Helpers::JavaScriptHelper
before do
- login_as :admin
+ gitlab_sign_in :admin
end
describe 'Copying rendered GFM' do
diff --git a/spec/features/cycle_analytics_spec.rb b/spec/features/cycle_analytics_spec.rb
index b416bbd3c79..5a7ea975455 100644
--- a/spec/features/cycle_analytics_spec.rb
+++ b/spec/features/cycle_analytics_spec.rb
@@ -14,7 +14,7 @@ feature 'Cycle Analytics', feature: true, js: true do
before do
project.add_master(user)
- login_as(user)
+ gitlab_sign_in(user)
visit namespace_project_cycle_analytics_path(project.namespace, project)
wait_for_requests
@@ -38,7 +38,7 @@ feature 'Cycle Analytics', feature: true, js: true do
create_cycle
deploy_master
- login_as(user)
+ gitlab_sign_in(user)
visit namespace_project_cycle_analytics_path(project.namespace, project)
end
@@ -70,7 +70,7 @@ feature 'Cycle Analytics', feature: true, js: true do
user.update_attribute(:preferred_language, 'es')
project.team << [user, :master]
- login_as(user)
+ gitlab_sign_in(user)
visit namespace_project_cycle_analytics_path(project.namespace, project)
wait_for_requests
end
@@ -93,7 +93,7 @@ feature 'Cycle Analytics', feature: true, js: true do
create_cycle
deploy_master
- login_as(guest)
+ gitlab_sign_in(guest)
visit namespace_project_cycle_analytics_path(project.namespace, project)
wait_for_requests
end
diff --git a/spec/features/dashboard/active_tab_spec.rb b/spec/features/dashboard/active_tab_spec.rb
index ae750be4d4a..f7ddded10c1 100644
--- a/spec/features/dashboard/active_tab_spec.rb
+++ b/spec/features/dashboard/active_tab_spec.rb
@@ -2,7 +2,7 @@ require 'spec_helper'
RSpec.describe 'Dashboard Active Tab', js: true, feature: true do
before do
- login_as :user
+ gitlab_sign_in :user
end
shared_examples 'page has active tab' do |title|
diff --git a/spec/features/dashboard/activity_spec.rb b/spec/features/dashboard/activity_spec.rb
index 0764044260e..1e9cabe7850 100644
--- a/spec/features/dashboard/activity_spec.rb
+++ b/spec/features/dashboard/activity_spec.rb
@@ -2,7 +2,7 @@ require 'spec_helper'
RSpec.describe 'Dashboard Activity', feature: true do
before do
- login_as(create :user)
+ gitlab_sign_in(create :user)
visit activity_dashboard_path
end
diff --git a/spec/features/dashboard/archived_projects_spec.rb b/spec/features/dashboard/archived_projects_spec.rb
index f33bcbb5318..a5ba3e7e3cf 100644
--- a/spec/features/dashboard/archived_projects_spec.rb
+++ b/spec/features/dashboard/archived_projects_spec.rb
@@ -9,7 +9,7 @@ RSpec.describe 'Dashboard Archived Project', feature: true do
project.team << [user, :master]
archived_project.team << [user, :master]
- login_as(user)
+ gitlab_sign_in(user)
visit dashboard_projects_path
end
diff --git a/spec/features/dashboard/datetime_on_tooltips_spec.rb b/spec/features/dashboard/datetime_on_tooltips_spec.rb
index 1793e323588..6931d0a840e 100644
--- a/spec/features/dashboard/datetime_on_tooltips_spec.rb
+++ b/spec/features/dashboard/datetime_on_tooltips_spec.rb
@@ -4,7 +4,7 @@ feature 'Tooltips on .timeago dates', feature: true, js: true do
let(:user) { create(:user) }
let(:project) { create(:project, name: 'test', namespace: user.namespace) }
let(:created_date) { Date.yesterday.to_time }
- let(:expected_format) { created_date.strftime('%b %-d, %Y %l:%M%P') }
+ let(:expected_format) { created_date.in_time_zone.strftime('%b %-d, %Y %l:%M%P') }
context 'on the activity tab' do
before do
@@ -13,7 +13,7 @@ feature 'Tooltips on .timeago dates', feature: true, js: true do
Event.create( project: project, author_id: user.id, action: Event::JOINED,
updated_at: created_date, created_at: created_date)
- login_as user
+ gitlab_sign_in user
visit user_path(user)
wait_for_requests()
@@ -30,7 +30,7 @@ feature 'Tooltips on .timeago dates', feature: true, js: true do
project.team << [user, :master]
create(:snippet, author: user, updated_at: created_date, created_at: created_date)
- login_as user
+ gitlab_sign_in user
visit user_snippets_path(user)
wait_for_requests()
diff --git a/spec/features/dashboard/group_spec.rb b/spec/features/dashboard/group_spec.rb
index 8e20fdec8ad..2f7245950ec 100644
--- a/spec/features/dashboard/group_spec.rb
+++ b/spec/features/dashboard/group_spec.rb
@@ -2,7 +2,7 @@ require 'spec_helper'
RSpec.describe 'Dashboard Group', feature: true do
before do
- login_as(:user)
+ gitlab_sign_in(:user)
end
it 'creates new group', js: true do
diff --git a/spec/features/dashboard/groups_list_spec.rb b/spec/features/dashboard/groups_list_spec.rb
index 7eb254f8451..e520027bc38 100644
--- a/spec/features/dashboard/groups_list_spec.rb
+++ b/spec/features/dashboard/groups_list_spec.rb
@@ -10,7 +10,7 @@ describe 'Dashboard Groups page', js: true, feature: true do
group.add_owner(user)
nested_group.add_owner(user)
- login_as(user)
+ gitlab_sign_in(user)
visit dashboard_groups_path
expect(page).to have_content(group.full_name)
@@ -23,7 +23,7 @@ describe 'Dashboard Groups page', js: true, feature: true do
group.add_owner(user)
nested_group.add_owner(user)
- login_as(user)
+ gitlab_sign_in(user)
visit dashboard_groups_path
end
@@ -58,7 +58,7 @@ describe 'Dashboard Groups page', js: true, feature: true do
group.add_owner(user)
subgroup.add_owner(user)
- login_as(user)
+ gitlab_sign_in(user)
visit dashboard_groups_path
end
@@ -98,7 +98,7 @@ describe 'Dashboard Groups page', js: true, feature: true do
allow(Kaminari.config).to receive(:default_per_page).and_return(1)
- login_as(user)
+ gitlab_sign_in(user)
visit dashboard_groups_path
end
diff --git a/spec/features/dashboard/help_spec.rb b/spec/features/dashboard/help_spec.rb
index 2803f7ec62b..25b0f40c9cd 100644
--- a/spec/features/dashboard/help_spec.rb
+++ b/spec/features/dashboard/help_spec.rb
@@ -2,7 +2,7 @@ require 'spec_helper'
RSpec.describe 'Dashboard Help', feature: true do
before do
- login_as(:user)
+ gitlab_sign_in(:user)
end
it 'renders correctly markdown' do
diff --git a/spec/features/dashboard/issuables_counter_spec.rb b/spec/features/dashboard/issuables_counter_spec.rb
index 354267dbee7..8a8a20fd5b1 100644
--- a/spec/features/dashboard/issuables_counter_spec.rb
+++ b/spec/features/dashboard/issuables_counter_spec.rb
@@ -9,7 +9,7 @@ describe 'Navigation bar counter', feature: true, caching: true do
before do
issue.assignees = [user]
merge_request.update(assignee: user)
- login_as(user)
+ gitlab_sign_in(user)
end
it 'reflects dashboard issues count' do
diff --git a/spec/features/dashboard/issues_spec.rb b/spec/features/dashboard/issues_spec.rb
index 2cea6b1563e..6f21cfd322d 100644
--- a/spec/features/dashboard/issues_spec.rb
+++ b/spec/features/dashboard/issues_spec.rb
@@ -12,7 +12,7 @@ RSpec.describe 'Dashboard Issues', feature: true do
before do
[project, project_with_issues_disabled].each { |project| project.team << [current_user, :master] }
- login_as(current_user)
+ gitlab_sign_in(current_user)
visit issues_dashboard_path(assignee_id: current_user.id)
end
diff --git a/spec/features/dashboard/label_filter_spec.rb b/spec/features/dashboard/label_filter_spec.rb
index 4cff12de854..88bbb9e75b9 100644
--- a/spec/features/dashboard/label_filter_spec.rb
+++ b/spec/features/dashboard/label_filter_spec.rb
@@ -11,7 +11,7 @@ describe 'Dashboard > label filter', feature: true, js: true do
project.labels << label
project2.labels << label2
- login_as(user)
+ gitlab_sign_in(user)
visit issues_dashboard_path
end
diff --git a/spec/features/dashboard/merge_requests_spec.rb b/spec/features/dashboard/merge_requests_spec.rb
index 9cebe52c444..69d5500848e 100644
--- a/spec/features/dashboard/merge_requests_spec.rb
+++ b/spec/features/dashboard/merge_requests_spec.rb
@@ -8,11 +8,13 @@ describe 'Dashboard Merge Requests' do
before do
[project, project_with_merge_requests_disabled].each { |project| project.team << [current_user, :master] }
- login_as(current_user)
+ gitlab_sign_in(current_user)
end
describe 'new merge request dropdown' do
- before { visit merge_requests_dashboard_path }
+ before do
+ visit merge_requests_dashboard_path
+ end
it 'shows projects only with merge requests feature enabled', js: true do
find('.new-project-item-select-button').trigger('click')
diff --git a/spec/features/dashboard/milestone_filter_spec.rb b/spec/features/dashboard/milestone_filter_spec.rb
index b5b92c36895..295262980a6 100644
--- a/spec/features/dashboard/milestone_filter_spec.rb
+++ b/spec/features/dashboard/milestone_filter_spec.rb
@@ -9,7 +9,7 @@ describe 'Dashboard > milestone filter', :feature, :js do
let!(:issue2) { create :issue, author: user, project: project, milestone: milestone2 }
before do
- login_as(user)
+ gitlab_sign_in(user)
visit issues_dashboard_path(author_id: user.id)
end
diff --git a/spec/features/dashboard/milestone_tabs_spec.rb b/spec/features/dashboard/milestone_tabs_spec.rb
index 0c7b992c500..cc4193b180f 100644
--- a/spec/features/dashboard/milestone_tabs_spec.rb
+++ b/spec/features/dashboard/milestone_tabs_spec.rb
@@ -15,7 +15,7 @@ describe 'Dashboard milestone tabs', :js, :feature do
before do
project.add_master(user)
- login_as(user)
+ gitlab_sign_in(user)
visit dashboard_milestone_path(milestone.safe_title, title: milestone.title)
end
@@ -23,7 +23,7 @@ describe 'Dashboard milestone tabs', :js, :feature do
it 'loads merge requests async' do
click_link 'Merge Requests'
- expect(page).to have_selector('.merge_requests-sortable-list')
+ expect(page).to have_selector('.milestone-merge_requests-list')
end
it 'loads participants async' do
diff --git a/spec/features/dashboard/project_member_activity_index_spec.rb b/spec/features/dashboard/project_member_activity_index_spec.rb
index cdf919af9b5..0ba87d921d0 100644
--- a/spec/features/dashboard/project_member_activity_index_spec.rb
+++ b/spec/features/dashboard/project_member_activity_index_spec.rb
@@ -17,19 +17,25 @@ feature 'Project member activity', feature: true, js: true do
subject { page.find(".event-title").text }
context 'when a user joins the project' do
- before { visit_activities_and_wait_with_event(Event::JOINED) }
+ before do
+ visit_activities_and_wait_with_event(Event::JOINED)
+ end
it { is_expected.to eq("#{user.name} joined project") }
end
context 'when a user leaves the project' do
- before { visit_activities_and_wait_with_event(Event::LEFT) }
+ before do
+ visit_activities_and_wait_with_event(Event::LEFT)
+ end
it { is_expected.to eq("#{user.name} left project") }
end
context 'when a users membership expires for the project' do
- before { visit_activities_and_wait_with_event(Event::EXPIRED) }
+ before do
+ visit_activities_and_wait_with_event(Event::EXPIRED)
+ end
it "presents the correct message" do
message = "#{user.name} removed due to membership expiration from project"
diff --git a/spec/features/dashboard/projects_spec.rb b/spec/features/dashboard/projects_spec.rb
index 3568954a548..2a8185ca669 100644
--- a/spec/features/dashboard/projects_spec.rb
+++ b/spec/features/dashboard/projects_spec.rb
@@ -7,7 +7,7 @@ RSpec.describe 'Dashboard Projects', feature: true do
before do
project.team << [user, :developer]
- login_as(user)
+ gitlab_sign_in(user)
end
it 'shows the project the user in a member of in the list' do
diff --git a/spec/features/dashboard/shortcuts_spec.rb b/spec/features/dashboard/shortcuts_spec.rb
index 349b948eaee..525b0e1b210 100644
--- a/spec/features/dashboard/shortcuts_spec.rb
+++ b/spec/features/dashboard/shortcuts_spec.rb
@@ -3,7 +3,7 @@ require 'spec_helper'
feature 'Dashboard shortcuts', :feature, :js do
context 'logged in' do
before do
- login_as :user
+ gitlab_sign_in :user
visit root_dashboard_path
end
diff --git a/spec/features/dashboard/snippets_spec.rb b/spec/features/dashboard/snippets_spec.rb
index c6ba118220a..0c069ae5cf0 100644
--- a/spec/features/dashboard/snippets_spec.rb
+++ b/spec/features/dashboard/snippets_spec.rb
@@ -6,7 +6,7 @@ describe 'Dashboard snippets', feature: true do
let!(:snippets) { create_list(:project_snippet, 2, :public, author: project.owner, project: project) }
before do
allow(Snippet).to receive(:default_per_page).and_return(1)
- login_as(project.owner)
+ gitlab_sign_in(project.owner)
visit dashboard_snippets_path
end
@@ -25,7 +25,7 @@ describe 'Dashboard snippets', feature: true do
end
before do
- login_as(user)
+ gitlab_sign_in(user)
visit dashboard_snippets_path
end
diff --git a/spec/features/dashboard/user_filters_projects_spec.rb b/spec/features/dashboard/user_filters_projects_spec.rb
index 34d6257f5fd..e9f34760143 100644
--- a/spec/features/dashboard/user_filters_projects_spec.rb
+++ b/spec/features/dashboard/user_filters_projects_spec.rb
@@ -9,7 +9,7 @@ describe 'Dashboard > User filters projects', :feature do
before do
project.team << [user, :master]
- login_as(user)
+ gitlab_sign_in(user)
end
describe 'filtering personal projects' do
diff --git a/spec/features/dashboard_issues_spec.rb b/spec/features/dashboard_issues_spec.rb
index 1c53f6dff06..c4dbaad2895 100644
--- a/spec/features/dashboard_issues_spec.rb
+++ b/spec/features/dashboard_issues_spec.rb
@@ -8,7 +8,7 @@ describe "Dashboard Issues filtering", feature: true, js: true do
context 'filtering by milestone' do
before do
project.team << [user, :master]
- login_as(user)
+ gitlab_sign_in(user)
create(:issue, project: project, author: user, assignees: [user])
create(:issue, project: project, author: user, assignees: [user], milestone: milestone)
diff --git a/spec/features/dashboard_milestones_spec.rb b/spec/features/dashboard_milestones_spec.rb
index f32fddbc9fa..b308a2297b9 100644
--- a/spec/features/dashboard_milestones_spec.rb
+++ b/spec/features/dashboard_milestones_spec.rb
@@ -17,7 +17,7 @@ feature 'Dashboard > Milestones', feature: true do
let!(:milestone) { create(:milestone, project: project) }
before do
project.team << [user, :master]
- login_with(user)
+ gitlab_sign_in(user)
visit dashboard_milestones_path
end
diff --git a/spec/features/discussion_comments/commit_spec.rb b/spec/features/discussion_comments/commit_spec.rb
index 96e0b78f6b9..96128061e4d 100644
--- a/spec/features/discussion_comments/commit_spec.rb
+++ b/spec/features/discussion_comments/commit_spec.rb
@@ -9,7 +9,7 @@ describe 'Discussion Comments Merge Request', :feature, :js do
before do
project.add_master(user)
- login_as(user)
+ gitlab_sign_in(user)
visit namespace_project_commit_path(project.namespace, project, sample_commit.id)
end
diff --git a/spec/features/discussion_comments/issue_spec.rb b/spec/features/discussion_comments/issue_spec.rb
index ccc9efccd18..d7c1cd12fb5 100644
--- a/spec/features/discussion_comments/issue_spec.rb
+++ b/spec/features/discussion_comments/issue_spec.rb
@@ -7,7 +7,7 @@ describe 'Discussion Comments Issue', :feature, :js do
before do
project.add_master(user)
- login_as(user)
+ gitlab_sign_in(user)
visit namespace_project_issue_path(project.namespace, project, issue)
end
diff --git a/spec/features/discussion_comments/merge_request_spec.rb b/spec/features/discussion_comments/merge_request_spec.rb
index f99ebeb9cd9..31fb9c72d25 100644
--- a/spec/features/discussion_comments/merge_request_spec.rb
+++ b/spec/features/discussion_comments/merge_request_spec.rb
@@ -7,7 +7,7 @@ describe 'Discussion Comments Merge Request', :feature, :js do
before do
project.add_master(user)
- login_as(user)
+ gitlab_sign_in(user)
visit namespace_project_merge_request_path(project.namespace, project, merge_request)
end
diff --git a/spec/features/discussion_comments/snippets_spec.rb b/spec/features/discussion_comments/snippets_spec.rb
index 19a306511b2..998d633c83d 100644
--- a/spec/features/discussion_comments/snippets_spec.rb
+++ b/spec/features/discussion_comments/snippets_spec.rb
@@ -7,7 +7,7 @@ describe 'Discussion Comments Issue', :feature, :js do
before do
project.add_master(user)
- login_as(user)
+ gitlab_sign_in(user)
visit namespace_project_snippet_path(project.namespace, project, snippet)
end
diff --git a/spec/features/expand_collapse_diffs_spec.rb b/spec/features/expand_collapse_diffs_spec.rb
index c4d5077e5e1..ea749528c11 100644
--- a/spec/features/expand_collapse_diffs_spec.rb
+++ b/spec/features/expand_collapse_diffs_spec.rb
@@ -10,7 +10,7 @@ feature 'Expand and collapse diffs', js: true, feature: true do
allow(Gitlab::Git::Diff).to receive(:size_limit).and_return(100.kilobytes)
allow(Gitlab::Git::Diff).to receive(:collapse_limit).and_return(10.kilobytes)
- login_as :admin
+ gitlab_sign_in :admin
# Ensure that undiffable.md is in .gitattributes
project.repository.copy_gitattributes(branch)
@@ -140,7 +140,9 @@ feature 'Expand and collapse diffs', js: true, feature: true do
end
context 'reloading the page' do
- before { refresh }
+ before do
+ refresh
+ end
it 'collapses the large diff by default' do
expect(large_diff).not_to have_selector('.code')
@@ -262,7 +264,7 @@ feature 'Expand and collapse diffs', js: true, feature: true do
# Wait for elements to appear to ensure full page reload
expect(page).to have_content('This diff was suppressed by a .gitattributes entry')
- expect(page).to have_content('This diff could not be displayed because it is too large.')
+ expect(page).to have_content('This source diff could not be displayed because it is too large.')
expect(page).to have_content('too_large_image.jpg')
find('.note-textarea')
diff --git a/spec/features/explore/groups_list_spec.rb b/spec/features/explore/groups_list_spec.rb
index d4284ed099b..6be5dee0c3c 100644
--- a/spec/features/explore/groups_list_spec.rb
+++ b/spec/features/explore/groups_list_spec.rb
@@ -10,7 +10,7 @@ describe 'Explore Groups page', :js, :feature do
before do
group.add_owner(user)
- login_as(user)
+ gitlab_sign_in(user)
visit explore_groups_path
end
diff --git a/spec/features/explore/new_menu_spec.rb b/spec/features/explore/new_menu_spec.rb
index 15a6354211b..2d7e703688f 100644
--- a/spec/features/explore/new_menu_spec.rb
+++ b/spec/features/explore/new_menu_spec.rb
@@ -16,7 +16,7 @@ feature 'Top Plus Menu', feature: true, js: true do
context 'used by full user' do
before do
- login_as(user)
+ gitlab_sign_in(user)
end
scenario 'click on New project shows new project page' do
@@ -103,7 +103,7 @@ feature 'Top Plus Menu', feature: true, js: true do
context 'used by guest user' do
before do
- login_as(guest_user)
+ gitlab_sign_in(guest_user)
end
scenario 'click on New issue shows new issue page' do
diff --git a/spec/features/gitlab_flavored_markdown_spec.rb b/spec/features/gitlab_flavored_markdown_spec.rb
index 55092412340..7758f00da7d 100644
--- a/spec/features/gitlab_flavored_markdown_spec.rb
+++ b/spec/features/gitlab_flavored_markdown_spec.rb
@@ -10,7 +10,7 @@ describe "GitLab Flavored Markdown", feature: true do
end
before do
- login_as(:user)
+ gitlab_sign_in(:user)
project.add_developer(@user)
end
diff --git a/spec/features/global_search_spec.rb b/spec/features/global_search_spec.rb
index 4b22b07494d..54ebfe6cf77 100644
--- a/spec/features/global_search_spec.rb
+++ b/spec/features/global_search_spec.rb
@@ -6,7 +6,7 @@ feature 'Global search', feature: true do
before do
project.team << [user, :master]
- login_with(user)
+ gitlab_sign_in(user)
end
describe 'I search through the issues and I see pagination' do
diff --git a/spec/features/groups/activity_spec.rb b/spec/features/groups/activity_spec.rb
index 81f9c103e95..9f66a3d8c72 100644
--- a/spec/features/groups/activity_spec.rb
+++ b/spec/features/groups/activity_spec.rb
@@ -7,7 +7,7 @@ feature 'Group activity page', feature: true do
context 'when signed in' do
before do
user = create(:group_member, :developer, user: create(:user), group: group ).user
- login_as(user)
+ gitlab_sign_in(user)
visit path
end
diff --git a/spec/features/groups/empty_states_spec.rb b/spec/features/groups/empty_states_spec.rb
index fef8e41bffe..b1c7151dfa8 100644
--- a/spec/features/groups/empty_states_spec.rb
+++ b/spec/features/groups/empty_states_spec.rb
@@ -5,7 +5,7 @@ feature 'Groups Merge Requests Empty States' do
let(:user) { create(:group_member, :developer, user: create(:user), group: group ).user }
before do
- login_as(user)
+ gitlab_sign_in(user)
end
context 'group has a project' do
diff --git a/spec/features/groups/group_name_toggle_spec.rb b/spec/features/groups/group_name_toggle_spec.rb
index dfc3c84f29a..f450626c370 100644
--- a/spec/features/groups/group_name_toggle_spec.rb
+++ b/spec/features/groups/group_name_toggle_spec.rb
@@ -9,7 +9,7 @@ feature 'Group name toggle', feature: true, js: true do
SMALL_SCREEN = 300
before do
- login_as :user
+ gitlab_sign_in :user
end
it 'is not present if enough horizontal space' do
diff --git a/spec/features/groups/group_settings_spec.rb b/spec/features/groups/group_settings_spec.rb
index cc25db4ad60..5ad777248ec 100644
--- a/spec/features/groups/group_settings_spec.rb
+++ b/spec/features/groups/group_settings_spec.rb
@@ -6,7 +6,7 @@ feature 'Edit group settings', feature: true do
background do
group.add_owner(user)
- login_as(user)
+ gitlab_sign_in(user)
end
describe 'when the group path is changed' do
@@ -52,9 +52,14 @@ feature 'Edit group settings', feature: true do
given!(:project) { create(:project, group: group, path: 'project') }
given(:old_project_full_path) { "/#{group.path}/#{project.path}" }
given(:new_project_full_path) { "/#{new_group_path}/#{project.path}" }
-
- before(:context) { TestEnv.clean_test_path }
- after(:example) { TestEnv.clean_test_path }
+
+ before(:context) do
+ TestEnv.clean_test_path
+ end
+
+ after(:example) do
+ TestEnv.clean_test_path
+ end
scenario 'the project is accessible via the new path' do
update_path(new_group_path)
diff --git a/spec/features/groups/labels/edit_spec.rb b/spec/features/groups/labels/edit_spec.rb
index 69281cecb7b..b33040ef843 100644
--- a/spec/features/groups/labels/edit_spec.rb
+++ b/spec/features/groups/labels/edit_spec.rb
@@ -7,7 +7,7 @@ feature 'Edit group label', feature: true do
background do
group.add_owner(user)
- login_as(user)
+ gitlab_sign_in(user)
visit edit_group_label_path(group, label)
end
diff --git a/spec/features/groups/members/last_owner_cannot_leave_group_spec.rb b/spec/features/groups/members/last_owner_cannot_leave_group_spec.rb
index be60b0489c7..5af94e4069b 100644
--- a/spec/features/groups/members/last_owner_cannot_leave_group_spec.rb
+++ b/spec/features/groups/members/last_owner_cannot_leave_group_spec.rb
@@ -6,7 +6,7 @@ feature 'Groups > Members > Last owner cannot leave group', feature: true do
background do
group.add_owner(owner)
- login_as(owner)
+ gitlab_sign_in(owner)
visit group_path(group)
end
diff --git a/spec/features/groups/members/list_spec.rb b/spec/features/groups/members/list_spec.rb
index f654fa16a06..5d00ed30c83 100644
--- a/spec/features/groups/members/list_spec.rb
+++ b/spec/features/groups/members/list_spec.rb
@@ -9,7 +9,7 @@ feature 'Groups members list', feature: true do
let(:nested_group) { create(:group, parent: group) }
background do
- login_as(user1)
+ gitlab_sign_in(user1)
end
scenario 'show members from current group and parent', :nested_groups do
diff --git a/spec/features/groups/members/member_cannot_request_access_to_his_project_spec.rb b/spec/features/groups/members/member_cannot_request_access_to_his_project_spec.rb
index 37c433cc09a..135bb3572bc 100644
--- a/spec/features/groups/members/member_cannot_request_access_to_his_project_spec.rb
+++ b/spec/features/groups/members/member_cannot_request_access_to_his_project_spec.rb
@@ -6,7 +6,7 @@ feature 'Groups > Members > Member cannot request access to his project', featur
background do
group.add_developer(member)
- login_as(member)
+ gitlab_sign_in(member)
visit group_path(group)
end
diff --git a/spec/features/groups/members/member_leaves_group_spec.rb b/spec/features/groups/members/member_leaves_group_spec.rb
index ac4d94658ae..40f3b166e74 100644
--- a/spec/features/groups/members/member_leaves_group_spec.rb
+++ b/spec/features/groups/members/member_leaves_group_spec.rb
@@ -8,7 +8,7 @@ feature 'Groups > Members > Member leaves group', feature: true do
background do
group.add_owner(owner)
group.add_developer(user)
- login_as(user)
+ gitlab_sign_in(user)
visit group_path(group)
end
diff --git a/spec/features/groups/members/owner_manages_access_requests_spec.rb b/spec/features/groups/members/owner_manages_access_requests_spec.rb
index dbe150823ba..4e4cf12e8af 100644
--- a/spec/features/groups/members/owner_manages_access_requests_spec.rb
+++ b/spec/features/groups/members/owner_manages_access_requests_spec.rb
@@ -8,7 +8,7 @@ feature 'Groups > Members > Owner manages access requests', feature: true do
background do
group.request_access(user)
group.add_owner(owner)
- login_as(owner)
+ gitlab_sign_in(owner)
end
scenario 'owner can see access requests' do
diff --git a/spec/features/groups/members/sorting_spec.rb b/spec/features/groups/members/sorting_spec.rb
index 902d3f789ff..719fa0b40b8 100644
--- a/spec/features/groups/members/sorting_spec.rb
+++ b/spec/features/groups/members/sorting_spec.rb
@@ -9,7 +9,7 @@ feature 'Groups > Members > Sorting', feature: true do
create(:group_member, :owner, user: owner, group: group, created_at: 5.days.ago)
create(:group_member, :developer, user: developer, group: group, created_at: 3.days.ago)
- login_as(owner)
+ gitlab_sign_in(owner)
end
scenario 'sorts alphabetically by default' do
diff --git a/spec/features/groups/members/user_requests_access_spec.rb b/spec/features/groups/members/user_requests_access_spec.rb
index e4b5ea91bd3..3813308c237 100644
--- a/spec/features/groups/members/user_requests_access_spec.rb
+++ b/spec/features/groups/members/user_requests_access_spec.rb
@@ -8,7 +8,7 @@ feature 'Groups > Members > User requests access', feature: true do
background do
group.add_owner(owner)
- login_as(user)
+ gitlab_sign_in(user)
visit group_path(group)
end
diff --git a/spec/features/groups/milestone_spec.rb b/spec/features/groups/milestone_spec.rb
index daa2c6afd63..330310eae6b 100644
--- a/spec/features/groups/milestone_spec.rb
+++ b/spec/features/groups/milestone_spec.rb
@@ -8,7 +8,7 @@ feature 'Group milestones', :feature, :js do
before do
Timecop.freeze
- login_as(user)
+ gitlab_sign_in(user)
end
after do
diff --git a/spec/features/groups/show_spec.rb b/spec/features/groups/show_spec.rb
index d3c49c37374..76575f61528 100644
--- a/spec/features/groups/show_spec.rb
+++ b/spec/features/groups/show_spec.rb
@@ -7,7 +7,7 @@ feature 'Group show page', feature: true do
context 'when signed in' do
before do
user = create(:group_member, :developer, user: create(:user), group: group ).user
- login_as(user)
+ gitlab_sign_in(user)
visit path
end
diff --git a/spec/features/groups_spec.rb b/spec/features/groups_spec.rb
index 24ea7aba0cc..ecacca00a61 100644
--- a/spec/features/groups_spec.rb
+++ b/spec/features/groups_spec.rb
@@ -2,7 +2,7 @@ require 'spec_helper'
feature 'Group', feature: true do
before do
- login_as(:admin)
+ gitlab_sign_in(:admin)
end
matcher :have_namespace_error_message do
@@ -12,7 +12,9 @@ feature 'Group', feature: true do
end
describe 'create a group' do
- before { visit new_group_path }
+ before do
+ visit new_group_path
+ end
describe 'with space in group path' do
it 'renders new group form with validation errors' do
@@ -106,8 +108,8 @@ feature 'Group', feature: true do
before do
group.add_owner(user)
- logout
- login_as(user)
+ gitlab_sign_out
+ gitlab_sign_in(user)
visit subgroups_group_path(group)
click_link 'New Subgroup'
@@ -126,8 +128,8 @@ feature 'Group', feature: true do
it 'checks permissions to avoid exposing groups by parent_id' do
group = create(:group, :private, path: 'secret-group')
- logout
- login_as(:user)
+ gitlab_sign_out
+ gitlab_sign_in(:user)
visit new_group_path(parent_id: group.id)
expect(page).not_to have_content('secret-group')
@@ -138,7 +140,9 @@ feature 'Group', feature: true do
let(:path) { edit_group_path(group) }
let(:new_name) { 'new-name' }
- before { visit path }
+ before do
+ visit path
+ end
it 'saves new settings' do
fill_in 'group_name', with: new_name
diff --git a/spec/features/help_pages_spec.rb b/spec/features/help_pages_spec.rb
index 31014f5cad2..b01ee1cf491 100644
--- a/spec/features/help_pages_spec.rb
+++ b/spec/features/help_pages_spec.rb
@@ -37,10 +37,10 @@ describe 'Help Pages', feature: true do
context 'in a production environment with version check enabled', :js do
before do
allow(Rails.env).to receive(:production?) { true }
- allow(current_application_settings).to receive(:version_check_enabled) { true }
+ allow_any_instance_of(ApplicationSetting).to receive(:version_check_enabled) { true }
allow_any_instance_of(VersionCheck).to receive(:url) { '/version-check-url' }
- login_as :user
+ gitlab_sign_in :user
visit help_path
end
@@ -53,4 +53,27 @@ describe 'Help Pages', feature: true do
expect(find('.js-version-status-badge', visible: false)).not_to be_visible
end
end
+
+ 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" }
+
+ gitlab_sign_in(:user)
+ visit help_path
+ end
+
+ it 'should display custom help page text' do
+ expect(page).to have_text "My Custom Text"
+ end
+
+ it 'should hide marketing content when enabled' do
+ expect(page).not_to have_link "Get a support subscription"
+ end
+
+ it 'should use a custom support url' do
+ expect(page).to have_link "See our website for getting help", href: "http://example.com/help"
+ end
+ end
end
diff --git a/spec/features/issuables/default_sort_order_spec.rb b/spec/features/issuables/default_sort_order_spec.rb
index bfe43bff10f..56c9b10e757 100644
--- a/spec/features/issuables/default_sort_order_spec.rb
+++ b/spec/features/issuables/default_sort_order_spec.rb
@@ -153,7 +153,9 @@ describe 'Projects > Issuables > Default sort order', feature: true do
context 'when the sort in the URL is id_desc' do
let(:issuable_type) { :issue }
- before { visit_issues(project, sort: 'id_desc') }
+ before do
+ visit_issues(project, sort: 'id_desc')
+ end
it 'shows the sort order as last created' do
expect(find('.issues-other-filters')).to have_content('Last created')
@@ -165,7 +167,9 @@ describe 'Projects > Issuables > Default sort order', feature: true do
context 'when the sort in the URL is id_asc' do
let(:issuable_type) { :issue }
- before { visit_issues(project, sort: 'id_asc') }
+ before do
+ visit_issues(project, sort: 'id_asc')
+ end
it 'shows the sort order as oldest created' do
expect(find('.issues-other-filters')).to have_content('Oldest created')
diff --git a/spec/features/issuables/issuable_list_spec.rb b/spec/features/issuables/issuable_list_spec.rb
index 414838fa22e..f3a5a8463d1 100644
--- a/spec/features/issuables/issuable_list_spec.rb
+++ b/spec/features/issuables/issuable_list_spec.rb
@@ -8,7 +8,7 @@ describe 'issuable list', feature: true do
before do
project.add_user(user, :developer)
- login_as(user)
+ gitlab_sign_in(user)
issuable_types.each { |type| create_issuables(type) }
end
diff --git a/spec/features/issues/award_emoji_spec.rb b/spec/features/issues/award_emoji_spec.rb
index 81ae54c7a10..6698e2c79a1 100644
--- a/spec/features/issues/award_emoji_spec.rb
+++ b/spec/features/issues/award_emoji_spec.rb
@@ -12,7 +12,7 @@ describe 'Awards Emoji', feature: true do
context 'authorized user' do
before do
project.team << [user, :master]
- login_as(user)
+ gitlab_sign_in(user)
end
describe 'visiting an issue with a legacy award emoji that is not valid anymore' do
@@ -81,13 +81,13 @@ describe 'Awards Emoji', feature: true do
end
end
- context 'execute /award slash command' do
+ context 'execute /award quick action' do
it 'toggles the emoji award on noteable', js: true do
- execute_slash_command('/award :100:')
+ execute_quick_action('/award :100:')
expect(find(noteable_award_counter)).to have_text("1")
- execute_slash_command('/award :100:')
+ execute_quick_action('/award :100:')
expect(page).not_to have_selector(noteable_award_counter)
end
@@ -105,7 +105,7 @@ describe 'Awards Emoji', feature: true do
end
end
- def execute_slash_command(cmd)
+ def execute_quick_action(cmd)
within('.js-main-target-form') do
fill_in 'note[note]', with: cmd
click_button 'Comment'
diff --git a/spec/features/issues/award_spec.rb b/spec/features/issues/award_spec.rb
index fcf22dd5033..a1c97caea20 100644
--- a/spec/features/issues/award_spec.rb
+++ b/spec/features/issues/award_spec.rb
@@ -7,7 +7,7 @@ feature 'Issue awards', js: true, feature: true do
describe 'logged in' do
before do
- login_as(user)
+ gitlab_sign_in(user)
visit namespace_project_issue_path(project.namespace, project, issue)
wait_for_requests
end
diff --git a/spec/features/issues/bulk_assignment_labels_spec.rb b/spec/features/issues/bulk_assignment_labels_spec.rb
index 95b4930cd32..2eb04df3cb3 100644
--- a/spec/features/issues/bulk_assignment_labels_spec.rb
+++ b/spec/features/issues/bulk_assignment_labels_spec.rb
@@ -13,7 +13,7 @@ feature 'Issues > Labels bulk assignment', feature: true do
before do
project.team << [user, :master]
- login_as user
+ gitlab_sign_in user
end
context 'can bulk assign' do
@@ -331,7 +331,7 @@ feature 'Issues > Labels bulk assignment', feature: true do
context 'as a guest' do
before do
- login_as user
+ gitlab_sign_in user
visit namespace_project_issues_path(project.namespace, project)
end
diff --git a/spec/features/issues/create_branch_merge_request_spec.rb b/spec/features/issues/create_branch_merge_request_spec.rb
index 1d7d8d291b2..aa538803dd8 100644
--- a/spec/features/issues/create_branch_merge_request_spec.rb
+++ b/spec/features/issues/create_branch_merge_request_spec.rb
@@ -8,7 +8,7 @@ feature 'Create Branch/Merge Request Dropdown on issue page', feature: true, js:
context 'for team members' do
before do
project.team << [user, :developer]
- login_as(user)
+ gitlab_sign_in(user)
end
it 'allows creating a merge request from the issue page' do
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 24e2419b5ce..5f631043e15 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
@@ -9,7 +9,7 @@ feature 'Resolving all open discussions in a merge request from an issue', featu
describe 'as a user with access to the project' do
before do
project.team << [user, :master]
- login_as user
+ gitlab_sign_in user
visit namespace_project_merge_request_path(project.namespace, project, merge_request)
end
@@ -82,7 +82,7 @@ feature 'Resolving all open discussions in a merge request from an issue', featu
describe 'as a reporter' do
before do
project.team << [user, :reporter]
- login_as user
+ gitlab_sign_in user
visit new_namespace_project_issue_path(project.namespace, 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 3a5a79e03f4..9e9e214060f 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
@@ -9,7 +9,7 @@ feature 'Resolve an open discussion in a merge request by creating an issue', fe
describe 'As a user with access to the project' do
before do
project.team << [user, :master]
- login_as user
+ gitlab_sign_in user
visit namespace_project_merge_request_path(project.namespace, project, merge_request)
end
@@ -66,7 +66,7 @@ feature 'Resolve an open discussion in a merge request by creating an issue', fe
describe 'as a reporter' do
before do
project.team << [user, :reporter]
- login_as user
+ gitlab_sign_in user
visit new_namespace_project_issue_path(project.namespace, 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 44353d880c2..96f6739af2d 100644
--- a/spec/features/issues/filtered_search/dropdown_assignee_spec.rb
+++ b/spec/features/issues/filtered_search/dropdown_assignee_spec.rb
@@ -23,7 +23,7 @@ describe 'Dropdown assignee', :feature, :js do
project.team << [user, :master]
project.team << [user_john, :master]
project.team << [user_jacob, :master]
- login_as(user)
+ gitlab_sign_in(user)
create(:issue, project: project)
visit namespace_project_issues_path(project.namespace, project)
diff --git a/spec/features/issues/filtered_search/dropdown_author_spec.rb b/spec/features/issues/filtered_search/dropdown_author_spec.rb
index 6b707c4be4a..5ee824c662a 100644
--- a/spec/features/issues/filtered_search/dropdown_author_spec.rb
+++ b/spec/features/issues/filtered_search/dropdown_author_spec.rb
@@ -31,7 +31,7 @@ describe 'Dropdown author', js: true, feature: true do
project.team << [user, :master]
project.team << [user_john, :master]
project.team << [user_jacob, :master]
- login_as(user)
+ gitlab_sign_in(user)
create(:issue, project: project)
visit namespace_project_issues_path(project.namespace, project)
diff --git a/spec/features/issues/filtered_search/dropdown_hint_spec.rb b/spec/features/issues/filtered_search/dropdown_hint_spec.rb
index b9a37cfcc22..a05e4394ffd 100644
--- a/spec/features/issues/filtered_search/dropdown_hint_spec.rb
+++ b/spec/features/issues/filtered_search/dropdown_hint_spec.rb
@@ -14,7 +14,7 @@ describe 'Dropdown hint', :js, :feature do
before do
project.team << [user, :master]
- login_as(user)
+ gitlab_sign_in(user)
create(:issue, project: project)
visit namespace_project_issues_path(project.namespace, project)
diff --git a/spec/features/issues/filtered_search/dropdown_label_spec.rb b/spec/features/issues/filtered_search/dropdown_label_spec.rb
index abe5d61e38c..aec9d7ceb5d 100644
--- a/spec/features/issues/filtered_search/dropdown_label_spec.rb
+++ b/spec/features/issues/filtered_search/dropdown_label_spec.rb
@@ -34,7 +34,7 @@ describe 'Dropdown label', js: true, feature: true do
before do
project.add_master(user)
- login_as(user)
+ gitlab_sign_in(user)
create(:issue, project: project)
visit namespace_project_issues_path(project.namespace, project)
diff --git a/spec/features/issues/filtered_search/dropdown_milestone_spec.rb b/spec/features/issues/filtered_search/dropdown_milestone_spec.rb
index 448259057b0..b21f41946b7 100644
--- a/spec/features/issues/filtered_search/dropdown_milestone_spec.rb
+++ b/spec/features/issues/filtered_search/dropdown_milestone_spec.rb
@@ -30,7 +30,7 @@ describe 'Dropdown milestone', :feature, :js do
before do
project.team << [user, :master]
- login_as(user)
+ gitlab_sign_in(user)
create(:issue, project: project)
visit namespace_project_issues_path(project.namespace, project)
diff --git a/spec/features/issues/filtered_search/search_bar_spec.rb b/spec/features/issues/filtered_search/search_bar_spec.rb
index 3ea95aed0a6..806c732b935 100644
--- a/spec/features/issues/filtered_search/search_bar_spec.rb
+++ b/spec/features/issues/filtered_search/search_bar_spec.rb
@@ -9,7 +9,7 @@ describe 'Search bar', js: true, feature: true do
before do
project.team << [user, :master]
- login_as(user)
+ gitlab_sign_in(user)
create(:issue, project: project)
visit namespace_project_issues_path(project.namespace, project)
diff --git a/spec/features/issues/filtered_search/visual_tokens_spec.rb b/spec/features/issues/filtered_search/visual_tokens_spec.rb
index ff32b0c7d11..22488f34813 100644
--- a/spec/features/issues/filtered_search/visual_tokens_spec.rb
+++ b/spec/features/issues/filtered_search/visual_tokens_spec.rb
@@ -25,7 +25,7 @@ describe 'Visual tokens', js: true, feature: true do
before do
project.add_user(user, :master)
project.add_user(user_rock, :master)
- login_as(user)
+ gitlab_sign_in(user)
create(:issue, project: project)
visit namespace_project_issues_path(project.namespace, project)
diff --git a/spec/features/issues/form_spec.rb b/spec/features/issues/form_spec.rb
index 96d37e33f3d..b369ef1ff79 100644
--- a/spec/features/issues/form_spec.rb
+++ b/spec/features/issues/form_spec.rb
@@ -16,7 +16,7 @@ describe 'New/edit issue', :feature, :js do
before do
project.team << [user, :master]
project.team << [user2, :master]
- login_as(user)
+ gitlab_sign_in(user)
end
context 'new issue' do
@@ -210,6 +210,13 @@ describe 'New/edit issue', :feature, :js do
expect(find('.js-assignee-search')).to have_content(user2.name)
end
+
+ it 'description has autocomplete' do
+ find('#issue_description').native.send_keys('')
+ fill_in 'issue_description', with: '@'
+
+ expect(page).to have_selector('.atwho-view')
+ end
end
context 'edit issue' do
@@ -258,6 +265,13 @@ describe 'New/edit issue', :feature, :js do
end
end
end
+
+ it 'description has autocomplete' do
+ find('#issue_description').native.send_keys('')
+ fill_in 'issue_description', with: '@'
+
+ expect(page).to have_selector('.atwho-view')
+ end
end
describe 'sub-group project' do
diff --git a/spec/features/issues/gfm_autocomplete_spec.rb b/spec/features/issues/gfm_autocomplete_spec.rb
index 350473437a8..e61eb5233d0 100644
--- a/spec/features/issues/gfm_autocomplete_spec.rb
+++ b/spec/features/issues/gfm_autocomplete_spec.rb
@@ -8,7 +8,7 @@ feature 'GFM autocomplete', feature: true, js: true do
before do
project.team << [user, :master]
- login_as(user)
+ gitlab_sign_in(user)
visit namespace_project_issue_path(project.namespace, project, issue)
wait_for_requests
@@ -208,7 +208,7 @@ feature 'GFM autocomplete', feature: true, js: true do
expect(page).not_to have_selector('.atwho-view')
end
- it 'triggers autocomplete after selecting a slash command' do
+ it 'triggers autocomplete after selecting a quick action' do
note = find('#note_note')
page.within '.timeline-content-form' do
note.native.send_keys('')
diff --git a/spec/features/issues/issue_sidebar_spec.rb b/spec/features/issues/issue_sidebar_spec.rb
index 96c24750250..163bc4bb32f 100644
--- a/spec/features/issues/issue_sidebar_spec.rb
+++ b/spec/features/issues/issue_sidebar_spec.rb
@@ -10,7 +10,7 @@ feature 'Issue Sidebar', feature: true do
let!(:label) { create(:label, project: project, title: 'bug') }
before do
- login_as(user)
+ gitlab_sign_in(user)
end
context 'assignee', js: true do
diff --git a/spec/features/issues/markdown_toolbar_spec.rb b/spec/features/issues/markdown_toolbar_spec.rb
index c8c9c50396b..66d823ec9d0 100644
--- a/spec/features/issues/markdown_toolbar_spec.rb
+++ b/spec/features/issues/markdown_toolbar_spec.rb
@@ -6,7 +6,7 @@ feature 'Issue markdown toolbar', feature: true, js: true do
let(:user) { create(:user) }
before do
- login_as(user)
+ gitlab_sign_in(user)
visit namespace_project_issue_path(project.namespace, project, issue)
end
diff --git a/spec/features/issues/move_spec.rb b/spec/features/issues/move_spec.rb
index e75bf059218..21a7637fe7f 100644
--- a/spec/features/issues/move_spec.rb
+++ b/spec/features/issues/move_spec.rb
@@ -9,7 +9,7 @@ feature 'issue move to another project' do
create(:issue, description: text, project: old_project, author: user)
end
- background { login_as(user) }
+ background { gitlab_sign_in(user) }
context 'user does not have permission to move issue' do
background do
diff --git a/spec/features/issues/note_polling_spec.rb b/spec/features/issues/note_polling_spec.rb
index 2c0a6ffd3cb..bd31e44ef33 100644
--- a/spec/features/issues/note_polling_spec.rb
+++ b/spec/features/issues/note_polling_spec.rb
@@ -27,7 +27,7 @@ feature 'Issue notes polling', :feature, :js do
let!(:existing_note) { create(:note, noteable: issue, project: project, author: user, note: note_text) }
before do
- login_as(user)
+ gitlab_sign_in(user)
visit namespace_project_issue_path(project.namespace, project, issue)
end
@@ -93,7 +93,7 @@ feature 'Issue notes polling', :feature, :js do
let!(:existing_note) { create(:note, noteable: issue, project: project, author: user1, note: note_text) }
before do
- login_as(user2)
+ gitlab_sign_in(user2)
visit namespace_project_issue_path(project.namespace, project, issue)
end
@@ -114,7 +114,7 @@ feature 'Issue notes polling', :feature, :js do
let!(:system_note) { create(:system_note, noteable: issue, project: project, author: user, note: note_text) }
before do
- login_as(user)
+ gitlab_sign_in(user)
visit namespace_project_issue_path(project.namespace, project, issue)
end
diff --git a/spec/features/issues/notes_on_issues_spec.rb b/spec/features/issues/notes_on_issues_spec.rb
index 15c817cabac..f648295416f 100644
--- a/spec/features/issues/notes_on_issues_spec.rb
+++ b/spec/features/issues/notes_on_issues_spec.rb
@@ -9,7 +9,7 @@ describe 'Create notes on issues', :js, :feature do
before do
project.team << [user, :developer]
- login_as(user)
+ gitlab_sign_in(user)
visit namespace_project_issue_path(project.namespace, project, issue)
fill_in 'note[note]', with: note_text
diff --git a/spec/features/issues/spam_issues_spec.rb b/spec/features/issues/spam_issues_spec.rb
index 6001476d0ca..57c783790b5 100644
--- a/spec/features/issues/spam_issues_spec.rb
+++ b/spec/features/issues/spam_issues_spec.rb
@@ -18,7 +18,7 @@ describe 'New issue', feature: true, js: true do
)
project.team << [user, :master]
- login_as(user)
+ gitlab_sign_in(user)
end
context 'when identified as a spam' do
diff --git a/spec/features/issues/todo_spec.rb b/spec/features/issues/todo_spec.rb
index 3fde85b0a5c..a1c00dd64f6 100644
--- a/spec/features/issues/todo_spec.rb
+++ b/spec/features/issues/todo_spec.rb
@@ -7,7 +7,7 @@ feature 'Manually create a todo item from issue', feature: true, js: true do
before do
project.team << [user, :master]
- login_as(user)
+ gitlab_sign_in(user)
visit namespace_project_issue_path(project.namespace, project, issue)
end
diff --git a/spec/features/issues/update_issues_spec.rb b/spec/features/issues/update_issues_spec.rb
index 8595847d313..dc981406e4e 100644
--- a/spec/features/issues/update_issues_spec.rb
+++ b/spec/features/issues/update_issues_spec.rb
@@ -7,7 +7,7 @@ feature 'Multiple issue updating from issues#index', feature: true do
before do
project.team << [user, :master]
- login_as(user)
+ gitlab_sign_in(user)
end
context 'status', js: true do
diff --git a/spec/features/issues/user_uses_slash_commands_spec.rb b/spec/features/issues/user_uses_slash_commands_spec.rb
index d14c319707c..168cdd08137 100644
--- a/spec/features/issues/user_uses_slash_commands_spec.rb
+++ b/spec/features/issues/user_uses_slash_commands_spec.rb
@@ -1,9 +1,9 @@
require 'rails_helper'
-feature 'Issues > User uses slash commands', feature: true, js: true do
- include SlashCommandsHelpers
+feature 'Issues > User uses quick actions', feature: true, js: true do
+ include QuickActionsHelpers
- it_behaves_like 'issuable record that supports slash commands in its description and notes', :issue do
+ it_behaves_like 'issuable record that supports quick actions in its description and notes', :issue do
let(:issuable) { create(:issue, project: project) }
end
@@ -13,7 +13,7 @@ feature 'Issues > User uses slash commands', feature: true, js: true do
before do
project.team << [user, :master]
- login_with(user)
+ gitlab_sign_in(user)
visit namespace_project_issue_path(project.namespace, project, issue)
end
@@ -41,8 +41,8 @@ feature 'Issues > User uses slash commands', feature: true, js: true do
let(:guest) { create(:user) }
before do
project.team << [guest, :guest]
- logout
- login_with(guest)
+ gitlab_sign_out
+ gitlab_sign_in(guest)
visit namespace_project_issue_path(project.namespace, project, issue)
end
@@ -81,8 +81,8 @@ feature 'Issues > User uses slash commands', feature: true, js: true do
let(:guest) { create(:user) }
before do
project.team << [guest, :guest]
- logout
- login_with(guest)
+ gitlab_sign_out
+ gitlab_sign_in(guest)
visit namespace_project_issue_path(project.namespace, project, issue)
end
diff --git a/spec/features/issues_spec.rb b/spec/features/issues_spec.rb
index eecc565d2bd..ea637018617 100644
--- a/spec/features/issues_spec.rb
+++ b/spec/features/issues_spec.rb
@@ -8,7 +8,7 @@ describe 'Issues', feature: true do
let(:project) { create(:empty_project, :public) }
before do
- login_as :user
+ gitlab_sign_in :user
user2 = create(:user)
project.team << [[@user, user2], :developer]
@@ -246,7 +246,10 @@ describe 'Issues', feature: true do
context 'with a filter on labels' do
let(:label) { create(:label, project: project) }
- before { create(:label_link, label: label, target: foo) }
+
+ 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)
@@ -480,8 +483,8 @@ describe 'Issues', feature: true do
end
it 'shows assignee text', js: true do
- logout
- login_with guest
+ gitlab_sign_out
+ gitlab_sign_in guest
visit namespace_project_issue_path(project.namespace, project, issue)
expect(page).to have_content issue.assignees.first.name
@@ -543,8 +546,8 @@ describe 'Issues', feature: true do
end
it 'shows milestone text', js: true do
- logout
- login_with guest
+ gitlab_sign_out
+ gitlab_sign_in guest
visit namespace_project_issue_path(project.namespace, project, issue)
expect(page).to have_content milestone.title
@@ -557,7 +560,7 @@ describe 'Issues', feature: true do
context 'by unauthenticated user' do
before do
- logout
+ gitlab_sign_out
end
it 'redirects to signin then back to new issue after signin' do
@@ -567,7 +570,7 @@ describe 'Issues', feature: true do
expect(current_path).to eq new_user_session_path
- login_as :user
+ gitlab_sign_in :user
expect(current_path).to eq new_namespace_project_issue_path(project.namespace, project)
end
diff --git a/spec/features/login_spec.rb b/spec/features/login_spec.rb
index c82e8c03343..b43e6a06a07 100644
--- a/spec/features/login_spec.rb
+++ b/spec/features/login_spec.rb
@@ -36,7 +36,7 @@ feature 'Login', feature: true do
it 'prevents the user from logging in' do
user = create(:user, :blocked)
- login_with(user)
+ gitlab_sign_in(user)
expect(page).to have_content('Your account has been blocked.')
end
@@ -44,19 +44,19 @@ feature 'Login', feature: true do
it 'does not update Devise trackable attributes', :redis do
user = create(:user, :blocked)
- expect { login_with(user) }.not_to change { user.reload.sign_in_count }
+ expect { gitlab_sign_in(user) }.not_to change { user.reload.sign_in_count }
end
end
describe 'with the ghost user' do
it 'disallows login' do
- login_with(User.ghost)
+ gitlab_sign_in(User.ghost)
expect(page).to have_content('Invalid Login or password.')
end
it 'does not update Devise trackable attributes', :redis do
- expect { login_with(User.ghost) }.not_to change { User.ghost.reload.sign_in_count }
+ expect { gitlab_sign_in(User.ghost) }.not_to change { User.ghost.reload.sign_in_count }
end
end
@@ -70,7 +70,7 @@ feature 'Login', feature: true do
let(:user) { create(:user, :two_factor) }
before do
- login_with(user, remember: true)
+ gitlab_sign_in(user, remember: true)
expect(page).to have_content('Two-Factor Authentication')
end
@@ -167,7 +167,7 @@ feature 'Login', feature: true do
it 'shows 2FA prompt after OAuth login' do
stub_omniauth_config(enabled: true, auto_link_saml_user: true, allow_single_sign_on: ['saml'], providers: [saml_config])
user = create(:omniauth_user, :two_factor, extern_uid: 'my-uid', provider: 'saml')
- login_via('saml', user, 'my-uid')
+ gitlab_sign_in_via('saml', user, 'my-uid')
expect(page).to have_content('Two-Factor Authentication')
enter_code(user.current_otp)
@@ -180,19 +180,19 @@ feature 'Login', feature: true do
let(:user) { create(:user) }
it 'allows basic login' do
- login_with(user)
+ gitlab_sign_in(user)
expect(current_path).to eq root_path
end
it 'does not show a "You are already signed in." error message' do
- login_with(user)
+ gitlab_sign_in(user)
expect(page).not_to have_content('You are already signed in.')
end
it 'blocks invalid login' do
user = create(:user, password: 'not-the-default')
- login_with(user)
+ gitlab_sign_in(user)
expect(page).to have_content('Invalid Login or password.')
end
end
@@ -202,12 +202,14 @@ feature 'Login', feature: true do
# TODO: otp_grace_period_started_at
context 'global setting' do
- before(:each) { stub_application_setting(require_two_factor_authentication: true) }
+ before do
+ stub_application_setting(require_two_factor_authentication: true)
+ end
context 'with grace period defined' do
- before(:each) do
+ before do
stub_application_setting(two_factor_grace_period: 48)
- login_with(user)
+ gitlab_sign_in(user)
end
context 'within the grace period' do
@@ -242,9 +244,9 @@ feature 'Login', feature: true do
end
context 'without grace period defined' do
- before(:each) do
+ before do
stub_application_setting(two_factor_grace_period: 0)
- login_with(user)
+ gitlab_sign_in(user)
end
it 'redirects to two-factor configuration page' do
@@ -265,9 +267,9 @@ feature 'Login', feature: true do
end
context 'with grace period defined' do
- before(:each) do
+ before do
stub_application_setting(two_factor_grace_period: 48)
- login_with(user)
+ gitlab_sign_in(user)
end
context 'within the grace period' do
@@ -306,9 +308,9 @@ feature 'Login', feature: true do
end
context 'without grace period defined' do
- before(:each) do
+ before do
stub_application_setting(two_factor_grace_period: 0)
- login_with(user)
+ gitlab_sign_in(user)
end
it 'redirects to two-factor configuration page' do
diff --git a/spec/features/merge_requests/assign_issues_spec.rb b/spec/features/merge_requests/assign_issues_spec.rb
index b306e2f5f75..cb835f533e0 100644
--- a/spec/features/merge_requests/assign_issues_spec.rb
+++ b/spec/features/merge_requests/assign_issues_spec.rb
@@ -13,7 +13,7 @@ feature 'Merge request issue assignment', js: true, feature: true do
end
def visit_merge_request(current_user = nil)
- login_as(current_user || user)
+ gitlab_sign_in(current_user || user)
visit namespace_project_merge_request_path(project.namespace, project, merge_request)
end
diff --git a/spec/features/merge_requests/award_spec.rb b/spec/features/merge_requests/award_spec.rb
index ac260e118d0..e9dd755b6af 100644
--- a/spec/features/merge_requests/award_spec.rb
+++ b/spec/features/merge_requests/award_spec.rb
@@ -7,7 +7,7 @@ feature 'Merge request awards', js: true, feature: true do
describe 'logged in' do
before do
- login_as(user)
+ gitlab_sign_in(user)
visit namespace_project_merge_request_path(project.namespace, project, merge_request)
end
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 fa306c02a43..060cfb8fdd1 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
@@ -6,7 +6,7 @@ feature 'Check if mergeable with unresolved discussions', js: true, feature: tru
let!(:merge_request) { create(:merge_request_with_diff_notes, source_project: project, author: user) }
before do
- login_as user
+ gitlab_sign_in user
project.team << [user, :master]
end
diff --git a/spec/features/merge_requests/cherry_pick_spec.rb b/spec/features/merge_requests/cherry_pick_spec.rb
index 6ba681e36f7..6ba96570e3d 100644
--- a/spec/features/merge_requests/cherry_pick_spec.rb
+++ b/spec/features/merge_requests/cherry_pick_spec.rb
@@ -7,7 +7,7 @@ describe 'Cherry-pick Merge Requests', js: true do
let(:merge_request) { create(:merge_request_with_diffs, source_project: project, author: user) }
before do
- login_as user
+ gitlab_sign_in user
project.team << [user, :master]
end
diff --git a/spec/features/merge_requests/closes_issues_spec.rb b/spec/features/merge_requests/closes_issues_spec.rb
index e627618042a..371aa2bdaa7 100644
--- a/spec/features/merge_requests/closes_issues_spec.rb
+++ b/spec/features/merge_requests/closes_issues_spec.rb
@@ -20,7 +20,7 @@ feature 'Merge Request closing issues message', feature: true, js: true do
before do
project.team << [user, :master]
- login_as user
+ gitlab_sign_in user
visit namespace_project_merge_request_path(project.namespace, project, merge_request)
wait_for_requests
@@ -36,7 +36,7 @@ feature 'Merge Request closing issues message', feature: true, js: true do
let(:merge_request_description) { "Description\n\nclosing #{issue_1.to_reference}, #{issue_2.to_reference}" }
it 'does not display closing issue message' do
- expect(page).to have_content("Closes issues #{issue_1.to_reference} and #{issue_2.to_reference}")
+ expect(page).to have_content("Closed issues #{issue_1.to_reference} and #{issue_2.to_reference}")
end
end
@@ -44,7 +44,7 @@ feature 'Merge Request closing issues message', feature: true, js: true do
let(:merge_request_description) { "Description\n\nRefers to #{issue_1.to_reference} and #{issue_2.to_reference}" }
it 'does not display closing issue message' do
- expect(page).to have_content("Issues #{issue_1.to_reference} and #{issue_2.to_reference} are mentioned but will not be closed.")
+ expect(page).to have_content("Issues #{issue_1.to_reference} and #{issue_2.to_reference} are mentioned but were not closed")
end
end
@@ -52,8 +52,8 @@ feature 'Merge Request closing issues message', feature: true, js: true do
let(:merge_request_title) { "closes #{issue_1.to_reference}\n\n refers to #{issue_2.to_reference}" }
it 'does not display closing issue message' do
- expect(page).to have_content("Closes issue #{issue_1.to_reference}.")
- expect(page).to have_content("Issue #{issue_2.to_reference} is mentioned but will not be closed.")
+ expect(page).to have_content("Closed issue #{issue_1.to_reference}")
+ expect(page).to have_content("Issue #{issue_2.to_reference} is mentioned but was not closed")
end
end
@@ -61,7 +61,7 @@ feature 'Merge Request closing issues message', feature: true, js: true do
let(:merge_request_title) { "closing #{issue_1.to_reference}, #{issue_2.to_reference}" }
it 'does not display closing issue message' do
- expect(page).to have_content("Closes issues #{issue_1.to_reference} and #{issue_2.to_reference}")
+ expect(page).to have_content("Closed issues #{issue_1.to_reference} and #{issue_2.to_reference}")
end
end
@@ -69,7 +69,7 @@ feature 'Merge Request closing issues message', feature: true, js: true do
let(:merge_request_title) { "Refers to #{issue_1.to_reference} and #{issue_2.to_reference}" }
it 'does not display closing issue message' do
- expect(page).to have_content("Issues #{issue_1.to_reference} and #{issue_2.to_reference} are mentioned but will not be closed.")
+ expect(page).to have_content("Issues #{issue_1.to_reference} and #{issue_2.to_reference} are mentioned but were not closed")
end
end
@@ -77,8 +77,8 @@ feature 'Merge Request closing issues message', feature: true, js: true do
let(:merge_request_title) { "closes #{issue_1.to_reference}\n\n refers to #{issue_2.to_reference}" }
it 'does not display closing issue message' do
- expect(page).to have_content("Closes issue #{issue_1.to_reference}. Issue #{issue_2.to_reference} is mentioned but will not be closed.")
- expect(page).to have_content("Issue #{issue_2.to_reference} is mentioned but will not be closed.")
+ expect(page).to have_content("Closed issue #{issue_1.to_reference}")
+ expect(page).to have_content("Issue #{issue_2.to_reference} is mentioned but was not closed")
end
end
end
diff --git a/spec/features/merge_requests/conflicts_spec.rb b/spec/features/merge_requests/conflicts_spec.rb
index 27e2d5d16f3..9c091befa27 100644
--- a/spec/features/merge_requests/conflicts_spec.rb
+++ b/spec/features/merge_requests/conflicts_spec.rb
@@ -79,20 +79,24 @@ feature 'Merge request conflict resolution', js: true, feature: true do
context 'can be resolved in the UI' do
before do
project.team << [user, :developer]
- login_as(user)
+ gitlab_sign_in(user)
end
context 'the conflicts are resolvable' do
let(:merge_request) { create_merge_request('conflict-resolvable') }
- before { visit namespace_project_merge_request_path(project.namespace, project, merge_request) }
+ before do
+ visit namespace_project_merge_request_path(project.namespace, project, merge_request)
+ end
it 'shows a link to the conflict resolution page' do
expect(page).to have_link('conflicts', href: /\/conflicts\Z/)
end
context 'in Inline view mode' do
- before { click_link('conflicts', href: /\/conflicts\Z/) }
+ before do
+ click_link('conflicts', href: /\/conflicts\Z/)
+ end
include_examples "conflicts are resolved in Interactive mode"
include_examples "conflicts are resolved in Edit inline mode"
@@ -160,7 +164,7 @@ feature 'Merge request conflict resolution', js: true, feature: true do
before do
project.team << [user, :developer]
- login_as(user)
+ gitlab_sign_in(user)
visit namespace_project_merge_request_path(project.namespace, project, merge_request)
end
diff --git a/spec/features/merge_requests/create_new_mr_spec.rb b/spec/features/merge_requests/create_new_mr_spec.rb
index 82987c768d1..8f7adbccaaa 100644
--- a/spec/features/merge_requests/create_new_mr_spec.rb
+++ b/spec/features/merge_requests/create_new_mr_spec.rb
@@ -7,7 +7,7 @@ feature 'Create New Merge Request', feature: true, js: true do
before do
project.team << [user, :master]
- login_as user
+ gitlab_sign_in user
end
it 'selects the source branch sha when a tag with the same name exists' do
diff --git a/spec/features/merge_requests/created_from_fork_spec.rb b/spec/features/merge_requests/created_from_fork_spec.rb
index bf34c99b92a..69059dfa562 100644
--- a/spec/features/merge_requests/created_from_fork_spec.rb
+++ b/spec/features/merge_requests/created_from_fork_spec.rb
@@ -16,7 +16,7 @@ feature 'Merge request created from fork' do
background do
fork_project.team << [user, :master]
- login_as user
+ gitlab_sign_in user
end
scenario 'user can access merge request' do
@@ -56,7 +56,7 @@ feature 'Merge request created from fork' do
visit_merge_request(merge_request)
page.within('.merge-request-tabs') { click_link 'Pipelines' }
- page.within('table.ci-table') do
+ page.within('.ci-table') do
expect(page).to have_content pipeline.status
expect(page).to have_content pipeline.id
end
diff --git a/spec/features/merge_requests/deleted_source_branch_spec.rb b/spec/features/merge_requests/deleted_source_branch_spec.rb
index 1723fb7d365..f2af3198319 100644
--- a/spec/features/merge_requests/deleted_source_branch_spec.rb
+++ b/spec/features/merge_requests/deleted_source_branch_spec.rb
@@ -8,7 +8,7 @@ describe 'Deleted source branch', feature: true, js: true do
let(:merge_request) { create(:merge_request) }
before do
- login_as user
+ gitlab_sign_in user
merge_request.project.team << [user, :master]
merge_request.update!(source_branch: 'this-branch-does-not-exist')
visit namespace_project_merge_request_path(
diff --git a/spec/features/merge_requests/diff_notes_avatars_spec.rb b/spec/features/merge_requests/diff_notes_avatars_spec.rb
index e23dc2cd940..989dfb71d10 100644
--- a/spec/features/merge_requests/diff_notes_avatars_spec.rb
+++ b/spec/features/merge_requests/diff_notes_avatars_spec.rb
@@ -20,7 +20,7 @@ feature 'Diff note avatars', feature: true, js: true do
before do
project.team << [user, :master]
- login_as user
+ gitlab_sign_in user
end
context 'discussion tab' do
diff --git a/spec/features/merge_requests/diff_notes_resolve_spec.rb b/spec/features/merge_requests/diff_notes_resolve_spec.rb
index 4d549f3bdbb..0f8ca6f90d1 100644
--- a/spec/features/merge_requests/diff_notes_resolve_spec.rb
+++ b/spec/features/merge_requests/diff_notes_resolve_spec.rb
@@ -19,7 +19,7 @@ feature 'Diff notes resolve', feature: true, js: true do
context 'no discussions' do
before do
project.team << [user, :master]
- login_as user
+ gitlab_sign_in user
note.destroy
visit_merge_request
end
@@ -33,7 +33,7 @@ feature 'Diff notes resolve', feature: true, js: true do
context 'as authorized user' do
before do
project.team << [user, :master]
- login_as user
+ gitlab_sign_in user
visit_merge_request
end
@@ -402,7 +402,7 @@ feature 'Diff notes resolve', feature: true, js: true do
before do
project.team << [guest, :guest]
- login_as guest
+ gitlab_sign_in guest
end
context 'someone elses merge request' do
diff --git a/spec/features/merge_requests/diffs_spec.rb b/spec/features/merge_requests/diffs_spec.rb
index 44013df3ea0..cb6cd6571a8 100644
--- a/spec/features/merge_requests/diffs_spec.rb
+++ b/spec/features/merge_requests/diffs_spec.rb
@@ -74,8 +74,7 @@ feature 'Diffs URL', js: true, feature: true do
context 'as author' do
it 'shows direct edit link' do
- login_as(author_user)
-
+ gitlab_sign_in(author_user)
visit diffs_namespace_project_merge_request_path(project.namespace, project, merge_request)
# Throws `Capybara::Poltergeist::InvalidSelector` if we try to use `#hash` syntax
@@ -85,8 +84,7 @@ feature 'Diffs URL', js: true, feature: true do
context 'as user who needs to fork' do
it 'shows fork/cancel confirmation' do
- login_as(user)
-
+ gitlab_sign_in(user)
visit diffs_namespace_project_merge_request_path(project.namespace, project, merge_request)
# Throws `Capybara::Poltergeist::InvalidSelector` if we try to use `#hash` syntax
diff --git a/spec/features/merge_requests/discussion_spec.rb b/spec/features/merge_requests/discussion_spec.rb
index 9db235f35ba..88ae257236c 100644
--- a/spec/features/merge_requests/discussion_spec.rb
+++ b/spec/features/merge_requests/discussion_spec.rb
@@ -2,7 +2,7 @@ require 'spec_helper'
feature 'Merge Request Discussions', feature: true do
before do
- login_as :admin
+ gitlab_sign_in :admin
end
describe "Diff discussions" do
diff --git a/spec/features/merge_requests/edit_mr_spec.rb b/spec/features/merge_requests/edit_mr_spec.rb
index c77a5c68bc6..804bf6967d6 100644
--- a/spec/features/merge_requests/edit_mr_spec.rb
+++ b/spec/features/merge_requests/edit_mr_spec.rb
@@ -8,7 +8,7 @@ feature 'Edit Merge Request', feature: true do
before do
project.team << [user, :master]
- login_as user
+ gitlab_sign_in user
visit edit_namespace_project_merge_request_path(project.namespace, project, merge_request)
end
diff --git a/spec/features/merge_requests/filter_by_labels_spec.rb b/spec/features/merge_requests/filter_by_labels_spec.rb
index 32a9082b9b9..9b677aeca0a 100644
--- a/spec/features/merge_requests/filter_by_labels_spec.rb
+++ b/spec/features/merge_requests/filter_by_labels_spec.rb
@@ -26,7 +26,7 @@ feature 'Issue filtering by Labels', feature: true, js: true do
mr3.labels << feature
project.team << [user, :master]
- login_as(user)
+ gitlab_sign_in(user)
visit namespace_project_merge_requests_path(project.namespace, project)
end
diff --git a/spec/features/merge_requests/filter_by_milestone_spec.rb b/spec/features/merge_requests/filter_by_milestone_spec.rb
index 265a0cfc198..79bca0c9de2 100644
--- a/spec/features/merge_requests/filter_by_milestone_spec.rb
+++ b/spec/features/merge_requests/filter_by_milestone_spec.rb
@@ -15,7 +15,7 @@ feature 'Merge Request filtering by Milestone', feature: true do
before do
project.team << [user, :master]
- login_as(user)
+ gitlab_sign_in(user)
end
scenario 'filters by no Milestone', js: true do
diff --git a/spec/features/merge_requests/filter_merge_requests_spec.rb b/spec/features/merge_requests/filter_merge_requests_spec.rb
index d086be70d69..c12edf1fdf3 100644
--- a/spec/features/merge_requests/filter_merge_requests_spec.rb
+++ b/spec/features/merge_requests/filter_merge_requests_spec.rb
@@ -14,7 +14,7 @@ describe 'Filter merge requests', feature: true do
before do
project.team << [user, :master]
group.add_developer(user)
- login_as(user)
+ gitlab_sign_in(user)
create(:merge_request, source_project: project, target_project: project)
visit namespace_project_merge_requests_path(project.namespace, project)
diff --git a/spec/features/merge_requests/form_spec.rb b/spec/features/merge_requests/form_spec.rb
index 00ef1ffdddc..1996c2fa09a 100644
--- a/spec/features/merge_requests/form_spec.rb
+++ b/spec/features/merge_requests/form_spec.rb
@@ -18,7 +18,7 @@ describe 'New/edit merge request', feature: true, js: true do
context 'owned projects' do
before do
- login_as(user)
+ gitlab_sign_in(user)
end
context 'new merge request' do
@@ -96,6 +96,13 @@ describe 'New/edit merge request', feature: true, js: true do
.to end_with(merge_request_path(merge_request))
end
end
+
+ it 'description has autocomplete' do
+ find('#merge_request_description').native.send_keys('')
+ fill_in 'merge_request_description', with: '@'
+
+ expect(page).to have_selector('.atwho-view')
+ end
end
context 'edit merge request' do
@@ -157,13 +164,20 @@ describe 'New/edit merge request', feature: true, js: true do
end
end
end
+
+ it 'description has autocomplete' do
+ find('#merge_request_description').native.send_keys('')
+ fill_in 'merge_request_description', with: '@'
+
+ expect(page).to have_selector('.atwho-view')
+ end
end
end
context 'forked project' do
before do
fork_project.team << [user, :master]
- login_as(user)
+ gitlab_sign_in(user)
end
context 'new merge request' do
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 221ddb5873c..27ba380b005 100644
--- a/spec/features/merge_requests/merge_commit_message_toggle_spec.rb
+++ b/spec/features/merge_requests/merge_commit_message_toggle_spec.rb
@@ -34,7 +34,7 @@ feature 'Clicking toggle commit message link', feature: true, js: true do
before do
project.team << [user, :master]
- login_as user
+ gitlab_sign_in user
visit namespace_project_merge_request_path(project.namespace, project, merge_request)
diff --git a/spec/features/merge_requests/merge_immediately_with_pipeline_spec.rb b/spec/features/merge_requests/merge_immediately_with_pipeline_spec.rb
index c1d4d508e57..8af7d985036 100644
--- a/spec/features/merge_requests/merge_immediately_with_pipeline_spec.rb
+++ b/spec/features/merge_requests/merge_immediately_with_pipeline_spec.rb
@@ -18,7 +18,9 @@ feature 'Merge immediately', :feature, :js do
sha: project.repository.commit('master').id)
end
- before { project.team << [user, :master] }
+ before do
+ project.team << [user, :master]
+ end
context 'when there is active pipeline for merge request' do
background do
@@ -26,7 +28,7 @@ feature 'Merge immediately', :feature, :js do
end
before do
- login_as user
+ gitlab_sign_in user
visit namespace_project_merge_request_path(merge_request.project.namespace, merge_request.project, merge_request)
end
diff --git a/spec/features/merge_requests/merge_when_pipeline_succeeds_spec.rb b/spec/features/merge_requests/merge_when_pipeline_succeeds_spec.rb
index 09f889d4dd6..bfadd7cb81a 100644
--- a/spec/features/merge_requests/merge_when_pipeline_succeeds_spec.rb
+++ b/spec/features/merge_requests/merge_when_pipeline_succeeds_spec.rb
@@ -28,7 +28,7 @@ feature 'Merge When Pipeline Succeeds', :feature, :js do
end
before do
- login_as user
+ gitlab_sign_in user
visit_merge_request(merge_request)
end
@@ -121,7 +121,7 @@ feature 'Merge When Pipeline Succeeds', :feature, :js do
end
before do
- login_as user
+ gitlab_sign_in user
visit_merge_request(merge_request)
end
diff --git a/spec/features/merge_requests/mini_pipeline_graph_spec.rb b/spec/features/merge_requests/mini_pipeline_graph_spec.rb
index 3a11ea3c8b2..7664fbfbb4c 100644
--- a/spec/features/merge_requests/mini_pipeline_graph_spec.rb
+++ b/spec/features/merge_requests/mini_pipeline_graph_spec.rb
@@ -11,7 +11,7 @@ feature 'Mini Pipeline Graph', :js, :feature do
before do
build.run
- login_as(user)
+ gitlab_sign_in(user)
visit_merge_request
end
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 b1dc81a606a..5cd9a7fbe26 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
@@ -5,7 +5,7 @@ feature 'Only allow merge requests to be merged if the pipeline succeeds', featu
let(:project) { merge_request.target_project }
before do
- login_as merge_request.author
+ gitlab_sign_in merge_request.author
project.team << [merge_request.author, :master]
end
diff --git a/spec/features/merge_requests/pipelines_spec.rb b/spec/features/merge_requests/pipelines_spec.rb
index 4c76004cb93..c2241317e04 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', feature: true, js: true do
before do
project.team << [user, :master]
- login_as user
+ gitlab_sign_in user
end
context 'with pipelines' do
@@ -28,7 +28,7 @@ feature 'Pipelines for Merge Requests', feature: true, js: true do
end
wait_for_requests
- expect(page).to have_selector('.pipeline-actions')
+ expect(page).to have_selector('.stage-cell')
end
end
diff --git a/spec/features/merge_requests/target_branch_spec.rb b/spec/features/merge_requests/target_branch_spec.rb
index c154cf8ade9..4328d66c748 100644
--- a/spec/features/merge_requests/target_branch_spec.rb
+++ b/spec/features/merge_requests/target_branch_spec.rb
@@ -13,7 +13,7 @@ describe 'Target branch', feature: true, js: true do
end
before do
- login_as user
+ gitlab_sign_in user
project.team << [user, :master]
end
diff --git a/spec/features/merge_requests/toggle_whitespace_changes_spec.rb b/spec/features/merge_requests/toggle_whitespace_changes_spec.rb
index 0f98737b700..cba9a2cda99 100644
--- a/spec/features/merge_requests/toggle_whitespace_changes_spec.rb
+++ b/spec/features/merge_requests/toggle_whitespace_changes_spec.rb
@@ -2,7 +2,7 @@ require 'spec_helper'
feature 'Toggle Whitespace Changes', js: true, feature: true do
before do
- login_as :admin
+ gitlab_sign_in :admin
merge_request = create(:merge_request)
project = merge_request.source_project
visit diffs_namespace_project_merge_request_path(project.namespace, project, merge_request)
diff --git a/spec/features/merge_requests/toggler_behavior_spec.rb b/spec/features/merge_requests/toggler_behavior_spec.rb
index 3acd3f6a8b3..c4c06e9a7a0 100644
--- a/spec/features/merge_requests/toggler_behavior_spec.rb
+++ b/spec/features/merge_requests/toggler_behavior_spec.rb
@@ -8,7 +8,7 @@ feature 'toggler_behavior', js: true, feature: true do
let(:fragment_id) { "#note_#{note.id}" }
before do
- login_as :admin
+ gitlab_sign_in :admin
project = merge_request.source_project
page.current_window.resize_to(1000, 300)
visit "#{namespace_project_merge_request_path(project.namespace, project, merge_request)}#{fragment_id}"
diff --git a/spec/features/merge_requests/update_merge_requests_spec.rb b/spec/features/merge_requests/update_merge_requests_spec.rb
index bcdfdf78a44..d0418c74699 100644
--- a/spec/features/merge_requests/update_merge_requests_spec.rb
+++ b/spec/features/merge_requests/update_merge_requests_spec.rb
@@ -7,7 +7,7 @@ feature 'Multiple merge requests updating from merge_requests#index', feature: t
before do
project.team << [user, :master]
- login_as(user)
+ gitlab_sign_in(user)
end
context 'status', js: true do
diff --git a/spec/features/merge_requests/user_posts_diff_notes_spec.rb b/spec/features/merge_requests/user_posts_diff_notes_spec.rb
index 14bc549c9f9..ac7e0eb2727 100644
--- a/spec/features/merge_requests/user_posts_diff_notes_spec.rb
+++ b/spec/features/merge_requests/user_posts_diff_notes_spec.rb
@@ -7,7 +7,7 @@ feature 'Merge requests > User posts diff notes', :js do
before do
project.add_developer(user)
- login_as(user)
+ gitlab_sign_in(user)
end
let(:comment_button_class) { '.add-diff-note' }
diff --git a/spec/features/merge_requests/user_posts_notes_spec.rb b/spec/features/merge_requests/user_posts_notes_spec.rb
index 22552529b9e..70652fcce8c 100644
--- a/spec/features/merge_requests/user_posts_notes_spec.rb
+++ b/spec/features/merge_requests/user_posts_notes_spec.rb
@@ -13,7 +13,7 @@ describe 'Merge requests > User posts notes', :js do
end
before do
- login_as :admin
+ gitlab_sign_in :admin
visit namespace_project_merge_request_path(project.namespace, project, merge_request)
end
diff --git a/spec/features/merge_requests/user_sees_system_notes_spec.rb b/spec/features/merge_requests/user_sees_system_notes_spec.rb
index 55d0f9d728c..0d88a8172b0 100644
--- a/spec/features/merge_requests/user_sees_system_notes_spec.rb
+++ b/spec/features/merge_requests/user_sees_system_notes_spec.rb
@@ -11,7 +11,7 @@ feature 'Merge requests > User sees system notes' do
before do
user = create(:user)
private_project.add_developer(user)
- login_as(user)
+ gitlab_sign_in(user)
end
it 'shows the system note' do
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 0e64a3e1a4b..71aa71e380e 100644
--- a/spec/features/merge_requests/user_uses_slash_commands_spec.rb
+++ b/spec/features/merge_requests/user_uses_slash_commands_spec.rb
@@ -1,14 +1,14 @@
require 'rails_helper'
-feature 'Merge Requests > User uses slash commands', feature: true, js: true do
- include SlashCommandsHelpers
+feature 'Merge Requests > User uses quick actions', feature: true, js: true do
+ include QuickActionsHelpers
let(:user) { create(:user) }
let(:project) { create(:project, :public) }
let(:merge_request) { create(:merge_request, source_project: project) }
let!(:milestone) { create(:milestone, project: project, title: 'ASAP') }
- it_behaves_like 'issuable record that supports slash commands in its description and notes', :merge_request do
+ it_behaves_like 'issuable record that supports quick actions in its description and notes', :merge_request do
let(:issuable) { create(:merge_request, source_project: project) }
let(:new_url_opts) { { merge_request: { source_branch: 'feature', target_branch: 'master' } } }
end
@@ -16,7 +16,7 @@ feature 'Merge Requests > User uses slash commands', feature: true, js: true do
describe 'merge-request-only commands' do
before do
project.team << [user, :master]
- login_with(user)
+ gitlab_sign_in(user)
visit namespace_project_merge_request_path(project.namespace, project, merge_request)
end
@@ -51,8 +51,8 @@ feature 'Merge Requests > User uses slash commands', feature: true, js: true do
let(:guest) { create(:user) }
before do
project.team << [guest, :guest]
- logout
- login_with(guest)
+ gitlab_sign_out
+ gitlab_sign_in(guest)
visit namespace_project_merge_request_path(project.namespace, project, merge_request)
end
@@ -97,8 +97,8 @@ feature 'Merge Requests > User uses slash commands', feature: true, js: true do
let(:guest) { create(:user) }
before do
project.team << [guest, :guest]
- logout
- login_with(guest)
+ gitlab_sign_out
+ gitlab_sign_in(guest)
visit namespace_project_merge_request_path(project.namespace, project, merge_request)
end
@@ -125,9 +125,9 @@ feature 'Merge Requests > User uses slash commands', feature: true, js: true do
let(:new_url_opts) { { merge_request: { source_branch: 'feature' } } }
before do
- logout
+ gitlab_sign_out
another_project.team << [user, :master]
- login_with(user)
+ gitlab_sign_in(user)
end
it 'changes target_branch in new merge_request' do
@@ -181,8 +181,8 @@ feature 'Merge Requests > User uses slash commands', feature: true, js: true do
let(:guest) { create(:user) }
before do
project.team << [guest, :guest]
- logout
- login_with(guest)
+ gitlab_sign_out
+ gitlab_sign_in(guest)
visit namespace_project_merge_request_path(project.namespace, project, merge_request)
end
diff --git a/spec/features/merge_requests/versions_spec.rb b/spec/features/merge_requests/versions_spec.rb
index aad522ee26e..04a72d3be34 100644
--- a/spec/features/merge_requests/versions_spec.rb
+++ b/spec/features/merge_requests/versions_spec.rb
@@ -8,7 +8,7 @@ feature 'Merge Request versions', js: true, feature: true do
let!(:merge_request_diff3) { merge_request.merge_request_diffs.create(head_commit_sha: '5937ac0a7beb003549fc5fd26fc247adbce4a52e') }
before do
- login_as :admin
+ gitlab_sign_in :admin
visit diffs_namespace_project_merge_request_path(project.namespace, project, merge_request)
end
diff --git a/spec/features/merge_requests/widget_deployments_spec.rb b/spec/features/merge_requests/widget_deployments_spec.rb
index 118ecd9cba5..e82e69c5f4a 100644
--- a/spec/features/merge_requests/widget_deployments_spec.rb
+++ b/spec/features/merge_requests/widget_deployments_spec.rb
@@ -12,7 +12,7 @@ feature 'Widget Deployments Header', feature: true, js: true do
given!(:manual) { }
background do
- login_as(user)
+ gitlab_sign_in(user)
project.team << [user, role]
visit namespace_project_merge_request_path(project.namespace, project, merge_request)
end
diff --git a/spec/features/merge_requests/widget_spec.rb b/spec/features/merge_requests/widget_spec.rb
index 4f3a5119915..3ac1f603de6 100644
--- a/spec/features/merge_requests/widget_spec.rb
+++ b/spec/features/merge_requests/widget_spec.rb
@@ -7,7 +7,7 @@ describe 'Merge request', :feature, :js do
before do
project.team << [user, :master]
- login_as(user)
+ gitlab_sign_in(user)
end
context 'new merge request' do
@@ -209,8 +209,8 @@ describe 'Merge request', :feature, :js do
before do
project.team << [user2, :master]
- logout
- login_as user2
+ gitlab_sign_out
+ gitlab_sign_in user2
merge_request.update(target_project: fork_project)
visit namespace_project_merge_request_path(project.namespace, project, merge_request)
end
diff --git a/spec/features/merge_requests/wip_message_spec.rb b/spec/features/merge_requests/wip_message_spec.rb
index 3311731b33b..72d001bf408 100644
--- a/spec/features/merge_requests/wip_message_spec.rb
+++ b/spec/features/merge_requests/wip_message_spec.rb
@@ -6,7 +6,7 @@ feature 'Work In Progress help message', feature: true do
before do
project.team << [user, :master]
- login_as(user)
+ gitlab_sign_in(user)
end
context 'with WIP commits' do
diff --git a/spec/features/milestone_spec.rb b/spec/features/milestone_spec.rb
index c07de01c594..58989581ffe 100644
--- a/spec/features/milestone_spec.rb
+++ b/spec/features/milestone_spec.rb
@@ -6,7 +6,7 @@ feature 'Milestone', feature: true do
before do
project.team << [user, :master]
- login_as(user)
+ gitlab_sign_in(user)
end
feature 'Create a milestone' do
diff --git a/spec/features/milestones/milestones_spec.rb b/spec/features/milestones/milestones_spec.rb
deleted file mode 100644
index b3dfd6d0e81..00000000000
--- a/spec/features/milestones/milestones_spec.rb
+++ /dev/null
@@ -1,101 +0,0 @@
-require 'rails_helper'
-
-describe 'Milestone draggable', feature: true, js: true do
- include DragTo
-
- let(:milestone) { create(:milestone, project: project, title: 8.14) }
- let(:project) { create(:empty_project, :public) }
- let(:user) { create(:user) }
-
- context 'issues' do
- let(:issue) { page.find_by_id('issues-list-unassigned').find('li') }
- let(:issue_target) { page.find_by_id('issues-list-ongoing') }
-
- it 'does not allow guest to drag issue' do
- create_and_drag_issue
-
- expect(issue_target).not_to have_selector('.issuable-row')
- end
-
- it 'does not allow authorized user to drag issue' do
- login_as(user)
- create_and_drag_issue
-
- expect(issue_target).not_to have_selector('.issuable-row')
- end
-
- it 'allows author to drag issue' do
- login_as(user)
- create_and_drag_issue(author: user)
-
- expect(issue_target).to have_selector('.issuable-row')
- end
-
- it 'allows admin to drag issue' do
- login_as(:admin)
- create_and_drag_issue
-
- expect(issue_target).to have_selector('.issuable-row')
- end
- end
-
- context 'merge requests' do
- let(:merge_request) { page.find_by_id('merge_requests-list-unassigned').find('li') }
- let(:merge_request_target) { page.find_by_id('merge_requests-list-ongoing') }
-
- it 'does not allow guest to drag merge request' do
- create_and_drag_merge_request
-
- expect(merge_request_target).not_to have_selector('.issuable-row')
- end
-
- it 'does not allow authorized user to drag merge request' do
- login_as(user)
- create_and_drag_merge_request
-
- expect(merge_request_target).not_to have_selector('.issuable-row')
- end
-
- it 'allows author to drag merge request' do
- login_as(user)
- create_and_drag_merge_request(author: user)
-
- expect(merge_request_target).to have_selector('.issuable-row')
- end
-
- it 'allows admin to drag merge request' do
- login_as(:admin)
- create_and_drag_merge_request
-
- expect(merge_request_target).to have_selector('.issuable-row')
- end
- end
-
- def create_and_drag_issue(params = {})
- create(:issue, params.merge(title: 'Foo', project: project, milestone: milestone))
-
- visit namespace_project_milestone_path(project.namespace, project, milestone)
- scroll_into_view('.milestone-content')
- drag_to(selector: '.issues-sortable-list', list_to_index: 1)
-
- wait_for_requests
- end
-
- def create_and_drag_merge_request(params = {})
- create(:merge_request, params.merge(title: 'Foo', source_project: project, target_project: project, milestone: milestone))
-
- visit namespace_project_milestone_path(project.namespace, project, milestone)
- page.find("a[href='#tab-merge-requests']").click
-
- wait_for_requests
-
- scroll_into_view('.milestone-content')
- drag_to(selector: '.merge_requests-sortable-list', list_to_index: 1)
-
- wait_for_requests
- end
-
- def scroll_into_view(selector)
- page.evaluate_script("document.querySelector('#{selector}').scrollIntoView();")
- end
-end
diff --git a/spec/features/milestones/show_spec.rb b/spec/features/milestones/show_spec.rb
index 227eb04ba72..cdf6cfba402 100644
--- a/spec/features/milestones/show_spec.rb
+++ b/spec/features/milestones/show_spec.rb
@@ -9,7 +9,7 @@ describe 'Milestone show', feature: true do
before do
project.add_user(user, :developer)
- login_as(user)
+ gitlab_sign_in(user)
end
def visit_milestone
diff --git a/spec/features/participants_autocomplete_spec.rb b/spec/features/participants_autocomplete_spec.rb
index 449ce80bc71..b8966cf621c 100644
--- a/spec/features/participants_autocomplete_spec.rb
+++ b/spec/features/participants_autocomplete_spec.rb
@@ -8,7 +8,7 @@ feature 'Member autocomplete', :js do
before do
note # actually create the note
- login_as(user)
+ gitlab_sign_in(user)
end
shared_examples "open suggestions when typing @" do
diff --git a/spec/features/profile_spec.rb b/spec/features/profile_spec.rb
index 7df628fd7a0..bb4263d83f3 100644
--- a/spec/features/profile_spec.rb
+++ b/spec/features/profile_spec.rb
@@ -4,7 +4,7 @@ describe 'Profile account page', feature: true do
let(:user) { create(:user) }
before do
- login_as(user)
+ gitlab_sign_in(user)
end
describe 'when signup is enabled' do
diff --git a/spec/features/profiles/account_spec.rb b/spec/features/profiles/account_spec.rb
index 05a7587f8d4..33fd29b429b 100644
--- a/spec/features/profiles/account_spec.rb
+++ b/spec/features/profiles/account_spec.rb
@@ -4,7 +4,7 @@ feature 'Profile > Account', feature: true do
given(:user) { create(:user, username: 'foo') }
before do
- login_as(user)
+ gitlab_sign_in(user)
end
describe 'Change username' do
@@ -31,8 +31,13 @@ feature 'Profile > Account', feature: true do
given(:new_project_path) { "/#{new_username}/#{project.path}" }
given(:old_project_path) { "/#{user.username}/#{project.path}" }
- before(:context) { TestEnv.clean_test_path }
- after(:example) { TestEnv.clean_test_path }
+ before(:context) do
+ TestEnv.clean_test_path
+ end
+
+ after(:example) do
+ TestEnv.clean_test_path
+ end
scenario 'the project is accessible via the new path' do
update_username(new_username)
diff --git a/spec/features/profiles/chat_names_spec.rb b/spec/features/profiles/chat_names_spec.rb
index 6f6f7029c0b..1a162d6be0e 100644
--- a/spec/features/profiles/chat_names_spec.rb
+++ b/spec/features/profiles/chat_names_spec.rb
@@ -5,7 +5,7 @@ feature 'Profile > Chat', feature: true do
given(:service) { create(:service) }
before do
- login_as(user)
+ gitlab_sign_in(user)
end
describe 'uses authorization link' do
diff --git a/spec/features/profiles/keys_spec.rb b/spec/features/profiles/keys_spec.rb
index 2f436f153aa..13f9afd4ce0 100644
--- a/spec/features/profiles/keys_spec.rb
+++ b/spec/features/profiles/keys_spec.rb
@@ -4,7 +4,7 @@ feature 'Profile > SSH Keys', feature: true do
let(:user) { create(:user) }
before do
- login_as(user)
+ gitlab_sign_in(user)
end
describe 'User adds a key' do
diff --git a/spec/features/profiles/oauth_applications_spec.rb b/spec/features/profiles/oauth_applications_spec.rb
index 1a5a9059dbd..a6f9beafe17 100644
--- a/spec/features/profiles/oauth_applications_spec.rb
+++ b/spec/features/profiles/oauth_applications_spec.rb
@@ -4,7 +4,7 @@ describe 'Profile > Applications', feature: true do
let(:user) { create(:user) }
before do
- login_as(user)
+ gitlab_sign_in(user)
end
describe 'User manages applications', js: true do
diff --git a/spec/features/profiles/password_spec.rb b/spec/features/profiles/password_spec.rb
index 4cbdd89d46f..2d36f3d020f 100644
--- a/spec/features/profiles/password_spec.rb
+++ b/spec/features/profiles/password_spec.rb
@@ -4,7 +4,7 @@ describe 'Profile > Password', feature: true do
let(:user) { create(:user, password_automatically_set: true) }
before do
- login_as(user)
+ gitlab_sign_in(user)
visit edit_profile_password_path
end
diff --git a/spec/features/profiles/personal_access_tokens_spec.rb b/spec/features/profiles/personal_access_tokens_spec.rb
index 7e2e685df26..d7acaaf1eb8 100644
--- a/spec/features/profiles/personal_access_tokens_spec.rb
+++ b/spec/features/profiles/personal_access_tokens_spec.rb
@@ -23,7 +23,7 @@ describe 'Profile > Personal Access Tokens', feature: true, js: true do
end
before do
- login_as(user)
+ gitlab_sign_in(user)
end
describe "token creation" do
diff --git a/spec/features/profiles/preferences_spec.rb b/spec/features/profiles/preferences_spec.rb
index d368bc4d753..8e7ef6bc110 100644
--- a/spec/features/profiles/preferences_spec.rb
+++ b/spec/features/profiles/preferences_spec.rb
@@ -4,7 +4,7 @@ describe 'Profile > Preferences', feature: true do
let(:user) { create(:user) }
before do
- login_as(user)
+ gitlab_sign_in(user)
visit profile_preferences_path
end
diff --git a/spec/features/profiles/user_changes_notified_of_own_activity_spec.rb b/spec/features/profiles/user_changes_notified_of_own_activity_spec.rb
index e05fbb3715c..c0092836e3b 100644
--- a/spec/features/profiles/user_changes_notified_of_own_activity_spec.rb
+++ b/spec/features/profiles/user_changes_notified_of_own_activity_spec.rb
@@ -4,7 +4,7 @@ feature 'Profile > Notifications > User changes notified_of_own_activity setting
let(:user) { create(:user) }
before do
- login_as(user)
+ gitlab_sign_in(user)
end
scenario 'User opts into receiving notifications about their own activity' do
diff --git a/spec/features/projects/activity/rss_spec.rb b/spec/features/projects/activity/rss_spec.rb
index 3c1de5c09b2..84c81d43448 100644
--- a/spec/features/projects/activity/rss_spec.rb
+++ b/spec/features/projects/activity/rss_spec.rb
@@ -12,7 +12,7 @@ feature 'Project Activity RSS' do
before do
user = create(:user)
project.team << [user, :developer]
- login_as(user)
+ gitlab_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 01a95bf49ac..9624e1a71b0 100644
--- a/spec/features/projects/badges/coverage_spec.rb
+++ b/spec/features/projects/badges/coverage_spec.rb
@@ -7,7 +7,7 @@ feature 'test coverage badge' do
context 'when user has access to view badge' do
background do
project.team << [user, :developer]
- login_as(user)
+ gitlab_sign_in(user)
end
scenario 'user requests coverage badge image for pipeline' do
@@ -45,7 +45,7 @@ feature 'test coverage badge' do
end
context 'when user does not have access to view badge' do
- background { login_as(user) }
+ background { gitlab_sign_in(user) }
scenario 'user requests test coverage badge image' do
show_test_coverage_badge
diff --git a/spec/features/projects/badges/list_spec.rb b/spec/features/projects/badges/list_spec.rb
index ae9db0c0d6e..348748152bb 100644
--- a/spec/features/projects/badges/list_spec.rb
+++ b/spec/features/projects/badges/list_spec.rb
@@ -5,7 +5,7 @@ feature 'list of badges' do
user = create(:user)
project = create(:project)
project.team << [user, :master]
- login_as(user)
+ gitlab_sign_in(user)
visit namespace_project_pipelines_settings_path(project.namespace, project)
end
diff --git a/spec/features/projects/blobs/blob_show_spec.rb b/spec/features/projects/blobs/blob_show_spec.rb
index 45fdb36e506..71ffa352f80 100644
--- a/spec/features/projects/blobs/blob_show_spec.rb
+++ b/spec/features/projects/blobs/blob_show_spec.rb
@@ -17,6 +17,7 @@ feature 'File blob', :js, feature: true do
it 'displays the blob' do
aggregate_failures do
# shows highlighted Ruby code
+ expect(page).to have_css(".js-syntax-highlight")
expect(page).to have_content("require 'fileutils'")
# does not show a viewer switcher
@@ -71,6 +72,7 @@ feature 'File blob', :js, feature: true do
expect(page).to have_selector('.blob-viewer[data-type="rich"]', visible: false)
# shows highlighted Markdown code
+ expect(page).to have_css(".js-syntax-highlight")
expect(page).to have_content("[PEP-8](http://www.python.org/dev/peps/pep-0008/)")
# shows an enabled copy button
@@ -114,6 +116,7 @@ feature 'File blob', :js, feature: true do
expect(page).to have_selector('#LC1.hll')
# shows highlighted Markdown code
+ expect(page).to have_css(".js-syntax-highlight")
expect(page).to have_content("[PEP-8](http://www.python.org/dev/peps/pep-0008/)")
# shows an enabled copy button
diff --git a/spec/features/projects/blobs/edit_spec.rb b/spec/features/projects/blobs/edit_spec.rb
index d04c3248ead..d0bc032ee93 100644
--- a/spec/features/projects/blobs/edit_spec.rb
+++ b/spec/features/projects/blobs/edit_spec.rb
@@ -14,7 +14,7 @@ feature 'Editing file blob', feature: true, js: true do
before do
project.team << [user, role]
- login_as(user)
+ gitlab_sign_in(user)
end
def edit_and_commit
@@ -61,7 +61,7 @@ feature 'Editing file blob', feature: true, js: true do
it 'redirects to sign in and returns' do
expect(page).to have_current_path(new_user_session_path)
- login_as(user)
+ gitlab_sign_in(user)
expect(page).to have_current_path(namespace_project_edit_blob_path(project.namespace, project, tree_join(branch, file_path)))
end
@@ -77,7 +77,7 @@ feature 'Editing file blob', feature: true, js: true do
it 'redirects to sign in and returns' do
expect(page).to have_current_path(new_user_session_path)
- login_as(user)
+ gitlab_sign_in(user)
expect(page).to have_current_path(namespace_project_blob_path(project.namespace, project, tree_join(branch, file_path)))
end
@@ -92,7 +92,7 @@ feature 'Editing file blob', feature: true, js: true do
project.team << [user, :developer]
project.repository.add_branch(user, protected_branch, 'master')
create(:protected_branch, project: project, name: protected_branch)
- login_as(user)
+ gitlab_sign_in(user)
end
context 'on some branch' do
@@ -122,7 +122,7 @@ feature 'Editing file blob', feature: true, js: true do
before do
project.team << [user, :master]
- login_as(user)
+ gitlab_sign_in(user)
visit namespace_project_edit_blob_path(project.namespace, 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 92028c19361..d8c4d475a2c 100644
--- a/spec/features/projects/branches/download_buttons_spec.rb
+++ b/spec/features/projects/branches/download_buttons_spec.rb
@@ -22,7 +22,7 @@ feature 'Download buttons in branches page', feature: true do
end
background do
- login_as(user)
+ gitlab_sign_in(user)
project.team << [user, role]
end
diff --git a/spec/features/projects/branches/new_branch_ref_dropdown_spec.rb b/spec/features/projects/branches/new_branch_ref_dropdown_spec.rb
index c5e0a0f0517..406fa52e723 100644
--- a/spec/features/projects/branches/new_branch_ref_dropdown_spec.rb
+++ b/spec/features/projects/branches/new_branch_ref_dropdown_spec.rb
@@ -8,7 +8,7 @@ describe 'New Branch Ref Dropdown', :js, :feature do
before do
project.add_master(user)
- login_as(user)
+ gitlab_sign_in(user)
visit new_namespace_project_branch_path(project.namespace, project)
end
diff --git a/spec/features/projects/branches_spec.rb b/spec/features/projects/branches_spec.rb
index 7668ce5f8be..5c3d1b3f1ee 100644
--- a/spec/features/projects/branches_spec.rb
+++ b/spec/features/projects/branches_spec.rb
@@ -12,7 +12,7 @@ describe 'Branches', feature: true do
context 'logged in as developer' do
before do
- login_as :user
+ gitlab_sign_in :user
project.team << [@user, :developer]
end
@@ -87,7 +87,7 @@ describe 'Branches', feature: true do
context 'logged in as master' do
before do
- login_as :user
+ gitlab_sign_in :user
project.team << [@user, :master]
end
diff --git a/spec/features/projects/commit/builds_spec.rb b/spec/features/projects/commit/builds_spec.rb
index 268d420c594..e5b1f95f2b9 100644
--- a/spec/features/projects/commit/builds_spec.rb
+++ b/spec/features/projects/commit/builds_spec.rb
@@ -6,7 +6,7 @@ feature 'project commit pipelines', js: true do
background do
user = create(:user)
project.team << [user, :master]
- login_as(user)
+ gitlab_sign_in(user)
end
context 'when no builds triggered yet' do
diff --git a/spec/features/projects/commit/cherry_pick_spec.rb b/spec/features/projects/commit/cherry_pick_spec.rb
index bc7ca0ddd38..1fb68a0f5e4 100644
--- a/spec/features/projects/commit/cherry_pick_spec.rb
+++ b/spec/features/projects/commit/cherry_pick_spec.rb
@@ -7,7 +7,7 @@ describe 'Cherry-pick Commits' do
let(:master_pickable_merge) { project.commit('e56497bb5f03a90a51293fc6d516788730953899') }
before do
- login_as :user
+ gitlab_sign_in :user
project.team << [@user, :master]
visit namespace_project_commit_path(project.namespace, project, master_pickable_commit.id)
end
diff --git a/spec/features/projects/commit/mini_pipeline_graph_spec.rb b/spec/features/projects/commit/mini_pipeline_graph_spec.rb
index f2de195eb7f..570a7ae7b16 100644
--- a/spec/features/projects/commit/mini_pipeline_graph_spec.rb
+++ b/spec/features/projects/commit/mini_pipeline_graph_spec.rb
@@ -5,7 +5,7 @@ feature 'Mini Pipeline Graph in Commit View', :js, :feature do
let(:project) { create(:project, :public) }
before do
- login_as(user)
+ gitlab_sign_in(user)
end
context 'when commit has pipelines' do
diff --git a/spec/features/projects/commit/rss_spec.rb b/spec/features/projects/commit/rss_spec.rb
index 03b6d560c96..f7548a56984 100644
--- a/spec/features/projects/commit/rss_spec.rb
+++ b/spec/features/projects/commit/rss_spec.rb
@@ -8,7 +8,7 @@ feature 'Project Commits RSS' do
before do
user = create(:user)
project.team << [user, :developer]
- login_as(user)
+ gitlab_sign_in(user)
visit path
end
diff --git a/spec/features/projects/compare_spec.rb b/spec/features/projects/compare_spec.rb
index ee6985ad993..4743d69fb75 100644
--- a/spec/features/projects/compare_spec.rb
+++ b/spec/features/projects/compare_spec.rb
@@ -6,7 +6,7 @@ describe "Compare", js: true do
before do
project.team << [user, :master]
- login_as user
+ gitlab_sign_in user
visit namespace_project_compare_index_path(project.namespace, 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 06abfbbc86b..a31960639fe 100644
--- a/spec/features/projects/deploy_keys_spec.rb
+++ b/spec/features/projects/deploy_keys_spec.rb
@@ -6,7 +6,7 @@ describe 'Project deploy keys', :js, :feature do
before do
project.team << [user, :master]
- login_as(user)
+ gitlab_sign_in(user)
end
describe 'removing key' do
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 0c51fe72ca4..a943f1e6a08 100644
--- a/spec/features/projects/developer_views_empty_project_instructions_spec.rb
+++ b/spec/features/projects/developer_views_empty_project_instructions_spec.rb
@@ -7,7 +7,7 @@ feature 'Developer views empty project instructions', feature: true do
background do
project.team << [developer, :developer]
- login_as(developer)
+ gitlab_sign_in(developer)
end
context 'without an SSH key' do
diff --git a/spec/features/projects/diffs/diff_show_spec.rb b/spec/features/projects/diffs/diff_show_spec.rb
new file mode 100644
index 00000000000..48b7f1e0f34
--- /dev/null
+++ b/spec/features/projects/diffs/diff_show_spec.rb
@@ -0,0 +1,133 @@
+require 'spec_helper'
+
+feature 'Diff file viewer', :js, feature: true do
+ let(:project) { create(:project, :public, :repository) }
+
+ def visit_commit(sha, anchor: nil)
+ visit namespace_project_commit_path(project.namespace, project, sha, anchor: anchor)
+
+ wait_for_requests
+ end
+
+ context 'Ruby file' do
+ before do
+ visit_commit('570e7b2abdd848b95f2f578043fc23bd6f6fd24d')
+ end
+
+ it 'shows highlighted Ruby code' do
+ within('.diff-file[id="2f6fcd96b88b36ce98c38da085c795a27d92a3dd"]') do
+ expect(page).to have_css(".js-syntax-highlight")
+ expect(page).to have_content("def popen(cmd, path=nil)")
+ end
+ end
+ end
+
+ context 'Ruby file (stored in LFS)' do
+ before do
+ project.add_master(project.creator)
+
+ @commit_id = Files::CreateService.new(
+ project,
+ project.creator,
+ start_branch: 'master',
+ branch_name: 'master',
+ commit_message: "Add Ruby file in LFS",
+ file_path: 'files/lfs/ruby.rb',
+ file_content: project.repository.blob_at('master', 'files/lfs/lfs_object.iso').data
+ ).execute[:result]
+ end
+
+ context 'when LFS is enabled on the project' do
+ before do
+ allow(Gitlab.config.lfs).to receive(:enabled).and_return(true)
+ project.update_attribute(:lfs_enabled, true)
+
+ visit_commit(@commit_id)
+ end
+
+ it 'shows an error message' do
+ expect(page).to have_content('This source diff could not be displayed because it is stored in LFS. You can view the blob instead.')
+ end
+ end
+
+ context 'when LFS is disabled on the project' do
+ before do
+ visit_commit(@commit_id)
+ end
+
+ it 'displays the diff' do
+ expect(page).to have_content('size 1575078')
+ end
+ end
+ end
+
+ context 'Image file' do
+ before do
+ visit_commit('2f63565e7aac07bcdadb654e253078b727143ec4')
+ end
+
+ it 'shows a rendered image' do
+ within('.diff-file[id="e986451b8f7397b617dbb6fffcb5539328c56921"]') do
+ expect(page).to have_css('img[alt="files/images/6049019_460s.jpg"]')
+ end
+ end
+ end
+
+ context 'ISO file (stored in LFS)' do
+ context 'when LFS is enabled on the project' do
+ before do
+ allow(Gitlab.config.lfs).to receive(:enabled).and_return(true)
+ project.update_attribute(:lfs_enabled, true)
+
+ visit_commit('048721d90c449b244b7b4c53a9186b04330174ec')
+ end
+
+ it 'shows that file was added' do
+ expect(page).to have_content('File added')
+ end
+ end
+
+ context 'when LFS is disabled on the project' do
+ before do
+ visit_commit('048721d90c449b244b7b4c53a9186b04330174ec')
+ end
+
+ it 'displays the diff' do
+ expect(page).to have_content('size 1575078')
+ end
+ end
+ end
+
+ context 'ZIP file' do
+ before do
+ visit_commit('ae73cb07c9eeaf35924a10f713b364d32b2dd34f')
+ end
+
+ it 'shows that file was added' do
+ expect(page).to have_content('File added')
+ end
+ end
+
+ context 'binary file that appears to be text in the first 1024 bytes' do
+ before do
+ visit_commit('7b1cf4336b528e0f3d1d140ee50cafdbc703597c')
+ end
+
+ it 'shows the diff is collapsed' do
+ expect(page).to have_content('This diff is collapsed. Click to expand it.')
+ end
+
+ context 'expanding the diff' do
+ before do
+ # We can't use `click_link` because the "link" doesn't have an `href`.
+ find('a.click-to-expand').click
+
+ wait_for_requests
+ end
+
+ it 'shows there is no preview' do
+ expect(page).to have_content('No preview for this file type')
+ end
+ end
+ end
+end
diff --git a/spec/features/projects/edit_spec.rb b/spec/features/projects/edit_spec.rb
index a263781c43c..ca202b95a44 100644
--- a/spec/features/projects/edit_spec.rb
+++ b/spec/features/projects/edit_spec.rb
@@ -6,7 +6,7 @@ feature 'Project edit', feature: true, js: true do
before do
project.team << [user, :master]
- login_as(user)
+ gitlab_sign_in(user)
visit edit_namespace_project_path(project.namespace, project)
end
diff --git a/spec/features/projects/environments/environment_metrics_spec.rb b/spec/features/projects/environments/environment_metrics_spec.rb
index ee925e811e1..b48dcf6c774 100644
--- a/spec/features/projects/environments/environment_metrics_spec.rb
+++ b/spec/features/projects/environments/environment_metrics_spec.rb
@@ -15,7 +15,7 @@ feature 'Environment > Metrics', :feature do
create(:deployment, environment: environment, deployable: build)
stub_all_prometheus_requests(environment.slug)
- login_as(user)
+ gitlab_sign_in(user)
visit_environment(environment)
end
diff --git a/spec/features/projects/environments/environment_spec.rb b/spec/features/projects/environments/environment_spec.rb
index 18b608c863e..7d565555f1f 100644
--- a/spec/features/projects/environments/environment_spec.rb
+++ b/spec/features/projects/environments/environment_spec.rb
@@ -6,7 +6,7 @@ feature 'Environment', :feature do
given(:role) { :developer }
background do
- login_as(user)
+ gitlab_sign_in(user)
project.team << [user, role]
end
diff --git a/spec/features/projects/environments/environments_spec.rb b/spec/features/projects/environments/environments_spec.rb
index 613b1edba36..83883dba0ba 100644
--- a/spec/features/projects/environments/environments_spec.rb
+++ b/spec/features/projects/environments/environments_spec.rb
@@ -7,7 +7,7 @@ feature 'Environments page', :feature, :js do
background do
project.team << [user, role]
- login_as(user)
+ gitlab_sign_in(user)
end
given!(:environment) { }
diff --git a/spec/features/projects/features_visibility_spec.rb b/spec/features/projects/features_visibility_spec.rb
index c49648f54bd..db2790a4bce 100644
--- a/spec/features/projects/features_visibility_spec.rb
+++ b/spec/features/projects/features_visibility_spec.rb
@@ -9,7 +9,7 @@ describe 'Edit Project Settings', feature: true do
describe 'project features visibility selectors', js: true do
before do
project.team << [member, :master]
- login_as(member)
+ gitlab_sign_in(member)
end
tools = { builds: "pipelines", issues: "issues", wiki: "wiki", snippets: "snippets", merge_requests: "merge_requests" }
@@ -68,9 +68,12 @@ describe 'Edit Project Settings', feature: true do
end
describe 'project features visibility pages' do
+ let(:pipeline) { create(:ci_empty_pipeline, project: project) }
+ let(:job) { create(:ci_build, pipeline: pipeline) }
+
let(:tools) do
{
- builds: namespace_project_pipelines_path(project.namespace, project),
+ builds: namespace_project_job_path(project.namespace, project, job),
issues: namespace_project_issues_path(project.namespace, project),
wiki: namespace_project_wiki_path(project.namespace, project, :home),
snippets: namespace_project_snippets_path(project.namespace, project),
@@ -80,7 +83,7 @@ describe 'Edit Project Settings', feature: true do
context 'normal user' do
before do
- login_as(member)
+ gitlab_sign_in(member)
end
it 'renders 200 if tool is enabled' do
@@ -127,7 +130,7 @@ describe 'Edit Project Settings', feature: true do
context 'admin user' do
before do
non_member.update_attribute(:admin, true)
- login_as(non_member)
+ gitlab_sign_in(non_member)
end
it 'renders 404 if feature is disabled' do
@@ -153,7 +156,7 @@ describe 'Edit Project Settings', feature: true do
describe 'repository visibility', js: true do
before do
project.team << [member, :master]
- login_as(member)
+ gitlab_sign_in(member)
visit edit_namespace_project_path(project.namespace, project)
end
@@ -239,7 +242,7 @@ describe 'Edit Project Settings', feature: true do
before do
project.team << [member, :guest]
- login_as(member)
+ gitlab_sign_in(member)
visit namespace_project_path(project.namespace, project)
end
diff --git a/spec/features/projects/files/browse_files_spec.rb b/spec/features/projects/files/browse_files_spec.rb
index 30a1eedbb48..2a82c3ac179 100644
--- a/spec/features/projects/files/browse_files_spec.rb
+++ b/spec/features/projects/files/browse_files_spec.rb
@@ -6,7 +6,7 @@ feature 'user browses project', feature: true, js: true do
before do
project.team << [user, :master]
- login_with(user)
+ gitlab_sign_in(user)
visit namespace_project_tree_path(project.namespace, 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 69744ac3948..2a1cc01fe68 100644
--- a/spec/features/projects/files/creating_a_file_spec.rb
+++ b/spec/features/projects/files/creating_a_file_spec.rb
@@ -6,7 +6,7 @@ feature 'User wants to create a file', feature: true do
background do
project.team << [user, :master]
- login_as user
+ gitlab_sign_in user
visit namespace_project_new_blob_path(project.namespace, 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 93909e91d05..4f1b8588462 100644
--- a/spec/features/projects/files/dockerfile_dropdown_spec.rb
+++ b/spec/features/projects/files/dockerfile_dropdown_spec.rb
@@ -7,7 +7,7 @@ feature 'User wants to add a Dockerfile file', feature: true do
project = create(:project)
project.team << [user, :master]
- login_as user
+ gitlab_sign_in user
visit namespace_project_new_blob_path(project.namespace, project, 'master', file_name: 'Dockerfile')
end
diff --git a/spec/features/projects/files/download_buttons_spec.rb b/spec/features/projects/files/download_buttons_spec.rb
index d7c29a7e074..60182bfebe9 100644
--- a/spec/features/projects/files/download_buttons_spec.rb
+++ b/spec/features/projects/files/download_buttons_spec.rb
@@ -22,7 +22,7 @@ feature 'Download buttons in files tree', feature: true do
end
background do
- login_as(user)
+ gitlab_sign_in(user)
project.team << [user, role]
end
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 012befa7990..6e361ac4312 100644
--- a/spec/features/projects/files/edit_file_soft_wrap_spec.rb
+++ b/spec/features/projects/files/edit_file_soft_wrap_spec.rb
@@ -5,7 +5,7 @@ feature 'User uses soft wrap whilst editing file', feature: true, js: true do
user = create(:user)
project = create(:project)
project.team << [user, :master]
- login_as user
+ gitlab_sign_in user
visit namespace_project_new_blob_path(project.namespace, project, 'master', file_name: 'test_file-name')
editor = find('.file-editor.code')
editor.click
diff --git a/spec/features/projects/files/editing_a_file_spec.rb b/spec/features/projects/files/editing_a_file_spec.rb
index 7a3afafec29..e97ff5fded7 100644
--- a/spec/features/projects/files/editing_a_file_spec.rb
+++ b/spec/features/projects/files/editing_a_file_spec.rb
@@ -17,7 +17,7 @@ feature 'User wants to edit a file', feature: true do
background do
project.team << [user, :master]
- login_as user
+ gitlab_sign_in user
visit namespace_project_edit_blob_path(project.namespace, project,
File.join(project.default_branch, '.gitignore'))
end
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 5c8105de4cb..83a837fba44 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
@@ -6,7 +6,7 @@ feature 'User views files page', feature: true do
before do
project.team << [user, :master]
- login_as user
+ gitlab_sign_in user
visit namespace_project_tree_path(project.namespace, 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 ee42bcaec4b..6a914820ac9 100644
--- a/spec/features/projects/files/find_file_keyboard_spec.rb
+++ b/spec/features/projects/files/find_file_keyboard_spec.rb
@@ -6,7 +6,7 @@ feature 'Find file keyboard shortcuts', feature: true, js: true do
before do
project.team << [user, :master]
- login_as user
+ gitlab_sign_in user
visit namespace_project_find_file_path(project.namespace, project, project.repository.root_ref)
diff --git a/spec/features/projects/files/find_files_spec.rb b/spec/features/projects/files/find_files_spec.rb
index 716b7591b95..166ec5c921b 100644
--- a/spec/features/projects/files/find_files_spec.rb
+++ b/spec/features/projects/files/find_files_spec.rb
@@ -5,7 +5,7 @@ feature 'Find files button in the tree header', feature: true do
given(:project) { create(:project) }
background do
- login_as(user)
+ gitlab_sign_in(user)
project.team << [user, :developer]
end
diff --git a/spec/features/projects/files/gitignore_dropdown_spec.rb b/spec/features/projects/files/gitignore_dropdown_spec.rb
index e9f49453121..7f02ec6b73d 100644
--- a/spec/features/projects/files/gitignore_dropdown_spec.rb
+++ b/spec/features/projects/files/gitignore_dropdown_spec.rb
@@ -5,7 +5,7 @@ feature 'User wants to add a .gitignore file', feature: true do
user = create(:user)
project = create(:project)
project.team << [user, :master]
- login_as user
+ gitlab_sign_in user
visit namespace_project_new_blob_path(project.namespace, 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 031b89d0499..f4b17c2518c 100644
--- a/spec/features/projects/files/gitlab_ci_yml_dropdown_spec.rb
+++ b/spec/features/projects/files/gitlab_ci_yml_dropdown_spec.rb
@@ -5,7 +5,7 @@ feature 'User wants to add a .gitlab-ci.yml file', feature: true do
user = create(:user)
project = create(:project)
project.team << [user, :master]
- login_as user
+ gitlab_sign_in user
visit namespace_project_new_blob_path(project.namespace, 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 8d410cc3f2e..7daf016dd22 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
@@ -7,7 +7,7 @@ feature 'project owner creates a license file', feature: true, js: true do
project.repository.delete_file(project_master, 'LICENSE',
message: 'Remove LICENSE', branch_name: 'master')
project.team << [project_master, :master]
- login_as(project_master)
+ gitlab_sign_in(project_master)
visit namespace_project_path(project.namespace, 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 8e197bccabf..eab19d52030 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
@@ -5,7 +5,7 @@ feature 'project owner sees a link to create a license file in empty project', f
let(:project) { create(:empty_project) }
background do
project.team << [project_master, :master]
- login_as(project_master)
+ gitlab_sign_in(project_master)
end
scenario 'project master creates a license file from a template' do
diff --git a/spec/features/projects/files/template_type_dropdown_spec.rb b/spec/features/projects/files/template_type_dropdown_spec.rb
index 9fcf12e6cb9..028a0919640 100644
--- a/spec/features/projects/files/template_type_dropdown_spec.rb
+++ b/spec/features/projects/files/template_type_dropdown_spec.rb
@@ -6,7 +6,7 @@ feature 'Template type dropdown selector', js: true do
before do
project.team << [user, :master]
- login_as user
+ gitlab_sign_in user
end
context 'editing a non-matching file' do
diff --git a/spec/features/projects/files/undo_template_spec.rb b/spec/features/projects/files/undo_template_spec.rb
index de10eec0557..4ccd123f46e 100644
--- a/spec/features/projects/files/undo_template_spec.rb
+++ b/spec/features/projects/files/undo_template_spec.rb
@@ -6,7 +6,7 @@ feature 'Template Undo Button', js: true do
before do
project.team << [user, :master]
- login_as user
+ gitlab_sign_in user
end
context 'editing a matching file and applying a template' do
diff --git a/spec/features/projects/gfm_autocomplete_load_spec.rb b/spec/features/projects/gfm_autocomplete_load_spec.rb
index 67bc9142356..aa4ed217a34 100644
--- a/spec/features/projects/gfm_autocomplete_load_spec.rb
+++ b/spec/features/projects/gfm_autocomplete_load_spec.rb
@@ -4,7 +4,7 @@ describe 'GFM autocomplete loading', feature: true, js: true do
let(:project) { create(:project) }
before do
- login_as :admin
+ gitlab_sign_in :admin
visit namespace_project_path(project.namespace, project)
end
diff --git a/spec/features/projects/group_links_spec.rb b/spec/features/projects/group_links_spec.rb
index 1b680a56492..778f5d61ae3 100644
--- a/spec/features/projects/group_links_spec.rb
+++ b/spec/features/projects/group_links_spec.rb
@@ -9,7 +9,7 @@ feature 'Project group links', :feature, :js do
background do
project.add_master(master)
- login_as(master)
+ gitlab_sign_in(master)
end
context 'setting an expiration date for a group link' do
diff --git a/spec/features/projects/guest_navigation_menu_spec.rb b/spec/features/projects/guest_navigation_menu_spec.rb
index b91c3eff478..e1f7f06c113 100644
--- a/spec/features/projects/guest_navigation_menu_spec.rb
+++ b/spec/features/projects/guest_navigation_menu_spec.rb
@@ -7,7 +7,7 @@ describe 'Guest navigation menu' do
before do
project.team << [guest, :guest]
- login_as(guest)
+ gitlab_sign_in(guest)
end
it 'shows allowed tabs only' do
diff --git a/spec/features/projects/import_export/export_file_spec.rb b/spec/features/projects/import_export/export_file_spec.rb
index 40caf89dd54..b5c64777934 100644
--- a/spec/features/projects/import_export/export_file_spec.rb
+++ b/spec/features/projects/import_export/export_file_spec.rb
@@ -33,7 +33,7 @@ feature 'Import/Export - project export integration test', feature: true, js: tr
context 'admin user' do
before do
- login_as(user)
+ gitlab_sign_in(user)
end
scenario 'exports a project successfully' do
diff --git a/spec/features/projects/import_export/import_file_spec.rb b/spec/features/projects/import_export/import_file_spec.rb
index 583f479ec18..a111aa87c52 100644
--- a/spec/features/projects/import_export/import_file_spec.rb
+++ b/spec/features/projects/import_export/import_file_spec.rb
@@ -19,7 +19,7 @@ feature 'Import/Export - project import integration test', feature: true, js: tr
let!(:namespace) { create(:namespace, name: "asd", owner: user) }
before do
- login_as(user)
+ gitlab_sign_in(user)
end
scenario 'user imports an exported project successfully' do
@@ -77,7 +77,7 @@ feature 'Import/Export - project import integration test', feature: true, js: tr
context 'when limited to the default user namespace' do
let(:user) { create(:user) }
before do
- login_as(user)
+ gitlab_sign_in(user)
end
scenario 'passes correct namespace ID in the URL' do
diff --git a/spec/features/projects/import_export/namespace_export_file_spec.rb b/spec/features/projects/import_export/namespace_export_file_spec.rb
index cb399ea55df..b0a68f0d61f 100644
--- a/spec/features/projects/import_export/namespace_export_file_spec.rb
+++ b/spec/features/projects/import_export/namespace_export_file_spec.rb
@@ -16,7 +16,7 @@ feature 'Import/Export - Namespace export file cleanup', feature: true, js: true
context 'admin user' do
before do
- login_as(:admin)
+ gitlab_sign_in(:admin)
end
context 'moving the namespace' do
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 4efd5a26a82..e03e7b88174 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 3076c863dcb..26a09985312 100644
--- a/spec/features/projects/issuable_templates_spec.rb
+++ b/spec/features/projects/issuable_templates_spec.rb
@@ -6,7 +6,7 @@ feature 'issuable templates', feature: true, js: true do
before do
project.team << [user, :master]
- login_as user
+ gitlab_sign_in user
end
context 'user creates an issue using templates' do
@@ -124,11 +124,11 @@ feature 'issuable templates', feature: true, js: true do
let(:merge_request) { create(:merge_request, :with_diffs, source_project: fork_project, target_project: project) }
background do
- logout
+ gitlab_sign_out
project.team << [fork_user, :developer]
fork_project.team << [fork_user, :master]
create(:forked_project_link, forked_to_project: fork_project, forked_from_project: project)
- login_as fork_user
+ gitlab_sign_in fork_user
project.repository.create_file(
fork_user,
'.gitlab/merge_request_templates/feature-proposal.md',
diff --git a/spec/features/projects/issues/list_spec.rb b/spec/features/projects/issues/list_spec.rb
index 3137af074ca..b2db07a75ef 100644
--- a/spec/features/projects/issues/list_spec.rb
+++ b/spec/features/projects/issues/list_spec.rb
@@ -7,7 +7,7 @@ feature 'Issues List' do
background do
project.team << [user, :developer]
- login_as(user)
+ gitlab_sign_in(user)
end
scenario 'user does not see create new list button' do
diff --git a/spec/features/projects/issues/rss_spec.rb b/spec/features/projects/issues/rss_spec.rb
index f6852192aef..38733d39932 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
before do
user = create(:user)
project.team << [user, :developer]
- login_as(user)
+ gitlab_sign_in(user)
visit path
end
diff --git a/spec/features/projects/jobs_spec.rb b/spec/features/projects/jobs_spec.rb
index 727ae7081b0..070cdbf1cef 100644
--- a/spec/features/projects/jobs_spec.rb
+++ b/spec/features/projects/jobs_spec.rb
@@ -8,8 +8,8 @@ feature 'Jobs', :feature do
let(:namespace) { project.namespace }
let(:pipeline) { create(:ci_pipeline, project: project) }
- let(:build) { create(:ci_build, :trace, pipeline: pipeline) }
- let(:build2) { create(:ci_build) }
+ let(:job) { create(:ci_build, :trace, pipeline: pipeline) }
+ let(:job2) { create(:ci_build) }
let(:artifacts_file) do
fixture_file_upload(Rails.root + 'spec/fixtures/banana_sample.gif', 'image/gif')
@@ -17,11 +17,11 @@ feature 'Jobs', :feature do
before do
project.team << [user, user_access_level]
- login_as(user)
+ gitlab_sign_in(user)
end
describe "GET /:project/jobs" do
- let!(:build) { create(:ci_build, pipeline: pipeline) }
+ let!(:job) { create(:ci_build, pipeline: pipeline) }
context "Pending scope" do
before do
@@ -31,30 +31,30 @@ feature 'Jobs', :feature do
it "shows Pending tab jobs" do
expect(page).to have_link 'Cancel running'
expect(page).to have_selector('.nav-links li.active', text: 'Pending')
- expect(page).to have_content build.short_sha
- expect(page).to have_content build.ref
- expect(page).to have_content build.name
+ expect(page).to have_content job.short_sha
+ expect(page).to have_content job.ref
+ expect(page).to have_content job.name
end
end
context "Running scope" do
before do
- build.run!
+ job.run!
visit namespace_project_jobs_path(project.namespace, project, scope: :running)
end
it "shows Running tab jobs" do
expect(page).to have_selector('.nav-links li.active', text: 'Running')
expect(page).to have_link 'Cancel running'
- expect(page).to have_content build.short_sha
- expect(page).to have_content build.ref
- expect(page).to have_content build.name
+ expect(page).to have_content job.short_sha
+ expect(page).to have_content job.ref
+ expect(page).to have_content job.name
end
end
context "Finished scope" do
before do
- build.run!
+ job.run!
visit namespace_project_jobs_path(project.namespace, project, scope: :finished)
end
@@ -73,9 +73,9 @@ feature 'Jobs', :feature do
it "shows All tab jobs" do
expect(page).to have_selector('.nav-links li.active', text: 'All')
- expect(page).to have_content build.short_sha
- expect(page).to have_content build.ref
- expect(page).to have_content build.name
+ expect(page).to have_content job.short_sha
+ expect(page).to have_content job.ref
+ expect(page).to have_content job.name
expect(page).not_to have_link 'Cancel running'
end
end
@@ -97,7 +97,7 @@ feature 'Jobs', :feature do
describe "POST /:project/jobs/:id/cancel_all" do
before do
- build.run!
+ job.run!
visit namespace_project_jobs_path(project.namespace, project)
click_link "Cancel running"
end
@@ -105,19 +105,19 @@ feature 'Jobs', :feature do
it 'shows all necessary content' do
expect(page).to have_selector('.nav-links li.active', text: 'All')
expect(page).to have_content 'canceled'
- expect(page).to have_content build.short_sha
- expect(page).to have_content build.ref
- expect(page).to have_content build.name
+ expect(page).to have_content job.short_sha
+ expect(page).to have_content job.ref
+ expect(page).to have_content job.name
expect(page).not_to have_link 'Cancel running'
end
end
describe "GET /:project/jobs/:id" do
context "Job from project" do
- let(:build) { create(:ci_build, :success, pipeline: pipeline) }
+ let(:job) { create(:ci_build, :success, pipeline: pipeline) }
before do
- visit namespace_project_job_path(project.namespace, project, build)
+ visit namespace_project_job_path(project.namespace, project, job)
end
it 'shows status name', :js do
@@ -131,33 +131,33 @@ feature 'Jobs', :feature do
expect(page).to have_content pipeline.git_author_name
end
- it 'shows active build' do
+ it 'shows active job' do
expect(page).to have_selector('.build-job.active')
end
end
context 'when job is not running', :js do
- let(:build) { create(:ci_build, :success, pipeline: pipeline) }
+ let(:job) { create(:ci_build, :success, pipeline: pipeline) }
before do
- visit namespace_project_job_path(project.namespace, project, build)
+ visit namespace_project_job_path(project.namespace, project, job)
end
it 'shows retry button' do
expect(page).to have_link('Retry')
end
- context 'if build passed' do
+ context 'if job passed' do
it 'does not show New issue button' do
expect(page).not_to have_link('New issue')
end
end
- context 'if build failed' do
- let(:build) { create(:ci_build, :failed, pipeline: pipeline) }
+ context 'if job failed' do
+ let(:job) { create(:ci_build, :failed, pipeline: pipeline) }
before do
- visit namespace_project_job_path(namespace, project, build)
+ visit namespace_project_job_path(namespace, project, job)
end
it 'shows New issue button' do
@@ -165,9 +165,9 @@ feature 'Jobs', :feature do
end
it 'links to issues/new with the title and description filled in' do
- button_title = "Build Failed ##{build.id}"
- build_path = namespace_project_job_path(namespace, project, build)
- options = { issue: { title: button_title, description: build_path } }
+ button_title = "Build Failed ##{job.id}"
+ job_path = namespace_project_job_path(namespace, project, job)
+ options = { issue: { title: button_title, description: job_path } }
href = new_namespace_project_issue_path(namespace, project, options)
@@ -180,7 +180,7 @@ feature 'Jobs', :feature do
context "Job from other project" do
before do
- visit namespace_project_job_path(project.namespace, project, build2)
+ visit namespace_project_job_path(project.namespace, project, job2)
end
it { expect(page.status_code).to eq(404) }
@@ -188,8 +188,8 @@ feature 'Jobs', :feature do
context "Download artifacts" do
before do
- build.update_attributes(artifacts_file: artifacts_file)
- visit namespace_project_job_path(project.namespace, project, build)
+ job.update_attributes(artifacts_file: artifacts_file)
+ visit namespace_project_job_path(project.namespace, project, job)
end
it 'has button to download artifacts' do
@@ -199,10 +199,10 @@ feature 'Jobs', :feature do
context 'Artifacts expire date' do
before do
- build.update_attributes(artifacts_file: artifacts_file,
- artifacts_expire_at: expire_at)
+ job.update_attributes(artifacts_file: artifacts_file,
+ artifacts_expire_at: expire_at)
- visit namespace_project_job_path(project.namespace, project, build)
+ visit namespace_project_job_path(project.namespace, project, job)
end
context 'no expire date defined' do
@@ -248,7 +248,7 @@ feature 'Jobs', :feature do
context "when visiting old URL" do
let(:job_url) do
- namespace_project_job_path(project.namespace, project, build)
+ namespace_project_job_path(project.namespace, project, job)
end
before do
@@ -262,9 +262,9 @@ feature 'Jobs', :feature do
feature 'Raw trace' do
before do
- build.run!
+ job.run!
- visit namespace_project_job_path(project.namespace, project, build)
+ visit namespace_project_job_path(project.namespace, project, job)
end
it do
@@ -274,16 +274,16 @@ feature 'Jobs', :feature do
feature 'HTML trace', :js do
before do
- build.run!
+ job.run!
- visit namespace_project_job_path(project.namespace, project, build)
+ visit namespace_project_job_path(project.namespace, project, job)
end
context 'when job has an initial trace' do
it 'loads job trace' do
expect(page).to have_content 'BUILD TRACE'
- build.trace.write do |stream|
+ job.trace.write do |stream|
stream.append(' and more trace', 11)
end
@@ -295,12 +295,12 @@ feature 'Jobs', :feature do
feature 'Variables' do
let(:trigger_request) { create(:ci_trigger_request_with_variables) }
- let(:build) do
+ let(:job) do
create :ci_build, pipeline: pipeline, trigger_request: trigger_request
end
before do
- visit namespace_project_job_path(project.namespace, project, build)
+ visit namespace_project_job_path(project.namespace, project, job)
end
it 'shows variable key and value after click', js: true do
@@ -322,20 +322,20 @@ feature 'Jobs', :feature do
context 'job is successfull and has deployment' do
let(:deployment) { create(:deployment) }
- let(:build) { create(:ci_build, :success, environment: environment.name, deployments: [deployment], pipeline: pipeline) }
+ let(:job) { create(:ci_build, :success, environment: environment.name, deployments: [deployment], pipeline: pipeline) }
it 'shows a link for the job' do
- visit namespace_project_job_path(project.namespace, project, build)
+ visit namespace_project_job_path(project.namespace, project, job)
expect(page).to have_link environment.name
end
end
context 'job is complete and not successful' do
- let(:build) { create(:ci_build, :failed, environment: environment.name, pipeline: pipeline) }
+ let(:job) { create(:ci_build, :failed, environment: environment.name, pipeline: pipeline) }
it 'shows a link for the job' do
- visit namespace_project_job_path(project.namespace, project, build)
+ visit namespace_project_job_path(project.namespace, project, job)
expect(page).to have_link environment.name
end
@@ -343,10 +343,10 @@ feature 'Jobs', :feature do
context 'job creates a new deployment' do
let!(:deployment) { create(:deployment, environment: environment, sha: project.commit.id) }
- let(:build) { create(:ci_build, :success, environment: environment.name, pipeline: pipeline) }
+ let(:job) { create(:ci_build, :success, environment: environment.name, pipeline: pipeline) }
it 'shows a link to latest deployment' do
- visit namespace_project_job_path(project.namespace, project, build)
+ visit namespace_project_job_path(project.namespace, project, job)
expect(page).to have_link('latest deployment')
end
@@ -357,8 +357,8 @@ feature 'Jobs', :feature do
describe "POST /:project/jobs/:id/cancel", :js do
context "Job from project" do
before do
- build.run!
- visit namespace_project_job_path(project.namespace, project, build)
+ job.run!
+ visit namespace_project_job_path(project.namespace, project, job)
find('.js-cancel-job').click()
end
@@ -372,8 +372,8 @@ feature 'Jobs', :feature do
describe "POST /:project/jobs/:id/retry" do
context "Job from project", :js do
before do
- build.run!
- visit namespace_project_job_path(project.namespace, project, build)
+ job.run!
+ visit namespace_project_job_path(project.namespace, project, job)
find('.js-cancel-job').click()
find('.js-retry-button').trigger('click')
end
@@ -388,13 +388,13 @@ feature 'Jobs', :feature do
context "Job that current user is not allowed to retry" do
before do
- build.run!
- build.cancel!
+ job.run!
+ job.cancel!
project.update(visibility_level: Gitlab::VisibilityLevel::PUBLIC)
- logout_direct
- login_with(create(:user))
- visit namespace_project_job_path(project.namespace, project, build)
+ gitlab_sign_out_direct
+ gitlab_sign_in(create(:user))
+ visit namespace_project_job_path(project.namespace, project, job)
end
it 'does not show the Retry button' do
@@ -407,15 +407,15 @@ feature 'Jobs', :feature do
describe "GET /:project/jobs/:id/download" do
before do
- build.update_attributes(artifacts_file: artifacts_file)
- visit namespace_project_job_path(project.namespace, project, build)
+ job.update_attributes(artifacts_file: artifacts_file)
+ visit namespace_project_job_path(project.namespace, project, job)
click_link 'Download'
end
context "Build from other project" do
before do
- build2.update_attributes(artifacts_file: artifacts_file)
- visit download_namespace_project_job_artifacts_path(project.namespace, project, build2)
+ job2.update_attributes(artifacts_file: artifacts_file)
+ visit download_namespace_project_job_artifacts_path(project.namespace, project, job2)
end
it { expect(page.status_code).to eq(404) }
@@ -427,23 +427,23 @@ feature 'Jobs', :feature do
context 'job from project' do
before do
Capybara.current_session.driver.headers = { 'X-Sendfile-Type' => 'X-Sendfile' }
- build.run!
- visit namespace_project_job_path(project.namespace, project, build)
+ job.run!
+ visit namespace_project_job_path(project.namespace, project, job)
find('.js-raw-link-controller').click()
end
it 'sends the right headers' do
expect(page.status_code).to eq(200)
expect(page.response_headers['Content-Type']).to eq('text/plain; charset=utf-8')
- expect(page.response_headers['X-Sendfile']).to eq(build.trace.send(:current_path))
+ expect(page.response_headers['X-Sendfile']).to eq(job.trace.send(:current_path))
end
end
context 'job from other project' do
before do
Capybara.current_session.driver.headers = { 'X-Sendfile-Type' => 'X-Sendfile' }
- build2.run!
- visit raw_namespace_project_job_path(project.namespace, project, build2)
+ job2.run!
+ visit raw_namespace_project_job_path(project.namespace, project, job2)
end
it 'sends the right headers' do
@@ -458,16 +458,16 @@ feature 'Jobs', :feature do
before do
Capybara.current_session.driver.headers = { 'X-Sendfile-Type' => 'X-Sendfile' }
- build.run!
+ job.run!
end
- context 'when build has trace in file', :js do
+ context 'when job has trace in file', :js do
before do
allow_any_instance_of(Gitlab::Ci::Trace)
.to receive(:paths)
.and_return([existing_file])
- visit namespace_project_job_path(namespace, project, build)
+ visit namespace_project_job_path(namespace, project, job)
find('.js-raw-link-controller').click
end
@@ -485,7 +485,7 @@ feature 'Jobs', :feature do
.to receive(:paths)
.and_return([])
- visit namespace_project_job_path(namespace, project, build)
+ visit namespace_project_job_path(namespace, project, job)
end
it 'sends the right headers' do
@@ -496,7 +496,7 @@ feature 'Jobs', :feature do
context "when visiting old URL" do
let(:raw_job_url) do
- raw_namespace_project_job_path(project.namespace, project, build)
+ raw_namespace_project_job_path(project.namespace, project, job)
end
before do
@@ -512,7 +512,7 @@ feature 'Jobs', :feature do
describe "GET /:project/jobs/:id/trace.json" do
context "Job from project" do
before do
- visit trace_namespace_project_job_path(project.namespace, project, build, format: :json)
+ visit trace_namespace_project_job_path(project.namespace, project, job, format: :json)
end
it { expect(page.status_code).to eq(200) }
@@ -520,7 +520,7 @@ feature 'Jobs', :feature do
context "Job from other project" do
before do
- visit trace_namespace_project_job_path(project.namespace, project, build2, format: :json)
+ visit trace_namespace_project_job_path(project.namespace, project, job2, format: :json)
end
it { expect(page.status_code).to eq(404) }
@@ -530,7 +530,7 @@ feature 'Jobs', :feature do
describe "GET /:project/jobs/:id/status" do
context "Job from project" do
before do
- visit status_namespace_project_job_path(project.namespace, project, build)
+ visit status_namespace_project_job_path(project.namespace, project, job)
end
it { expect(page.status_code).to eq(200) }
@@ -538,7 +538,7 @@ feature 'Jobs', :feature do
context "Job from other project" do
before do
- visit status_namespace_project_job_path(project.namespace, project, build2)
+ visit status_namespace_project_job_path(project.namespace, project, job2)
end
it { expect(page.status_code).to eq(404) }
diff --git a/spec/features/projects/labels/issues_sorted_by_priority_spec.rb b/spec/features/projects/labels/issues_sorted_by_priority_spec.rb
index e2911a37e40..2c47758f30e 100644
--- a/spec/features/projects/labels/issues_sorted_by_priority_spec.rb
+++ b/spec/features/projects/labels/issues_sorted_by_priority_spec.rb
@@ -28,7 +28,7 @@ feature 'Issue prioritization', feature: true do
issue_2.labels << label_4
issue_1.labels << label_5
- login_as user
+ gitlab_sign_in user
visit namespace_project_issues_path(project.namespace, project, sort: 'label_priority')
# Ensure we are indicating that issues are sorted by priority
@@ -67,7 +67,7 @@ feature 'Issue prioritization', feature: true do
issue_4.labels << label_4 # 7
issue_6.labels << label_5 # 8 - No priority
- login_as user
+ gitlab_sign_in user
visit namespace_project_issues_path(project.namespace, project, sort: 'label_priority')
expect(page).to have_selector('.dropdown-toggle', text: 'Label priority')
diff --git a/spec/features/projects/labels/subscription_spec.rb b/spec/features/projects/labels/subscription_spec.rb
index 3130d87fba5..584dc294f05 100644
--- a/spec/features/projects/labels/subscription_spec.rb
+++ b/spec/features/projects/labels/subscription_spec.rb
@@ -10,7 +10,7 @@ feature 'Labels subscription', feature: true do
context 'when signed in' do
before do
project.team << [user, :developer]
- login_as user
+ gitlab_sign_in user
end
scenario 'users can subscribe/unsubscribe to labels', js: true do
diff --git a/spec/features/projects/labels/update_prioritization_spec.rb b/spec/features/projects/labels/update_prioritization_spec.rb
index 34fafe072a3..589bfb9fbc9 100644
--- a/spec/features/projects/labels/update_prioritization_spec.rb
+++ b/spec/features/projects/labels/update_prioritization_spec.rb
@@ -14,7 +14,7 @@ feature 'Prioritize labels', feature: true do
before do
project.team << [user, :developer]
- login_as user
+ gitlab_sign_in user
end
scenario 'user can prioritize a group label', js: true do
@@ -120,7 +120,7 @@ feature 'Prioritize labels', feature: true do
it 'does not prioritize labels' do
guest = create(:user)
- login_as guest
+ gitlab_sign_in guest
visit namespace_project_labels_path(project.namespace, project)
diff --git a/spec/features/projects/main/download_buttons_spec.rb b/spec/features/projects/main/download_buttons_spec.rb
index 02198ff3e41..514453db472 100644
--- a/spec/features/projects/main/download_buttons_spec.rb
+++ b/spec/features/projects/main/download_buttons_spec.rb
@@ -22,7 +22,7 @@ feature 'Download buttons in project main page', feature: true do
end
background do
- login_as(user)
+ gitlab_sign_in(user)
project.team << [user, role]
end
diff --git a/spec/features/projects/main/rss_spec.rb b/spec/features/projects/main/rss_spec.rb
index 53966229a2a..fee8cfe2c33 100644
--- a/spec/features/projects/main/rss_spec.rb
+++ b/spec/features/projects/main/rss_spec.rb
@@ -8,7 +8,7 @@ feature 'Project RSS' do
before do
user = create(:user)
project.team << [user, :developer]
- login_as(user)
+ gitlab_sign_in(user)
visit path
end
diff --git a/spec/features/projects/members/group_links_spec.rb b/spec/features/projects/members/group_links_spec.rb
index 3d253f01484..00d2a27597b 100644
--- a/spec/features/projects/members/group_links_spec.rb
+++ b/spec/features/projects/members/group_links_spec.rb
@@ -9,7 +9,7 @@ feature 'Projects > Members > Anonymous user sees members', feature: true, js: t
project.team << [user, :master]
@group_link = create(:project_group_link, project: project, group: group)
- login_as(user)
+ gitlab_sign_in(user)
visit namespace_project_settings_members_path(project.namespace, project)
end
diff --git a/spec/features/projects/members/group_member_cannot_leave_group_project_spec.rb b/spec/features/projects/members/group_member_cannot_leave_group_project_spec.rb
index b483ba4c54c..7e71dbc24c0 100644
--- a/spec/features/projects/members/group_member_cannot_leave_group_project_spec.rb
+++ b/spec/features/projects/members/group_member_cannot_leave_group_project_spec.rb
@@ -7,7 +7,7 @@ feature 'Projects > Members > Group member cannot leave group project', feature:
background do
group.add_developer(user)
- login_as(user)
+ gitlab_sign_in(user)
visit namespace_project_path(project.namespace, project)
end
diff --git a/spec/features/projects/members/group_member_cannot_request_access_to_his_group_project_spec.rb b/spec/features/projects/members/group_member_cannot_request_access_to_his_group_project_spec.rb
index ff9b6007806..60a5cd9ec63 100644
--- a/spec/features/projects/members/group_member_cannot_request_access_to_his_group_project_spec.rb
+++ b/spec/features/projects/members/group_member_cannot_request_access_to_his_group_project_spec.rb
@@ -41,7 +41,7 @@ feature 'Projects > Members > Group member cannot request access to his group pr
end
def login_and_visit_project_page(user)
- login_as(user)
+ gitlab_sign_in(user)
visit namespace_project_path(project.namespace, project)
end
end
diff --git a/spec/features/projects/members/group_members_spec.rb b/spec/features/projects/members/group_members_spec.rb
index 3385e5972ff..76fe6a00dab 100644
--- a/spec/features/projects/members/group_members_spec.rb
+++ b/spec/features/projects/members/group_members_spec.rb
@@ -13,7 +13,7 @@ feature 'Projects members', feature: true do
background do
project.team << [developer, :developer]
group.add_owner(user)
- login_as(user)
+ gitlab_sign_in(user)
end
context 'with a group invitee' do
diff --git a/spec/features/projects/members/group_requester_cannot_request_access_to_project_spec.rb b/spec/features/projects/members/group_requester_cannot_request_access_to_project_spec.rb
index bdeeef57273..66da28b07fe 100644
--- a/spec/features/projects/members/group_requester_cannot_request_access_to_project_spec.rb
+++ b/spec/features/projects/members/group_requester_cannot_request_access_to_project_spec.rb
@@ -8,7 +8,7 @@ feature 'Projects > Members > Group requester cannot request access to project',
background do
group.add_owner(owner)
- login_as(user)
+ gitlab_sign_in(user)
visit group_path(group)
perform_enqueued_jobs { click_link 'Request Access' }
visit namespace_project_path(project.namespace, project)
diff --git a/spec/features/projects/members/list_spec.rb b/spec/features/projects/members/list_spec.rb
index deea34214fb..9fdd7df0ee5 100644
--- a/spec/features/projects/members/list_spec.rb
+++ b/spec/features/projects/members/list_spec.rb
@@ -9,7 +9,7 @@ feature 'Project members list', feature: true do
let(:project) { create(:project, namespace: group) }
background do
- login_as(user1)
+ gitlab_sign_in(user1)
group.add_owner(user1)
end
diff --git a/spec/features/projects/members/master_adds_member_with_expiration_date_spec.rb b/spec/features/projects/members/master_adds_member_with_expiration_date_spec.rb
index 1e6f15d8258..21b48b7fdd1 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
@@ -10,7 +10,7 @@ feature 'Projects > Members > Master adds member with expiration date', feature:
background do
project.team << [master, :master]
- login_as(master)
+ gitlab_sign_in(master)
end
scenario 'expiration date is displayed in the members list' do
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 143390b71cd..bd445e27243 100644
--- a/spec/features/projects/members/master_manages_access_requests_spec.rb
+++ b/spec/features/projects/members/master_manages_access_requests_spec.rb
@@ -8,7 +8,7 @@ feature 'Projects > Members > Master manages access requests', feature: true do
background do
project.request_access(user)
project.team << [master, :master]
- login_as(master)
+ gitlab_sign_in(master)
end
scenario 'master can see access requests' do
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 9564347e733..703f5dff6b5 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
@@ -6,7 +6,7 @@ feature 'Projects > Members > Member cannot request access to his project', feat
background do
project.team << [member, :developer]
- login_as(member)
+ gitlab_sign_in(member)
visit namespace_project_path(project.namespace, 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 5daa932e4e6..8e1788f7f2a 100644
--- a/spec/features/projects/members/member_leaves_project_spec.rb
+++ b/spec/features/projects/members/member_leaves_project_spec.rb
@@ -6,7 +6,7 @@ feature 'Projects > Members > Member leaves project', feature: true do
background do
project.team << [user, :developer]
- login_as(user)
+ gitlab_sign_in(user)
visit namespace_project_path(project.namespace, project)
end
diff --git a/spec/features/projects/members/owner_cannot_leave_project_spec.rb b/spec/features/projects/members/owner_cannot_leave_project_spec.rb
index b26d55c5d5d..70e4bb19c0f 100644
--- a/spec/features/projects/members/owner_cannot_leave_project_spec.rb
+++ b/spec/features/projects/members/owner_cannot_leave_project_spec.rb
@@ -4,7 +4,7 @@ feature 'Projects > Members > Owner cannot leave project', feature: true do
let(:project) { create(:project) }
background do
- login_as(project.owner)
+ gitlab_sign_in(project.owner)
visit namespace_project_path(project.namespace, project)
end
diff --git a/spec/features/projects/members/owner_cannot_request_access_to_his_project_spec.rb b/spec/features/projects/members/owner_cannot_request_access_to_his_project_spec.rb
index 4ca9272b9c1..0cd7e3afeda 100644
--- a/spec/features/projects/members/owner_cannot_request_access_to_his_project_spec.rb
+++ b/spec/features/projects/members/owner_cannot_request_access_to_his_project_spec.rb
@@ -4,7 +4,7 @@ feature 'Projects > Members > Owner cannot request access to his project', featu
let(:project) { create(:project) }
background do
- login_as(project.owner)
+ gitlab_sign_in(project.owner)
visit namespace_project_path(project.namespace, project)
end
diff --git a/spec/features/projects/members/sorting_spec.rb b/spec/features/projects/members/sorting_spec.rb
index d428f6fcf22..66d98ef8b90 100644
--- a/spec/features/projects/members/sorting_spec.rb
+++ b/spec/features/projects/members/sorting_spec.rb
@@ -8,7 +8,7 @@ feature 'Projects > Members > Sorting', feature: true do
background do
create(:project_member, :developer, user: developer, project: project, created_at: 3.days.ago)
- login_as(master)
+ gitlab_sign_in(master)
end
scenario 'sorts alphabetically by default' do
diff --git a/spec/features/projects/members/user_requests_access_spec.rb b/spec/features/projects/members/user_requests_access_spec.rb
index ec48a4bd726..081009f2325 100644
--- a/spec/features/projects/members/user_requests_access_spec.rb
+++ b/spec/features/projects/members/user_requests_access_spec.rb
@@ -6,7 +6,7 @@ feature 'Projects > Members > User requests access', feature: true do
let(:master) { project.owner }
background do
- login_as(user)
+ gitlab_sign_in(user)
visit namespace_project_path(project.namespace, project)
end
diff --git a/spec/features/projects/merge_request_button_spec.rb b/spec/features/projects/merge_request_button_spec.rb
index 1370ab1c521..6de8855016d 100644
--- a/spec/features/projects/merge_request_button_spec.rb
+++ b/spec/features/projects/merge_request_button_spec.rb
@@ -18,7 +18,7 @@ feature 'Merge Request button', feature: true do
context 'logged in as developer' do
before do
- login_as(user)
+ gitlab_sign_in(user)
project.team << [user, :developer]
end
@@ -52,7 +52,7 @@ feature 'Merge Request button', feature: true do
context 'logged in as non-member' do
before do
- login_as(user)
+ gitlab_sign_in(user)
end
it 'does not show Create merge request button' do
diff --git a/spec/features/projects/merge_requests/list_spec.rb b/spec/features/projects/merge_requests/list_spec.rb
index 7e8a796c55d..f2a2fd0311f 100644
--- a/spec/features/projects/merge_requests/list_spec.rb
+++ b/spec/features/projects/merge_requests/list_spec.rb
@@ -7,7 +7,7 @@ feature 'Merge Requests List' do
background do
project.team << [user, :developer]
- login_as(user)
+ gitlab_sign_in(user)
end
scenario 'user does not see create new list button' do
diff --git a/spec/features/projects/milestones/milestone_spec.rb b/spec/features/projects/milestones/milestone_spec.rb
index b4fc0edbde8..a02e4118784 100644
--- a/spec/features/projects/milestones/milestone_spec.rb
+++ b/spec/features/projects/milestones/milestone_spec.rb
@@ -6,7 +6,7 @@ feature 'Project milestone', :feature do
let(:milestone) { create(:milestone, project: project) }
before do
- login_as(user)
+ gitlab_sign_in(user)
end
context 'when project has enabled issues' do
diff --git a/spec/features/projects/milestones/milestones_sorting_spec.rb b/spec/features/projects/milestones/milestones_sorting_spec.rb
index da3eaed707a..2350089255d 100644
--- a/spec/features/projects/milestones/milestones_sorting_spec.rb
+++ b/spec/features/projects/milestones/milestones_sorting_spec.rb
@@ -15,7 +15,7 @@ feature 'Milestones sorting', :feature, :js do
due_date: 11.days.from_now,
created_at: 1.hour.ago,
title: "bbb", project: project)
- login_as(user)
+ gitlab_sign_in(user)
end
scenario 'visit project milestones and sort by due_date_asc' do
diff --git a/spec/features/projects/milestones/new_spec.rb b/spec/features/projects/milestones/new_spec.rb
new file mode 100644
index 00000000000..7403822c7fb
--- /dev/null
+++ b/spec/features/projects/milestones/new_spec.rb
@@ -0,0 +1,18 @@
+require 'spec_helper'
+
+feature 'Creating a new project milestone', :feature, :js do
+ let(:user) { create(:user) }
+ let(:project) { create(:empty_project, name: 'test', namespace: user.namespace) }
+
+ before do
+ login_as(user)
+ visit new_namespace_project_milestone_path(project.namespace, project)
+ end
+
+ it 'description has autocomplete' do
+ find('#milestone_description').native.send_keys('')
+ fill_in 'milestone_description', with: '@'
+
+ expect(page).to have_selector('.atwho-view')
+ end
+end
diff --git a/spec/features/projects/new_project_spec.rb b/spec/features/projects/new_project_spec.rb
index b1f9eb15667..37d9a97033b 100644
--- a/spec/features/projects/new_project_spec.rb
+++ b/spec/features/projects/new_project_spec.rb
@@ -4,7 +4,7 @@ feature "New project", feature: true do
let(:user) { create(:admin) }
before do
- login_as(user)
+ gitlab_sign_in(user)
end
context "Visibility level selector" do
diff --git a/spec/features/projects/pages_spec.rb b/spec/features/projects/pages_spec.rb
index 11793c0f303..e9a3cfb7f60 100644
--- a/spec/features/projects/pages_spec.rb
+++ b/spec/features/projects/pages_spec.rb
@@ -10,7 +10,7 @@ feature 'Pages', feature: true do
project.team << [user, role]
- login_as(user)
+ gitlab_sign_in(user)
end
shared_examples 'no pages deployed' do
diff --git a/spec/features/projects/pipeline_schedules_spec.rb b/spec/features/projects/pipeline_schedules_spec.rb
index 317949d6b56..dfb973c37e5 100644
--- a/spec/features/projects/pipeline_schedules_spec.rb
+++ b/spec/features/projects/pipeline_schedules_spec.rb
@@ -12,7 +12,7 @@ feature 'Pipeline Schedules', :feature do
before do
project.add_master(user)
- login_as(user)
+ gitlab_sign_in(user)
visit_page
end
@@ -127,7 +127,7 @@ feature 'Pipeline Schedules', :feature do
end
it 'shows the pipeline schedule with default ref' do
- page.within('.git-revision-dropdown-toggle') do
+ page.within('.js-target-branch-dropdown') do
expect(first('.dropdown-toggle-text').text).to eq('master')
end
end
diff --git a/spec/features/projects/pipelines/pipeline_spec.rb b/spec/features/projects/pipelines/pipeline_spec.rb
index 36a3ddca6ef..e182995922d 100644
--- a/spec/features/projects/pipelines/pipeline_spec.rb
+++ b/spec/features/projects/pipelines/pipeline_spec.rb
@@ -7,7 +7,7 @@ describe 'Pipeline', :feature, :js do
let(:user) { create(:user) }
before do
- login_as(user)
+ gitlab_sign_in(user)
project.team << [user, :developer]
end
@@ -47,7 +47,9 @@ describe 'Pipeline', :feature, :js do
let(:project) { create(:project) }
let(:pipeline) { create(:ci_pipeline, project: project, ref: 'master', sha: project.commit.id, user: user) }
- before { visit namespace_project_pipeline_path(project.namespace, project, pipeline) }
+ before do
+ visit namespace_project_pipeline_path(project.namespace, project, pipeline)
+ end
it 'shows the pipeline graph' do
expect(page).to have_selector('.pipeline-visualization')
@@ -164,7 +166,9 @@ describe 'Pipeline', :feature, :js do
it { expect(page).not_to have_content('retried') }
context 'when retrying' do
- before { find('.js-retry-button').trigger('click') }
+ before do
+ find('.js-retry-button').trigger('click')
+ end
it { expect(page).not_to have_content('Retry') }
end
@@ -174,7 +178,9 @@ describe 'Pipeline', :feature, :js do
it { expect(page).not_to have_selector('.ci-canceled') }
context 'when canceling' do
- before { click_on 'Cancel running' }
+ before do
+ click_on 'Cancel running'
+ end
it { expect(page).not_to have_content('Cancel running') }
end
@@ -226,7 +232,9 @@ describe 'Pipeline', :feature, :js do
it { expect(page).not_to have_content('retried') }
context 'when retrying' do
- before { find('.js-retry-button').trigger('click') }
+ before do
+ find('.js-retry-button').trigger('click')
+ end
it { expect(page).not_to have_content('Retry') }
end
@@ -236,7 +244,9 @@ describe 'Pipeline', :feature, :js do
it { expect(page).not_to have_selector('.ci-canceled') }
context 'when canceling' do
- before { click_on 'Cancel running' }
+ before do
+ click_on 'Cancel running'
+ end
it { expect(page).not_to have_content('Cancel running') }
end
diff --git a/spec/features/projects/pipelines/pipelines_spec.rb b/spec/features/projects/pipelines/pipelines_spec.rb
index 05c2bf350f1..d36d073e022 100644
--- a/spec/features/projects/pipelines/pipelines_spec.rb
+++ b/spec/features/projects/pipelines/pipelines_spec.rb
@@ -7,7 +7,7 @@ describe 'Pipelines', :feature, :js do
let(:user) { create(:user) }
before do
- login_as(user)
+ gitlab_sign_in(user)
project.team << [user, :developer]
end
@@ -149,7 +149,9 @@ describe 'Pipelines', :feature, :js do
create(:ci_pipeline, :invalid, project: project)
end
- before { visit_project_pipelines }
+ before do
+ visit_project_pipelines
+ end
it 'contains badge that indicates errors' do
expect(page).to have_content 'yaml invalid'
@@ -171,10 +173,12 @@ describe 'Pipelines', :feature, :js do
commands: 'test')
end
- before { visit_project_pipelines }
+ before do
+ visit_project_pipelines
+ end
it 'has a dropdown with play button' do
- expect(page).to have_selector('.dropdown-toggle.btn.btn-default .icon-play')
+ expect(page).to have_selector('.dropdown-new.btn.btn-default .icon-play')
end
it 'has link to the manual action' do
@@ -204,7 +208,9 @@ describe 'Pipelines', :feature, :js do
stage: 'test')
end
- before { visit_project_pipelines }
+ before do
+ visit_project_pipelines
+ end
it 'is cancelable' do
expect(page).to have_selector('.js-pipelines-cancel-button')
@@ -215,7 +221,9 @@ describe 'Pipelines', :feature, :js do
end
context 'when canceling' do
- before { find('.js-pipelines-cancel-button').trigger('click') }
+ before do
+ find('.js-pipelines-cancel-button').trigger('click')
+ end
it 'indicates that pipeline was canceled' do
expect(page).not_to have_selector('.js-pipelines-cancel-button')
@@ -255,7 +263,9 @@ describe 'Pipelines', :feature, :js do
stage: 'test')
end
- before { visit_project_pipelines }
+ before do
+ visit_project_pipelines
+ end
it 'has artifats' do
expect(page).to have_selector('.build-artifacts')
@@ -284,7 +294,9 @@ describe 'Pipelines', :feature, :js do
stage: 'test')
end
- before { visit_project_pipelines }
+ before do
+ visit_project_pipelines
+ end
it { expect(page).not_to have_selector('.build-artifacts') }
end
@@ -297,7 +309,9 @@ describe 'Pipelines', :feature, :js do
stage: 'test')
end
- before { visit_project_pipelines }
+ before do
+ visit_project_pipelines
+ end
it { expect(page).not_to have_selector('.build-artifacts') }
end
@@ -310,7 +324,9 @@ describe 'Pipelines', :feature, :js do
name: 'build')
end
- before { visit_project_pipelines }
+ before do
+ visit_project_pipelines
+ end
it 'should render a mini pipeline graph' do
expect(page).to have_selector('.js-mini-pipeline-graph')
@@ -437,7 +453,9 @@ describe 'Pipelines', :feature, :js do
end
context 'with gitlab-ci.yml' do
- before { stub_ci_pipeline_to_return_yaml_file }
+ before do
+ stub_ci_pipeline_to_return_yaml_file
+ end
it 'creates a new pipeline' do
expect { click_on 'Create pipeline' }
@@ -448,7 +466,9 @@ describe 'Pipelines', :feature, :js do
end
context 'without gitlab-ci.yml' do
- before { click_on 'Create pipeline' }
+ before do
+ click_on 'Create pipeline'
+ end
it { expect(page).to have_content('Missing .gitlab-ci.yml file') }
end
diff --git a/spec/features/projects/project_settings_spec.rb b/spec/features/projects/project_settings_spec.rb
index 11dcab4d737..baa38ff8cca 100644
--- a/spec/features/projects/project_settings_spec.rb
+++ b/spec/features/projects/project_settings_spec.rb
@@ -7,7 +7,7 @@ describe 'Edit Project Settings', feature: true do
let(:project) { create(:empty_project, namespace: user.namespace, path: 'gitlab', name: 'sample') }
before do
- login_as(user)
+ gitlab_sign_in(user)
end
describe 'Project settings section', js: true do
@@ -58,8 +58,13 @@ describe 'Edit Project Settings', feature: true do
# Not using empty project because we need a repo to exist
let(:project) { create(:project, namespace: user.namespace, name: 'gitlabhq') }
- before(:context) { TestEnv.clean_test_path }
- after(:example) { TestEnv.clean_test_path }
+ before(:context) do
+ TestEnv.clean_test_path
+ end
+
+ after(:example) do
+ TestEnv.clean_test_path
+ end
specify 'the project is accessible via the new path' do
rename_project(project, path: 'bar')
@@ -96,9 +101,17 @@ describe 'Edit Project Settings', feature: true do
let!(:project) { create(:project, namespace: user.namespace, name: 'gitlabhq') }
let!(:group) { create(:group) }
- before(:context) { TestEnv.clean_test_path }
- before(:example) { group.add_owner(user) }
- after(:example) { TestEnv.clean_test_path }
+ before(:context) do
+ TestEnv.clean_test_path
+ end
+
+ before(:example) do
+ group.add_owner(user)
+ end
+
+ after(:example) do
+ TestEnv.clean_test_path
+ end
specify 'the project is accessible via the new path' do
transfer_project(project, group)
diff --git a/spec/features/projects/ref_switcher_spec.rb b/spec/features/projects/ref_switcher_spec.rb
index 04414490571..016a992bdcf 100644
--- a/spec/features/projects/ref_switcher_spec.rb
+++ b/spec/features/projects/ref_switcher_spec.rb
@@ -6,7 +6,7 @@ feature 'Ref switcher', feature: true, js: true do
before do
project.team << [user, :master]
- login_as(user)
+ gitlab_sign_in(user)
visit namespace_project_tree_path(project.namespace, project, 'master')
end
diff --git a/spec/features/projects/services/jira_service_spec.rb b/spec/features/projects/services/jira_service_spec.rb
index c96d87e5708..2ea50e8f672 100644
--- a/spec/features/projects/services/jira_service_spec.rb
+++ b/spec/features/projects/services/jira_service_spec.rb
@@ -20,7 +20,7 @@ feature 'Setup Jira service', :feature, :js do
before do
project.team << [user, :master]
- login_as(user)
+ gitlab_sign_in(user)
visit namespace_project_settings_integrations_path(project.namespace, project)
end
diff --git a/spec/features/projects/services/mattermost_slash_command_spec.rb b/spec/features/projects/services/mattermost_slash_command_spec.rb
index 1fe82222e59..d87985f1c92 100644
--- a/spec/features/projects/services/mattermost_slash_command_spec.rb
+++ b/spec/features/projects/services/mattermost_slash_command_spec.rb
@@ -9,7 +9,7 @@ feature 'Setup Mattermost slash commands', :feature, :js do
before do
stub_mattermost_setting(enabled: mattermost_enabled)
project.team << [user, :master]
- login_as(user)
+ gitlab_sign_in(user)
visit edit_namespace_project_service_path(project.namespace, project, service)
end
diff --git a/spec/features/projects/services/slack_service_spec.rb b/spec/features/projects/services/slack_service_spec.rb
index c0a4a1e4bf5..50707e6a49f 100644
--- a/spec/features/projects/services/slack_service_spec.rb
+++ b/spec/features/projects/services/slack_service_spec.rb
@@ -9,7 +9,7 @@ feature 'Projects > Slack service > Setup events', feature: true do
service.fields
service.update_attributes(push_channel: 1, issue_channel: 2, merge_request_channel: 3, note_channel: 4, tag_push_channel: 5, pipeline_channel: 6, wiki_page_channel: 7)
project.team << [user, :master]
- login_as(user)
+ gitlab_sign_in(user)
end
scenario 'user can filter events by channel' do
diff --git a/spec/features/projects/services/slack_slash_command_spec.rb b/spec/features/projects/services/slack_slash_command_spec.rb
index f53b820c460..3fae38c1799 100644
--- a/spec/features/projects/services/slack_slash_command_spec.rb
+++ b/spec/features/projects/services/slack_slash_command_spec.rb
@@ -7,7 +7,7 @@ feature 'Slack slash commands', feature: true do
background do
project.team << [user, :master]
- login_as(user)
+ gitlab_sign_in(user)
visit edit_namespace_project_service_path(project.namespace, project, service)
end
diff --git a/spec/features/projects/settings/integration_settings_spec.rb b/spec/features/projects/settings/integration_settings_spec.rb
index fbaea14a2be..a59374b37ea 100644
--- a/spec/features/projects/settings/integration_settings_spec.rb
+++ b/spec/features/projects/settings/integration_settings_spec.rb
@@ -7,7 +7,7 @@ feature 'Integration settings', feature: true do
let(:integrations_path) { namespace_project_settings_integrations_path(project.namespace, project) }
background do
- login_as(user)
+ gitlab_sign_in(user)
project.team << [user, role]
end
diff --git a/spec/features/projects/settings/merge_requests_settings_spec.rb b/spec/features/projects/settings/merge_requests_settings_spec.rb
index 321af416c91..f2af14ceab2 100644
--- a/spec/features/projects/settings/merge_requests_settings_spec.rb
+++ b/spec/features/projects/settings/merge_requests_settings_spec.rb
@@ -8,7 +8,7 @@ feature 'Project settings > Merge Requests', feature: true, js: true do
background do
project.team << [user, :master]
- login_as(user)
+ gitlab_sign_in(user)
end
context 'when Merge Request and Pipelines are initially enabled' do
diff --git a/spec/features/projects/settings/pipelines_settings_spec.rb b/spec/features/projects/settings/pipelines_settings_spec.rb
index 035c57eaa47..c33fbd49d21 100644
--- a/spec/features/projects/settings/pipelines_settings_spec.rb
+++ b/spec/features/projects/settings/pipelines_settings_spec.rb
@@ -8,7 +8,7 @@ feature "Pipelines settings", feature: true do
let(:role) { :developer }
background do
- login_as(user)
+ gitlab_sign_in(user)
project.team << [user, role]
visit namespace_project_pipelines_settings_path(project.namespace, project)
end
diff --git a/spec/features/projects/settings/repository_settings_spec.rb b/spec/features/projects/settings/repository_settings_spec.rb
index 4cc38c5286e..2956ef73746 100644
--- a/spec/features/projects/settings/repository_settings_spec.rb
+++ b/spec/features/projects/settings/repository_settings_spec.rb
@@ -7,7 +7,7 @@ feature 'Repository settings', feature: true do
background do
project.team << [user, role]
- login_as(user)
+ gitlab_sign_in(user)
end
context 'for developer' do
diff --git a/spec/features/projects/settings/visibility_settings_spec.rb b/spec/features/projects/settings/visibility_settings_spec.rb
index fac4506bdf6..18c71dee41b 100644
--- a/spec/features/projects/settings/visibility_settings_spec.rb
+++ b/spec/features/projects/settings/visibility_settings_spec.rb
@@ -6,7 +6,7 @@ feature 'Visibility settings', feature: true, js: true do
context 'as owner' do
before do
- login_as(user)
+ gitlab_sign_in(user)
visit edit_namespace_project_path(project.namespace, project)
end
@@ -32,7 +32,7 @@ feature 'Visibility settings', feature: true, js: true do
before do
project.team << [master_user, :master]
- login_as(master_user)
+ gitlab_sign_in(master_user)
visit edit_namespace_project_path(project.namespace, project)
end
diff --git a/spec/features/projects/shortcuts_spec.rb b/spec/features/projects/shortcuts_spec.rb
index 54aa9c66a08..cec79277c33 100644
--- a/spec/features/projects/shortcuts_spec.rb
+++ b/spec/features/projects/shortcuts_spec.rb
@@ -7,7 +7,7 @@ feature 'Project shortcuts', feature: true do
describe 'On a project', js: true do
before do
project.team << [user, :master]
- login_as user
+ gitlab_sign_in user
visit namespace_project_path(project.namespace, project)
end
diff --git a/spec/features/projects/snippets/create_snippet_spec.rb b/spec/features/projects/snippets/create_snippet_spec.rb
index 5ac1ca45c74..c75d6dbc307 100644
--- a/spec/features/projects/snippets/create_snippet_spec.rb
+++ b/spec/features/projects/snippets/create_snippet_spec.rb
@@ -17,7 +17,7 @@ feature 'Create Snippet', :js, feature: true do
context 'when a user is authenticated' do
before do
project.team << [user, :master]
- login_as(user)
+ gitlab_sign_in(user)
visit namespace_project_snippets_path(project.namespace, project)
diff --git a/spec/features/projects/snippets/show_spec.rb b/spec/features/projects/snippets/show_spec.rb
index b844e60e5d5..9e73ba4123b 100644
--- a/spec/features/projects/snippets/show_spec.rb
+++ b/spec/features/projects/snippets/show_spec.rb
@@ -7,7 +7,7 @@ feature 'Project snippet', :js, feature: true do
before do
project.team << [user, :master]
- login_as(user)
+ gitlab_sign_in(user)
end
context 'Ruby file' do
diff --git a/spec/features/projects/snippets_spec.rb b/spec/features/projects/snippets_spec.rb
index 18689c17fe9..80dbffaffc7 100644
--- a/spec/features/projects/snippets_spec.rb
+++ b/spec/features/projects/snippets_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe 'Project snippets', feature: true do
+describe 'Project snippets', :js, feature: true do
context 'when the project has snippets' do
let(:project) { create(:empty_project, :public) }
let!(:snippets) { create_list(:project_snippet, 2, :public, author: project.owner, project: project) }
@@ -26,5 +26,19 @@ describe 'Project snippets', feature: true do
expect(page).to have_content(snippets[1].title)
end
end
+
+ context 'when submitting a note' do
+ before do
+ gitlab_sign_in :admin
+ visit namespace_project_snippet_path(project.namespace, project, snippets[0])
+ end
+
+ it 'should have autocomplete' do
+ find('#note_note').native.send_keys('')
+ fill_in 'note[note]', with: '@'
+
+ expect(page).to have_selector('.atwho-view')
+ end
+ end
end
end
diff --git a/spec/features/projects/sub_group_issuables_spec.rb b/spec/features/projects/sub_group_issuables_spec.rb
index e88907b8016..63eb97d5a92 100644
--- a/spec/features/projects/sub_group_issuables_spec.rb
+++ b/spec/features/projects/sub_group_issuables_spec.rb
@@ -8,7 +8,7 @@ describe 'Subgroup Issuables', :feature, :js, :nested_groups do
before do
project.add_master(user)
- login_as user
+ gitlab_sign_in user
end
it 'shows the full subgroup title when issues index page is empty' do
diff --git a/spec/features/projects/tags/download_buttons_spec.rb b/spec/features/projects/tags/download_buttons_spec.rb
index dd93d25c2c6..ca00a51aa3c 100644
--- a/spec/features/projects/tags/download_buttons_spec.rb
+++ b/spec/features/projects/tags/download_buttons_spec.rb
@@ -23,7 +23,7 @@ feature 'Download buttons in tags page', feature: true do
end
background do
- login_as(user)
+ gitlab_sign_in(user)
project.team << [user, role]
end
diff --git a/spec/features/projects/tree/rss_spec.rb b/spec/features/projects/tree/rss_spec.rb
index 9bf59c4139c..135584e5bf8 100644
--- a/spec/features/projects/tree/rss_spec.rb
+++ b/spec/features/projects/tree/rss_spec.rb
@@ -8,7 +8,7 @@ feature 'Project Tree RSS' do
before do
user = create(:user)
project.team << [user, :developer]
- login_as(user)
+ gitlab_sign_in(user)
visit path
end
diff --git a/spec/features/projects/user_create_dir_spec.rb b/spec/features/projects/user_create_dir_spec.rb
index aeb7e0b7c33..f375e1215db 100644
--- a/spec/features/projects/user_create_dir_spec.rb
+++ b/spec/features/projects/user_create_dir_spec.rb
@@ -6,7 +6,7 @@ feature 'New directory creation', feature: true, js: true do
given(:project) { create(:project) }
background do
- login_as(user)
+ gitlab_sign_in(user)
project.team << [user, role]
visit namespace_project_tree_path(project.namespace, project, 'master')
open_new_directory_modal
diff --git a/spec/features/projects/view_on_env_spec.rb b/spec/features/projects/view_on_env_spec.rb
index 640f1376548..f6a640b90b4 100644
--- a/spec/features/projects/view_on_env_spec.rb
+++ b/spec/features/projects/view_on_env_spec.rb
@@ -50,7 +50,7 @@ describe 'View on environment', js: true do
let(:merge_request) { create(:merge_request, :simple, source_project: project, source_branch: branch_name) }
before do
- login_as(user)
+ gitlab_sign_in(user)
visit diffs_namespace_project_merge_request_path(project.namespace, project, merge_request)
@@ -66,7 +66,7 @@ describe 'View on environment', js: true do
context 'when visiting a comparison for the branch' do
before do
- login_as(user)
+ gitlab_sign_in(user)
visit namespace_project_compare_path(project.namespace, project, from: 'master', to: branch_name)
@@ -80,7 +80,7 @@ describe 'View on environment', js: true do
context 'when visiting a comparison for the commit' do
before do
- login_as(user)
+ gitlab_sign_in(user)
visit namespace_project_compare_path(project.namespace, project, from: 'master', to: sha)
@@ -94,7 +94,7 @@ describe 'View on environment', js: true do
context 'when visiting a blob on the branch' do
before do
- login_as(user)
+ gitlab_sign_in(user)
visit namespace_project_blob_path(project.namespace, project, File.join(branch_name, file_path))
@@ -108,7 +108,7 @@ describe 'View on environment', js: true do
context 'when visiting a blob on the commit' do
before do
- login_as(user)
+ gitlab_sign_in(user)
visit namespace_project_blob_path(project.namespace, project, File.join(sha, file_path))
@@ -122,7 +122,7 @@ describe 'View on environment', js: true do
context 'when visiting the commit' do
before do
- login_as(user)
+ gitlab_sign_in(user)
visit namespace_project_commit_path(project.namespace, project, sha)
diff --git a/spec/features/projects/wiki/markdown_preview_spec.rb b/spec/features/projects/wiki/markdown_preview_spec.rb
index 94f6bb16730..fd6c09943e3 100644
--- a/spec/features/projects/wiki/markdown_preview_spec.rb
+++ b/spec/features/projects/wiki/markdown_preview_spec.rb
@@ -16,7 +16,7 @@ feature 'Projects > Wiki > User previews markdown changes', feature: true, js: t
project.team << [user, :master]
WikiPages::CreateService.new(project, user, title: 'home', content: 'Home page').execute
- login_as(user)
+ gitlab_sign_in(user)
visit namespace_project_path(project.namespace, project)
find('.shortcuts-wiki').trigger('click')
diff --git a/spec/features/projects/wiki/shortcuts_spec.rb b/spec/features/projects/wiki/shortcuts_spec.rb
index c1f6b0cce3b..ab0ed9b8204 100644
--- a/spec/features/projects/wiki/shortcuts_spec.rb
+++ b/spec/features/projects/wiki/shortcuts_spec.rb
@@ -8,7 +8,7 @@ feature 'Wiki shortcuts', :feature, :js do
end
before do
- login_as(user)
+ gitlab_sign_in(user)
visit namespace_project_wiki_path(project.namespace, project, wiki_page)
end
diff --git a/spec/features/projects/wiki/user_creates_wiki_page_spec.rb b/spec/features/projects/wiki/user_creates_wiki_page_spec.rb
index 8912d575878..a477dcf7ee9 100644
--- a/spec/features/projects/wiki/user_creates_wiki_page_spec.rb
+++ b/spec/features/projects/wiki/user_creates_wiki_page_spec.rb
@@ -5,7 +5,7 @@ feature 'Projects > Wiki > User creates wiki page', js: true, feature: true do
background do
project.team << [user, :master]
- login_as(user)
+ gitlab_sign_in(user)
visit namespace_project_path(project.namespace, project)
find('.shortcuts-wiki').trigger('click')
@@ -133,6 +133,22 @@ feature 'Projects > Wiki > User creates wiki page', js: true, feature: true do
expect(page).to have_content('My awesome wiki!')
end
end
+
+ scenario 'content has autocomplete', :js do
+ click_link 'New page'
+
+ page.within '#modal-new-wiki' do
+ fill_in :new_wiki_path, with: 'test-autocomplete'
+ click_button 'Create page'
+ end
+
+ page.within '.wiki-form' do
+ find('#wiki_content').native.send_keys('')
+ fill_in :wiki_content, with: '@'
+ end
+
+ expect(page).to have_selector('.atwho-view')
+ end
end
end
diff --git a/spec/features/projects/wiki/user_git_access_wiki_page_spec.rb b/spec/features/projects/wiki/user_git_access_wiki_page_spec.rb
index 95826e7e5be..7d31122af35 100644
--- a/spec/features/projects/wiki/user_git_access_wiki_page_spec.rb
+++ b/spec/features/projects/wiki/user_git_access_wiki_page_spec.rb
@@ -13,7 +13,7 @@ describe 'Projects > Wiki > User views Git access wiki page', :feature do
end
before do
- login_as(user)
+ gitlab_sign_in(user)
end
scenario 'Visit Wiki Page Current Commit' do
diff --git a/spec/features/projects/wiki/user_updates_wiki_page_spec.rb b/spec/features/projects/wiki/user_updates_wiki_page_spec.rb
index 86cf520ea80..64a30438681 100644
--- a/spec/features/projects/wiki/user_updates_wiki_page_spec.rb
+++ b/spec/features/projects/wiki/user_updates_wiki_page_spec.rb
@@ -5,11 +5,10 @@ feature 'Projects > Wiki > User updates wiki page', feature: true do
background do
project.team << [user, :master]
- login_as(user)
-
- visit namespace_project_path(project.namespace, project)
WikiPages::CreateService.new(project, user, title: 'home', content: 'Home page').execute
- click_link 'Wiki'
+ gitlab_sign_in(user)
+
+ visit namespace_project_wikis_path(project.namespace, project)
end
context 'in the user namespace' do
@@ -42,6 +41,15 @@ feature 'Projects > Wiki > User updates wiki page', feature: true do
expect(page).to have_content('Content can\'t be blank')
expect(find('textarea#wiki_content').value).to eq ''
end
+
+ scenario 'content has autocomplete', :js do
+ click_link 'Edit'
+
+ find('#wiki_content').native.send_keys('')
+ fill_in :wiki_content, with: '@'
+
+ expect(page).to have_selector('.atwho-view')
+ end
end
end
diff --git a/spec/features/projects/wiki/user_views_project_wiki_page_spec.rb b/spec/features/projects/wiki/user_views_project_wiki_page_spec.rb
index c17e06612de..8a88ab247f3 100644
--- a/spec/features/projects/wiki/user_views_project_wiki_page_spec.rb
+++ b/spec/features/projects/wiki/user_views_project_wiki_page_spec.rb
@@ -15,7 +15,7 @@ feature 'Projects > Wiki > User views the wiki page', feature: true do
background do
project.team << [user, :master]
- login_as(user)
+ gitlab_sign_in(user)
WikiPages::UpdateService.new(
project,
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 20219f3cc9a..36799925167 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
@@ -5,7 +5,7 @@ describe 'Projects > Wiki > User views wiki in project page', feature: true do
before do
project.team << [user, :master]
- login_as(user)
+ gitlab_sign_in(user)
end
context 'when repository is disabled for project' do
diff --git a/spec/features/projects_spec.rb b/spec/features/projects_spec.rb
index 060e19596ae..7e8a703db93 100644
--- a/spec/features/projects_spec.rb
+++ b/spec/features/projects_spec.rb
@@ -6,7 +6,7 @@ feature 'Project', feature: true do
let(:path) { namespace_project_path(project.namespace, project) }
before do
- login_as(:admin)
+ gitlab_sign_in(:admin)
end
it 'parses Markdown' do
@@ -39,7 +39,7 @@ feature 'Project', feature: true do
let(:project) { create(:empty_project, namespace: user.namespace) }
before do
- login_with user
+ gitlab_sign_in user
create(:forked_project_link, forked_to_project: project)
visit edit_namespace_project_path(project.namespace, project)
end
@@ -60,7 +60,7 @@ feature 'Project', feature: true do
let(:project) { create(:empty_project, namespace: user.namespace, name: 'project1') }
before do
- login_with(user)
+ gitlab_sign_in(user)
project.team << [user, :master]
visit edit_namespace_project_path(project.namespace, project)
end
@@ -79,7 +79,7 @@ feature 'Project', feature: true do
let(:project) { create(:empty_project, namespace: user.namespace) }
before do
- login_with(user)
+ gitlab_sign_in(user)
project.add_user(user, Gitlab::Access::MASTER)
visit namespace_project_path(project.namespace, project)
end
@@ -98,7 +98,7 @@ feature 'Project', feature: true do
context 'on issues page', js: true do
before do
- login_with(user)
+ gitlab_sign_in(user)
project.add_user(user, Gitlab::Access::MASTER)
project2.add_user(user, Gitlab::Access::MASTER)
visit namespace_project_issue_path(project.namespace, project, issue)
@@ -123,7 +123,7 @@ feature 'Project', feature: true do
before do
project.team << [user, :master]
- login_as user
+ gitlab_sign_in user
visit namespace_project_path(project.namespace, project)
end
diff --git a/spec/features/protected_branches_spec.rb b/spec/features/protected_branches_spec.rb
index 667895bffa5..20b8e10f0f7 100644
--- a/spec/features/protected_branches_spec.rb
+++ b/spec/features/protected_branches_spec.rb
@@ -4,7 +4,9 @@ feature 'Protected Branches', feature: true, js: true do
let(:user) { create(:user, :admin) }
let(:project) { create(:project, :repository) }
- before { login_as(user) }
+ before do
+ gitlab_sign_in(user)
+ end
def set_protected_branch_name(branch_name)
find(".js-protected-branch-select").trigger('click')
diff --git a/spec/features/protected_tags_spec.rb b/spec/features/protected_tags_spec.rb
index 66236dbc7fc..73a80692154 100644
--- a/spec/features/protected_tags_spec.rb
+++ b/spec/features/protected_tags_spec.rb
@@ -4,7 +4,9 @@ feature 'Projected Tags', feature: true, js: true do
let(:user) { create(:user, :admin) }
let(:project) { create(:project, :repository) }
- before { login_as(user) }
+ before do
+ gitlab_sign_in(user)
+ end
def set_protected_tag_name(tag_name)
find(".js-protected-tag-select").click
diff --git a/spec/features/reportable_note/commit_spec.rb b/spec/features/reportable_note/commit_spec.rb
index 39b1c4acf52..12049822753 100644
--- a/spec/features/reportable_note/commit_spec.rb
+++ b/spec/features/reportable_note/commit_spec.rb
@@ -8,7 +8,7 @@ describe 'Reportable note on commit', :feature, :js do
before do
project.add_master(user)
- login_as user
+ gitlab_sign_in(user)
end
context 'a normal note' do
diff --git a/spec/features/reportable_note/issue_spec.rb b/spec/features/reportable_note/issue_spec.rb
index 5f526818994..ca2a7f41496 100644
--- a/spec/features/reportable_note/issue_spec.rb
+++ b/spec/features/reportable_note/issue_spec.rb
@@ -8,7 +8,7 @@ describe 'Reportable note on issue', :feature, :js do
before do
project.add_master(user)
- login_as user
+ gitlab_sign_in(user)
visit namespace_project_issue_path(project.namespace, project, issue)
end
diff --git a/spec/features/reportable_note/merge_request_spec.rb b/spec/features/reportable_note/merge_request_spec.rb
index 6d053d26626..8e75b4af3eb 100644
--- a/spec/features/reportable_note/merge_request_spec.rb
+++ b/spec/features/reportable_note/merge_request_spec.rb
@@ -7,7 +7,7 @@ describe 'Reportable note on merge request', :feature, :js do
before do
project.add_master(user)
- login_as user
+ gitlab_sign_in(user)
visit namespace_project_merge_request_path(project.namespace, project, merge_request)
end
diff --git a/spec/features/reportable_note/snippets_spec.rb b/spec/features/reportable_note/snippets_spec.rb
index 3f1e0cf9097..a88990eada0 100644
--- a/spec/features/reportable_note/snippets_spec.rb
+++ b/spec/features/reportable_note/snippets_spec.rb
@@ -6,7 +6,7 @@ describe 'Reportable note on snippets', :feature, :js do
before do
project.add_master(user)
- login_as user
+ gitlab_sign_in(user)
end
describe 'on project snippet' do
diff --git a/spec/features/runners_spec.rb b/spec/features/runners_spec.rb
index 0e1cc9a0f73..ea18879b4bf 100644
--- a/spec/features/runners_spec.rb
+++ b/spec/features/runners_spec.rb
@@ -4,7 +4,10 @@ describe "Runners" do
include GitlabRoutingHelper
let(:user) { create(:user) }
- before { login_as(user) }
+
+ before do
+ gitlab_sign_in(user)
+ end
describe "specific runners" do
before do
@@ -127,7 +130,9 @@ describe "Runners" do
end
context 'when runner has tags' do
- before { runner.update_attribute(:tag_list, ['tag']) }
+ before do
+ runner.update_attribute(:tag_list, ['tag'])
+ end
scenario 'user wants to prevent runner from running untagged job' do
visit runners_path(project)
diff --git a/spec/features/search_spec.rb b/spec/features/search_spec.rb
index 7834807b1f1..64469f999af 100644
--- a/spec/features/search_spec.rb
+++ b/spec/features/search_spec.rb
@@ -9,7 +9,7 @@ describe "Search", feature: true do
let!(:issue2) { create(:issue, project: project, author: user) }
before do
- login_with(user)
+ gitlab_sign_in(user)
project.team << [user, :reporter]
visit search_path
end
@@ -83,7 +83,9 @@ describe "Search", feature: true do
let(:project) { create(:project, :repository) }
let(:note) { create(:note_on_commit, author: user, project: project, commit_id: project.repository.commit.id, note: 'Bug here') }
- before { note.update_attributes(commit_id: 12345678) }
+ before do
+ note.update_attributes(commit_id: 12345678)
+ end
it 'finds comment' do
visit namespace_project_path(project.namespace, project)
diff --git a/spec/features/security/project/internal_access_spec.rb b/spec/features/security/project/internal_access_spec.rb
index 2a2655bbdb5..f33406a40a7 100644
--- a/spec/features/security/project/internal_access_spec.rb
+++ b/spec/features/security/project/internal_access_spec.rb
@@ -337,7 +337,9 @@ describe "Internal Project Access", feature: true do
subject { namespace_project_jobs_path(project.namespace, project) }
context "when allowed for public and internal" do
- before { project.update(public_builds: true) }
+ before do
+ project.update(public_builds: true)
+ end
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
@@ -351,7 +353,9 @@ describe "Internal Project Access", feature: true do
end
context "when disallowed for public and internal" do
- before { project.update(public_builds: false) }
+ before do
+ project.update(public_builds: false)
+ end
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
@@ -371,7 +375,9 @@ describe "Internal Project Access", feature: true do
subject { namespace_project_job_path(project.namespace, project, build.id) }
context "when allowed for public and internal" do
- before { project.update(public_builds: true) }
+ before do
+ project.update(public_builds: true)
+ end
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
@@ -385,7 +391,9 @@ describe "Internal Project Access", feature: true do
end
context "when disallowed for public and internal" do
- before { project.update(public_builds: false) }
+ before do
+ project.update(public_builds: false)
+ end
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
diff --git a/spec/features/security/project/public_access_spec.rb b/spec/features/security/project/public_access_spec.rb
index 35d5163941e..16a1331b2f3 100644
--- a/spec/features/security/project/public_access_spec.rb
+++ b/spec/features/security/project/public_access_spec.rb
@@ -157,7 +157,9 @@ describe "Public Project Access", feature: true do
subject { namespace_project_jobs_path(project.namespace, project) }
context "when allowed for public" do
- before { project.update(public_builds: true) }
+ before do
+ project.update(public_builds: true)
+ end
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
@@ -171,7 +173,9 @@ describe "Public Project Access", feature: true do
end
context "when disallowed for public" do
- before { project.update(public_builds: false) }
+ before do
+ project.update(public_builds: false)
+ end
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
@@ -191,7 +195,9 @@ describe "Public Project Access", feature: true do
subject { namespace_project_job_path(project.namespace, project, build.id) }
context "when allowed for public" do
- before { project.update(public_builds: true) }
+ before do
+ project.update(public_builds: true)
+ end
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
@@ -205,7 +211,9 @@ describe "Public Project Access", feature: true do
end
context "when disallowed for public" do
- before { project.update(public_builds: false) }
+ before do
+ project.update(public_builds: false)
+ end
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
diff --git a/spec/features/signup_spec.rb b/spec/features/signup_spec.rb
index d7b6dda4946..5d6d1e79af2 100644
--- a/spec/features/signup_spec.rb
+++ b/spec/features/signup_spec.rb
@@ -3,7 +3,9 @@ require 'spec_helper'
feature 'Signup', feature: true do
describe 'signup with no errors' do
context "when sending confirmation email" do
- before { stub_application_setting(send_user_confirmation_email: true) }
+ before do
+ stub_application_setting(send_user_confirmation_email: true)
+ end
it 'creates the user account and sends a confirmation email' do
user = build(:user)
@@ -23,7 +25,9 @@ feature 'Signup', feature: true do
end
context "when not sending confirmation email" do
- before { stub_application_setting(send_user_confirmation_email: false) }
+ before do
+ stub_application_setting(send_user_confirmation_email: false)
+ end
it 'creates the user account and goes to dashboard' do
user = build(:user)
diff --git a/spec/features/snippets/create_snippet_spec.rb b/spec/features/snippets/create_snippet_spec.rb
index ddd31ede064..ac5c14ed427 100644
--- a/spec/features/snippets/create_snippet_spec.rb
+++ b/spec/features/snippets/create_snippet_spec.rb
@@ -4,7 +4,7 @@ feature 'Create Snippet', :js, feature: true do
include DropzoneHelper
before do
- login_as :user
+ gitlab_sign_in :user
visit new_snippet_path
end
diff --git a/spec/features/snippets/edit_snippet_spec.rb b/spec/features/snippets/edit_snippet_spec.rb
index 89ae593db88..860e1b156d6 100644
--- a/spec/features/snippets/edit_snippet_spec.rb
+++ b/spec/features/snippets/edit_snippet_spec.rb
@@ -10,7 +10,7 @@ feature 'Edit Snippet', :js, feature: true do
let(:snippet) { create(:personal_snippet, :public, file_name: file_name, content: content, author: user) }
before do
- login_as(user)
+ gitlab_sign_in(user)
visit edit_snippet_path(snippet)
wait_for_requests
diff --git a/spec/features/snippets/explore_spec.rb b/spec/features/snippets/explore_spec.rb
index fd097fe2e74..ec75817b942 100644
--- a/spec/features/snippets/explore_spec.rb
+++ b/spec/features/snippets/explore_spec.rb
@@ -6,7 +6,7 @@ feature 'Explore Snippets', feature: true do
let!(:private_snippet) { create(:personal_snippet, :private) }
scenario 'User should see snippets that are not private' do
- login_as create(:user)
+ gitlab_sign_in create(:user)
visit explore_snippets_path
expect(page).to have_content(public_snippet.title)
@@ -15,7 +15,7 @@ feature 'Explore Snippets', feature: true do
end
scenario 'External user should see only public snippets' do
- login_as create(:user, :external)
+ gitlab_sign_in create(:user, :external)
visit explore_snippets_path
expect(page).to have_content(public_snippet.title)
diff --git a/spec/features/snippets/internal_snippet_spec.rb b/spec/features/snippets/internal_snippet_spec.rb
index 93382f4c359..3babb1c02cc 100644
--- a/spec/features/snippets/internal_snippet_spec.rb
+++ b/spec/features/snippets/internal_snippet_spec.rb
@@ -5,7 +5,7 @@ feature 'Internal Snippets', feature: true, js: true do
describe 'normal user' do
before do
- login_as :user
+ gitlab_sign_in :user
end
scenario 'sees internal snippets' do
diff --git a/spec/features/snippets/notes_on_personal_snippets_spec.rb b/spec/features/snippets/notes_on_personal_snippets_spec.rb
index 44b0c89fac7..6f7e6f543b2 100644
--- a/spec/features/snippets/notes_on_personal_snippets_spec.rb
+++ b/spec/features/snippets/notes_on_personal_snippets_spec.rb
@@ -14,7 +14,7 @@ describe 'Comments on personal snippets', :js, feature: true do
let!(:other_note) { create(:note_on_personal_snippet) }
before do
- login_as user
+ gitlab_sign_in user
visit snippet_path(snippet)
end
@@ -70,6 +70,22 @@ describe 'Comments on personal snippets', :js, feature: true do
expect(find('div#notes')).to have_content('This is awesome!')
end
+
+ it 'should not have autocomplete' do
+ wait_for_requests
+ request_count_before = page.driver.network_traffic.count
+
+ find('#note_note').native.send_keys('')
+ fill_in 'note[note]', with: '@'
+
+ wait_for_requests
+ request_count_after = page.driver.network_traffic.count
+
+ # This selector probably won't be in place even if autocomplete was enabled
+ # but we want to make sure
+ expect(page).not_to have_selector('.atwho-view')
+ expect(request_count_before).to eq(request_count_after)
+ end
end
context 'when editing a note' do
diff --git a/spec/features/snippets/search_snippets_spec.rb b/spec/features/snippets/search_snippets_spec.rb
index 146cd3af848..4c21e7321f4 100644
--- a/spec/features/snippets/search_snippets_spec.rb
+++ b/spec/features/snippets/search_snippets_spec.rb
@@ -5,7 +5,7 @@ feature 'Search Snippets', feature: true do
public_snippet = create(:personal_snippet, :public, title: 'Beginning and Middle')
private_snippet = create(:personal_snippet, :private, title: 'Middle and End')
- login_as private_snippet.author
+ gitlab_sign_in private_snippet.author
visit dashboard_snippets_path
page.within '.search' do
@@ -41,7 +41,7 @@ feature 'Search Snippets', feature: true do
CONTENT
)
- login_as create(:user)
+ gitlab_sign_in create(:user)
visit dashboard_snippets_path
page.within '.search' do
diff --git a/spec/features/snippets/user_snippets_spec.rb b/spec/features/snippets/user_snippets_spec.rb
index 191c2fb9a22..b971c6aab53 100644
--- a/spec/features/snippets/user_snippets_spec.rb
+++ b/spec/features/snippets/user_snippets_spec.rb
@@ -7,7 +7,7 @@ feature 'User Snippets', feature: true do
let!(:private_snippet) { create(:personal_snippet, :private, author: author, title: "This is a private snippet") }
background do
- login_as author
+ gitlab_sign_in author
visit dashboard_snippets_path
end
diff --git a/spec/features/tags/master_creates_tag_spec.rb b/spec/features/tags/master_creates_tag_spec.rb
index af25eebed13..52db3583dac 100644
--- a/spec/features/tags/master_creates_tag_spec.rb
+++ b/spec/features/tags/master_creates_tag_spec.rb
@@ -6,62 +6,80 @@ feature 'Master creates tag', feature: true do
before do
project.team << [user, :master]
- login_with(user)
- visit namespace_project_tags_path(project.namespace, project)
+ gitlab_sign_in(user)
end
- scenario 'with an invalid name displays an error' do
- create_tag_in_form(tag: 'v 1.0', ref: 'master')
+ context 'from tag list' do
+ before do
+ visit namespace_project_tags_path(project.namespace, project)
+ end
- expect(page).to have_content 'Tag name invalid'
- end
+ scenario 'with an invalid name displays an error' do
+ create_tag_in_form(tag: 'v 1.0', ref: 'master')
- scenario 'with an invalid reference displays an error' do
- create_tag_in_form(tag: 'v2.0', ref: 'foo')
+ expect(page).to have_content 'Tag name invalid'
+ end
- expect(page).to have_content 'Target foo is invalid'
- end
+ scenario 'with an invalid reference displays an error' do
+ create_tag_in_form(tag: 'v2.0', ref: 'foo')
- scenario 'that already exists displays an error' do
- create_tag_in_form(tag: 'v1.1.0', ref: 'master')
+ expect(page).to have_content 'Target foo is invalid'
+ end
- expect(page).to have_content 'Tag v1.1.0 already exists'
- end
+ scenario 'that already exists displays an error' do
+ create_tag_in_form(tag: 'v1.1.0', ref: 'master')
+
+ expect(page).to have_content 'Tag v1.1.0 already exists'
+ end
- scenario 'with multiline message displays the message in a <pre> block' do
- create_tag_in_form(tag: 'v3.0', ref: 'master', message: "Awesome tag message\n\n- hello\n- world")
+ scenario 'with multiline message displays the message in a <pre> block' do
+ create_tag_in_form(tag: 'v3.0', ref: 'master', message: "Awesome tag message\n\n- hello\n- world")
- expect(current_path).to eq(
- namespace_project_tag_path(project.namespace, project, 'v3.0'))
- expect(page).to have_content 'v3.0'
- page.within 'pre.wrap' do
- expect(page).to have_content "Awesome tag message\n\n- hello\n- world"
+ expect(current_path).to eq(
+ namespace_project_tag_path(project.namespace, project, 'v3.0'))
+ expect(page).to have_content 'v3.0'
+ page.within 'pre.wrap' do
+ expect(page).to have_content "Awesome tag message\n\n- hello\n- world"
+ end
end
- end
- scenario 'with multiline release notes parses the release note as Markdown' do
- create_tag_in_form(tag: 'v4.0', ref: 'master', desc: "Awesome release notes\n\n- hello\n- world")
+ scenario 'with multiline release notes parses the release note as Markdown' do
+ create_tag_in_form(tag: 'v4.0', ref: 'master', desc: "Awesome release notes\n\n- hello\n- world")
- expect(current_path).to eq(
- namespace_project_tag_path(project.namespace, project, 'v4.0'))
- expect(page).to have_content 'v4.0'
- page.within '.description' do
- expect(page).to have_content 'Awesome release notes'
- expect(page).to have_selector('ul li', count: 2)
+ expect(current_path).to eq(
+ namespace_project_tag_path(project.namespace, project, 'v4.0'))
+ expect(page).to have_content 'v4.0'
+ page.within '.description' do
+ expect(page).to have_content 'Awesome release notes'
+ expect(page).to have_selector('ul li', count: 2)
+ end
+ end
+
+ scenario 'opens dropdown for ref', js: true do
+ click_link 'New tag'
+ ref_row = find('.form-group:nth-of-type(2) .col-sm-10')
+ page.within ref_row do
+ ref_input = find('[name="ref"]', visible: false)
+ expect(ref_input.value).to eq 'master'
+ expect(find('.dropdown-toggle-text')).to have_content 'master'
+
+ find('.js-branch-select').trigger('click')
+
+ expect(find('.dropdown-menu')).to have_content 'empty-branch'
+ end
end
end
- scenario 'opens dropdown for ref', js: true do
- click_link 'New tag'
- ref_row = find('.form-group:nth-of-type(2) .col-sm-10')
- page.within ref_row do
- ref_input = find('[name="ref"]', visible: false)
- expect(ref_input.value).to eq 'master'
- expect(find('.dropdown-toggle-text')).to have_content 'master'
+ context 'from new tag page' do
+ before do
+ visit new_namespace_project_tag_path(project.namespace, project)
+ end
- find('.js-branch-select').trigger('click')
+ it 'description has autocomplete', :js do
+ find('#release_description').native.send_keys('')
+ fill_in 'release_description', with: '@'
- expect(find('.dropdown-menu')).to have_content 'empty-branch'
+ expect(page).to have_selector('.atwho-view')
end
end
diff --git a/spec/features/tags/master_deletes_tag_spec.rb b/spec/features/tags/master_deletes_tag_spec.rb
index ccfafe6db7d..58f33e954f9 100644
--- a/spec/features/tags/master_deletes_tag_spec.rb
+++ b/spec/features/tags/master_deletes_tag_spec.rb
@@ -6,7 +6,7 @@ feature 'Master deletes tag', feature: true do
before do
project.team << [user, :master]
- login_with(user)
+ gitlab_sign_in(user)
visit namespace_project_tags_path(project.namespace, project)
end
diff --git a/spec/features/tags/master_updates_tag_spec.rb b/spec/features/tags/master_updates_tag_spec.rb
index 6b5b3122f72..18c8c4c511c 100644
--- a/spec/features/tags/master_updates_tag_spec.rb
+++ b/spec/features/tags/master_updates_tag_spec.rb
@@ -6,7 +6,7 @@ feature 'Master updates tag', feature: true do
before do
project.team << [user, :master]
- login_with(user)
+ gitlab_sign_in(user)
visit namespace_project_tags_path(project.namespace, project)
end
@@ -24,6 +24,17 @@ feature 'Master updates tag', feature: true do
expect(page).to have_content 'v1.1.0'
expect(page).to have_content 'Awesome release notes'
end
+
+ scenario 'description has autocomplete', :js do
+ page.within(first('.content-list .controls')) do
+ click_link 'Edit release notes'
+ end
+
+ find('#release_description').native.send_keys('')
+ fill_in 'release_description', with: '@'
+
+ expect(page).to have_selector('.atwho-view')
+ end
end
context 'from a specific tag page' do
diff --git a/spec/features/tags/master_views_tags_spec.rb b/spec/features/tags/master_views_tags_spec.rb
index 922ac15a2eb..3c21fa06694 100644
--- a/spec/features/tags/master_views_tags_spec.rb
+++ b/spec/features/tags/master_views_tags_spec.rb
@@ -5,7 +5,7 @@ feature 'Master views tags', feature: true do
before do
project.team << [user, :master]
- login_with(user)
+ gitlab_sign_in(user)
end
context 'when project has no tags' do
diff --git a/spec/features/task_lists_spec.rb b/spec/features/task_lists_spec.rb
index 563e65d3cc5..51b1b8e2328 100644
--- a/spec/features/task_lists_spec.rb
+++ b/spec/features/task_lists_spec.rb
@@ -144,7 +144,9 @@ feature 'Task Lists', feature: true do
describe 'nested tasks', js: true do
let(:issue) { create(:issue, description: nested_tasks_markdown, author: user, project: project) }
- before { visit_issue(project, issue) }
+ before do
+ visit_issue(project, issue)
+ end
it 'renders' do
expect(page).to have_selector('ul.task-list', count: 2)
diff --git a/spec/features/todos/target_state_spec.rb b/spec/features/todos/target_state_spec.rb
index 32fa88a2b21..99b70b3d3a1 100644
--- a/spec/features/todos/target_state_spec.rb
+++ b/spec/features/todos/target_state_spec.rb
@@ -6,7 +6,7 @@ feature 'Todo target states', feature: true do
let(:project) { create(:project, visibility_level: Gitlab::VisibilityLevel::PUBLIC) }
before do
- login_as user
+ gitlab_sign_in user
end
scenario 'on a closed issue todo has closed label' do
diff --git a/spec/features/todos/todos_filtering_spec.rb b/spec/features/todos/todos_filtering_spec.rb
index bbfa4e08379..032fb479076 100644
--- a/spec/features/todos/todos_filtering_spec.rb
+++ b/spec/features/todos/todos_filtering_spec.rb
@@ -17,7 +17,7 @@ describe 'Dashboard > User filters todos', feature: true, js: true do
project_1.team << [user_1, :developer]
project_2.team << [user_1, :developer]
- login_as(user_1)
+ gitlab_sign_in(user_1)
visit dashboard_todos_path
end
diff --git a/spec/features/todos/todos_sorting_spec.rb b/spec/features/todos/todos_sorting_spec.rb
index 4d5bd476301..498bbac6d14 100644
--- a/spec/features/todos/todos_sorting_spec.rb
+++ b/spec/features/todos/todos_sorting_spec.rb
@@ -8,7 +8,9 @@ describe "Dashboard > User sorts todos", feature: true do
let(:label_2) { create(:label, title: 'label_2', project: project, priority: 2) }
let(:label_3) { create(:label, title: 'label_3', project: project, priority: 3) }
- before { project.team << [user, :developer] }
+ before do
+ project.team << [user, :developer]
+ end
context 'sort options' do
let(:issue_1) { create(:issue, title: 'issue_1', project: project) }
@@ -30,7 +32,7 @@ describe "Dashboard > User sorts todos", feature: true do
issue_2.labels << label_3
issue_1.labels << label_2
- login_as(user)
+ gitlab_sign_in(user)
visit dashboard_todos_path
end
@@ -81,7 +83,7 @@ describe "Dashboard > User sorts todos", feature: true do
create(:todo, user: user, project: project, target: issue_2)
create(:todo, user: user, project: project, target: merge_request_1)
- login_as(user)
+ gitlab_sign_in(user)
visit dashboard_todos_path
end
diff --git a/spec/features/todos/todos_spec.rb b/spec/features/todos/todos_spec.rb
index feb2fe8a7d1..41b32bdedc3 100644
--- a/spec/features/todos/todos_spec.rb
+++ b/spec/features/todos/todos_spec.rb
@@ -9,7 +9,7 @@ describe 'Dashboard Todos', feature: true do
describe 'GET /dashboard/todos' do
context 'User does not have todos' do
before do
- login_as(user)
+ gitlab_sign_in(user)
visit dashboard_todos_path
end
it 'shows "All done" message' do
@@ -20,7 +20,7 @@ describe 'Dashboard Todos', feature: true do
context 'User has a todo', js: true do
before do
create(:todo, :mentioned, user: user, project: project, target: issue, author: author)
- login_as(user)
+ gitlab_sign_in(user)
visit dashboard_todos_path
end
@@ -101,7 +101,7 @@ describe 'Dashboard Todos', feature: true do
context 'User created todos for themself' do
before do
- login_as(user)
+ gitlab_sign_in(user)
end
context 'issue assigned todo' do
@@ -179,7 +179,7 @@ describe 'Dashboard Todos', feature: true do
context 'User has done todos', js: true do
before do
create(:todo, :mentioned, :done, user: user, project: project, target: issue, author: author)
- login_as(user)
+ gitlab_sign_in(user)
visit dashboard_todos_path(state: :done)
end
@@ -217,7 +217,7 @@ describe 'Dashboard Todos', feature: true do
note2 = create(:note_on_issue, note: "Test #{label2.to_reference(format: :name)}", noteable_id: issue2.id, noteable_type: 'Issue', project: project2)
create(:todo, :mentioned, project: project2, target: issue2, user: user, note_id: note2.id)
- login_as(user)
+ gitlab_sign_in(user)
visit dashboard_todos_path
end
@@ -233,7 +233,7 @@ describe 'Dashboard Todos', feature: true do
# Create just enough records to cause us to paginate
create_list(:todo, 2, :mentioned, user: user, project: project, target: issue, author: author)
- login_as(user)
+ gitlab_sign_in(user)
end
it 'is paginated' do
@@ -321,7 +321,7 @@ describe 'Dashboard Todos', feature: true do
deleted_project = create(:project, visibility_level: Gitlab::VisibilityLevel::PUBLIC, pending_delete: true)
create(:todo, :mentioned, user: user, project: deleted_project, target: issue, author: author)
create(:todo, :mentioned, user: user, project: deleted_project, target: issue, author: author, state: :done)
- login_as(user)
+ gitlab_sign_in(user)
visit dashboard_todos_path
end
@@ -337,7 +337,7 @@ describe 'Dashboard Todos', feature: true do
let!(:todo) { create(:todo, :build_failed, user: user, project: project, author: author) }
before do
- login_as user
+ gitlab_sign_in user
visit dashboard_todos_path
end
diff --git a/spec/features/triggers_spec.rb b/spec/features/triggers_spec.rb
index c1ae6db00c6..bc3c9917a42 100644
--- a/spec/features/triggers_spec.rb
+++ b/spec/features/triggers_spec.rb
@@ -5,13 +5,15 @@ feature 'Triggers', feature: true, js: true do
let(:user) { create(:user) }
let(:user2) { create(:user) }
let(:guest_user) { create(:user) }
- before { login_as(user) }
before do
+ gitlab_sign_in(user)
+
@project = create(:empty_project)
@project.team << [user, :master]
@project.team << [user2, :master]
@project.team << [guest_user, :guest]
+
visit namespace_project_settings_ci_cd_path(@project.namespace, @project)
end
diff --git a/spec/features/u2f_spec.rb b/spec/features/u2f_spec.rb
index 2fed8067042..f3662cb184f 100644
--- a/spec/features/u2f_spec.rb
+++ b/spec/features/u2f_spec.rb
@@ -1,7 +1,9 @@
require 'spec_helper'
feature 'Using U2F (Universal 2nd Factor) Devices for Authentication', :js do
- before { allow_any_instance_of(U2fHelper).to receive(:inject_u2f_api?).and_return(true) }
+ before do
+ allow_any_instance_of(U2fHelper).to receive(:inject_u2f_api?).and_return(true)
+ end
def manage_two_factor_authentication
click_on 'Manage two-factor authentication'
@@ -23,12 +25,14 @@ feature 'Using U2F (Universal 2nd Factor) Devices for Authentication', :js do
let(:user) { create(:user) }
before do
- login_as(user)
+ gitlab_sign_in(user)
user.update_attribute(:otp_required_for_login, true)
end
describe 'when 2FA via OTP is disabled' do
- before { user.update_attribute(:otp_required_for_login, false) }
+ before do
+ user.update_attribute(:otp_required_for_login, false)
+ end
it 'does not allow registering a new device' do
visit profile_account_path
@@ -89,10 +93,10 @@ feature 'Using U2F (Universal 2nd Factor) Devices for Authentication', :js do
manage_two_factor_authentication
u2f_device = register_u2f_device
expect(page).to have_content('Your U2F device was registered')
- logout
+ gitlab_sign_out
# Second user
- user = login_as(:user)
+ user = gitlab_sign_in(:user)
user.update_attribute(:otp_required_for_login, true)
visit profile_account_path
manage_two_factor_authentication
@@ -143,18 +147,18 @@ feature 'Using U2F (Universal 2nd Factor) Devices for Authentication', :js do
before do
# Register and logout
- login_as(user)
+ gitlab_sign_in(user)
user.update_attribute(:otp_required_for_login, true)
visit profile_account_path
manage_two_factor_authentication
@u2f_device = register_u2f_device
- logout
+ gitlab_sign_out
end
describe "when 2FA via OTP is disabled" do
it "allows logging in with the U2F device" do
user.update_attribute(:otp_required_for_login, false)
- login_with(user)
+ gitlab_sign_in(user)
@u2f_device.respond_to_u2f_authentication
@@ -166,7 +170,7 @@ feature 'Using U2F (Universal 2nd Factor) Devices for Authentication', :js do
describe "when 2FA via OTP is enabled" do
it "allows logging in with the U2F device" do
user.update_attribute(:otp_required_for_login, true)
- login_with(user)
+ gitlab_sign_in(user)
@u2f_device.respond_to_u2f_authentication
@@ -176,7 +180,7 @@ feature 'Using U2F (Universal 2nd Factor) Devices for Authentication', :js do
end
it 'persists remember_me value via hidden field' do
- login_with(user, remember: true)
+ gitlab_sign_in(user, remember: true)
@u2f_device.respond_to_u2f_authentication
expect(page).to have_content('We heard back from your U2F device')
@@ -191,15 +195,15 @@ feature 'Using U2F (Universal 2nd Factor) Devices for Authentication', :js do
describe "but not the current user" do
it "does not allow logging in with that particular device" do
# Register current user with the different U2F device
- current_user = login_as(:user)
+ current_user = gitlab_sign_in(:user)
current_user.update_attribute(:otp_required_for_login, true)
visit profile_account_path
manage_two_factor_authentication
register_u2f_device(name: 'My other device')
- logout
+ gitlab_sign_out
# Try authenticating user with the old U2F device
- login_as(current_user)
+ gitlab_sign_in(current_user)
@u2f_device.respond_to_u2f_authentication
expect(page).to have_content('We heard back from your U2F device')
expect(page).to have_content('Authentication via U2F device failed')
@@ -209,15 +213,15 @@ feature 'Using U2F (Universal 2nd Factor) Devices for Authentication', :js do
describe "and also the current user" do
it "allows logging in with that particular device" do
# Register current user with the same U2F device
- current_user = login_as(:user)
+ current_user = gitlab_sign_in(:user)
current_user.update_attribute(:otp_required_for_login, true)
visit profile_account_path
manage_two_factor_authentication
register_u2f_device(@u2f_device)
- logout
+ gitlab_sign_out
# Try authenticating user with the same U2F device
- login_as(current_user)
+ gitlab_sign_in(current_user)
@u2f_device.respond_to_u2f_authentication
expect(page).to have_content('We heard back from your U2F device')
@@ -229,7 +233,7 @@ feature 'Using U2F (Universal 2nd Factor) Devices for Authentication', :js do
describe "when a given U2F device has not been registered" do
it "does not allow logging in with that particular device" do
unregistered_device = FakeU2fDevice.new(page, 'My device')
- login_as(user)
+ gitlab_sign_in(user)
unregistered_device.respond_to_u2f_authentication
expect(page).to have_content('We heard back from your U2F device')
@@ -240,7 +244,7 @@ feature 'Using U2F (Universal 2nd Factor) Devices for Authentication', :js do
describe "when more than one device has been registered by the same user" do
it "allows logging in with either device" do
# Register first device
- user = login_as(:user)
+ user = gitlab_sign_in(:user)
user.update_attribute(:otp_required_for_login, true)
visit profile_two_factor_auth_path
expect(page).to have_content("Your U2F device needs to be set up.")
@@ -250,17 +254,17 @@ feature 'Using U2F (Universal 2nd Factor) Devices for Authentication', :js do
visit profile_two_factor_auth_path
expect(page).to have_content("Your U2F device needs to be set up.")
second_device = register_u2f_device(name: 'My other device')
- logout
+ gitlab_sign_out
# Authenticate as both devices
[first_device, second_device].each do |device|
- login_as(user)
+ gitlab_sign_in(user)
device.respond_to_u2f_authentication
expect(page).to have_content('We heard back from your U2F device')
expect(page).to have_css('.sign-out-link', visible: false)
- logout
+ gitlab_sign_out
end
end
end
@@ -269,7 +273,7 @@ feature 'Using U2F (Universal 2nd Factor) Devices for Authentication', :js do
let(:user) { create(:user) }
before do
- user = login_as(:user)
+ user = gitlab_sign_in(:user)
user.update_attribute(:otp_required_for_login, true)
visit profile_account_path
manage_two_factor_authentication
@@ -296,15 +300,15 @@ feature 'Using U2F (Universal 2nd Factor) Devices for Authentication', :js do
before do
# Register and logout
- login_as(user)
+ gitlab_sign_in(user)
user.update_attribute(:otp_required_for_login, true)
visit profile_account_path
end
describe 'when no u2f device is registered' do
before do
- logout
- login_with(user)
+ gitlab_sign_out
+ gitlab_sign_in(user)
end
it 'shows the fallback otp code UI' do
@@ -316,8 +320,8 @@ feature 'Using U2F (Universal 2nd Factor) Devices for Authentication', :js do
before do
manage_two_factor_authentication
@u2f_device = register_u2f_device
- logout
- login_with(user)
+ gitlab_sign_out
+ gitlab_sign_in(user)
end
it 'provides a button that shows the fallback otp code UI' do
diff --git a/spec/features/unsubscribe_links_spec.rb b/spec/features/unsubscribe_links_spec.rb
index 8509551ce4a..352f8ba70ac 100644
--- a/spec/features/unsubscribe_links_spec.rb
+++ b/spec/features/unsubscribe_links_spec.rb
@@ -56,7 +56,9 @@ describe 'Unsubscribe links', feature: true do
end
context 'when logged in' do
- before { login_as(recipient) }
+ before do
+ sign_in(recipient)
+ end
it 'unsubscribes from the issue when visiting the link from the email body' do
visit body_link
diff --git a/spec/features/uploads/user_uploads_avatar_to_group_spec.rb b/spec/features/uploads/user_uploads_avatar_to_group_spec.rb
index d9d6f2e2382..797b7b3d50d 100644
--- a/spec/features/uploads/user_uploads_avatar_to_group_spec.rb
+++ b/spec/features/uploads/user_uploads_avatar_to_group_spec.rb
@@ -5,7 +5,7 @@ feature 'User uploads avatar to group', feature: true do
user = create(:user)
group = create(:group)
group.add_owner(user)
- login_as(user)
+ gitlab_sign_in(user)
visit edit_group_path(group)
attach_file(
diff --git a/spec/features/uploads/user_uploads_avatar_to_profile_spec.rb b/spec/features/uploads/user_uploads_avatar_to_profile_spec.rb
index eb8dbd76aab..a3f8027f4da 100644
--- a/spec/features/uploads/user_uploads_avatar_to_profile_spec.rb
+++ b/spec/features/uploads/user_uploads_avatar_to_profile_spec.rb
@@ -3,7 +3,7 @@ require 'rails_helper'
feature 'User uploads avatar to profile', feature: true do
scenario 'they see their new avatar' do
user = create(:user)
- login_as(user)
+ gitlab_sign_in(user)
visit profile_path
attach_file(
diff --git a/spec/features/uploads/user_uploads_file_to_note_spec.rb b/spec/features/uploads/user_uploads_file_to_note_spec.rb
index 9332d3b88d2..77a1012762d 100644
--- a/spec/features/uploads/user_uploads_file_to_note_spec.rb
+++ b/spec/features/uploads/user_uploads_file_to_note_spec.rb
@@ -8,7 +8,7 @@ feature 'User uploads file to note', feature: true do
let(:issue) { create(:issue, project: project, author: user) }
before do
- login_as(user)
+ gitlab_sign_in(user)
visit namespace_project_issue_path(project.namespace, project, issue)
end
diff --git a/spec/features/user_callout_spec.rb b/spec/features/user_callout_spec.rb
index b84f834ff1e..7538a6e4a04 100644
--- a/spec/features/user_callout_spec.rb
+++ b/spec/features/user_callout_spec.rb
@@ -6,7 +6,7 @@ describe 'User Callouts', js: true do
let(:project) { create(:empty_project, path: 'gitlab', name: 'sample') }
before do
- login_as(user)
+ gitlab_sign_in(user)
project.team << [user, :master]
end
diff --git a/spec/features/user_can_display_performance_bar_spec.rb b/spec/features/user_can_display_performance_bar_spec.rb
index c2842255b86..1bd7e038939 100644
--- a/spec/features/user_can_display_performance_bar_spec.rb
+++ b/spec/features/user_can_display_performance_bar_spec.rb
@@ -57,7 +57,7 @@ describe 'User can display performacne bar', :js do
context 'when user is logged-in' do
before do
- login_as :user
+ gitlab_sign_in(create(:user))
visit root_path
end
diff --git a/spec/features/users/projects_spec.rb b/spec/features/users/projects_spec.rb
index 67ce4b44464..377b1a0148f 100644
--- a/spec/features/users/projects_spec.rb
+++ b/spec/features/users/projects_spec.rb
@@ -8,7 +8,7 @@ describe 'Projects tab on a user profile', :feature, :js do
before do
allow(Project).to receive(:default_per_page).and_return(1)
- login_as(user)
+ gitlab_sign_in(user)
visit user_path(user)
diff --git a/spec/features/users/rss_spec.rb b/spec/features/users/rss_spec.rb
index dbd5f66b55e..797b317a9bb 100644
--- a/spec/features/users/rss_spec.rb
+++ b/spec/features/users/rss_spec.rb
@@ -5,7 +5,7 @@ feature 'User RSS' do
context 'when signed in' do
before do
- login_as(create(:user))
+ gitlab_sign_in(create(:user))
visit path
end
diff --git a/spec/features/users/snippets_spec.rb b/spec/features/users/snippets_spec.rb
index 2e388115633..74c5cbd7887 100644
--- a/spec/features/users/snippets_spec.rb
+++ b/spec/features/users/snippets_spec.rb
@@ -24,7 +24,7 @@ describe 'Snippets tab on a user profile', feature: true, js: true do
let!(:other_snippet) { create(:snippet, :public) }
it 'contains only internal and public snippets of a user when a user is logged in' do
- login_as(:user)
+ gitlab_sign_in(:user)
visit user_path(user)
page.within('.user-profile-nav') { click_link 'Snippets' }
wait_for_requests
diff --git a/spec/features/users_spec.rb b/spec/features/users_spec.rb
index fbe078bd136..84af13d3e49 100644
--- a/spec/features/users_spec.rb
+++ b/spec/features/users_spec.rb
@@ -24,7 +24,7 @@ feature 'Users', feature: true, js: true do
user.reload
expect(user.reset_password_token).not_to be_nil
- login_with(user)
+ gitlab_sign_in(user)
expect(current_path).to eq root_path
user.reload
@@ -45,7 +45,9 @@ feature 'Users', feature: true, js: true do
end
describe 'redirect alias routes' do
- before { user }
+ before do
+ expect(user).to be_persisted
+ end
scenario '/u/user1 redirects to user page' do
visit '/u/user1'
diff --git a/spec/features/variables_spec.rb b/spec/features/variables_spec.rb
index d0c982919db..85085bf305a 100644
--- a/spec/features/variables_spec.rb
+++ b/spec/features/variables_spec.rb
@@ -6,7 +6,7 @@ describe 'Project variables', js: true do
let(:variable) { create(:ci_variable, key: 'test_key', value: 'test value') }
before do
- login_as(user)
+ gitlab_sign_in(user)
project.team << [user, :master]
project.variables << variable
diff --git a/spec/finders/groups_finder_spec.rb b/spec/finders/groups_finder_spec.rb
index 5b3591550c1..9e70cccc3c4 100644
--- a/spec/finders/groups_finder_spec.rb
+++ b/spec/finders/groups_finder_spec.rb
@@ -38,28 +38,79 @@ describe GroupsFinder do
end
end
- context 'subgroups' do
+ context 'subgroups', :nested_groups do
let!(:parent_group) { create(:group, :public) }
let!(:public_subgroup) { create(:group, :public, parent: parent_group) }
let!(:internal_subgroup) { create(:group, :internal, parent: parent_group) }
let!(:private_subgroup) { create(:group, :private, parent: parent_group) }
context 'without a user' do
- it 'only returns public subgroups' do
- expect(described_class.new(nil, parent: parent_group).execute).to contain_exactly(public_subgroup)
+ it 'only returns parent and public subgroups' do
+ expect(described_class.new(nil).execute).to contain_exactly(parent_group, public_subgroup)
end
end
context 'with a user' do
- it 'returns public and internal subgroups' do
- expect(described_class.new(user, parent: parent_group).execute).to contain_exactly(public_subgroup, internal_subgroup)
+ subject { described_class.new(user).execute }
+
+ it 'returns parent, public, and internal subgroups' do
+ is_expected.to contain_exactly(parent_group, public_subgroup, internal_subgroup)
end
context 'being member' do
- it 'returns public subgroups, internal subgroups, and private subgroups user is member of' do
+ it 'returns parent, public subgroups, internal subgroups, and private subgroups user is member of' do
private_subgroup.add_guest(user)
- expect(described_class.new(user, parent: parent_group).execute).to contain_exactly(public_subgroup, internal_subgroup, private_subgroup)
+ is_expected.to contain_exactly(parent_group, public_subgroup, internal_subgroup, private_subgroup)
+ end
+ end
+
+ context 'parent group private' do
+ before do
+ parent_group.update_attribute(:visibility_level, Gitlab::VisibilityLevel::PRIVATE)
+ end
+
+ context 'being member of parent group' do
+ it 'returns all subgroups' do
+ parent_group.add_guest(user)
+
+ is_expected.to contain_exactly(parent_group, public_subgroup, internal_subgroup, private_subgroup)
+ end
+ end
+
+ context 'authorized to private project' do
+ context 'project one level deep' do
+ let!(:subproject) { create(:empty_project, :private, namespace: private_subgroup) }
+ before do
+ subproject.add_guest(user)
+ end
+
+ it 'includes the subgroup of the project' do
+ is_expected.to include(private_subgroup)
+ end
+
+ it 'does not include private subgroups deeper down' do
+ subsubgroup = create(:group, :private, parent: private_subgroup)
+
+ is_expected.not_to include(subsubgroup)
+ end
+ end
+
+ context 'project two levels deep' do
+ let!(:private_subsubgroup) { create(:group, :private, parent: private_subgroup) }
+ let!(:subsubproject) { create(:empty_project, :private, namespace: private_subsubgroup) }
+ before do
+ subsubproject.add_guest(user)
+ end
+
+ it 'returns all the ancestor groups' do
+ is_expected.to include(private_subsubgroup, private_subgroup, parent_group)
+ end
+
+ it 'returns the groups for a given parent' do
+ expect(described_class.new(user, parent: parent_group).execute).to include(private_subgroup)
+ end
+ end
end
end
end
diff --git a/spec/finders/issues_finder_spec.rb b/spec/finders/issues_finder_spec.rb
index 96151689359..8ace1fb5751 100644
--- a/spec/finders/issues_finder_spec.rb
+++ b/spec/finders/issues_finder_spec.rb
@@ -7,9 +7,9 @@ describe IssuesFinder do
set(:project2) { create(:empty_project) }
set(:milestone) { create(:milestone, project: project1) }
set(:label) { create(:label, project: project2) }
- set(:issue1) { create(:issue, author: user, assignees: [user], project: project1, milestone: milestone, title: 'gitlab') }
+ set(:issue1) { create(:issue, author: user, assignees: [user], project: project1, milestone: milestone, title: 'gitlab', created_at: 1.week.ago) }
set(:issue2) { create(:issue, author: user, assignees: [user], project: project2, description: 'gitlab') }
- set(:issue3) { create(:issue, author: user2, assignees: [user2], project: project2, title: 'tanuki', description: 'tanuki') }
+ set(:issue3) { create(:issue, author: user2, assignees: [user2], project: project2, title: 'tanuki', description: 'tanuki', created_at: 1.week.from_now) }
describe '#execute' do
set(:closed_issue) { create(:issue, author: user2, assignees: [user2], project: project2, state: 'closed') }
@@ -148,7 +148,9 @@ describe IssuesFinder do
let(:params) { { label_name: [label.title, label2.title].join(',') } }
let(:label2) { create(:label, project: project2) }
- before { create(:label_link, label: label2, target: issue2) }
+ before do
+ create(:label_link, label: label2, target: issue2)
+ end
it 'returns the unique issues with any of those labels' do
expect(issues).to contain_exactly(issue2)
@@ -213,6 +215,24 @@ describe IssuesFinder do
end
end
+ context 'filtering by created_at' do
+ context 'through created_after' do
+ let(:params) { { created_after: issue3.created_at } }
+
+ it 'returns issues created on or after the given date' do
+ expect(issues).to contain_exactly(issue3)
+ end
+ end
+
+ context 'through created_before' do
+ let(:params) { { created_before: issue1.created_at + 1.second } }
+
+ it 'returns issues created on or before the given date' do
+ expect(issues).to contain_exactly(issue1)
+ end
+ end
+ end
+
context 'when the user is unauthorized' do
let(:search_user) { nil }
diff --git a/spec/finders/merge_requests_finder_spec.rb b/spec/finders/merge_requests_finder_spec.rb
index 58b7cd5e098..5eb26de6c92 100644
--- a/spec/finders/merge_requests_finder_spec.rb
+++ b/spec/finders/merge_requests_finder_spec.rb
@@ -46,5 +46,47 @@ describe MergeRequestsFinder do
expect(merge_requests).to contain_exactly(merge_request1)
end
+
+ context 'with created_after and created_before params' do
+ let(:project4) { create(:empty_project, forked_from_project: project1) }
+
+ let!(:new_merge_request) do
+ create(:merge_request,
+ :simple,
+ author: user,
+ created_at: 1.week.from_now,
+ source_project: project4,
+ target_project: project1)
+ end
+
+ let!(:old_merge_request) do
+ create(:merge_request,
+ :simple,
+ author: user,
+ created_at: 1.week.ago,
+ source_project: project4,
+ target_project: project4)
+ end
+
+ before do
+ project4.add_master(user)
+ end
+
+ it 'filters by created_after' do
+ params = { project_id: project1.id, created_after: new_merge_request.created_at }
+
+ merge_requests = described_class.new(user, params).execute
+
+ expect(merge_requests).to contain_exactly(new_merge_request)
+ end
+
+ it 'filters by created_before' do
+ params = { project_id: project4.id, created_before: old_merge_request.created_at + 1.second }
+
+ merge_requests = described_class.new(user, params).execute
+
+ expect(merge_requests).to contain_exactly(old_merge_request)
+ end
+ end
end
end
diff --git a/spec/finders/personal_access_tokens_finder_spec.rb b/spec/finders/personal_access_tokens_finder_spec.rb
index fd92664ca24..3f22b3a253d 100644
--- a/spec/finders/personal_access_tokens_finder_spec.rb
+++ b/spec/finders/personal_access_tokens_finder_spec.rb
@@ -25,49 +25,65 @@ describe PersonalAccessTokensFinder do
end
describe 'without impersonation' do
- before { params[:impersonation] = false }
+ before do
+ params[:impersonation] = false
+ end
it { is_expected.to contain_exactly(active_personal_access_token, revoked_personal_access_token, expired_personal_access_token) }
describe 'with active state' do
- before { params[:state] = 'active' }
+ before do
+ params[:state] = 'active'
+ end
it { is_expected.to contain_exactly(active_personal_access_token) }
end
describe 'with inactive state' do
- before { params[:state] = 'inactive' }
+ before do
+ params[:state] = 'inactive'
+ end
it { is_expected.to contain_exactly(revoked_personal_access_token, expired_personal_access_token) }
end
end
describe 'with impersonation' do
- before { params[:impersonation] = true }
+ before do
+ params[:impersonation] = true
+ end
it { is_expected.to contain_exactly(active_impersonation_token, revoked_impersonation_token, expired_impersonation_token) }
describe 'with active state' do
- before { params[:state] = 'active' }
+ before do
+ params[:state] = 'active'
+ end
it { is_expected.to contain_exactly(active_impersonation_token) }
end
describe 'with inactive state' do
- before { params[:state] = 'inactive' }
+ before do
+ params[:state] = 'inactive'
+ end
it { is_expected.to contain_exactly(revoked_impersonation_token, expired_impersonation_token) }
end
end
describe 'with active state' do
- before { params[:state] = 'active' }
+ before do
+ params[:state] = 'active'
+ end
it { is_expected.to contain_exactly(active_personal_access_token, active_impersonation_token) }
end
describe 'with inactive state' do
- before { params[:state] = 'inactive' }
+ before do
+ params[:state] = 'inactive'
+ end
it do
is_expected.to contain_exactly(expired_personal_access_token, revoked_personal_access_token,
@@ -81,7 +97,9 @@ describe PersonalAccessTokensFinder do
it { is_expected.to eq(active_personal_access_token) }
describe 'with impersonation' do
- before { params[:impersonation] = true }
+ before do
+ params[:impersonation] = true
+ end
it { is_expected.to be_nil }
end
@@ -93,7 +111,9 @@ describe PersonalAccessTokensFinder do
it { is_expected.to eq(active_personal_access_token) }
describe 'with impersonation' do
- before { params[:impersonation] = true }
+ before do
+ params[:impersonation] = true
+ end
it { is_expected.to be_nil }
end
@@ -109,7 +129,9 @@ describe PersonalAccessTokensFinder do
let!(:other_user_expired_impersonation_token) { create(:personal_access_token, :expired, :impersonation, user: user2) }
let!(:other_user_revoked_impersonation_token) { create(:personal_access_token, :revoked, :impersonation, user: user2) }
- before { params[:user] = user }
+ before do
+ params[:user] = user
+ end
it do
is_expected.to contain_exactly(active_personal_access_token, active_impersonation_token,
@@ -118,49 +140,65 @@ describe PersonalAccessTokensFinder do
end
describe 'without impersonation' do
- before { params[:impersonation] = false }
+ before do
+ params[:impersonation] = false
+ end
it { is_expected.to contain_exactly(active_personal_access_token, revoked_personal_access_token, expired_personal_access_token) }
describe 'with active state' do
- before { params[:state] = 'active' }
+ before do
+ params[:state] = 'active'
+ end
it { is_expected.to contain_exactly(active_personal_access_token) }
end
describe 'with inactive state' do
- before { params[:state] = 'inactive' }
+ before do
+ params[:state] = 'inactive'
+ end
it { is_expected.to contain_exactly(revoked_personal_access_token, expired_personal_access_token) }
end
end
describe 'with impersonation' do
- before { params[:impersonation] = true }
+ before do
+ params[:impersonation] = true
+ end
it { is_expected.to contain_exactly(active_impersonation_token, revoked_impersonation_token, expired_impersonation_token) }
describe 'with active state' do
- before { params[:state] = 'active' }
+ before do
+ params[:state] = 'active'
+ end
it { is_expected.to contain_exactly(active_impersonation_token) }
end
describe 'with inactive state' do
- before { params[:state] = 'inactive' }
+ before do
+ params[:state] = 'inactive'
+ end
it { is_expected.to contain_exactly(revoked_impersonation_token, expired_impersonation_token) }
end
end
describe 'with active state' do
- before { params[:state] = 'active' }
+ before do
+ params[:state] = 'active'
+ end
it { is_expected.to contain_exactly(active_personal_access_token, active_impersonation_token) }
end
describe 'with inactive state' do
- before { params[:state] = 'inactive' }
+ before do
+ params[:state] = 'inactive'
+ end
it do
is_expected.to contain_exactly(expired_personal_access_token, revoked_personal_access_token,
@@ -174,7 +212,9 @@ describe PersonalAccessTokensFinder do
it { is_expected.to eq(active_personal_access_token) }
describe 'with impersonation' do
- before { params[:impersonation] = true }
+ before do
+ params[:impersonation] = true
+ end
it { is_expected.to be_nil }
end
@@ -186,7 +226,9 @@ describe PersonalAccessTokensFinder do
it { is_expected.to eq(active_personal_access_token) }
describe 'with impersonation' do
- before { params[:impersonation] = true }
+ before do
+ params[:impersonation] = true
+ end
it { is_expected.to be_nil }
end
diff --git a/spec/finders/personal_projects_finder_spec.rb b/spec/finders/personal_projects_finder_spec.rb
index e0e17af681a..304b0fb67fb 100644
--- a/spec/finders/personal_projects_finder_spec.rb
+++ b/spec/finders/personal_projects_finder_spec.rb
@@ -32,7 +32,9 @@ describe PersonalProjectsFinder do
end
context 'external' do
- before { current_user.update_attributes(external: true) }
+ before do
+ current_user.update_attributes(external: true)
+ end
it { is_expected.to eq([private_project, public_project]) }
end
diff --git a/spec/finders/pipelines_finder_spec.rb b/spec/finders/pipelines_finder_spec.rb
index f2aeda241c1..2b19cda35b0 100644
--- a/spec/finders/pipelines_finder_spec.rb
+++ b/spec/finders/pipelines_finder_spec.rb
@@ -170,7 +170,7 @@ describe PipelinesFinder do
context 'when order_by and sort are specified' do
context 'when order_by user_id' do
let(:params) { { order_by: 'user_id', sort: 'asc' } }
- let!(:pipelines) { create_list(:ci_pipeline, 2, project: project, user: create(:user)) }
+ let!(:pipelines) { Array.new(2) { create(:ci_pipeline, project: project, user: create(:user)) } }
it 'sorts as user_id: :asc' do
is_expected.to match_array(pipelines)
diff --git a/spec/finders/todos_finder_spec.rb b/spec/finders/todos_finder_spec.rb
index f7e7e733cf7..8be447418b0 100644
--- a/spec/finders/todos_finder_spec.rb
+++ b/spec/finders/todos_finder_spec.rb
@@ -6,7 +6,9 @@ describe TodosFinder do
let(:project) { create(:empty_project) }
let(:finder) { described_class }
- before { project.team << [user, :developer] }
+ before do
+ project.team << [user, :developer]
+ end
describe '#sort' do
context 'by date' do
diff --git a/spec/helpers/application_helper_spec.rb b/spec/helpers/application_helper_spec.rb
index 49df91b236f..cc7f889b927 100644
--- a/spec/helpers/application_helper_spec.rb
+++ b/spec/helpers/application_helper_spec.rb
@@ -257,4 +257,24 @@ describe ApplicationHelper do
it { expect(helper.active_when(true)).to eq('active') }
it { expect(helper.active_when(false)).to eq(nil) }
end
+
+ describe '#support_url' do
+ context 'when alternate support url is specified' do
+ let(:alternate_url) { 'http://company.example.com/getting-help' }
+
+ before do
+ allow(current_application_settings).to receive(:help_page_support_url) { alternate_url }
+ end
+
+ it 'returns the alternate support url' do
+ expect(helper.support_url).to eq(alternate_url)
+ end
+ end
+
+ context 'when alternate support url is not specified' do
+ it 'builds the support url from the promo_url' do
+ expect(helper.support_url).to eq(helper.promo_url + '/getting-help/')
+ end
+ end
+ end
end
diff --git a/spec/helpers/blame_helper_spec.rb b/spec/helpers/blame_helper_spec.rb
new file mode 100644
index 00000000000..b4368516d83
--- /dev/null
+++ b/spec/helpers/blame_helper_spec.rb
@@ -0,0 +1,59 @@
+require 'spec_helper'
+
+describe BlameHelper do
+ describe '#get_age_map_start_date' do
+ let(:dates) do
+ [Time.zone.local(2014, 3, 17, 0, 0, 0),
+ Time.zone.local(2011, 11, 2, 0, 0, 0),
+ Time.zone.local(2015, 7, 9, 0, 0, 0),
+ Time.zone.local(2013, 2, 24, 0, 0, 0),
+ Time.zone.local(2010, 9, 22, 0, 0, 0)]
+ end
+ let(:blame_groups) do
+ [
+ { commit: double(committed_date: dates[0]) },
+ { commit: double(committed_date: dates[1]) },
+ { commit: double(committed_date: dates[2]) }
+ ]
+ end
+
+ it 'returns the earliest date from a blame group' do
+ project = double(created_at: dates[3])
+
+ duration = helper.age_map_duration(blame_groups, project)
+
+ expect(duration[:started_days_ago]).to eq((duration[:now] - dates[1]).to_i / 1.day)
+ end
+
+ it 'returns the earliest date from a project' do
+ project = double(created_at: dates[4])
+
+ duration = helper.age_map_duration(blame_groups, project)
+
+ expect(duration[:started_days_ago]).to eq((duration[:now] - dates[4]).to_i / 1.day)
+ end
+ end
+
+ describe '#age_map_class' do
+ let(:dates) do
+ [Time.zone.local(2014, 3, 17, 0, 0, 0)]
+ end
+ let(:blame_groups) do
+ [
+ { commit: double(committed_date: dates[0]) }
+ ]
+ end
+ let(:duration) do
+ project = double(created_at: dates[0])
+ helper.age_map_duration(blame_groups, project)
+ end
+
+ it 'returns blame-commit-age-9 when oldest' do
+ expect(helper.age_map_class(dates[0], duration)).to eq 'blame-commit-age-9'
+ end
+
+ it 'returns blame-commit-age-0 class when newest' do
+ expect(helper.age_map_class(duration[:now], duration)).to eq 'blame-commit-age-0'
+ end
+ end
+end
diff --git a/spec/helpers/diff_helper_spec.rb b/spec/helpers/diff_helper_spec.rb
index a74615e07f9..0d909e6e140 100644
--- a/spec/helpers/diff_helper_spec.rb
+++ b/spec/helpers/diff_helper_spec.rb
@@ -8,7 +8,7 @@ describe DiffHelper do
let(:commit) { project.commit(sample_commit.id) }
let(:diffs) { commit.raw_diffs }
let(:diff) { diffs.first }
- let(:diff_refs) { [commit.parent, commit] }
+ let(:diff_refs) { commit.diff_refs }
let(:diff_file) { Gitlab::Diff::File.new(diff, diff_refs: diff_refs, repository: repository) }
describe 'diff_view' do
@@ -148,12 +148,21 @@ describe DiffHelper do
it 'puts comments on added lines' do
left = Gitlab::Diff::Line.new('\\nonewline', 'old-nonewline', 3, 3, 3)
- right = Gitlab::Diff::Line.new('new line', 'add', 3, 3, 3)
+ right = Gitlab::Diff::Line.new('new line', 'new', 3, 3, 3)
result = helper.parallel_diff_discussions(left, right, diff_file)
expect(result).to eq([nil, 'comment'])
end
+
+ it 'puts comments on unchanged lines' do
+ left = Gitlab::Diff::Line.new('unchanged line', nil, 3, 3, 3)
+ right = Gitlab::Diff::Line.new('unchanged line', nil, 3, 3, 3)
+
+ result = helper.parallel_diff_discussions(left, right, diff_file)
+
+ expect(result).to eq(['comment', nil])
+ end
end
describe "#diff_match_line" do
@@ -207,4 +216,41 @@ describe DiffHelper do
expect(output).not_to have_css 'td:nth-child(3)'
end
end
+
+ context 'viewer related' do
+ let(:viewer) { diff_file.simple_viewer }
+
+ before do
+ assign(:project, project)
+ end
+
+ describe '#diff_render_error_reason' do
+ context 'for error :too_large' do
+ before do
+ expect(viewer).to receive(:render_error).and_return(:too_large)
+ end
+
+ it 'returns an error message' do
+ expect(helper.diff_render_error_reason(viewer)).to eq('it is too large')
+ end
+ end
+
+ context 'for error :server_side_but_stored_externally' do
+ before do
+ expect(viewer).to receive(:render_error).and_return(:server_side_but_stored_externally)
+ expect(diff_file).to receive(:external_storage).and_return(:lfs)
+ end
+
+ it 'returns an error message' do
+ expect(helper.diff_render_error_reason(viewer)).to eq('it is stored in LFS')
+ end
+ end
+ end
+
+ describe '#diff_render_error_options' do
+ it 'includes a "view the blob" link' do
+ expect(helper.diff_render_error_options(viewer)).to include(/view the blob/)
+ end
+ end
+ end
end
diff --git a/spec/helpers/projects_helper_spec.rb b/spec/helpers/projects_helper_spec.rb
index a695621b87a..9a4086725d2 100644
--- a/spec/helpers/projects_helper_spec.rb
+++ b/spec/helpers/projects_helper_spec.rb
@@ -250,7 +250,9 @@ describe ProjectsHelper do
end
context "when project is private" do
- before { project.update_attributes(visibility_level: Gitlab::VisibilityLevel::PRIVATE) }
+ before do
+ project.update_attributes(visibility_level: Gitlab::VisibilityLevel::PRIVATE)
+ end
it "shows only allowed options" do
helper.instance_variable_set(:@project, project)
@@ -300,4 +302,37 @@ describe ProjectsHelper do
expect(helper.send(:visibility_select_options, project, Gitlab::VisibilityLevel::PRIVATE)).to include('Private')
end
end
+
+ describe '#get_project_nav_tabs' do
+ let(:project) { create(:empty_project) }
+ let(:user) { create(:user) }
+
+ before do
+ allow(helper).to receive(:can?) { true }
+ end
+
+ subject do
+ helper.send(:get_project_nav_tabs, project, user)
+ end
+
+ context 'when builds feature is enabled' do
+ before do
+ allow(project).to receive(:builds_enabled?).and_return(true)
+ end
+
+ it "does include pipelines tab" do
+ is_expected.to include(:pipelines)
+ end
+ end
+
+ context 'when builds feature is disabled' do
+ before do
+ allow(project).to receive(:builds_enabled?).and_return(false)
+ end
+
+ it "do not include pipelines tab" do
+ is_expected.not_to include(:pipelines)
+ end
+ end
+ end
end
diff --git a/spec/initializers/8_metrics_spec.rb b/spec/initializers/8_metrics_spec.rb
index 570754621f3..a507d7f7f2b 100644
--- a/spec/initializers/8_metrics_spec.rb
+++ b/spec/initializers/8_metrics_spec.rb
@@ -7,6 +7,7 @@ describe 'instrument_classes', lib: true do
before do
allow(config).to receive(:instrument_method)
allow(config).to receive(:instrument_methods)
+ allow(config).to receive(:instrument_instance_method)
allow(config).to receive(:instrument_instance_methods)
end
diff --git a/spec/javascripts/bootstrap_linked_tabs_spec.js b/spec/javascripts/bootstrap_linked_tabs_spec.js
index a27dc48b3fd..93dc60d59fe 100644
--- a/spec/javascripts/bootstrap_linked_tabs_spec.js
+++ b/spec/javascripts/bootstrap_linked_tabs_spec.js
@@ -1,15 +1,6 @@
import LinkedTabs from '~/lib/utils/bootstrap_linked_tabs';
(() => {
- // TODO: remove this hack!
- // PhantomJS causes spyOn to panic because replaceState isn't "writable"
- let phantomjs;
- try {
- phantomjs = !Object.getOwnPropertyDescriptor(window.history, 'replaceState').writable;
- } catch (err) {
- phantomjs = false;
- }
-
describe('Linked Tabs', () => {
preloadFixtures('static/linked_tabs.html.raw');
@@ -19,9 +10,7 @@ import LinkedTabs from '~/lib/utils/bootstrap_linked_tabs';
describe('when is initialized', () => {
beforeEach(() => {
- if (!phantomjs) {
- spyOn(window.history, 'replaceState').and.callFake(function () {});
- }
+ spyOn(window.history, 'replaceState').and.callFake(function () {});
});
it('should activate the tab correspondent to the given action', () => {
@@ -47,7 +36,7 @@ import LinkedTabs from '~/lib/utils/bootstrap_linked_tabs';
describe('on click', () => {
it('should change the url according to the clicked tab', () => {
- const historySpy = !phantomjs && spyOn(history, 'replaceState').and.callFake(() => {});
+ const historySpy = spyOn(history, 'replaceState').and.callFake(() => {});
const linkedTabs = new LinkedTabs({
action: 'show',
diff --git a/spec/javascripts/commits_spec.js b/spec/javascripts/commits_spec.js
index 44a4386b250..ace95000468 100644
--- a/spec/javascripts/commits_spec.js
+++ b/spec/javascripts/commits_spec.js
@@ -5,15 +5,6 @@ import '~/pager';
import '~/commits';
(() => {
- // TODO: remove this hack!
- // PhantomJS causes spyOn to panic because replaceState isn't "writable"
- let phantomjs;
- try {
- phantomjs = !Object.getOwnPropertyDescriptor(window.history, 'replaceState').writable;
- } catch (err) {
- phantomjs = false;
- }
-
describe('Commits List', () => {
beforeEach(() => {
setFixtures(`
@@ -61,9 +52,7 @@ import '~/commits';
CommitsList.init(25);
CommitsList.searchField.val('');
- if (!phantomjs) {
- spyOn(history, 'replaceState').and.stub();
- }
+ spyOn(history, 'replaceState').and.stub();
ajaxSpy = spyOn(jQuery, 'ajax').and.callFake((req) => {
req.success({
data: '<li>Result</li>',
diff --git a/spec/javascripts/groups/mock_data.js b/spec/javascripts/groups/mock_data.js
index 1c0ec7a97d0..b3f5d791b89 100644
--- a/spec/javascripts/groups/mock_data.js
+++ b/spec/javascripts/groups/mock_data.js
@@ -6,6 +6,7 @@ const group1 = {
visibility: 'public',
avatar_url: null,
web_url: 'http://localhost:3000/groups/level1',
+ group_path: '/level1',
full_name: 'level1',
full_path: 'level1',
parent_id: null,
@@ -28,6 +29,7 @@ const group14 = {
visibility: 'public',
avatar_url: null,
web_url: 'http://localhost:3000/groups/level1/level2/level3/level4',
+ group_path: '/level1/level2/level3/level4',
full_name: 'level1 / level2 / level3 / level4',
full_path: 'level1/level2/level3/level4',
parent_id: 1127,
@@ -49,6 +51,7 @@ const group2 = {
visibility: 'public',
avatar_url: null,
web_url: 'http://localhost:3000/groups/devops',
+ group_path: '/devops',
full_name: 'devops',
full_path: 'devops',
parent_id: null,
@@ -70,6 +73,7 @@ const group21 = {
visibility: 'public',
avatar_url: null,
web_url: 'http://localhost:3000/groups/devops/chef',
+ group_path: '/devops/chef',
full_name: 'devops / chef',
full_path: 'devops/chef',
parent_id: 1119,
diff --git a/spec/javascripts/issue_show/components/app_spec.js b/spec/javascripts/issue_show/components/app_spec.js
index 59c006aa0af..2ccc4f16192 100644
--- a/spec/javascripts/issue_show/components/app_spec.js
+++ b/spec/javascripts/issue_show/components/app_spec.js
@@ -126,7 +126,7 @@ describe('Issuable output', () => {
describe('updateIssuable', () => {
it('fetches new data after update', (done) => {
- spyOn(vm.service, 'getData');
+ spyOn(vm.service, 'getData').and.callThrough();
spyOn(vm.service, 'updateIssuable').and.callFake(() => new Promise((resolve) => {
resolve({
json() {
diff --git a/spec/javascripts/merge_request_tabs_spec.js b/spec/javascripts/merge_request_tabs_spec.js
index 7b910282cc8..9916d2c1e21 100644
--- a/spec/javascripts/merge_request_tabs_spec.js
+++ b/spec/javascripts/merge_request_tabs_spec.js
@@ -12,15 +12,6 @@ import '~/notes';
import 'vendor/jquery.scrollTo';
(function () {
- // TODO: remove this hack!
- // PhantomJS causes spyOn to panic because replaceState isn't "writable"
- var phantomjs;
- try {
- phantomjs = !Object.getOwnPropertyDescriptor(window.history, 'replaceState').writable;
- } catch (err) {
- phantomjs = false;
- }
-
describe('MergeRequestTabs', function () {
var stubLocation = {};
var setLocation = function (stubs) {
@@ -37,11 +28,9 @@ import 'vendor/jquery.scrollTo';
this.class = new gl.MergeRequestTabs({ stubLocation: stubLocation });
setLocation();
- if (!phantomjs) {
- this.spies = {
- history: spyOn(window.history, 'replaceState').and.callFake(function () {})
- };
- }
+ this.spies = {
+ history: spyOn(window.history, 'replaceState').and.callFake(function () {})
+ };
});
afterEach(function () {
@@ -208,11 +197,9 @@ import 'vendor/jquery.scrollTo';
pathname: '/foo/bar/merge_requests/1'
});
newState = this.subject('commits');
- if (!phantomjs) {
- expect(this.spies.history).toHaveBeenCalledWith({
- url: newState
- }, document.title, newState);
- }
+ expect(this.spies.history).toHaveBeenCalledWith({
+ url: newState
+ }, document.title, newState);
});
it('treats "show" like "notes"', function () {
diff --git a/spec/javascripts/notes_spec.js b/spec/javascripts/notes_spec.js
index bfd8b8648a6..b272f43a166 100644
--- a/spec/javascripts/notes_spec.js
+++ b/spec/javascripts/notes_spec.js
@@ -126,6 +126,7 @@ import '~/notes';
const deferred = $.Deferred();
spyOn($, 'ajax').and.returnValue(deferred.promise());
spyOn(this.notes, 'revertNoteEditForm');
+ spyOn(this.notes, 'setupNewNote');
$('.js-comment-button').click();
deferred.resolve(noteEntity);
@@ -136,6 +137,46 @@ import '~/notes';
this.notes.updateNote(updatedNote, $targetNote);
expect(this.notes.revertNoteEditForm).toHaveBeenCalledWith($targetNote);
+ expect(this.notes.setupNewNote).toHaveBeenCalled();
+ });
+ });
+
+ describe('updateNoteTargetSelector', () => {
+ const hash = 'note_foo';
+ let $note;
+
+ beforeEach(() => {
+ $note = $(`<div id="${hash}"></div>`);
+ spyOn($note, 'filter').and.callThrough();
+ spyOn($note, 'toggleClass').and.callThrough();
+ });
+
+ it('sets target when hash matches', () => {
+ spyOn(gl.utils, 'getLocationHash');
+ gl.utils.getLocationHash.and.returnValue(hash);
+
+ Notes.updateNoteTargetSelector($note);
+
+ expect($note.filter).toHaveBeenCalledWith(`#${hash}`);
+ expect($note.toggleClass).toHaveBeenCalledWith('target', true);
+ });
+
+ it('unsets target when hash does not match', () => {
+ spyOn(gl.utils, 'getLocationHash');
+ gl.utils.getLocationHash.and.returnValue('note_doesnotexist');
+
+ Notes.updateNoteTargetSelector($note);
+
+ expect($note.toggleClass).toHaveBeenCalledWith('target', false);
+ });
+
+ it('unsets target when there is not a hash fragment anymore', () => {
+ spyOn(gl.utils, 'getLocationHash');
+ gl.utils.getLocationHash.and.returnValue(null);
+
+ Notes.updateNoteTargetSelector($note);
+
+ expect($note.toggleClass).toHaveBeenCalledWith('target', null);
});
});
@@ -189,9 +230,13 @@ import '~/notes';
Notes.isUpdatedNote.and.returnValue(true);
const $note = $('<div>');
$notesList.find.and.returnValue($note);
+ const $newNote = $(note.html);
+ Notes.animateUpdateNote.and.returnValue($newNote);
+
Notes.prototype.renderNote.call(notes, note, null, $notesList);
expect(Notes.animateUpdateNote).toHaveBeenCalledWith(note.html, $note);
+ expect(notes.setupNewNote).toHaveBeenCalledWith($newNote);
});
describe('while editing', () => {
@@ -378,6 +423,23 @@ import '~/notes';
});
});
+ describe('putEditFormInPlace', () => {
+ it('should call gl.GLForm with GFM parameter passed through', () => {
+ spyOn(gl, 'GLForm');
+
+ const $el = jasmine.createSpyObj('$form', ['find', 'closest']);
+ $el.find.and.returnValue($('<div>'));
+ $el.closest.and.returnValue($('<div>'));
+
+ Notes.prototype.putEditFormInPlace.call({
+ getEditFormSelector: () => '',
+ enableGFM: true
+ }, $el);
+
+ expect(gl.GLForm).toHaveBeenCalledWith(jasmine.any(Object), true);
+ });
+ });
+
describe('postComment & updateComment', () => {
const sampleComment = 'foo';
const updatedComment = 'bar';
@@ -533,46 +595,46 @@ import '~/notes';
});
});
- describe('hasSlashCommands', () => {
+ describe('hasQuickActions', () => {
beforeEach(() => {
this.notes = new Notes('', []);
});
- it('should return true when comment begins with a slash command', () => {
+ it('should return true when comment begins with a quick action', () => {
const sampleComment = '/wip\n/milestone %1.0\n/merge\n/unassign Merging this';
- const hasSlashCommands = this.notes.hasSlashCommands(sampleComment);
+ const hasQuickActions = this.notes.hasQuickActions(sampleComment);
- expect(hasSlashCommands).toBeTruthy();
+ expect(hasQuickActions).toBeTruthy();
});
- it('should return false when comment does NOT begin with a slash command', () => {
+ it('should return false when comment does NOT begin with a quick action', () => {
const sampleComment = 'Hey, /unassign Merging this';
- const hasSlashCommands = this.notes.hasSlashCommands(sampleComment);
+ const hasQuickActions = this.notes.hasQuickActions(sampleComment);
- expect(hasSlashCommands).toBeFalsy();
+ expect(hasQuickActions).toBeFalsy();
});
- it('should return false when comment does NOT have any slash commands', () => {
+ it('should return false when comment does NOT have any quick actions', () => {
const sampleComment = 'Looking good, Awesome!';
- const hasSlashCommands = this.notes.hasSlashCommands(sampleComment);
+ const hasQuickActions = this.notes.hasQuickActions(sampleComment);
- expect(hasSlashCommands).toBeFalsy();
+ expect(hasQuickActions).toBeFalsy();
});
});
- describe('stripSlashCommands', () => {
- it('should strip slash commands from the comment which begins with a slash command', () => {
+ describe('stripQuickActions', () => {
+ it('should strip quick actions from the comment which begins with a quick action', () => {
this.notes = new Notes();
const sampleComment = '/wip\n/milestone %1.0\n/merge\n/unassign Merging this';
- const stripedComment = this.notes.stripSlashCommands(sampleComment);
+ const stripedComment = this.notes.stripQuickActions(sampleComment);
expect(stripedComment).toBe('');
});
- it('should strip slash commands from the comment but leaves plain comment if it is present', () => {
+ it('should strip quick actions from the comment but leaves plain comment if it is present', () => {
this.notes = new Notes();
const sampleComment = '/wip\n/milestone %1.0\n/merge\n/unassign\nMerging this';
- const stripedComment = this.notes.stripSlashCommands(sampleComment);
+ const stripedComment = this.notes.stripQuickActions(sampleComment);
expect(stripedComment).toBe('Merging this');
});
@@ -580,14 +642,14 @@ import '~/notes';
it('should NOT strip string that has slashes within', () => {
this.notes = new Notes();
const sampleComment = 'http://127.0.0.1:3000/root/gitlab-shell/issues/1';
- const stripedComment = this.notes.stripSlashCommands(sampleComment);
+ const stripedComment = this.notes.stripQuickActions(sampleComment);
expect(stripedComment).toBe(sampleComment);
});
});
- describe('getSlashCommandDescription', () => {
- const availableSlashCommands = [
+ describe('getQuickActionDescription', () => {
+ const availableQuickActions = [
{ name: 'close', description: 'Close this issue', params: [] },
{ name: 'title', description: 'Change title', params: [{}] },
{ name: 'estimate', description: 'Set time estimate', params: [{}] }
@@ -597,19 +659,19 @@ import '~/notes';
this.notes = new Notes();
});
- it('should return executing slash command description when note has single slash command', () => {
+ it('should return executing quick action description when note has single quick action', () => {
const sampleComment = '/close';
- expect(this.notes.getSlashCommandDescription(sampleComment, availableSlashCommands)).toBe('Applying command to close this issue');
+ expect(this.notes.getQuickActionDescription(sampleComment, availableQuickActions)).toBe('Applying command to close this issue');
});
- it('should return generic multiple slash command description when note has multiple slash commands', () => {
+ it('should return generic multiple quick action description when note has multiple quick actions', () => {
const sampleComment = '/close\n/title [Duplicate] Issue foobar';
- expect(this.notes.getSlashCommandDescription(sampleComment, availableSlashCommands)).toBe('Applying multiple commands');
+ expect(this.notes.getQuickActionDescription(sampleComment, availableQuickActions)).toBe('Applying multiple commands');
});
- it('should return generic slash command description when available slash commands list is not populated', () => {
+ it('should return generic quick action description when available quick actions list is not populated', () => {
const sampleComment = '/close\n/title [Duplicate] Issue foobar';
- expect(this.notes.getSlashCommandDescription(sampleComment)).toBe('Applying command');
+ expect(this.notes.getQuickActionDescription(sampleComment)).toBe('Applying command');
});
});
diff --git a/spec/javascripts/pipeline_schedules/interval_pattern_input_spec.js b/spec/javascripts/pipeline_schedules/interval_pattern_input_spec.js
index 845b371d90c..56c57d94798 100644
--- a/spec/javascripts/pipeline_schedules/interval_pattern_input_spec.js
+++ b/spec/javascripts/pipeline_schedules/interval_pattern_input_spec.js
@@ -95,7 +95,7 @@ describe('Interval Pattern Input Component', function () {
describe('User Actions', function () {
beforeEach(function () {
- // For an unknown reason, Phantom.js doesn't trigger click events
+ // For an unknown reason, some browsers do not propagate click events
// on radio buttons in a way Vue can register. So, we have to mount
// to a fixture.
setFixtures('<div id="my-mount"></div>');
diff --git a/spec/javascripts/pipelines/pipeline_url_spec.js b/spec/javascripts/pipelines/pipeline_url_spec.js
index 594a9856d2c..3c4b20a5f06 100644
--- a/spec/javascripts/pipelines/pipeline_url_spec.js
+++ b/spec/javascripts/pipelines/pipeline_url_spec.js
@@ -19,7 +19,7 @@ describe('Pipeline Url Component', () => {
},
}).$mount();
- expect(component.$el.tagName).toEqual('TD');
+ expect(component.$el.getAttribute('class')).toContain('table-section');
});
it('should render a link the provided path and id', () => {
@@ -94,7 +94,7 @@ describe('Pipeline Url Component', () => {
},
}).$mount();
- expect(component.$el.querySelector('.js-pipeline-url-lastest').textContent).toContain('latest');
+ expect(component.$el.querySelector('.js-pipeline-url-latest').textContent).toContain('latest');
expect(component.$el.querySelector('.js-pipeline-url-yaml').textContent).toContain('yaml invalid');
expect(component.$el.querySelector('.js-pipeline-url-stuck').textContent).toContain('stuck');
});
diff --git a/spec/javascripts/pipelines/pipelines_actions_spec.js b/spec/javascripts/pipelines/pipelines_actions_spec.js
index c89dacbcd93..8a58b77f1e3 100644
--- a/spec/javascripts/pipelines/pipelines_actions_spec.js
+++ b/spec/javascripts/pipelines/pipelines_actions_spec.js
@@ -1,5 +1,5 @@
import Vue from 'vue';
-import pipelinesActionsComp from '~/pipelines/components/pipelines_actions';
+import pipelinesActionsComp from '~/pipelines/components/pipelines_actions.vue';
describe('Pipelines Actions dropdown', () => {
let component;
diff --git a/spec/javascripts/pipelines/pipelines_artifacts_spec.js b/spec/javascripts/pipelines/pipelines_artifacts_spec.js
index 9724b63d957..acb67d0ec21 100644
--- a/spec/javascripts/pipelines/pipelines_artifacts_spec.js
+++ b/spec/javascripts/pipelines/pipelines_artifacts_spec.js
@@ -1,5 +1,5 @@
import Vue from 'vue';
-import artifactsComp from '~/pipelines/components/pipelines_artifacts';
+import artifactsComp from '~/pipelines/components/pipelines_artifacts.vue';
describe('Pipelines Artifacts dropdown', () => {
let component;
diff --git a/spec/javascripts/pipelines/pipelines_spec.js b/spec/javascripts/pipelines/pipelines_spec.js
index 3a56156358b..c30abb2edb0 100644
--- a/spec/javascripts/pipelines/pipelines_spec.js
+++ b/spec/javascripts/pipelines/pipelines_spec.js
@@ -1,5 +1,5 @@
import Vue from 'vue';
-import pipelinesComp from '~/pipelines/pipelines';
+import pipelinesComp from '~/pipelines/components/pipelines.vue';
import Store from '~/pipelines/stores/pipelines_store';
describe('Pipelines', () => {
diff --git a/spec/javascripts/pipelines/time_ago_spec.js b/spec/javascripts/pipelines/time_ago_spec.js
index 24581e8c672..42b34c82f89 100644
--- a/spec/javascripts/pipelines/time_ago_spec.js
+++ b/spec/javascripts/pipelines/time_ago_spec.js
@@ -1,5 +1,5 @@
import Vue from 'vue';
-import timeAgo from '~/pipelines/components/time_ago';
+import timeAgo from '~/pipelines/components/time_ago.vue';
describe('Timeago component', () => {
let TimeAgo;
diff --git a/spec/javascripts/pipelines_spec.js b/spec/javascripts/pipelines_spec.js
index 81ac589f4e6..c08a73851be 100644
--- a/spec/javascripts/pipelines_spec.js
+++ b/spec/javascripts/pipelines_spec.js
@@ -1,10 +1,5 @@
import Pipelines from '~/pipelines';
-// Fix for phantomJS
-if (!Element.prototype.matches && Element.prototype.webkitMatchesSelector) {
- Element.prototype.matches = Element.prototype.webkitMatchesSelector;
-}
-
describe('Pipelines', () => {
preloadFixtures('static/pipeline_graph.html.raw');
diff --git a/spec/javascripts/project_title_spec.js b/spec/javascripts/project_title_spec.js
index 3dba2e817ff..cc336180ff7 100644
--- a/spec/javascripts/project_title_spec.js
+++ b/spec/javascripts/project_title_spec.js
@@ -1,4 +1,3 @@
-/* eslint-disable space-before-function-paren, no-unused-expressions, no-return-assign, no-param-reassign, no-var, new-cap, wrap-iife, no-unused-vars, quotes, jasmine/no-expect-in-setup-teardown, max-len */
/* global Project */
import 'select2/select2';
@@ -7,47 +6,52 @@ import '~/api';
import '~/project_select';
import '~/project';
-(function() {
- describe('Project Title', function() {
- preloadFixtures('issues/open-issue.html.raw');
- loadJSONFixtures('projects.json');
+describe('Project Title', () => {
+ preloadFixtures('issues/open-issue.html.raw');
+ loadJSONFixtures('projects.json');
- beforeEach(function() {
- loadFixtures('issues/open-issue.html.raw');
+ beforeEach(() => {
+ loadFixtures('issues/open-issue.html.raw');
- window.gon = {};
- window.gon.api_version = 'v3';
+ window.gon = {};
+ window.gon.api_version = 'v3';
- return this.project = new Project();
- });
+ // eslint-disable-next-line no-new
+ new Project();
+ });
- describe('project list', function() {
- var fakeAjaxResponse = function fakeAjaxResponse(req) {
- var d;
- expect(req.url).toBe('/api/v3/projects.json?simple=true');
- expect(req.data).toEqual({ search: '', order_by: 'last_activity_at', per_page: 20, membership: true });
- d = $.Deferred();
- d.resolve(this.projects_data);
- return d.promise();
- };
-
- beforeEach((function(_this) {
- return function() {
- _this.projects_data = getJSONFixture('projects.json');
- return spyOn(jQuery, 'ajax').and.callFake(fakeAjaxResponse.bind(_this));
- };
- })(this));
- it('toggles dropdown', function() {
- var menu = $('.js-dropdown-menu-projects');
- $('.js-projects-dropdown-toggle').click();
- expect(menu).toHaveClass('open');
- menu.find('.dropdown-menu-close-icon').click();
- expect(menu).not.toHaveClass('open');
+ describe('project list', () => {
+ let reqUrl;
+ let reqData;
+
+ beforeEach(() => {
+ const fakeResponseData = getJSONFixture('projects.json');
+ spyOn(jQuery, 'ajax').and.callFake((req) => {
+ const def = $.Deferred();
+ reqUrl = req.url;
+ reqData = req.data;
+ def.resolve(fakeResponseData);
+ return def.promise();
});
});
- afterEach(() => {
- window.gon = {};
+ it('toggles dropdown', () => {
+ const $menu = $('.js-dropdown-menu-projects');
+ $('.js-projects-dropdown-toggle').click();
+ expect($menu).toHaveClass('open');
+ expect(reqUrl).toBe('/api/v3/projects.json?simple=true');
+ expect(reqData).toEqual({
+ search: '',
+ order_by: 'last_activity_at',
+ per_page: 20,
+ membership: true,
+ });
+ $menu.find('.dropdown-menu-close-icon').click();
+ expect($menu).not.toHaveClass('open');
});
});
-}).call(window);
+
+ afterEach(() => {
+ window.gon = {};
+ });
+});
diff --git a/spec/javascripts/test_bundle.js b/spec/javascripts/test_bundle.js
index 2c34402576b..729db02e06c 100644
--- a/spec/javascripts/test_bundle.js
+++ b/spec/javascripts/test_bundle.js
@@ -16,6 +16,14 @@ window.gl = window.gl || {};
window.gl.TEST_HOST = 'http://test.host';
window.gon = window.gon || {};
+// HACK: Chrome 59 disconnects if there are too many synchronous tests in a row
+// because it appears to lock up the thread that communicates to Karma's socket
+// This async beforeEach gets called on every spec and releases the JS thread long
+// enough for the socket to continue to communicate.
+// The downside is that it creates a minor performance penalty in the time it takes
+// to run our unit tests.
+beforeEach(done => done()); // eslint-disable-line jasmine/no-global-setup
+
// render all of our tests
const testsContext = require.context('.', true, /_spec$/);
testsContext.keys().forEach(function (path) {
diff --git a/spec/javascripts/vue_mr_widget/components/mr_widget_related_links_spec.js b/spec/javascripts/vue_mr_widget/components/mr_widget_related_links_spec.js
index f6e0c3dfb74..6a44c54cdee 100644
--- a/spec/javascripts/vue_mr_widget/components/mr_widget_related_links_spec.js
+++ b/spec/javascripts/vue_mr_widget/components/mr_widget_related_links_spec.js
@@ -1,20 +1,31 @@
import Vue from 'vue';
-import relatedLinksComponent from '~/vue_merge_request_widget/components/mr_widget_related_links';
+import MRWidgetRelatedLinks from '~/vue_merge_request_widget/components/mr_widget_related_links';
-const createComponent = (data) => {
- const Component = Vue.extend(relatedLinksComponent);
+describe('MRWidgetRelatedLinks', () => {
+ let vm;
+
+ beforeEach(() => {
+ const Component = Vue.extend(MRWidgetRelatedLinks);
+ vm = new Component({
+ el: document.createElement('div'),
+ propsData: {
+ isMerged: false,
+ relatedLinks: {},
+ },
+ });
+ });
- return new Component({
- el: document.createElement('div'),
- propsData: data,
+ afterEach(() => {
+ vm.$destroy();
});
-};
-describe('MRWidgetRelatedLinks', () => {
describe('props', () => {
it('should have props', () => {
- const { relatedLinks } = relatedLinksComponent.props;
+ const { isMerged, relatedLinks } = MRWidgetRelatedLinks.props;
+ expect(isMerged).toBeDefined();
+ expect(isMerged.type).toBe(Boolean);
+ expect(isMerged.required).toBeTruthy();
expect(relatedLinks).toBeDefined();
expect(relatedLinks.type instanceof Object).toBeTruthy();
expect(relatedLinks.required).toBeTruthy();
@@ -22,16 +33,38 @@ describe('MRWidgetRelatedLinks', () => {
});
describe('computed', () => {
+ describe('closingText', () => {
+ const dummyIssueLabel = 'dummy label';
+
+ beforeEach(() => {
+ spyOn(vm, 'issueLabel').and.returnValue(dummyIssueLabel);
+ });
+
+ it('outputs text for closing issues', () => {
+ vm.isMerged = false;
+
+ const text = vm.closingText;
+
+ expect(text).toBe(`Closes ${dummyIssueLabel}`);
+ });
+
+ it('outputs text for closed issues', () => {
+ vm.isMerged = true;
+
+ const text = vm.closingText;
+
+ expect(text).toBe(`Closed ${dummyIssueLabel}`);
+ });
+ });
+
describe('hasLinks', () => {
it('should return correct value when we have links reference', () => {
- const data = {
- relatedLinks: {
- closing: '/foo',
- mentioned: '/foo',
- assignToMe: '/foo',
- },
+ vm.relatedLinks = {
+ closing: '/foo',
+ mentioned: '/foo',
+ assignToMe: '/foo',
};
- const vm = createComponent(data);
+
expect(vm.hasLinks).toBeTruthy();
vm.relatedLinks.closing = null;
@@ -44,95 +77,160 @@ describe('MRWidgetRelatedLinks', () => {
expect(vm.hasLinks).toBeFalsy();
});
});
- });
- describe('methods', () => {
- const data = {
- relatedLinks: {
- closing: '<a href="#">#23</a> and <a>#42</a>',
- mentioned: '<a href="#">#7</a>',
- },
- };
- const vm = createComponent(data);
+ describe('mentionedText', () => {
+ it('outputs text for one mentioned issue before merging', () => {
+ vm.isMerged = false;
+ spyOn(vm, 'hasMultipleIssues').and.returnValue(false);
- describe('hasMultipleIssues', () => {
- it('should return true if the given text has multiple issues', () => {
- expect(vm.hasMultipleIssues(data.relatedLinks.closing)).toBeTruthy();
+ const text = vm.mentionedText;
+
+ expect(text).toBe('is mentioned but will not be closed');
});
- it('should return false if the given text has one issue', () => {
- expect(vm.hasMultipleIssues(data.relatedLinks.mentioned)).toBeFalsy();
+ it('outputs text for one mentioned issue after merging', () => {
+ vm.isMerged = true;
+ spyOn(vm, 'hasMultipleIssues').and.returnValue(false);
+
+ const text = vm.mentionedText;
+
+ expect(text).toBe('is mentioned but was not closed');
+ });
+
+ it('outputs text for multiple mentioned issue before merging', () => {
+ vm.isMerged = false;
+ spyOn(vm, 'hasMultipleIssues').and.returnValue(true);
+
+ const text = vm.mentionedText;
+
+ expect(text).toBe('are mentioned but will not be closed');
+ });
+
+ it('outputs text for multiple mentioned issue after merging', () => {
+ vm.isMerged = true;
+ spyOn(vm, 'hasMultipleIssues').and.returnValue(true);
+
+ const text = vm.mentionedText;
+
+ expect(text).toBe('are mentioned but were not closed');
});
});
+ });
- describe('issueLabel', () => {
+ describe('methods', () => {
+ const relatedLinks = {
+ oneIssue: '<a href="#">#7</a>',
+ twoIssues: '<a href="#">#23</a> and <a>#42</a>',
+ threeIssues: '<a href="#">#1</a>, <a>#2</a>, and <a>#3</a>',
+ };
+
+ beforeEach(() => {
+ vm.relatedLinks = relatedLinks;
+ });
+
+ describe('hasMultipleIssues', () => {
it('should return true if the given text has multiple issues', () => {
- expect(vm.issueLabel('closing')).toEqual('issues');
+ expect(vm.hasMultipleIssues(relatedLinks.twoIssues)).toBeTruthy();
+ expect(vm.hasMultipleIssues(relatedLinks.threeIssues)).toBeTruthy();
});
it('should return false if the given text has one issue', () => {
- expect(vm.issueLabel('mentioned')).toEqual('issue');
+ expect(vm.hasMultipleIssues(relatedLinks.oneIssue)).toBeFalsy();
});
});
- describe('verbLabel', () => {
+ describe('issueLabel', () => {
it('should return true if the given text has multiple issues', () => {
- expect(vm.verbLabel('closing')).toEqual('are');
+ expect(vm.issueLabel('twoIssues')).toEqual('issues');
+ expect(vm.issueLabel('threeIssues')).toEqual('issues');
});
it('should return false if the given text has one issue', () => {
- expect(vm.verbLabel('mentioned')).toEqual('is');
+ expect(vm.issueLabel('oneIssue')).toEqual('issue');
});
});
});
describe('template', () => {
- it('should have only have closing issues text', () => {
- const vm = createComponent({
- relatedLinks: {
- closing: '<a href="#">#23</a> and <a>#42</a>',
- },
- });
- const content = vm.$el.textContent.replace(/\n(\s)+/g, ' ').trim();
-
- expect(content).toContain('Closes issues #23 and #42');
- expect(content).not.toContain('mentioned');
- });
+ it('should have only have closing issues text', (done) => {
+ vm.relatedLinks = {
+ closing: '<a href="#">#23</a> and <a>#42</a>',
+ };
- it('should have only have mentioned issues text', () => {
- const vm = createComponent({
- relatedLinks: {
- mentioned: '<a href="#">#7</a>',
- },
- });
+ Vue.nextTick()
+ .then(() => {
+ const content = vm.$el.textContent.replace(/\n(\s)+/g, ' ').trim();
- expect(vm.$el.innerText).toContain('issue #7');
- expect(vm.$el.innerText).toContain('is mentioned but will not be closed.');
- expect(vm.$el.innerText).not.toContain('Closes');
+ expect(content).toContain('Closes issues #23 and #42');
+ expect(content).not.toContain('mentioned');
+ })
+ .then(done)
+ .catch(done.fail);
});
- it('should have closing and mentioned issues at the same time', () => {
- const vm = createComponent({
- relatedLinks: {
- closing: '<a href="#">#7</a>',
- mentioned: '<a href="#">#23</a> and <a>#42</a>',
- },
- });
- const content = vm.$el.textContent.replace(/\n(\s)+/g, ' ').trim();
+ it('should have only have mentioned issues text', (done) => {
+ vm.relatedLinks = {
+ mentioned: '<a href="#">#7</a>',
+ };
+
+ Vue.nextTick()
+ .then(() => {
+ expect(vm.$el.innerText).toContain('issue #7');
+ expect(vm.$el.innerText).toContain('is mentioned but will not be closed');
+ expect(vm.$el.innerText).not.toContain('Closes');
+ })
+ .then(done)
+ .catch(done.fail);
+ });
- expect(content).toContain('Closes issue #7.');
- expect(content).toContain('issues #23 and #42');
- expect(content).toContain('are mentioned but will not be closed.');
+ it('should have closing and mentioned issues at the same time', (done) => {
+ vm.relatedLinks = {
+ closing: '<a href="#">#7</a>',
+ mentioned: '<a href="#">#23</a> and <a>#42</a>',
+ };
+
+ Vue.nextTick()
+ .then(() => {
+ const content = vm.$el.textContent.replace(/\n(\s)+/g, ' ').trim();
+
+ expect(content).toContain('Closes issue #7.');
+ expect(content).toContain('issues #23 and #42');
+ expect(content).toContain('are mentioned but will not be closed');
+ })
+ .then(done)
+ .catch(done.fail);
});
- it('should have assing issues link', () => {
- const vm = createComponent({
- relatedLinks: {
- assignToMe: '<a href="#">Assign yourself to these issues</a>',
- },
- });
+ it('should have assing issues link', (done) => {
+ vm.relatedLinks = {
+ assignToMe: '<a href="#">Assign yourself to these issues</a>',
+ };
+
+ Vue.nextTick()
+ .then(() => {
+ expect(vm.$el.innerText).toContain('Assign yourself to these issues');
+ })
+ .then(done)
+ .catch(done.fail);
+ });
- expect(vm.$el.innerText).toContain('Assign yourself to these issues');
+ it('should use different wording after merging', (done) => {
+ vm.isMerged = true;
+ vm.relatedLinks = {
+ closing: '<a href="#">#7</a>',
+ mentioned: '<a href="#">#23</a> and <a>#42</a>',
+ };
+
+ Vue.nextTick()
+ .then(() => {
+ const content = vm.$el.textContent.replace(/\n(\s)+/g, ' ').trim();
+
+ expect(content).toContain('Closed issue #7.');
+ expect(content).toContain('issues #23 and #42');
+ expect(content).toContain('are mentioned but were not closed');
+ })
+ .then(done)
+ .catch(done.fail);
});
});
});
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 3a0c50b750f..425dff89439 100644
--- a/spec/javascripts/vue_mr_widget/mr_widget_options_spec.js
+++ b/spec/javascripts/vue_mr_widget/mr_widget_options_spec.js
@@ -48,12 +48,13 @@ describe('mrWidgetOptions', () => {
});
describe('shouldRenderMergeHelp', () => {
- it('should return false for the initial merged state', () => {
+ it('should return false after merging', () => {
+ vm.mr.isMerged = true;
expect(vm.shouldRenderMergeHelp).toBeFalsy();
});
- it('should return true for a state which requires help widget', () => {
- vm.mr.state = 'conflicts';
+ it('should return true before merging', () => {
+ vm.mr.isMerged = false;
expect(vm.shouldRenderMergeHelp).toBeTruthy();
});
});
diff --git a/spec/javascripts/vue_mr_widget/stores/mr_widget_store_spec.js b/spec/javascripts/vue_mr_widget/stores/mr_widget_store_spec.js
index 56dd0198ae2..71285866302 100644
--- a/spec/javascripts/vue_mr_widget/stores/mr_widget_store_spec.js
+++ b/spec/javascripts/vue_mr_widget/stores/mr_widget_store_spec.js
@@ -18,5 +18,17 @@ describe('MergeRequestStore', () => {
store.setData({ ...mockData, work_in_progress: !mockData.work_in_progress });
expect(store.hasSHAChanged).toBe(false);
});
+
+ it('sets isMerged to true for merged state', () => {
+ store.setData({ ...mockData, state: 'merged' });
+
+ expect(store.isMerged).toBe(true);
+ });
+
+ it('sets isMerged to false for readyToMerge state', () => {
+ store.setData({ ...mockData, state: 'readyToMerge' });
+
+ expect(store.isMerged).toBe(false);
+ });
});
});
diff --git a/spec/javascripts/vue_shared/components/commit_spec.js b/spec/javascripts/vue_shared/components/commit_spec.js
index 540245fe71e..1c3188cdda2 100644
--- a/spec/javascripts/vue_shared/components/commit_spec.js
+++ b/spec/javascripts/vue_shared/components/commit_spec.js
@@ -1,5 +1,5 @@
import Vue from 'vue';
-import commitComp from '~/vue_shared/components/commit';
+import commitComp from '~/vue_shared/components/commit.vue';
describe('Commit component', () => {
let props;
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 e28639f12f3..b4553acb341 100644
--- a/spec/javascripts/vue_shared/components/header_ci_component_spec.js
+++ b/spec/javascripts/vue_shared/components/header_ci_component_spec.js
@@ -87,7 +87,7 @@ describe('Header CI Component', () => {
vm.actions[0].isLoading = true;
Vue.nextTick(() => {
- expect(vm.$el.querySelector('.btn .fa-spinner').getAttribute('style')).toEqual('');
+ expect(vm.$el.querySelector('.btn .fa-spinner').getAttribute('style')).toBeFalsy();
done();
});
});
diff --git a/spec/javascripts/vue_shared/components/pipelines_table_row_spec.js b/spec/javascripts/vue_shared/components/pipelines_table_row_spec.js
index 67419cfcbea..9475ee28a03 100644
--- a/spec/javascripts/vue_shared/components/pipelines_table_row_spec.js
+++ b/spec/javascripts/vue_shared/components/pipelines_table_row_spec.js
@@ -1,5 +1,5 @@
import Vue from 'vue';
-import tableRowComp from '~/vue_shared/components/pipelines_table_row';
+import tableRowComp from '~/vue_shared/components/pipelines_table_row.vue';
describe('Pipelines Table Row', () => {
const jsonFixtureName = 'pipelines/pipelines.json';
@@ -34,7 +34,7 @@ describe('Pipelines Table Row', () => {
it('should render a table row', () => {
component = buildComponent(pipeline);
- expect(component.$el).toEqual('TR');
+ expect(component.$el.getAttribute('class')).toContain('gl-responsive-table-row');
});
describe('status column', () => {
@@ -44,13 +44,13 @@ describe('Pipelines Table Row', () => {
it('should render a pipeline link', () => {
expect(
- component.$el.querySelector('td.commit-link a').getAttribute('href'),
+ component.$el.querySelector('.table-section.commit-link a').getAttribute('href'),
).toEqual(pipeline.path);
});
it('should render status text', () => {
expect(
- component.$el.querySelector('td.commit-link a').textContent,
+ component.$el.querySelector('.table-section.commit-link a').textContent,
).toContain(pipeline.details.status.text);
});
});
@@ -62,24 +62,24 @@ describe('Pipelines Table Row', () => {
it('should render a pipeline link', () => {
expect(
- component.$el.querySelector('td:nth-child(2) a').getAttribute('href'),
+ component.$el.querySelector('.table-section:nth-child(2) a').getAttribute('href'),
).toEqual(pipeline.path);
});
it('should render pipeline ID', () => {
expect(
- component.$el.querySelector('td:nth-child(2) a > span').textContent,
+ component.$el.querySelector('.table-section:nth-child(2) a > span').textContent,
).toEqual(`#${pipeline.id}`);
});
describe('when a user is provided', () => {
it('should render user information', () => {
expect(
- component.$el.querySelector('td:nth-child(2) a:nth-child(3)').getAttribute('href'),
+ component.$el.querySelector('.table-section:nth-child(2) a:nth-child(3)').getAttribute('href'),
).toEqual(pipeline.user.path);
expect(
- component.$el.querySelector('td:nth-child(2) img').getAttribute('data-original-title'),
+ component.$el.querySelector('.table-section:nth-child(2) img').getAttribute('data-original-title'),
).toEqual(pipeline.user.name);
});
});
@@ -142,7 +142,7 @@ describe('Pipelines Table Row', () => {
it('should render an icon for each stage', () => {
expect(
- component.$el.querySelectorAll('td:nth-child(4) .js-builds-dropdown-button').length,
+ component.$el.querySelectorAll('.table-section:nth-child(4) .js-builds-dropdown-button').length,
).toEqual(pipeline.details.stages.length);
});
});
@@ -154,7 +154,7 @@ describe('Pipelines Table Row', () => {
it('should render the provided actions', () => {
expect(
- component.$el.querySelectorAll('td:nth-child(6) ul li').length,
+ component.$el.querySelectorAll('.table-section:nth-child(6) ul li').length,
).toEqual(pipeline.details.manual_actions.length);
});
});
diff --git a/spec/javascripts/vue_shared/components/pipelines_table_spec.js b/spec/javascripts/vue_shared/components/pipelines_table_spec.js
index 6cc178b8f1d..4c35d702004 100644
--- a/spec/javascripts/vue_shared/components/pipelines_table_spec.js
+++ b/spec/javascripts/vue_shared/components/pipelines_table_spec.js
@@ -1,5 +1,5 @@
import Vue from 'vue';
-import pipelinesTableComp from '~/vue_shared/components/pipelines_table';
+import pipelinesTableComp from '~/vue_shared/components/pipelines_table.vue';
import '~/lib/utils/datetime_utility';
describe('Pipelines Table', () => {
@@ -32,16 +32,14 @@ describe('Pipelines Table', () => {
});
it('should render a table', () => {
- expect(component.$el).toEqual('TABLE');
+ expect(component.$el.getAttribute('class')).toContain('ci-table');
});
it('should render table head with correct columns', () => {
- expect(component.$el.querySelector('th.js-pipeline-status').textContent).toEqual('Status');
- expect(component.$el.querySelector('th.js-pipeline-info').textContent).toEqual('Pipeline');
- expect(component.$el.querySelector('th.js-pipeline-commit').textContent).toEqual('Commit');
- expect(component.$el.querySelector('th.js-pipeline-stages').textContent).toEqual('Stages');
- expect(component.$el.querySelector('th.js-pipeline-date').textContent).toEqual('');
- expect(component.$el.querySelector('th.js-pipeline-actions').textContent).toEqual('');
+ expect(component.$el.querySelector('.table-section.js-pipeline-status').textContent.trim()).toEqual('Status');
+ expect(component.$el.querySelector('.table-section.js-pipeline-info').textContent.trim()).toEqual('Pipeline');
+ expect(component.$el.querySelector('.table-section.js-pipeline-commit').textContent.trim()).toEqual('Commit');
+ expect(component.$el.querySelector('.table-section.js-pipeline-stages').textContent.trim()).toEqual('Stages');
});
});
@@ -53,7 +51,7 @@ describe('Pipelines Table', () => {
service: {},
},
}).$mount();
- expect(component.$el.querySelectorAll('tbody tr').length).toEqual(0);
+ expect(component.$el.querySelectorAll('.commit.gl-responsive-table-row').length).toEqual(0);
});
});
@@ -67,7 +65,7 @@ describe('Pipelines Table', () => {
},
}).$mount();
- expect(component.$el.querySelectorAll('tbody tr').length).toEqual(1);
+ expect(component.$el.querySelectorAll('.commit.gl-responsive-table-row').length).toEqual(1);
});
});
});
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 bf28019ef24..f3b4adc0b70 100644
--- a/spec/javascripts/vue_shared/components/time_ago_tooltip_spec.js
+++ b/spec/javascripts/vue_shared/components/time_ago_tooltip_spec.js
@@ -22,7 +22,7 @@ describe('Time ago with tooltip component', () => {
}).$mount();
expect(vm.$el.tagName).toEqual('TIME');
- expect(vm.$el.classList.contains('js-timeago')).toEqual(true);
+ expect(vm.$el.classList.contains('js-vue-timeago')).toEqual(true);
expect(
vm.$el.getAttribute('data-original-title'),
).toEqual(gl.utils.formatDate('2017-05-08T14:57:39.781Z'));
@@ -44,17 +44,6 @@ describe('Time ago with tooltip component', () => {
expect(vm.$el.getAttribute('data-placement')).toEqual('bottom');
});
- it('should render short format class', () => {
- vm = new TimeagoTooltip({
- propsData: {
- time: '2017-05-08T14:57:39.781Z',
- shortFormat: true,
- },
- }).$mount();
-
- expect(vm.$el.classList.contains('js-short-timeago')).toEqual(true);
- });
-
it('should render provided html class', () => {
vm = new TimeagoTooltip({
propsData: {
diff --git a/spec/lib/banzai/filter/external_issue_reference_filter_spec.rb b/spec/lib/banzai/filter/external_issue_reference_filter_spec.rb
index fbf7a461fa5..76cefe112fb 100644
--- a/spec/lib/banzai/filter/external_issue_reference_filter_spec.rb
+++ b/spec/lib/banzai/filter/external_issue_reference_filter_spec.rb
@@ -82,7 +82,9 @@ describe Banzai::Filter::ExternalIssueReferenceFilter, lib: true do
context 'with RequestStore enabled' do
let(:reference_filter) { HTML::Pipeline.new([described_class]) }
- before { allow(RequestStore).to receive(:active?).and_return(true) }
+ before do
+ allow(RequestStore).to receive(:active?).and_return(true)
+ end
it 'queries the collection on the first call' do
expect_any_instance_of(Project).to receive(:default_issues_tracker?).once.and_call_original
diff --git a/spec/lib/banzai/filter/redactor_filter_spec.rb b/spec/lib/banzai/filter/redactor_filter_spec.rb
index 7c4a0f32c7b..97504aebed5 100644
--- a/spec/lib/banzai/filter/redactor_filter_spec.rb
+++ b/spec/lib/banzai/filter/redactor_filter_spec.rb
@@ -39,7 +39,9 @@ describe Banzai::Filter::RedactorFilter, lib: true do
end
context 'valid projects' do
- before { allow_any_instance_of(Banzai::ReferenceParser::BaseParser).to receive(:can_read_reference?).and_return(true) }
+ before do
+ allow_any_instance_of(Banzai::ReferenceParser::BaseParser).to receive(:can_read_reference?).and_return(true)
+ end
it 'allows permitted Project references' do
user = create(:user)
@@ -54,7 +56,9 @@ describe Banzai::Filter::RedactorFilter, lib: true do
end
context 'invalid projects' do
- before { allow_any_instance_of(Banzai::ReferenceParser::BaseParser).to receive(:can_read_reference?).and_return(false) }
+ before do
+ allow_any_instance_of(Banzai::ReferenceParser::BaseParser).to receive(:can_read_reference?).and_return(false)
+ end
it 'removes unpermitted references' do
user = create(:user)
diff --git a/spec/lib/banzai/reference_parser/base_parser_spec.rb b/spec/lib/banzai/reference_parser/base_parser_spec.rb
index f4f42bfc3ed..76fab93821a 100644
--- a/spec/lib/banzai/reference_parser/base_parser_spec.rb
+++ b/spec/lib/banzai/reference_parser/base_parser_spec.rb
@@ -114,7 +114,7 @@ describe Banzai::ReferenceParser::BaseParser, lib: true do
expect(hash).to eq({ link => user })
end
- it 'returns an empty Hash when entry does not exist in the database' do
+ it 'returns an empty Hash when entry does not exist in the database', :request_store do
link = double(:link)
expect(link).to receive(:has_attribute?).
diff --git a/spec/lib/banzai/reference_parser/commit_parser_spec.rb b/spec/lib/banzai/reference_parser/commit_parser_spec.rb
index 412ffa77c36..583ce63a8ab 100644
--- a/spec/lib/banzai/reference_parser/commit_parser_spec.rb
+++ b/spec/lib/banzai/reference_parser/commit_parser_spec.rb
@@ -10,7 +10,9 @@ describe Banzai::ReferenceParser::CommitParser, lib: true do
describe '#nodes_visible_to_user' do
context 'when the link has a data-issue attribute' do
- before { link['data-commit'] = 123 }
+ before do
+ link['data-commit'] = 123
+ end
it_behaves_like "referenced feature visibility", "repository"
end
diff --git a/spec/lib/banzai/reference_parser/commit_range_parser_spec.rb b/spec/lib/banzai/reference_parser/commit_range_parser_spec.rb
index 96e55b0997a..8c0f5d7df97 100644
--- a/spec/lib/banzai/reference_parser/commit_range_parser_spec.rb
+++ b/spec/lib/banzai/reference_parser/commit_range_parser_spec.rb
@@ -10,7 +10,9 @@ describe Banzai::ReferenceParser::CommitRangeParser, lib: true do
describe '#nodes_visible_to_user' do
context 'when the link has a data-issue attribute' do
- before { link['data-commit-range'] = '123..456' }
+ before do
+ link['data-commit-range'] = '123..456'
+ end
it_behaves_like "referenced feature visibility", "repository"
end
diff --git a/spec/lib/banzai/reference_parser/external_issue_parser_spec.rb b/spec/lib/banzai/reference_parser/external_issue_parser_spec.rb
index 0af36776a54..d212bbac619 100644
--- a/spec/lib/banzai/reference_parser/external_issue_parser_spec.rb
+++ b/spec/lib/banzai/reference_parser/external_issue_parser_spec.rb
@@ -10,7 +10,9 @@ describe Banzai::ReferenceParser::ExternalIssueParser, lib: true do
describe '#nodes_visible_to_user' do
context 'when the link has a data-issue attribute' do
- before { link['data-external-issue'] = 123 }
+ before do
+ link['data-external-issue'] = 123
+ end
levels = [ProjectFeature::DISABLED, ProjectFeature::PRIVATE, ProjectFeature::ENABLED]
diff --git a/spec/lib/banzai/reference_parser/label_parser_spec.rb b/spec/lib/banzai/reference_parser/label_parser_spec.rb
index 8c540d35ddd..ddd699f3c25 100644
--- a/spec/lib/banzai/reference_parser/label_parser_spec.rb
+++ b/spec/lib/banzai/reference_parser/label_parser_spec.rb
@@ -11,7 +11,9 @@ describe Banzai::ReferenceParser::LabelParser, lib: true do
describe '#nodes_visible_to_user' do
context 'when the link has a data-issue attribute' do
- before { link['data-label'] = label.id.to_s }
+ before do
+ link['data-label'] = label.id.to_s
+ end
it_behaves_like "referenced feature visibility", "issues", "merge_requests"
end
diff --git a/spec/lib/banzai/reference_parser/milestone_parser_spec.rb b/spec/lib/banzai/reference_parser/milestone_parser_spec.rb
index 2d4d589ae34..72d4f3bc18e 100644
--- a/spec/lib/banzai/reference_parser/milestone_parser_spec.rb
+++ b/spec/lib/banzai/reference_parser/milestone_parser_spec.rb
@@ -11,7 +11,9 @@ describe Banzai::ReferenceParser::MilestoneParser, lib: true do
describe '#nodes_visible_to_user' do
context 'when the link has a data-issue attribute' do
- before { link['data-milestone'] = milestone.id.to_s }
+ before do
+ link['data-milestone'] = milestone.id.to_s
+ end
it_behaves_like "referenced feature visibility", "issues", "merge_requests"
end
diff --git a/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb b/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb
index 2ca0773ad1d..af0e7855a9b 100644
--- a/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb
+++ b/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb
@@ -596,62 +596,117 @@ module Ci
end
describe "Image and service handling" do
- it "returns image and service when defined" do
- config = YAML.dump({
- image: "ruby:2.1",
- services: ["mysql"],
- before_script: ["pwd"],
- rspec: { script: "rspec" }
- })
+ context "when extended docker configuration is used" do
+ it "returns image and service when defined" do
+ config = YAML.dump({ image: { name: "ruby:2.1" },
+ services: ["mysql", { name: "docker:dind", alias: "docker" }],
+ before_script: ["pwd"],
+ rspec: { script: "rspec" } })
- config_processor = GitlabCiYamlProcessor.new(config, path)
+ config_processor = GitlabCiYamlProcessor.new(config, path)
- expect(config_processor.builds_for_stage_and_ref("test", "master").size).to eq(1)
- expect(config_processor.builds_for_stage_and_ref("test", "master").first).to eq({
- stage: "test",
- stage_idx: 1,
- name: "rspec",
- commands: "pwd\nrspec",
- coverage_regex: nil,
- tag_list: [],
- options: {
- image: "ruby:2.1",
- services: ["mysql"]
- },
- allow_failure: false,
- when: "on_success",
- environment: nil,
- yaml_variables: []
- })
+ expect(config_processor.builds_for_stage_and_ref("test", "master").size).to eq(1)
+ expect(config_processor.builds_for_stage_and_ref("test", "master").first).to eq({
+ stage: "test",
+ stage_idx: 1,
+ name: "rspec",
+ commands: "pwd\nrspec",
+ coverage_regex: nil,
+ tag_list: [],
+ options: {
+ image: { name: "ruby:2.1" },
+ services: [{ name: "mysql" }, { name: "docker:dind", alias: "docker" }]
+ },
+ allow_failure: false,
+ when: "on_success",
+ environment: nil,
+ yaml_variables: []
+ })
+ end
+
+ it "returns image and service when overridden for job" do
+ config = YAML.dump({ image: "ruby:2.1",
+ services: ["mysql"],
+ before_script: ["pwd"],
+ rspec: { image: { name: "ruby:2.5" },
+ services: [{ name: "postgresql", alias: "db-pg" }, "docker:dind"], script: "rspec" } })
+
+ config_processor = GitlabCiYamlProcessor.new(config, path)
+
+ expect(config_processor.builds_for_stage_and_ref("test", "master").size).to eq(1)
+ expect(config_processor.builds_for_stage_and_ref("test", "master").first).to eq({
+ stage: "test",
+ stage_idx: 1,
+ name: "rspec",
+ commands: "pwd\nrspec",
+ coverage_regex: nil,
+ tag_list: [],
+ options: {
+ image: { name: "ruby:2.5" },
+ services: [{ name: "postgresql", alias: "db-pg" }, { name: "docker:dind" }]
+ },
+ allow_failure: false,
+ when: "on_success",
+ environment: nil,
+ yaml_variables: []
+ })
+ end
end
- it "returns image and service when overridden for job" do
- config = YAML.dump({
- image: "ruby:2.1",
- services: ["mysql"],
- before_script: ["pwd"],
- rspec: { image: "ruby:2.5", services: ["postgresql"], script: "rspec" }
- })
+ context "when etended docker configuration is not used" do
+ it "returns image and service when defined" do
+ config = YAML.dump({ image: "ruby:2.1",
+ services: ["mysql", "docker:dind"],
+ before_script: ["pwd"],
+ rspec: { script: "rspec" } })
- config_processor = GitlabCiYamlProcessor.new(config, path)
+ config_processor = GitlabCiYamlProcessor.new(config, path)
- expect(config_processor.builds_for_stage_and_ref("test", "master").size).to eq(1)
- expect(config_processor.builds_for_stage_and_ref("test", "master").first).to eq({
- stage: "test",
- stage_idx: 1,
- name: "rspec",
- commands: "pwd\nrspec",
- coverage_regex: nil,
- tag_list: [],
- options: {
- image: "ruby:2.5",
- services: ["postgresql"]
- },
- allow_failure: false,
- when: "on_success",
- environment: nil,
- yaml_variables: []
- })
+ expect(config_processor.builds_for_stage_and_ref("test", "master").size).to eq(1)
+ expect(config_processor.builds_for_stage_and_ref("test", "master").first).to eq({
+ stage: "test",
+ stage_idx: 1,
+ name: "rspec",
+ commands: "pwd\nrspec",
+ coverage_regex: nil,
+ tag_list: [],
+ options: {
+ image: { name: "ruby:2.1" },
+ services: [{ name: "mysql" }, { name: "docker:dind" }]
+ },
+ allow_failure: false,
+ when: "on_success",
+ environment: nil,
+ yaml_variables: []
+ })
+ end
+
+ it "returns image and service when overridden for job" do
+ config = YAML.dump({ image: "ruby:2.1",
+ services: ["mysql"],
+ before_script: ["pwd"],
+ rspec: { image: "ruby:2.5", services: ["postgresql", "docker:dind"], script: "rspec" } })
+
+ config_processor = GitlabCiYamlProcessor.new(config, path)
+
+ expect(config_processor.builds_for_stage_and_ref("test", "master").size).to eq(1)
+ expect(config_processor.builds_for_stage_and_ref("test", "master").first).to eq({
+ stage: "test",
+ stage_idx: 1,
+ name: "rspec",
+ commands: "pwd\nrspec",
+ coverage_regex: nil,
+ tag_list: [],
+ options: {
+ image: { name: "ruby:2.5" },
+ services: [{ name: "postgresql" }, { name: "docker:dind" }]
+ },
+ allow_failure: false,
+ when: "on_success",
+ environment: nil,
+ yaml_variables: []
+ })
+ end
end
end
@@ -884,8 +939,8 @@ module Ci
coverage_regex: nil,
tag_list: [],
options: {
- image: "ruby:2.1",
- services: ["mysql"],
+ image: { name: "ruby:2.1" },
+ services: [{ name: "mysql" }],
artifacts: {
name: "custom_name",
paths: ["logs/", "binaries/"],
@@ -1261,7 +1316,7 @@ EOT
config = YAML.dump({ image: ["test"], rspec: { script: "test" } })
expect do
GitlabCiYamlProcessor.new(config, path)
- end.to raise_error(GitlabCiYamlProcessor::ValidationError, "image config should be a string")
+ end.to raise_error(GitlabCiYamlProcessor::ValidationError, "image config should be a hash or a string")
end
it "returns errors if job name is blank" do
@@ -1282,35 +1337,35 @@ EOT
config = YAML.dump({ rspec: { script: "test", image: ["test"] } })
expect do
GitlabCiYamlProcessor.new(config, path)
- end.to raise_error(GitlabCiYamlProcessor::ValidationError, "jobs:rspec:image config should be a string")
+ end.to raise_error(GitlabCiYamlProcessor::ValidationError, "jobs:rspec:image config should be a hash or a string")
end
it "returns errors if services parameter is not an array" do
config = YAML.dump({ services: "test", rspec: { script: "test" } })
expect do
GitlabCiYamlProcessor.new(config, path)
- end.to raise_error(GitlabCiYamlProcessor::ValidationError, "services config should be an array of strings")
+ end.to raise_error(GitlabCiYamlProcessor::ValidationError, "services config should be a array")
end
it "returns errors if services parameter is not an array of strings" do
config = YAML.dump({ services: [10, "test"], rspec: { script: "test" } })
expect do
GitlabCiYamlProcessor.new(config, path)
- end.to raise_error(GitlabCiYamlProcessor::ValidationError, "services config should be an array of strings")
+ end.to raise_error(GitlabCiYamlProcessor::ValidationError, "service config should be a hash or a string")
end
it "returns errors if job services parameter is not an array" do
config = YAML.dump({ rspec: { script: "test", services: "test" } })
expect do
GitlabCiYamlProcessor.new(config, path)
- end.to raise_error(GitlabCiYamlProcessor::ValidationError, "jobs:rspec:services config should be an array of strings")
+ end.to raise_error(GitlabCiYamlProcessor::ValidationError, "jobs:rspec:services config should be a array")
end
it "returns errors if job services parameter is not an array of strings" do
config = YAML.dump({ rspec: { script: "test", services: [10, "test"] } })
expect do
GitlabCiYamlProcessor.new(config, path)
- end.to raise_error(GitlabCiYamlProcessor::ValidationError, "jobs:rspec:services config should be an array of strings")
+ end.to raise_error(GitlabCiYamlProcessor::ValidationError, "service config should be a hash or a string")
end
it "returns error if job configuration is invalid" do
@@ -1324,7 +1379,7 @@ EOT
config = YAML.dump({ extra: { script: 'rspec', services: "test" } })
expect do
GitlabCiYamlProcessor.new(config, path)
- end.to raise_error(GitlabCiYamlProcessor::ValidationError, "jobs:extra:services config should be an array of strings")
+ end.to raise_error(GitlabCiYamlProcessor::ValidationError, "jobs:extra:services config should be a array")
end
it "returns errors if there are no jobs defined" do
diff --git a/spec/lib/extracts_path_spec.rb b/spec/lib/extracts_path_spec.rb
index 33ab005667a..2b26a318583 100644
--- a/spec/lib/extracts_path_spec.rb
+++ b/spec/lib/extracts_path_spec.rb
@@ -77,7 +77,10 @@ describe ExtractsPath, lib: true do
context 'without a path' do
let(:params) { { ref: 'v1.0.0.atom' } }
- before { assign_ref_vars }
+
+ before do
+ assign_ref_vars
+ end
it 'sets the un-suffixed version as @ref' do
expect(@ref).to eq('v1.0.0')
diff --git a/spec/lib/gitlab/auth/unique_ips_limiter_spec.rb b/spec/lib/gitlab/auth/unique_ips_limiter_spec.rb
index 94dcddcc30c..fc72df575be 100644
--- a/spec/lib/gitlab/auth/unique_ips_limiter_spec.rb
+++ b/spec/lib/gitlab/auth/unique_ips_limiter_spec.rb
@@ -40,7 +40,9 @@ describe Gitlab::Auth::UniqueIpsLimiter, :redis, lib: true do
end
context 'allow 2 unique ips' do
- before { current_application_settings.update!(unique_ips_limit_per_user: 2) }
+ before do
+ current_application_settings.update!(unique_ips_limit_per_user: 2)
+ end
it 'blocks user trying to login from third ip' do
change_ip('ip1')
diff --git a/spec/lib/gitlab/badge/build/status_spec.rb b/spec/lib/gitlab/badge/build/status_spec.rb
index 3c5414701a7..6abf4ca46a9 100644
--- a/spec/lib/gitlab/badge/build/status_spec.rb
+++ b/spec/lib/gitlab/badge/build/status_spec.rb
@@ -29,7 +29,9 @@ describe Gitlab::Badge::Build::Status do
let!(:build) { create_build(project, sha, branch) }
context 'build success' do
- before { build.success! }
+ before do
+ build.success!
+ end
describe '#status' do
it 'is successful' do
@@ -39,7 +41,9 @@ describe Gitlab::Badge::Build::Status do
end
context 'build failed' do
- before { build.drop! }
+ before do
+ build.drop!
+ end
describe '#status' do
it 'failed' do
diff --git a/spec/lib/gitlab/checks/change_access_spec.rb b/spec/lib/gitlab/checks/change_access_spec.rb
index c0c309d8179..643e590438a 100644
--- a/spec/lib/gitlab/checks/change_access_spec.rb
+++ b/spec/lib/gitlab/checks/change_access_spec.rb
@@ -20,7 +20,9 @@ describe Gitlab::Checks::ChangeAccess, lib: true do
).exec
end
- before { project.add_developer(user) }
+ before do
+ project.add_developer(user)
+ end
context 'without failed checks' do
it "doesn't raise an error" do
@@ -50,7 +52,9 @@ describe Gitlab::Checks::ChangeAccess, lib: true do
let!(:protected_tag) { create(:protected_tag, project: project, name: 'v*') }
context 'as master' do
- before { project.add_master(user) }
+ before do
+ project.add_master(user)
+ end
context 'deletion' do
let(:oldrev) { 'be93687618e4b132087f430a4d8fc3a609c9b77c' }
diff --git a/spec/lib/gitlab/ci/build/image_spec.rb b/spec/lib/gitlab/ci/build/image_spec.rb
index 382385dfd6b..773a52cdfbc 100644
--- a/spec/lib/gitlab/ci/build/image_spec.rb
+++ b/spec/lib/gitlab/ci/build/image_spec.rb
@@ -10,12 +10,28 @@ describe Gitlab::Ci::Build::Image do
let(:image_name) { 'ruby:2.1' }
let(:job) { create(:ci_build, options: { image: image_name } ) }
- it 'fabricates an object of the proper class' do
- is_expected.to be_kind_of(described_class)
+ context 'when image is defined as string' do
+ it 'fabricates an object of the proper class' do
+ is_expected.to be_kind_of(described_class)
+ end
+
+ it 'populates fabricated object with the proper name attribute' do
+ expect(subject.name).to eq(image_name)
+ end
end
- it 'populates fabricated object with the proper name attribute' do
- expect(subject.name).to eq(image_name)
+ context 'when image is defined as hash' do
+ let(:entrypoint) { '/bin/sh' }
+ let(:job) { create(:ci_build, options: { image: { name: image_name, entrypoint: entrypoint } } ) }
+
+ it 'fabricates an object of the proper class' do
+ is_expected.to be_kind_of(described_class)
+ end
+
+ it 'populates fabricated object with the proper attributes' do
+ expect(subject.name).to eq(image_name)
+ expect(subject.entrypoint).to eq(entrypoint)
+ end
end
context 'when image name is empty' do
@@ -41,10 +57,39 @@ describe Gitlab::Ci::Build::Image do
let(:service_image_name) { 'postgres' }
let(:job) { create(:ci_build, options: { services: [service_image_name] }) }
- it 'fabricates an non-empty array of objects' do
- is_expected.to be_kind_of(Array)
- is_expected.not_to be_empty
- expect(subject.first.name).to eq(service_image_name)
+ context 'when service is defined as string' do
+ it 'fabricates an non-empty array of objects' do
+ is_expected.to be_kind_of(Array)
+ is_expected.not_to be_empty
+ end
+
+ it 'populates fabricated objects with the proper name attributes' do
+ expect(subject.first).to be_kind_of(described_class)
+ expect(subject.first.name).to eq(service_image_name)
+ end
+ end
+
+ context 'when service is defined as hash' do
+ let(:service_entrypoint) { '/bin/sh' }
+ let(:service_alias) { 'db' }
+ let(:service_command) { 'sleep 30' }
+ let(:job) do
+ create(:ci_build, options: { services: [{ name: service_image_name, entrypoint: service_entrypoint,
+ alias: service_alias, command: service_command }] })
+ end
+
+ it 'fabricates an non-empty array of objects' do
+ is_expected.to be_kind_of(Array)
+ is_expected.not_to be_empty
+ expect(subject.first).to be_kind_of(described_class)
+ end
+
+ it 'populates fabricated objects with the proper attributes' do
+ expect(subject.first.name).to eq(service_image_name)
+ expect(subject.first.entrypoint).to eq(service_entrypoint)
+ expect(subject.first.alias).to eq(service_alias)
+ expect(subject.first.command).to eq(service_command)
+ end
end
context 'when service image name is empty' do
diff --git a/spec/lib/gitlab/ci/config/entry/cache_spec.rb b/spec/lib/gitlab/ci/config/entry/cache_spec.rb
index 2ed120f356a..878b1d6b862 100644
--- a/spec/lib/gitlab/ci/config/entry/cache_spec.rb
+++ b/spec/lib/gitlab/ci/config/entry/cache_spec.rb
@@ -4,7 +4,9 @@ describe Gitlab::Ci::Config::Entry::Cache do
let(:entry) { described_class.new(config) }
describe 'validations' do
- before { entry.compose! }
+ before do
+ entry.compose!
+ end
context 'when entry config value is correct' do
let(:config) do
diff --git a/spec/lib/gitlab/ci/config/entry/environment_spec.rb b/spec/lib/gitlab/ci/config/entry/environment_spec.rb
index c330e609337..3c0007f4d57 100644
--- a/spec/lib/gitlab/ci/config/entry/environment_spec.rb
+++ b/spec/lib/gitlab/ci/config/entry/environment_spec.rb
@@ -3,7 +3,9 @@ require 'spec_helper'
describe Gitlab::Ci::Config::Entry::Environment do
let(:entry) { described_class.new(config) }
- before { entry.compose! }
+ before do
+ entry.compose!
+ end
context 'when configuration is a string' do
let(:config) { 'production' }
diff --git a/spec/lib/gitlab/ci/config/entry/global_spec.rb b/spec/lib/gitlab/ci/config/entry/global_spec.rb
index 23270ad5053..293f112b2b0 100644
--- a/spec/lib/gitlab/ci/config/entry/global_spec.rb
+++ b/spec/lib/gitlab/ci/config/entry/global_spec.rb
@@ -33,7 +33,9 @@ describe Gitlab::Ci::Config::Entry::Global do
end
describe '#compose!' do
- before { global.compose! }
+ before do
+ global.compose!
+ end
it 'creates nodes hash' do
expect(global.descendants).to be_an Array
@@ -79,7 +81,9 @@ describe Gitlab::Ci::Config::Entry::Global do
end
context 'when composed' do
- before { global.compose! }
+ before do
+ global.compose!
+ end
describe '#errors' do
it 'has no errors' do
@@ -95,13 +99,13 @@ describe Gitlab::Ci::Config::Entry::Global do
describe '#image_value' do
it 'returns valid image' do
- expect(global.image_value).to eq 'ruby:2.2'
+ expect(global.image_value).to eq(name: 'ruby:2.2')
end
end
describe '#services_value' do
it 'returns array of services' do
- expect(global.services_value).to eq ['postgres:9.1', 'mysql:5.5']
+ expect(global.services_value).to eq [{ name: 'postgres:9.1' }, { name: 'mysql:5.5' }]
end
end
@@ -150,8 +154,8 @@ describe Gitlab::Ci::Config::Entry::Global do
script: %w[rspec ls],
before_script: %w(ls pwd),
commands: "ls\npwd\nrspec\nls",
- image: 'ruby:2.2',
- services: ['postgres:9.1', 'mysql:5.5'],
+ image: { name: 'ruby:2.2' },
+ services: [{ name: 'postgres:9.1' }, { name: 'mysql:5.5' }],
stage: 'test',
cache: { key: 'k', untracked: true, paths: ['public/'] },
variables: { 'VAR' => 'value' },
@@ -161,8 +165,8 @@ describe Gitlab::Ci::Config::Entry::Global do
before_script: [],
script: %w[spinach],
commands: 'spinach',
- image: 'ruby:2.2',
- services: ['postgres:9.1', 'mysql:5.5'],
+ image: { name: 'ruby:2.2' },
+ services: [{ name: 'postgres:9.1' }, { name: 'mysql:5.5' }],
stage: 'test',
cache: { key: 'k', untracked: true, paths: ['public/'] },
variables: {},
@@ -175,7 +179,9 @@ describe Gitlab::Ci::Config::Entry::Global do
end
context 'when most of entires not defined' do
- before { global.compose! }
+ before do
+ global.compose!
+ end
let(:hash) do
{ cache: { key: 'a' }, rspec: { script: %w[ls] } }
@@ -218,7 +224,9 @@ describe Gitlab::Ci::Config::Entry::Global do
# details.
#
context 'when entires specified but not defined' do
- before { global.compose! }
+ before do
+ global.compose!
+ end
let(:hash) do
{ variables: nil, rspec: { script: 'rspec' } }
@@ -233,7 +241,9 @@ describe Gitlab::Ci::Config::Entry::Global do
end
context 'when configuration is not valid' do
- before { global.compose! }
+ before do
+ global.compose!
+ end
context 'when before script is not an array' do
let(:hash) do
@@ -297,7 +307,9 @@ describe Gitlab::Ci::Config::Entry::Global do
end
describe '#[]' do
- before { global.compose! }
+ before do
+ global.compose!
+ end
let(:hash) do
{ cache: { key: 'a' }, rspec: { script: 'ls' } }
diff --git a/spec/lib/gitlab/ci/config/entry/image_spec.rb b/spec/lib/gitlab/ci/config/entry/image_spec.rb
index 3c99cb0a1ee..bca22e39500 100644
--- a/spec/lib/gitlab/ci/config/entry/image_spec.rb
+++ b/spec/lib/gitlab/ci/config/entry/image_spec.rb
@@ -3,43 +3,104 @@ require 'spec_helper'
describe Gitlab::Ci::Config::Entry::Image do
let(:entry) { described_class.new(config) }
- describe 'validation' do
- context 'when entry config value is correct' do
- let(:config) { 'ruby:2.2' }
+ context 'when configuration is a string' do
+ let(:config) { 'ruby:2.2' }
- describe '#value' do
- it 'returns image string' do
- expect(entry.value).to eq 'ruby:2.2'
- end
+ describe '#value' do
+ it 'returns image hash' do
+ expect(entry.value).to eq({ name: 'ruby:2.2' })
end
+ end
+
+ describe '#errors' do
+ it 'does not append errors' do
+ expect(entry.errors).to be_empty
+ end
+ end
+
+ describe '#valid?' do
+ it 'is valid' do
+ expect(entry).to be_valid
+ end
+ end
+
+ describe '#image' do
+ it "returns image's name" do
+ expect(entry.name).to eq 'ruby:2.2'
+ end
+ end
- describe '#errors' do
- it 'does not append errors' do
- expect(entry.errors).to be_empty
- end
+ describe '#entrypoint' do
+ it "returns image's entrypoint" do
+ expect(entry.entrypoint).to be_nil
end
+ end
+ end
- describe '#valid?' do
- it 'is valid' do
- expect(entry).to be_valid
- end
+ context 'when configuration is a hash' do
+ let(:config) { { name: 'ruby:2.2', entrypoint: '/bin/sh' } }
+
+ describe '#value' do
+ it 'returns image hash' do
+ expect(entry.value).to eq(config)
end
end
- context 'when entry value is not correct' do
- let(:config) { ['ruby:2.2'] }
+ describe '#errors' do
+ it 'does not append errors' do
+ expect(entry.errors).to be_empty
+ end
+ end
+
+ describe '#valid?' do
+ it 'is valid' do
+ expect(entry).to be_valid
+ end
+ end
- describe '#errors' do
- it 'saves errors' do
- expect(entry.errors)
- .to include 'image config should be a string'
- end
+ describe '#image' do
+ it "returns image's name" do
+ expect(entry.name).to eq 'ruby:2.2'
end
+ end
+
+ describe '#entrypoint' do
+ it "returns image's entrypoint" do
+ expect(entry.entrypoint).to eq '/bin/sh'
+ end
+ end
+ end
+
+ context 'when entry value is not correct' do
+ let(:config) { ['ruby:2.2'] }
+
+ describe '#errors' do
+ it 'saves errors' do
+ expect(entry.errors)
+ .to include 'image config should be a hash or a string'
+ end
+ end
+
+ describe '#valid?' do
+ it 'is not valid' do
+ expect(entry).not_to be_valid
+ end
+ end
+ end
+
+ context 'when unexpected key is specified' do
+ let(:config) { { name: 'ruby:2.2', non_existing: 'test' } }
+
+ describe '#errors' do
+ it 'saves errors' do
+ expect(entry.errors)
+ .to include 'image config contains unknown keys: non_existing'
+ end
+ end
- describe '#valid?' do
- it 'is not valid' do
- expect(entry).not_to be_valid
- end
+ describe '#valid?' do
+ it 'is not valid' do
+ expect(entry).not_to be_valid
end
end
end
diff --git a/spec/lib/gitlab/ci/config/entry/job_spec.rb b/spec/lib/gitlab/ci/config/entry/job_spec.rb
index 9249bb9c172..92cba689f47 100644
--- a/spec/lib/gitlab/ci/config/entry/job_spec.rb
+++ b/spec/lib/gitlab/ci/config/entry/job_spec.rb
@@ -18,7 +18,9 @@ describe Gitlab::Ci::Config::Entry::Job do
end
describe 'validations' do
- before { entry.compose! }
+ before do
+ entry.compose!
+ end
context 'when entry config value is correct' do
let(:config) { { script: 'rspec' } }
@@ -97,14 +99,16 @@ describe Gitlab::Ci::Config::Entry::Job do
let(:deps) { double('deps', '[]' => unspecified) }
context 'when job config overrides global config' do
- before { entry.compose!(deps) }
+ before do
+ entry.compose!(deps)
+ end
let(:config) do
{ script: 'rspec', image: 'some_image', cache: { key: 'test' } }
end
it 'overrides global config' do
- expect(entry[:image].value).to eq 'some_image'
+ expect(entry[:image].value).to eq(name: 'some_image')
expect(entry[:cache].value).to eq(key: 'test')
end
end
@@ -125,10 +129,14 @@ describe Gitlab::Ci::Config::Entry::Job do
end
context 'when composed' do
- before { entry.compose! }
+ before do
+ entry.compose!
+ end
describe '#value' do
- before { entry.compose! }
+ before do
+ entry.compose!
+ end
context 'when entry is correct' do
let(:config) do
diff --git a/spec/lib/gitlab/ci/config/entry/jobs_spec.rb b/spec/lib/gitlab/ci/config/entry/jobs_spec.rb
index 7d104372ac6..c0a2b6517e3 100644
--- a/spec/lib/gitlab/ci/config/entry/jobs_spec.rb
+++ b/spec/lib/gitlab/ci/config/entry/jobs_spec.rb
@@ -4,7 +4,9 @@ describe Gitlab::Ci::Config::Entry::Jobs do
let(:entry) { described_class.new(config) }
describe 'validations' do
- before { entry.compose! }
+ before do
+ entry.compose!
+ end
context 'when entry config value is correct' do
let(:config) { { rspec: { script: 'rspec' } } }
@@ -48,7 +50,9 @@ describe Gitlab::Ci::Config::Entry::Jobs do
end
context 'when valid job entries composed' do
- before { entry.compose! }
+ before do
+ entry.compose!
+ end
let(:config) do
{ rspec: { script: 'rspec' },
diff --git a/spec/lib/gitlab/ci/config/entry/service_spec.rb b/spec/lib/gitlab/ci/config/entry/service_spec.rb
new file mode 100644
index 00000000000..7202fe525e4
--- /dev/null
+++ b/spec/lib/gitlab/ci/config/entry/service_spec.rb
@@ -0,0 +1,119 @@
+require 'spec_helper'
+
+describe Gitlab::Ci::Config::Entry::Service do
+ let(:entry) { described_class.new(config) }
+
+ before do
+ entry.compose!
+ end
+
+ context 'when configuration is a string' do
+ let(:config) { 'postgresql:9.5' }
+
+ describe '#valid?' do
+ it 'is valid' do
+ expect(entry).to be_valid
+ end
+ end
+
+ describe '#value' do
+ it 'returns valid hash' do
+ expect(entry.value).to include(name: 'postgresql:9.5')
+ end
+ end
+
+ describe '#image' do
+ it "returns service's image name" do
+ expect(entry.name).to eq 'postgresql:9.5'
+ end
+ end
+
+ describe '#alias' do
+ it "returns service's alias" do
+ expect(entry.alias).to be_nil
+ end
+ end
+
+ describe '#command' do
+ it "returns service's command" do
+ expect(entry.command).to be_nil
+ end
+ end
+ end
+
+ context 'when configuration is a hash' do
+ let(:config) do
+ { name: 'postgresql:9.5', alias: 'db', command: 'cmd', entrypoint: '/bin/sh' }
+ end
+
+ describe '#valid?' do
+ it 'is valid' do
+ expect(entry).to be_valid
+ end
+ end
+
+ describe '#value' do
+ it 'returns valid hash' do
+ expect(entry.value).to eq config
+ end
+ end
+
+ describe '#image' do
+ it "returns service's image name" do
+ expect(entry.name).to eq 'postgresql:9.5'
+ end
+ end
+
+ describe '#alias' do
+ it "returns service's alias" do
+ expect(entry.alias).to eq 'db'
+ end
+ end
+
+ describe '#command' do
+ it "returns service's command" do
+ expect(entry.command).to eq 'cmd'
+ end
+ end
+
+ describe '#entrypoint' do
+ it "returns service's entrypoint" do
+ expect(entry.entrypoint).to eq '/bin/sh'
+ end
+ end
+ end
+
+ context 'when entry value is not correct' do
+ let(:config) { ['postgresql:9.5'] }
+
+ describe '#errors' do
+ it 'saves errors' do
+ expect(entry.errors)
+ .to include 'service config should be a hash or a string'
+ end
+ end
+
+ describe '#valid?' do
+ it 'is not valid' do
+ expect(entry).not_to be_valid
+ end
+ end
+ end
+
+ context 'when unexpected key is specified' do
+ let(:config) { { name: 'postgresql:9.5', non_existing: 'test' } }
+
+ describe '#errors' do
+ it 'saves errors' do
+ expect(entry.errors)
+ .to include 'service config contains unknown keys: non_existing'
+ end
+ end
+
+ describe '#valid?' do
+ it 'is not valid' do
+ expect(entry).not_to be_valid
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/ci/config/entry/services_spec.rb b/spec/lib/gitlab/ci/config/entry/services_spec.rb
index 66fad3b6b16..7c4319aee63 100644
--- a/spec/lib/gitlab/ci/config/entry/services_spec.rb
+++ b/spec/lib/gitlab/ci/config/entry/services_spec.rb
@@ -3,37 +3,32 @@ require 'spec_helper'
describe Gitlab::Ci::Config::Entry::Services do
let(:entry) { described_class.new(config) }
- describe 'validations' do
- context 'when entry config value is correct' do
- let(:config) { ['postgres:9.1', 'mysql:5.5'] }
+ before do
+ entry.compose!
+ end
- describe '#value' do
- it 'returns array of services as is' do
- expect(entry.value).to eq config
- end
- end
+ context 'when configuration is valid' do
+ let(:config) { ['postgresql:9.5', { name: 'postgresql:9.1', alias: 'postgres_old' }] }
- describe '#valid?' do
- it 'is valid' do
- expect(entry).to be_valid
- end
+ describe '#valid?' do
+ it 'is valid' do
+ expect(entry).to be_valid
end
end
- context 'when entry value is not correct' do
- let(:config) { 'ls' }
-
- describe '#errors' do
- it 'saves errors' do
- expect(entry.errors)
- .to include 'services config should be an array of strings'
- end
+ describe '#value' do
+ it 'returns valid array' do
+ expect(entry.value).to eq([{ name: 'postgresql:9.5' }, { name: 'postgresql:9.1', alias: 'postgres_old' }])
end
+ end
+ end
+
+ context 'when configuration is invalid' do
+ let(:config) { 'postgresql:9.5' }
- describe '#valid?' do
- it 'is not valid' do
- expect(entry).not_to be_valid
- end
+ describe '#valid?' do
+ it 'is invalid' do
+ expect(entry).not_to be_valid
end
end
end
diff --git a/spec/lib/gitlab/ci/status/build/cancelable_spec.rb b/spec/lib/gitlab/ci/status/build/cancelable_spec.rb
index 8ad9b7cdf07..114d2490490 100644
--- a/spec/lib/gitlab/ci/status/build/cancelable_spec.rb
+++ b/spec/lib/gitlab/ci/status/build/cancelable_spec.rb
@@ -47,7 +47,9 @@ describe Gitlab::Ci::Status::Build::Cancelable do
describe '#has_action?' do
context 'when user is allowed to update build' do
- before { build.project.team << [user, :developer] }
+ before do
+ build.project.team << [user, :developer]
+ end
it { is_expected.to have_action }
end
diff --git a/spec/lib/gitlab/ci/status/build/common_spec.rb b/spec/lib/gitlab/ci/status/build/common_spec.rb
index 72bd7c4eb93..03d1f46b517 100644
--- a/spec/lib/gitlab/ci/status/build/common_spec.rb
+++ b/spec/lib/gitlab/ci/status/build/common_spec.rb
@@ -17,13 +17,17 @@ describe Gitlab::Ci::Status::Build::Common do
describe '#has_details?' do
context 'when user has access to read build' do
- before { project.team << [user, :developer] }
+ before do
+ project.team << [user, :developer]
+ end
it { is_expected.to have_details }
end
context 'when user does not have access to read build' do
- before { project.update(public_builds: false) }
+ before do
+ project.update(public_builds: false)
+ end
it { is_expected.not_to have_details }
end
diff --git a/spec/lib/gitlab/ci/status/build/factory_spec.rb b/spec/lib/gitlab/ci/status/build/factory_spec.rb
index 3f30b2c38f2..c8a97016f20 100644
--- a/spec/lib/gitlab/ci/status/build/factory_spec.rb
+++ b/spec/lib/gitlab/ci/status/build/factory_spec.rb
@@ -6,7 +6,9 @@ describe Gitlab::Ci::Status::Build::Factory do
let(:status) { factory.fabricate! }
let(:factory) { described_class.new(build, user) }
- before { project.team << [user, :developer] }
+ before do
+ project.team << [user, :developer]
+ end
context 'when build is successful' do
let(:build) { create(:ci_build, :success) }
diff --git a/spec/lib/gitlab/ci/status/build/play_spec.rb b/spec/lib/gitlab/ci/status/build/play_spec.rb
index 0e15a5f3c6b..32b2e62e4e0 100644
--- a/spec/lib/gitlab/ci/status/build/play_spec.rb
+++ b/spec/lib/gitlab/ci/status/build/play_spec.rb
@@ -28,7 +28,9 @@ describe Gitlab::Ci::Status::Build::Play do
end
context 'when user can not push to the branch' do
- before { build.project.add_developer(user) }
+ before do
+ build.project.add_developer(user)
+ end
it { is_expected.not_to have_action }
end
diff --git a/spec/lib/gitlab/ci/status/build/retryable_spec.rb b/spec/lib/gitlab/ci/status/build/retryable_spec.rb
index 2db0f8d29bd..099d873fc01 100644
--- a/spec/lib/gitlab/ci/status/build/retryable_spec.rb
+++ b/spec/lib/gitlab/ci/status/build/retryable_spec.rb
@@ -47,7 +47,9 @@ describe Gitlab::Ci::Status::Build::Retryable do
describe '#has_action?' do
context 'when user is allowed to update build' do
- before { build.project.team << [user, :developer] }
+ before do
+ build.project.team << [user, :developer]
+ end
it { is_expected.to have_action }
end
diff --git a/spec/lib/gitlab/ci/status/build/stop_spec.rb b/spec/lib/gitlab/ci/status/build/stop_spec.rb
index 8d021c35a69..23902f26b1a 100644
--- a/spec/lib/gitlab/ci/status/build/stop_spec.rb
+++ b/spec/lib/gitlab/ci/status/build/stop_spec.rb
@@ -19,7 +19,9 @@ describe Gitlab::Ci::Status::Build::Stop do
describe '#has_action?' do
context 'when user is allowed to update build' do
- before { build.project.team << [user, :developer] }
+ before do
+ build.project.team << [user, :developer]
+ end
it { is_expected.to have_action }
end
diff --git a/spec/lib/gitlab/ci/status/external/common_spec.rb b/spec/lib/gitlab/ci/status/external/common_spec.rb
index 5a97d98b55f..b38fbee2486 100644
--- a/spec/lib/gitlab/ci/status/external/common_spec.rb
+++ b/spec/lib/gitlab/ci/status/external/common_spec.rb
@@ -4,9 +4,10 @@ describe Gitlab::Ci::Status::External::Common do
let(:user) { create(:user) }
let(:project) { external_status.project }
let(:external_target_url) { 'http://example.gitlab.com/status' }
+ let(:external_description) { 'my description' }
let(:external_status) do
- create(:generic_commit_status, target_url: external_target_url)
+ create(:generic_commit_status, target_url: external_target_url, description: external_description)
end
subject do
@@ -15,13 +16,21 @@ describe Gitlab::Ci::Status::External::Common do
.extend(described_class)
end
+ describe '#label' do
+ it 'returns description' do
+ expect(subject.label).to eq external_description
+ end
+ end
+
describe '#has_action?' do
it { is_expected.not_to have_action }
end
describe '#has_details?' do
context 'when user has access to read commit status' do
- before { project.team << [user, :developer] }
+ before do
+ project.team << [user, :developer]
+ end
it { is_expected.to have_details }
end
diff --git a/spec/lib/gitlab/ci/status/pipeline/common_spec.rb b/spec/lib/gitlab/ci/status/pipeline/common_spec.rb
index d665674bf70..f5fd31e8d03 100644
--- a/spec/lib/gitlab/ci/status/pipeline/common_spec.rb
+++ b/spec/lib/gitlab/ci/status/pipeline/common_spec.rb
@@ -17,7 +17,9 @@ describe Gitlab::Ci::Status::Pipeline::Common do
describe '#has_details?' do
context 'when user has access to read pipeline' do
- before { project.team << [user, :developer] }
+ before do
+ project.team << [user, :developer]
+ end
it { is_expected.to have_details }
end
diff --git a/spec/lib/gitlab/current_settings_spec.rb b/spec/lib/gitlab/current_settings_spec.rb
index fda39d78610..a566f24f6a6 100644
--- a/spec/lib/gitlab/current_settings_spec.rb
+++ b/spec/lib/gitlab/current_settings_spec.rb
@@ -32,6 +32,37 @@ describe Gitlab::CurrentSettings do
expect(current_application_settings).to be_a(ApplicationSetting)
end
+
+ context 'with migrations pending' do
+ before do
+ expect(ActiveRecord::Migrator).to receive(:needs_migration?).and_return(true)
+ end
+
+ it 'returns an in-memory ApplicationSetting object' do
+ settings = current_application_settings
+
+ expect(settings).to be_a(OpenStruct)
+ expect(settings.sign_in_enabled?).to eq(settings.sign_in_enabled)
+ expect(settings.sign_up_enabled?).to eq(settings.sign_up_enabled)
+ end
+
+ it 'uses the existing database settings and falls back to defaults' do
+ db_settings = create(:application_setting,
+ home_page_url: 'http://mydomain.com',
+ signup_enabled: false)
+ settings = current_application_settings
+ app_defaults = ApplicationSetting.last
+
+ expect(settings).to be_a(OpenStruct)
+ expect(settings.home_page_url).to eq(db_settings.home_page_url)
+ expect(settings.signup_enabled?).to be_falsey
+ expect(settings.signup_enabled).to be_falsey
+
+ # Check that unspecified values use the defaults
+ settings.reject! { |key, _| [:home_page_url, :signup_enabled].include? key }
+ settings.each { |key, _| expect(settings[key]).to eq(app_defaults[key]) }
+ end
+ end
end
context 'with DB unavailable' do
diff --git a/spec/lib/gitlab/database/migration_helpers_spec.rb b/spec/lib/gitlab/database/migration_helpers_spec.rb
index 3fdafd867da..30aa463faf8 100644
--- a/spec/lib/gitlab/database/migration_helpers_spec.rb
+++ b/spec/lib/gitlab/database/migration_helpers_spec.rb
@@ -7,7 +7,42 @@ describe Gitlab::Database::MigrationHelpers, lib: true do
)
end
- before { allow(model).to receive(:puts) }
+ before do
+ allow(model).to receive(:puts)
+ end
+
+ describe '#add_timestamps_with_timezone' do
+ before do
+ allow(model).to receive(:transaction_open?).and_return(false)
+ end
+
+ context 'using PostgreSQL' do
+ before do
+ allow(Gitlab::Database).to receive(:postgresql?).and_return(true)
+ allow(model).to receive(:disable_statement_timeout)
+ end
+
+ it 'adds "created_at" and "updated_at" fields with the "datetime_with_timezone" data type' do
+ expect(model).to receive(:add_column).with(:foo, :created_at, :datetime_with_timezone, { null: false })
+ expect(model).to receive(:add_column).with(:foo, :updated_at, :datetime_with_timezone, { null: false })
+
+ model.add_timestamps_with_timezone(:foo)
+ end
+ end
+
+ context 'using MySQL' do
+ before do
+ allow(Gitlab::Database).to receive(:postgresql?).and_return(false)
+ end
+
+ it 'adds "created_at" and "updated_at" fields with "datetime_with_timezone" data type' do
+ expect(model).to receive(:add_column).with(:foo, :created_at, :datetime_with_timezone, { null: false })
+ expect(model).to receive(:add_column).with(:foo, :updated_at, :datetime_with_timezone, { null: false })
+
+ model.add_timestamps_with_timezone(:foo)
+ end
+ end
+ end
describe '#add_concurrent_index' do
context 'outside a transaction' do
diff --git a/spec/lib/gitlab/database_spec.rb b/spec/lib/gitlab/database_spec.rb
index 9b1d66a1b1c..428b6edb7d6 100644
--- a/spec/lib/gitlab/database_spec.rb
+++ b/spec/lib/gitlab/database_spec.rb
@@ -53,14 +53,18 @@ describe Gitlab::Database, lib: true do
describe '.nulls_last_order' do
context 'when using PostgreSQL' do
- before { expect(described_class).to receive(:postgresql?).and_return(true) }
+ before do
+ expect(described_class).to receive(:postgresql?).and_return(true)
+ end
it { expect(described_class.nulls_last_order('column', 'ASC')).to eq 'column ASC NULLS LAST'}
it { expect(described_class.nulls_last_order('column', 'DESC')).to eq 'column DESC NULLS LAST'}
end
context 'when using MySQL' do
- before { expect(described_class).to receive(:postgresql?).and_return(false) }
+ before do
+ expect(described_class).to receive(:postgresql?).and_return(false)
+ end
it { expect(described_class.nulls_last_order('column', 'ASC')).to eq 'column IS NULL, column ASC'}
it { expect(described_class.nulls_last_order('column', 'DESC')).to eq 'column DESC'}
@@ -69,14 +73,18 @@ describe Gitlab::Database, lib: true do
describe '.nulls_first_order' do
context 'when using PostgreSQL' do
- before { expect(described_class).to receive(:postgresql?).and_return(true) }
+ before do
+ expect(described_class).to receive(:postgresql?).and_return(true)
+ end
it { expect(described_class.nulls_first_order('column', 'ASC')).to eq 'column ASC NULLS FIRST'}
it { expect(described_class.nulls_first_order('column', 'DESC')).to eq 'column DESC NULLS FIRST'}
end
context 'when using MySQL' do
- before { expect(described_class).to receive(:postgresql?).and_return(false) }
+ before do
+ expect(described_class).to receive(:postgresql?).and_return(false)
+ end
it { expect(described_class.nulls_first_order('column', 'ASC')).to eq 'column ASC'}
it { expect(described_class.nulls_first_order('column', 'DESC')).to eq 'column IS NULL, column DESC'}
@@ -121,6 +129,55 @@ describe Gitlab::Database, lib: true do
end
end
+ describe '.bulk_insert' do
+ before do
+ allow(described_class).to receive(:connection).and_return(connection)
+ allow(connection).to receive(:quote_column_name, &:itself)
+ allow(connection).to receive(:quote, &:itself)
+ allow(connection).to receive(:execute)
+ end
+
+ let(:connection) { double(:connection) }
+
+ let(:rows) do
+ [
+ { a: 1, b: 2, c: 3 },
+ { c: 6, a: 4, b: 5 }
+ ]
+ end
+
+ it 'does nothing with empty rows' do
+ expect(connection).not_to receive(:execute)
+
+ described_class.bulk_insert('test', [])
+ end
+
+ it 'uses the ordering from the first row' do
+ expect(connection).to receive(:execute) do |sql|
+ expect(sql).to include('(1, 2, 3)')
+ expect(sql).to include('(4, 5, 6)')
+ end
+
+ described_class.bulk_insert('test', rows)
+ end
+
+ it 'quotes column names' do
+ expect(connection).to receive(:quote_column_name).with(:a)
+ expect(connection).to receive(:quote_column_name).with(:b)
+ expect(connection).to receive(:quote_column_name).with(:c)
+
+ described_class.bulk_insert('test', rows)
+ end
+
+ it 'quotes values' do
+ 1.upto(6) do |i|
+ expect(connection).to receive(:quote).with(i)
+ end
+
+ described_class.bulk_insert('test', rows)
+ end
+ end
+
describe '.create_connection_pool' do
it 'creates a new connection pool with specific pool size' do
pool = described_class.create_connection_pool(5)
diff --git a/spec/lib/gitlab/diff/file_spec.rb b/spec/lib/gitlab/diff/file_spec.rb
index a9953bb0d01..f289131cc3a 100644
--- a/spec/lib/gitlab/diff/file_spec.rb
+++ b/spec/lib/gitlab/diff/file_spec.rb
@@ -92,4 +92,305 @@ describe Gitlab::Diff::File, lib: true do
expect(diff_file.diffable?).to be_falsey
end
end
+
+ describe '#content_changed?' do
+ context 'when created' do
+ let(:commit) { project.commit('33f3729a45c02fc67d00adb1b8bca394b0e761d9') }
+ let(:diff_file) { commit.diffs.diff_file_with_new_path('files/images/6049019_460s.jpg') }
+
+ it 'returns false' do
+ expect(diff_file.content_changed?).to be_falsey
+ end
+ end
+
+ context 'when deleted' do
+ let(:commit) { project.commit('d59c60028b053793cecfb4022de34602e1a9218e') }
+ let(:diff_file) { commit.diffs.diff_file_with_old_path('files/js/commit.js.coffee') }
+
+ it 'returns false' do
+ expect(diff_file.content_changed?).to be_falsey
+ end
+ end
+
+ context 'when renamed' do
+ let(:commit) { project.commit('6907208d755b60ebeacb2e9dfea74c92c3449a1f') }
+ let(:diff_file) { commit.diffs.diff_file_with_new_path('files/js/commit.coffee') }
+
+ before do
+ allow(diff_file.new_blob).to receive(:id).and_return(diff_file.old_blob.id)
+ end
+
+ it 'returns false' do
+ expect(diff_file.content_changed?).to be_falsey
+ end
+ end
+
+ context 'when content changed' do
+ context 'when binary' do
+ let(:commit) { project.commit('2f63565e7aac07bcdadb654e253078b727143ec4') }
+ let(:diff_file) { commit.diffs.diff_file_with_new_path('files/images/6049019_460s.jpg') }
+
+ it 'returns true' do
+ expect(diff_file.content_changed?).to be_truthy
+ end
+ end
+
+ context 'when not binary' do
+ let(:commit) { project.commit('570e7b2abdd848b95f2f578043fc23bd6f6fd24d') }
+ let(:diff_file) { commit.diffs.diff_file_with_new_path('files/ruby/popen.rb') }
+
+ it 'returns true' do
+ expect(diff_file.content_changed?).to be_truthy
+ end
+ end
+ end
+ end
+
+ describe '#simple_viewer' do
+ context 'when the file is not diffable' do
+ before do
+ allow(diff_file).to receive(:diffable?).and_return(false)
+ end
+
+ it 'returns a Not Diffable viewer' do
+ expect(diff_file.simple_viewer).to be_a(DiffViewer::NotDiffable)
+ end
+ end
+
+ context 'when the content changed' do
+ context 'when the file represented by the diff file is binary' do
+ before do
+ allow(diff_file).to receive(:raw_binary?).and_return(true)
+ end
+
+ it 'returns a No Preview viewer' do
+ expect(diff_file.simple_viewer).to be_a(DiffViewer::NoPreview)
+ end
+ end
+
+ context 'when the diff file old and new blob types are different' do
+ before do
+ allow(diff_file).to receive(:different_type?).and_return(true)
+ end
+
+ it 'returns a No Preview viewer' do
+ expect(diff_file.simple_viewer).to be_a(DiffViewer::NoPreview)
+ end
+ end
+
+ context 'when the file represented by the diff file is text-based' do
+ it 'returns a text viewer' do
+ expect(diff_file.simple_viewer).to be_a(DiffViewer::Text)
+ end
+ end
+ end
+
+ context 'when created' do
+ let(:commit) { project.commit('913c66a37b4a45b9769037c55c2d238bd0942d2e') }
+ let(:diff_file) { commit.diffs.diff_file_with_new_path('files/ruby/popen.rb') }
+
+ before do
+ allow(diff_file).to receive(:content_changed?).and_return(nil)
+ end
+
+ context 'when the file represented by the diff file is binary' do
+ before do
+ allow(diff_file).to receive(:raw_binary?).and_return(true)
+ end
+
+ it 'returns an Added viewer' do
+ expect(diff_file.simple_viewer).to be_a(DiffViewer::Added)
+ end
+ end
+
+ context 'when the diff file old and new blob types are different' do
+ before do
+ allow(diff_file).to receive(:different_type?).and_return(true)
+ end
+
+ it 'returns an Added viewer' do
+ expect(diff_file.simple_viewer).to be_a(DiffViewer::Added)
+ end
+ end
+
+ context 'when the file represented by the diff file is text-based' do
+ it 'returns a text viewer' do
+ expect(diff_file.simple_viewer).to be_a(DiffViewer::Text)
+ end
+ end
+ end
+
+ context 'when deleted' do
+ let(:commit) { project.commit('d59c60028b053793cecfb4022de34602e1a9218e') }
+ let(:diff_file) { commit.diffs.diff_file_with_old_path('files/js/commit.js.coffee') }
+
+ before do
+ allow(diff_file).to receive(:content_changed?).and_return(nil)
+ end
+
+ context 'when the file represented by the diff file is binary' do
+ before do
+ allow(diff_file).to receive(:raw_binary?).and_return(true)
+ end
+
+ it 'returns a Deleted viewer' do
+ expect(diff_file.simple_viewer).to be_a(DiffViewer::Deleted)
+ end
+ end
+
+ context 'when the diff file old and new blob types are different' do
+ before do
+ allow(diff_file).to receive(:different_type?).and_return(true)
+ end
+
+ it 'returns a Deleted viewer' do
+ expect(diff_file.simple_viewer).to be_a(DiffViewer::Deleted)
+ end
+ end
+
+ context 'when the file represented by the diff file is text-based' do
+ it 'returns a text viewer' do
+ expect(diff_file.simple_viewer).to be_a(DiffViewer::Text)
+ end
+ end
+ end
+
+ context 'when renamed' do
+ let(:commit) { project.commit('6907208d755b60ebeacb2e9dfea74c92c3449a1f') }
+ let(:diff_file) { commit.diffs.diff_file_with_new_path('files/js/commit.coffee') }
+
+ before do
+ allow(diff_file).to receive(:content_changed?).and_return(nil)
+ end
+
+ it 'returns a Renamed viewer' do
+ expect(diff_file.simple_viewer).to be_a(DiffViewer::Renamed)
+ end
+ end
+
+ context 'when mode changed' do
+ before do
+ allow(diff_file).to receive(:content_changed?).and_return(nil)
+ allow(diff_file).to receive(:mode_changed?).and_return(true)
+ end
+
+ it 'returns a Mode Changed viewer' do
+ expect(diff_file.simple_viewer).to be_a(DiffViewer::ModeChanged)
+ end
+ end
+ end
+
+ describe '#rich_viewer' do
+ let(:commit) { project.commit('2f63565e7aac07bcdadb654e253078b727143ec4') }
+ let(:diff_file) { commit.diffs.diff_file_with_new_path('files/images/6049019_460s.jpg') }
+
+ context 'when the diff file has a matching viewer' do
+ context 'when the diff file content did not change' do
+ before do
+ allow(diff_file).to receive(:content_changed?).and_return(false)
+ end
+
+ it 'returns nil' do
+ expect(diff_file.rich_viewer).to be_nil
+ end
+ end
+
+ context 'when the diff file is not diffable' do
+ before do
+ allow(diff_file).to receive(:diffable?).and_return(false)
+ end
+
+ it 'returns nil' do
+ expect(diff_file.rich_viewer).to be_nil
+ end
+ end
+
+ context 'when the diff file old and new blob types are different' do
+ before do
+ allow(diff_file).to receive(:different_type?).and_return(true)
+ end
+
+ it 'returns nil' do
+ expect(diff_file.rich_viewer).to be_nil
+ end
+ end
+
+ context 'when the diff file has an external storage error' do
+ before do
+ allow(diff_file).to receive(:external_storage_error?).and_return(true)
+ end
+
+ it 'returns nil' do
+ expect(diff_file.rich_viewer).to be_nil
+ end
+ end
+
+ context 'when everything is right' do
+ it 'returns the viewer' do
+ expect(diff_file.rich_viewer).to be_a(DiffViewer::Image)
+ end
+ end
+ end
+
+ context 'when the diff file does not have a matching viewer' do
+ let(:commit) { project.commit('570e7b2abdd848b95f2f578043fc23bd6f6fd24d') }
+ let(:diff_file) { commit.diffs.diff_file_with_new_path('files/ruby/popen.rb') }
+
+ it 'returns nil' do
+ expect(diff_file.rich_viewer).to be_nil
+ end
+ end
+ end
+
+ describe '#rendered_as_text?' do
+ context 'when the simple viewer is text-based' do
+ let(:commit) { project.commit('570e7b2abdd848b95f2f578043fc23bd6f6fd24d') }
+ let(:diff_file) { commit.diffs.diff_file_with_new_path('files/ruby/popen.rb') }
+
+ context 'when ignoring errors' do
+ context 'when the viewer has render errors' do
+ before do
+ diff_file.diff.too_large!
+ end
+
+ it 'returns true' do
+ expect(diff_file.rendered_as_text?).to be_truthy
+ end
+ end
+
+ context "when the viewer doesn't have render errors" do
+ it 'returns true' do
+ expect(diff_file.rendered_as_text?).to be_truthy
+ end
+ end
+ end
+
+ context 'when not ignoring errors' do
+ context 'when the viewer has render errors' do
+ before do
+ diff_file.diff.too_large!
+ end
+
+ it 'returns false' do
+ expect(diff_file.rendered_as_text?(ignore_errors: false)).to be_falsey
+ end
+ end
+
+ context "when the viewer doesn't have render errors" do
+ it 'returns true' do
+ expect(diff_file.rendered_as_text?(ignore_errors: false)).to be_truthy
+ end
+ end
+ end
+ end
+
+ context 'when the simple viewer is binary' do
+ let(:commit) { project.commit('2f63565e7aac07bcdadb654e253078b727143ec4') }
+ let(:diff_file) { commit.diffs.diff_file_with_new_path('files/images/6049019_460s.jpg') }
+
+ it 'returns false' do
+ expect(diff_file.rendered_as_text?).to be_falsey
+ 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 3f79eaf7afb..cd0309e248d 100644
--- a/spec/lib/gitlab/email/handler/create_note_handler_spec.rb
+++ b/spec/lib/gitlab/email/handler/create_note_handler_spec.rb
@@ -91,7 +91,7 @@ describe Gitlab::Email::Handler::CreateNoteHandler, lib: true do
end
end
- context 'when the note contains slash commands' do
+ context 'when the note contains quick actions' do
let!(:email_raw) { fixture_file("emails/commands_in_reply.eml") }
context 'and current user cannot update noteable' do
diff --git a/spec/lib/gitlab/etag_caching/middleware_spec.rb b/spec/lib/gitlab/etag_caching/middleware_spec.rb
index 3c6ef7c7ccb..4acf4f047f1 100644
--- a/spec/lib/gitlab/etag_caching/middleware_spec.rb
+++ b/spec/lib/gitlab/etag_caching/middleware_spec.rb
@@ -15,13 +15,13 @@ describe Gitlab::EtagCaching::Middleware do
end
it 'does not add ETag header' do
- _, headers, _ = middleware.call(build_env(path, if_none_match))
+ _, headers, _ = middleware.call(build_request(path, if_none_match))
expect(headers['ETag']).to be_nil
end
it 'passes status code from app' do
- status, _, _ = middleware.call(build_env(path, if_none_match))
+ status, _, _ = middleware.call(build_request(path, if_none_match))
expect(status).to eq app_status_code
end
@@ -39,7 +39,7 @@ describe Gitlab::EtagCaching::Middleware do
expect_any_instance_of(Gitlab::EtagCaching::Store)
.to receive(:touch).and_return('123')
- middleware.call(build_env(path, if_none_match))
+ middleware.call(build_request(path, if_none_match))
end
context 'when If-None-Match header was specified' do
@@ -51,7 +51,7 @@ describe Gitlab::EtagCaching::Middleware do
expect(Gitlab::Metrics).to receive(:add_event)
.with(:etag_caching_key_not_found, endpoint: 'issue_notes')
- middleware.call(build_env(path, if_none_match))
+ middleware.call(build_request(path, if_none_match))
end
end
end
@@ -65,7 +65,7 @@ describe Gitlab::EtagCaching::Middleware do
end
it 'returns this value as header' do
- _, headers, _ = middleware.call(build_env(path, if_none_match))
+ _, headers, _ = middleware.call(build_request(path, if_none_match))
expect(headers['ETag']).to eq 'W/"123"'
end
@@ -82,17 +82,17 @@ describe Gitlab::EtagCaching::Middleware do
it 'does not call app' do
expect(app).not_to receive(:call)
- middleware.call(build_env(path, if_none_match))
+ middleware.call(build_request(path, if_none_match))
end
it 'returns status code 304' do
- status, _, _ = middleware.call(build_env(path, if_none_match))
+ status, _, _ = middleware.call(build_request(path, if_none_match))
expect(status).to eq 304
end
it 'returns empty body' do
- _, _, body = middleware.call(build_env(path, if_none_match))
+ _, _, body = middleware.call(build_request(path, if_none_match))
expect(body).to be_empty
end
@@ -103,7 +103,7 @@ describe Gitlab::EtagCaching::Middleware do
expect(Gitlab::Metrics).to receive(:add_event)
.with(:etag_caching_cache_hit, endpoint: 'issue_notes')
- middleware.call(build_env(path, if_none_match))
+ middleware.call(build_request(path, if_none_match))
end
context 'when polling is disabled' do
@@ -113,7 +113,7 @@ describe Gitlab::EtagCaching::Middleware do
end
it 'returns status code 429' do
- status, _, _ = middleware.call(build_env(path, if_none_match))
+ status, _, _ = middleware.call(build_request(path, if_none_match))
expect(status).to eq 429
end
@@ -131,7 +131,7 @@ describe Gitlab::EtagCaching::Middleware do
it 'calls app' do
expect(app).to receive(:call).and_return([app_status_code, {}, ['body']])
- middleware.call(build_env(path, if_none_match))
+ middleware.call(build_request(path, if_none_match))
end
it 'tracks "etag_caching_resource_changed" event' do
@@ -142,7 +142,7 @@ describe Gitlab::EtagCaching::Middleware do
expect(Gitlab::Metrics).to receive(:add_event)
.with(:etag_caching_resource_changed, endpoint: 'issue_notes')
- middleware.call(build_env(path, if_none_match))
+ middleware.call(build_request(path, if_none_match))
end
end
@@ -160,7 +160,7 @@ describe Gitlab::EtagCaching::Middleware do
expect(Gitlab::Metrics).to receive(:add_event)
.with(:etag_caching_header_missing, endpoint: 'issue_notes')
- middleware.call(build_env(path, if_none_match))
+ middleware.call(build_request(path, if_none_match))
end
end
@@ -192,10 +192,7 @@ describe Gitlab::EtagCaching::Middleware do
.to receive(:get).and_return(value)
end
- def build_env(path, if_none_match)
- {
- 'PATH_INFO' => path,
- 'HTTP_IF_NONE_MATCH' => if_none_match
- }
+ def build_request(path, if_none_match)
+ { 'PATH_INFO' => path, 'HTTP_IF_NONE_MATCH' => if_none_match }
end
end
diff --git a/spec/lib/gitlab/etag_caching/router_spec.rb b/spec/lib/gitlab/etag_caching/router_spec.rb
index 2bb40827fcf..f69cb502ca6 100644
--- a/spec/lib/gitlab/etag_caching/router_spec.rb
+++ b/spec/lib/gitlab/etag_caching/router_spec.rb
@@ -2,115 +2,91 @@ require 'spec_helper'
describe Gitlab::EtagCaching::Router do
it 'matches issue notes endpoint' do
- request = build_request(
+ result = described_class.match(
'/my-group/and-subgroup/here-comes-the-project/noteable/issue/1/notes'
)
- result = described_class.match(request)
-
expect(result).to be_present
expect(result.name).to eq 'issue_notes'
end
it 'matches issue title endpoint' do
- request = build_request(
+ result = described_class.match(
'/my-group/my-project/issues/123/realtime_changes'
)
- result = described_class.match(request)
-
expect(result).to be_present
expect(result.name).to eq 'issue_title'
end
it 'matches project pipelines endpoint' do
- request = build_request(
+ result = described_class.match(
'/my-group/my-project/pipelines.json'
)
- result = described_class.match(request)
-
expect(result).to be_present
expect(result.name).to eq 'project_pipelines'
end
it 'matches commit pipelines endpoint' do
- request = build_request(
+ result = described_class.match(
'/my-group/my-project/commit/aa8260d253a53f73f6c26c734c72fdd600f6e6d4/pipelines.json'
)
- result = described_class.match(request)
-
expect(result).to be_present
expect(result.name).to eq 'commit_pipelines'
end
it 'matches new merge request pipelines endpoint' do
- request = build_request(
+ result = described_class.match(
'/my-group/my-project/merge_requests/new.json'
)
- result = described_class.match(request)
-
expect(result).to be_present
expect(result.name).to eq 'new_merge_request_pipelines'
end
it 'matches merge request pipelines endpoint' do
- request = build_request(
+ result = described_class.match(
'/my-group/my-project/merge_requests/234/pipelines.json'
)
- result = described_class.match(request)
-
expect(result).to be_present
expect(result.name).to eq 'merge_request_pipelines'
end
it 'matches build endpoint' do
- request = build_request(
+ result = described_class.match(
'/my-group/my-project/builds/234.json'
)
- result = described_class.match(request)
-
expect(result).to be_present
expect(result.name).to eq 'project_build'
end
it 'does not match blob with confusing name' do
- request = build_request(
+ result = described_class.match(
'/my-group/my-project/blob/master/pipelines.json'
)
- result = described_class.match(request)
-
expect(result).to be_blank
end
it 'matches the environments path' do
- request = build_request(
+ result = described_class.match(
'/my-group/my-project/environments.json'
)
- result = described_class.match(request)
expect(result).to be_present
-
expect(result.name).to eq 'environments'
end
it 'matches pipeline#show endpoint' do
- request = build_request(
+ result = described_class.match(
'/my-group/my-project/pipelines/2.json'
)
- result = described_class.match(request)
-
expect(result).to be_present
expect(result.name).to eq 'project_pipeline'
end
-
- def build_request(path)
- double(path_info: path)
- end
end
diff --git a/spec/lib/gitlab/fake_application_settings_spec.rb b/spec/lib/gitlab/fake_application_settings_spec.rb
new file mode 100644
index 00000000000..b793176d84a
--- /dev/null
+++ b/spec/lib/gitlab/fake_application_settings_spec.rb
@@ -0,0 +1,32 @@
+require 'spec_helper'
+
+describe Gitlab::FakeApplicationSettings do
+ let(:defaults) { { signin_enabled: false, foobar: 'asdf', signup_enabled: true, 'test?' => 123 } }
+
+ subject { described_class.new(defaults) }
+
+ it 'wraps OpenStruct variables properly' do
+ expect(subject.signin_enabled).to be_falsey
+ expect(subject.signup_enabled).to be_truthy
+ expect(subject.foobar).to eq('asdf')
+ end
+
+ it 'defines predicate methods' do
+ expect(subject.signin_enabled?).to be_falsey
+ expect(subject.signup_enabled?).to be_truthy
+ end
+
+ it 'predicate method changes when value is updated' do
+ subject.signin_enabled = true
+
+ expect(subject.signin_enabled?).to be_truthy
+ end
+
+ it 'does not define a predicate method' do
+ expect(subject.foobar?).to be_nil
+ end
+
+ it 'does not override an existing predicate method' do
+ expect(subject.test?).to eq(123)
+ end
+end
diff --git a/spec/lib/gitlab/gfm/reference_rewriter_spec.rb b/spec/lib/gitlab/gfm/reference_rewriter_spec.rb
index 5d416c9eec3..eaec699ad90 100644
--- a/spec/lib/gitlab/gfm/reference_rewriter_spec.rb
+++ b/spec/lib/gitlab/gfm/reference_rewriter_spec.rb
@@ -6,7 +6,9 @@ describe Gitlab::Gfm::ReferenceRewriter do
let(:new_project) { create(:empty_project, name: 'new-project') }
let(:user) { create(:user) }
- before { old_project.team << [user, :reporter] }
+ before do
+ old_project.team << [user, :reporter]
+ end
describe '#rewrite' do
subject do
diff --git a/spec/lib/gitlab/git/diff_spec.rb b/spec/lib/gitlab/git/diff_spec.rb
index da213f617cc..78d741f0110 100644
--- a/spec/lib/gitlab/git/diff_spec.rb
+++ b/spec/lib/gitlab/git/diff_spec.rb
@@ -90,7 +90,7 @@ EOT
let(:diff) { described_class.new(@rugged_diff) }
it 'initializes the diff' do
- expect(diff.to_hash).to eq(@raw_diff_hash.merge(too_large: nil))
+ expect(diff.to_hash).to eq(@raw_diff_hash)
end
it 'does not prune the diff' do
diff --git a/spec/lib/gitlab/git/gitmodules_parser_spec.rb b/spec/lib/gitlab/git/gitmodules_parser_spec.rb
new file mode 100644
index 00000000000..143aa2218c9
--- /dev/null
+++ b/spec/lib/gitlab/git/gitmodules_parser_spec.rb
@@ -0,0 +1,28 @@
+require 'spec_helper'
+
+describe Gitlab::Git::GitmodulesParser do
+ it 'should parse a .gitmodules file correctly' do
+ parser = described_class.new(<<-'GITMODULES'.strip_heredoc)
+ [submodule "vendor/libgit2"]
+ path = vendor/libgit2
+ [submodule "vendor/libgit2"]
+ url = https://github.com/nodegit/libgit2.git
+
+ # a comment
+ [submodule "moved"]
+ path = new/path
+ url = https://example.com/some/project
+ [submodule "bogus"]
+ url = https://example.com/another/project
+ GITMODULES
+
+ modules = parser.parse
+
+ expect(modules).to eq({
+ 'vendor/libgit2' => { 'name' => 'vendor/libgit2',
+ 'url' => 'https://github.com/nodegit/libgit2.git' },
+ 'new/path' => { 'name' => 'moved',
+ 'url' => 'https://example.com/some/project' }
+ })
+ end
+end
diff --git a/spec/lib/gitlab/git/repository_spec.rb b/spec/lib/gitlab/git/repository_spec.rb
index e1e4aa9fde9..02b3f167250 100644
--- a/spec/lib/gitlab/git/repository_spec.rb
+++ b/spec/lib/gitlab/git/repository_spec.rb
@@ -16,7 +16,9 @@ describe Gitlab::Git::Repository, seed_helper: true do
describe '#root_ref' do
context 'with gitaly disabled' do
- before { allow(Gitlab::GitalyClient).to receive(:feature_enabled?).and_return(false) }
+ before do
+ allow(Gitlab::GitalyClient).to receive(:feature_enabled?).and_return(false)
+ end
it 'calls #discover_default_branch' do
expect(repository).to receive(:discover_default_branch)
@@ -25,8 +27,13 @@ describe Gitlab::Git::Repository, seed_helper: true do
end
context 'with gitaly enabled' do
- before { stub_gitaly }
- after { Gitlab::GitalyClient.clear_stubs! }
+ before do
+ stub_gitaly
+ end
+
+ after do
+ Gitlab::GitalyClient.clear_stubs!
+ end
it 'gets the branch name from GitalyClient' do
expect_any_instance_of(Gitlab::GitalyClient::Ref).to receive(:default_branch_name)
@@ -120,8 +127,13 @@ describe Gitlab::Git::Repository, seed_helper: true do
it { is_expected.not_to include("branch-from-space") }
context 'with gitaly enabled' do
- before { stub_gitaly }
- after { Gitlab::GitalyClient.clear_stubs! }
+ before do
+ stub_gitaly
+ end
+
+ after do
+ Gitlab::GitalyClient.clear_stubs!
+ end
it 'gets the branch names from GitalyClient' do
expect_any_instance_of(Gitlab::GitalyClient::Ref).to receive(:branch_names)
@@ -158,8 +170,13 @@ describe Gitlab::Git::Repository, seed_helper: true do
it { is_expected.not_to include("v5.0.0") }
context 'with gitaly enabled' do
- before { stub_gitaly }
- after { Gitlab::GitalyClient.clear_stubs! }
+ before do
+ stub_gitaly
+ end
+
+ after do
+ Gitlab::GitalyClient.clear_stubs!
+ end
it 'gets the tag names from GitalyClient' do
expect_any_instance_of(Gitlab::GitalyClient::Ref).to receive(:tag_names)
@@ -341,7 +358,7 @@ describe Gitlab::Git::Repository, seed_helper: true do
expect(submodule).to eq([
"six", {
"id" => "409f37c4f05865e4fb208c771485f211a22c4c2d",
- "path" => "six",
+ "name" => "six",
"url" => "git://github.com/randx/six.git"
}
])
@@ -349,14 +366,14 @@ describe Gitlab::Git::Repository, seed_helper: true do
it 'should handle nested submodules correctly' do
nested = submodules['nested/six']
- expect(nested['path']).to eq('nested/six')
+ expect(nested['name']).to eq('nested/six')
expect(nested['url']).to eq('git://github.com/randx/six.git')
expect(nested['id']).to eq('24fb71c79fcabc63dfd8832b12ee3bf2bf06b196')
end
it 'should handle deeply nested submodules correctly' do
nested = submodules['deeper/nested/six']
- expect(nested['path']).to eq('deeper/nested/six')
+ expect(nested['name']).to eq('deeper/nested/six')
expect(nested['url']).to eq('git://github.com/randx/six.git')
expect(nested['id']).to eq('24fb71c79fcabc63dfd8832b12ee3bf2bf06b196')
end
@@ -376,7 +393,7 @@ describe Gitlab::Git::Repository, seed_helper: true do
expect(submodules.first).to eq([
"six", {
"id" => "409f37c4f05865e4fb208c771485f211a22c4c2d",
- "path" => "six",
+ "name" => "six",
"url" => "git://github.com/randx/six.git"
}
])
@@ -1280,8 +1297,13 @@ describe Gitlab::Git::Repository, seed_helper: true do
end
context 'with gitaly enabled' do
- before { stub_gitaly }
- after { Gitlab::GitalyClient.clear_stubs! }
+ before do
+ stub_gitaly
+ end
+
+ after do
+ Gitlab::GitalyClient.clear_stubs!
+ end
it 'gets the branches from GitalyClient' do
expect_any_instance_of(Gitlab::GitalyClient::Ref).to receive(:local_branches).
diff --git a/spec/lib/gitlab/git_access_spec.rb b/spec/lib/gitlab/git_access_spec.rb
index 36d1d777583..9a86cfa66e4 100644
--- a/spec/lib/gitlab/git_access_spec.rb
+++ b/spec/lib/gitlab/git_access_spec.rb
@@ -3,11 +3,12 @@ require 'spec_helper'
describe Gitlab::GitAccess, lib: true do
let(:pull_access_check) { access.check('git-upload-pack', '_any') }
let(:push_access_check) { access.check('git-receive-pack', '_any') }
- let(:access) { Gitlab::GitAccess.new(actor, project, protocol, authentication_abilities: authentication_abilities) }
+ let(:access) { Gitlab::GitAccess.new(actor, project, protocol, authentication_abilities: authentication_abilities, redirected_path: redirected_path) }
let(:project) { create(:project, :repository) }
let(:user) { create(:user) }
let(:actor) { user }
let(:protocol) { 'ssh' }
+ let(:redirected_path) { nil }
let(:authentication_abilities) do
[
:read_project,
@@ -60,7 +61,9 @@ describe Gitlab::GitAccess, lib: true do
let(:actor) { deploy_key }
context 'when the DeployKey has access to the project' do
- before { deploy_key.projects << project }
+ before do
+ deploy_key.projects << project
+ end
it 'allows pull access' do
expect { pull_access_check }.not_to raise_error
@@ -84,7 +87,9 @@ describe Gitlab::GitAccess, lib: true do
context 'when actor is a User' do
context 'when the User can read the project' do
- before { project.team << [user, :master] }
+ before do
+ project.team << [user, :master]
+ end
it 'allows pull access' do
expect { pull_access_check }.not_to raise_error
@@ -158,8 +163,50 @@ describe Gitlab::GitAccess, lib: true do
end
end
+ describe '#check_project_moved!' do
+ before do
+ project.team << [user, :master]
+ end
+
+ context 'when a redirect was not followed to find the project' do
+ context 'pull code' do
+ it { expect { pull_access_check }.not_to raise_error }
+ end
+
+ context 'push code' do
+ it { expect { push_access_check }.not_to raise_error }
+ end
+ end
+
+ context 'when a redirect was followed to find the project' do
+ let(:redirected_path) { 'some/other-path' }
+
+ context 'pull code' do
+ it { expect { pull_access_check }.to raise_not_found(/Project '#{redirected_path}' was moved to '#{project.full_path}'/) }
+ it { expect { pull_access_check }.to raise_not_found(/git remote set-url origin #{project.ssh_url_to_repo}/) }
+
+ context 'http protocol' do
+ let(:protocol) { 'http' }
+ it { expect { pull_access_check }.to raise_not_found(/git remote set-url origin #{project.http_url_to_repo}/) }
+ end
+ end
+
+ context 'push code' do
+ it { expect { push_access_check }.to raise_not_found(/Project '#{redirected_path}' was moved to '#{project.full_path}'/) }
+ it { expect { push_access_check }.to raise_not_found(/git remote set-url origin #{project.ssh_url_to_repo}/) }
+
+ context 'http protocol' do
+ let(:protocol) { 'http' }
+ it { expect { push_access_check }.to raise_not_found(/git remote set-url origin #{project.http_url_to_repo}/) }
+ end
+ end
+ end
+ end
+
describe '#check_command_disabled!' do
- before { project.team << [user, :master] }
+ before do
+ project.team << [user, :master]
+ end
context 'over http' do
let(:protocol) { 'http' }
@@ -196,7 +243,9 @@ describe Gitlab::GitAccess, lib: true do
describe '#check_download_access!' do
describe 'master permissions' do
- before { project.team << [user, :master] }
+ before do
+ project.team << [user, :master]
+ end
context 'pull code' do
it { expect { pull_access_check }.not_to raise_error }
@@ -204,7 +253,9 @@ describe Gitlab::GitAccess, lib: true do
end
describe 'guest permissions' do
- before { project.team << [user, :guest] }
+ before do
+ project.team << [user, :guest]
+ end
context 'pull code' do
it { expect { pull_access_check }.to raise_unauthorized('You are not allowed to download code from this project.') }
@@ -253,7 +304,9 @@ describe Gitlab::GitAccess, lib: true do
context 'pull code' do
context 'when project is authorized' do
- before { key.projects << project }
+ before do
+ key.projects << project
+ end
it { expect { pull_access_check }.not_to raise_error }
end
@@ -292,7 +345,9 @@ describe Gitlab::GitAccess, lib: true do
end
describe 'reporter user' do
- before { project.team << [user, :reporter] }
+ before do
+ project.team << [user, :reporter]
+ end
context 'pull code' do
it { expect { pull_access_check }.not_to raise_error }
@@ -303,7 +358,9 @@ describe Gitlab::GitAccess, lib: true do
let(:user) { create(:admin) }
context 'when member of the project' do
- before { project.team << [user, :reporter] }
+ before do
+ project.team << [user, :reporter]
+ end
context 'pull code' do
it { expect { pull_access_check }.not_to raise_error }
@@ -328,7 +385,9 @@ describe Gitlab::GitAccess, lib: true do
end
describe '#check_push_access!' do
- before { merge_into_protected_branch }
+ before do
+ merge_into_protected_branch
+ end
let(:unprotected_branch) { 'unprotected_branch' }
let(:changes) do
@@ -457,19 +516,25 @@ describe Gitlab::GitAccess, lib: true do
[%w(feature exact), ['feat*', 'wildcard']].each do |protected_branch_name, protected_branch_type|
context do
- before { create(:protected_branch, name: protected_branch_name, project: project) }
+ before do
+ create(:protected_branch, name: protected_branch_name, project: project)
+ end
run_permission_checks(permissions_matrix)
end
context "when developers are allowed to push into the #{protected_branch_type} protected branch" do
- before { create(:protected_branch, :developers_can_push, name: protected_branch_name, project: project) }
+ before do
+ create(:protected_branch, :developers_can_push, name: protected_branch_name, project: project)
+ end
run_permission_checks(permissions_matrix.deep_merge(developer: { push_protected_branch: true, push_all: true, merge_into_protected_branch: true }))
end
context "developers are allowed to merge into the #{protected_branch_type} protected branch" do
- before { create(:protected_branch, :developers_can_merge, name: protected_branch_name, project: project) }
+ before do
+ create(:protected_branch, :developers_can_merge, name: protected_branch_name, project: project)
+ end
context "when a merge request exists for the given source/target branch" do
context "when the merge request is in progress" do
@@ -496,13 +561,17 @@ describe Gitlab::GitAccess, lib: true do
end
context "when developers are allowed to push and merge into the #{protected_branch_type} protected branch" do
- before { create(:protected_branch, :developers_can_merge, :developers_can_push, name: protected_branch_name, project: project) }
+ before do
+ create(:protected_branch, :developers_can_merge, :developers_can_push, name: protected_branch_name, project: project)
+ end
run_permission_checks(permissions_matrix.deep_merge(developer: { push_protected_branch: true, push_all: true, merge_into_protected_branch: true }))
end
context "when no one is allowed to push to the #{protected_branch_name} protected branch" do
- before { create(:protected_branch, :no_one_can_push, name: protected_branch_name, project: project) }
+ before do
+ create(:protected_branch, :no_one_can_push, name: protected_branch_name, project: project)
+ end
run_permission_checks(permissions_matrix.deep_merge(developer: { push_protected_branch: false, push_all: false, merge_into_protected_branch: false },
master: { push_protected_branch: false, push_all: false, merge_into_protected_branch: false },
@@ -515,7 +584,9 @@ describe Gitlab::GitAccess, lib: true do
let(:authentication_abilities) { build_authentication_abilities }
context 'when project is authorized' do
- before { project.team << [user, :reporter] }
+ before do
+ project.team << [user, :reporter]
+ end
it { expect { push_access_check }.to raise_unauthorized('You are not allowed to upload code for this project.') }
end
@@ -549,7 +620,9 @@ describe Gitlab::GitAccess, lib: true do
let(:can_push) { true }
context 'when project is authorized' do
- before { key.projects << project }
+ before do
+ key.projects << project
+ end
it { expect { push_access_check }.not_to raise_error }
end
@@ -579,7 +652,9 @@ describe Gitlab::GitAccess, lib: true do
let(:can_push) { false }
context 'when project is authorized' do
- before { key.projects << project }
+ before do
+ key.projects << project
+ end
it { expect { push_access_check }.to raise_unauthorized('This deploy key does not have write access to this project.') }
end
diff --git a/spec/lib/gitlab/git_access_wiki_spec.rb b/spec/lib/gitlab/git_access_wiki_spec.rb
index a1eb95750ba..797ec8cb23e 100644
--- a/spec/lib/gitlab/git_access_wiki_spec.rb
+++ b/spec/lib/gitlab/git_access_wiki_spec.rb
@@ -1,9 +1,10 @@
require 'spec_helper'
describe Gitlab::GitAccessWiki, lib: true do
- let(:access) { Gitlab::GitAccessWiki.new(user, project, 'web', authentication_abilities: authentication_abilities) }
+ let(:access) { Gitlab::GitAccessWiki.new(user, project, 'web', authentication_abilities: authentication_abilities, redirected_path: redirected_path) }
let(:project) { create(:project, :repository) }
let(:user) { create(:user) }
+ let(:redirected_path) { nil }
let(:authentication_abilities) do
[
:read_project,
diff --git a/spec/lib/gitlab/gitaly_client/notifications_spec.rb b/spec/lib/gitlab/gitaly_client/notifications_spec.rb
index b87dacb175b..e5c9e06a15e 100644
--- a/spec/lib/gitlab/gitaly_client/notifications_spec.rb
+++ b/spec/lib/gitlab/gitaly_client/notifications_spec.rb
@@ -3,12 +3,13 @@ require 'spec_helper'
describe Gitlab::GitalyClient::Notifications do
describe '#post_receive' do
let(:project) { create(:empty_project) }
- let(:repo_path) { project.repository.path_to_repo }
+ let(:storage_name) { project.repository_storage }
+ let(:relative_path) { project.path_with_namespace + '.git' }
subject { described_class.new(project.repository) }
it 'sends a post_receive message' do
expect_any_instance_of(Gitaly::Notifications::Stub).
- to receive(:post_receive).with(gitaly_request_with_repo_path(repo_path))
+ to receive(:post_receive).with(gitaly_request_with_path(storage_name, relative_path))
subject.post_receive
end
diff --git a/spec/lib/gitlab/gitaly_client/ref_spec.rb b/spec/lib/gitlab/gitaly_client/ref_spec.rb
index d8cd2dcbd2a..2ea44ef74b0 100644
--- a/spec/lib/gitlab/gitaly_client/ref_spec.rb
+++ b/spec/lib/gitlab/gitaly_client/ref_spec.rb
@@ -2,7 +2,8 @@ require 'spec_helper'
describe Gitlab::GitalyClient::Ref do
let(:project) { create(:empty_project) }
- let(:repo_path) { project.repository.path_to_repo }
+ let(:storage_name) { project.repository_storage }
+ let(:relative_path) { project.path_with_namespace + '.git' }
let(:client) { described_class.new(project.repository) }
before do
@@ -19,7 +20,8 @@ describe Gitlab::GitalyClient::Ref do
describe '#branch_names' do
it 'sends a find_all_branch_names message' do
expect_any_instance_of(Gitaly::Ref::Stub).
- to receive(:find_all_branch_names).with(gitaly_request_with_repo_path(repo_path)).
+ to receive(:find_all_branch_names).
+ with(gitaly_request_with_path(storage_name, relative_path)).
and_return([])
client.branch_names
@@ -29,7 +31,8 @@ describe Gitlab::GitalyClient::Ref do
describe '#tag_names' do
it 'sends a find_all_tag_names message' do
expect_any_instance_of(Gitaly::Ref::Stub).
- to receive(:find_all_tag_names).with(gitaly_request_with_repo_path(repo_path)).
+ to receive(:find_all_tag_names).
+ with(gitaly_request_with_path(storage_name, relative_path)).
and_return([])
client.tag_names
@@ -39,7 +42,8 @@ describe Gitlab::GitalyClient::Ref do
describe '#default_branch_name' do
it 'sends a find_default_branch_name message' do
expect_any_instance_of(Gitaly::Ref::Stub).
- to receive(:find_default_branch_name).with(gitaly_request_with_repo_path(repo_path)).
+ to receive(:find_default_branch_name).
+ with(gitaly_request_with_path(storage_name, relative_path)).
and_return(double(name: 'foo'))
client.default_branch_name
@@ -49,7 +53,8 @@ describe Gitlab::GitalyClient::Ref do
describe '#local_branches' do
it 'sends a find_local_branches message' do
expect_any_instance_of(Gitaly::Ref::Stub).
- to receive(:find_local_branches).with(gitaly_request_with_repo_path(repo_path)).
+ to receive(:find_local_branches).
+ with(gitaly_request_with_path(storage_name, relative_path)).
and_return([])
client.local_branches
diff --git a/spec/lib/gitlab/gitaly_client_spec.rb b/spec/lib/gitlab/gitaly_client_spec.rb
index 95ecba67532..ce7b18b784a 100644
--- a/spec/lib/gitlab/gitaly_client_spec.rb
+++ b/spec/lib/gitlab/gitaly_client_spec.rb
@@ -5,7 +5,9 @@ require 'spec_helper'
describe Gitlab::GitalyClient, lib: true, skip_gitaly_mock: true do
describe '.stub' do
# Notice that this is referring to gRPC "stubs", not rspec stubs
- before { described_class.clear_stubs! }
+ before do
+ described_class.clear_stubs!
+ end
context 'when passed a UNIX socket address' do
it 'passes the address as-is to GRPC' do
@@ -41,7 +43,9 @@ describe Gitlab::GitalyClient, lib: true, skip_gitaly_mock: true do
let(:real_feature_name) { "gitaly_#{feature_name}" }
context 'when Gitaly is disabled' do
- before { allow(described_class).to receive(:enabled?).and_return(false) }
+ before do
+ allow(described_class).to receive(:enabled?).and_return(false)
+ end
it 'returns false' do
expect(described_class.feature_enabled?(feature_name)).to be(false)
@@ -66,7 +70,9 @@ describe Gitlab::GitalyClient, lib: true, skip_gitaly_mock: true do
end
context "when the feature flag is set to disable" do
- before { Feature.get(real_feature_name).disable }
+ before do
+ Feature.get(real_feature_name).disable
+ end
it 'returns false' do
expect(described_class.feature_enabled?(feature_name, status: feature_status)).to be(false)
@@ -74,7 +80,9 @@ describe Gitlab::GitalyClient, lib: true, skip_gitaly_mock: true do
end
context "when the feature flag is set to enable" do
- before { Feature.get(real_feature_name).enable }
+ before do
+ Feature.get(real_feature_name).enable
+ end
it 'returns true' do
expect(described_class.feature_enabled?(feature_name, status: feature_status)).to be(true)
@@ -82,7 +90,9 @@ describe Gitlab::GitalyClient, lib: true, skip_gitaly_mock: true do
end
context "when the feature flag is set to a percentage of time" do
- before { Feature.get(real_feature_name).enable_percentage_of_time(70) }
+ before do
+ Feature.get(real_feature_name).enable_percentage_of_time(70)
+ end
it 'bases the result on pseudo-random numbers' do
expect(Random).to receive(:rand).and_return(0.3)
@@ -104,7 +114,9 @@ describe Gitlab::GitalyClient, lib: true, skip_gitaly_mock: true do
end
context "when the feature flag is set to disable" do
- before { Feature.get(real_feature_name).disable }
+ before do
+ Feature.get(real_feature_name).disable
+ end
it 'returns false' do
expect(described_class.feature_enabled?(feature_name, status: feature_status)).to be(false)
diff --git a/spec/lib/gitlab/group_hierarchy_spec.rb b/spec/lib/gitlab/group_hierarchy_spec.rb
index 5d0ed1522b3..08010c2d0e2 100644
--- a/spec/lib/gitlab/group_hierarchy_spec.rb
+++ b/spec/lib/gitlab/group_hierarchy_spec.rb
@@ -17,6 +17,12 @@ describe Gitlab::GroupHierarchy, :postgresql do
it 'includes all of the ancestors' do
expect(relation).to include(parent, child1)
end
+
+ it 'uses ancestors_base #initialize argument' do
+ relation = described_class.new(Group.where(id: child2.id), Group.none).base_and_ancestors
+
+ expect(relation).to include(parent, child1, child2)
+ end
end
describe '#base_and_descendants' do
@@ -31,6 +37,12 @@ describe Gitlab::GroupHierarchy, :postgresql do
it 'includes all the descendants' do
expect(relation).to include(child1, child2)
end
+
+ it 'uses descendants_base #initialize argument' do
+ relation = described_class.new(Group.none, Group.where(id: parent.id)).base_and_descendants
+
+ expect(relation).to include(parent, child1, child2)
+ end
end
describe '#all_groups' do
@@ -49,5 +61,17 @@ describe Gitlab::GroupHierarchy, :postgresql do
it 'includes the descendants' do
expect(relation).to include(child2)
end
+
+ it 'uses ancestors_base #initialize argument for ancestors' do
+ relation = described_class.new(Group.where(id: child1.id), Group.where(id: Group.maximum(:id).succ)).all_groups
+
+ expect(relation).to include(parent)
+ end
+
+ it 'uses descendants_base #initialize argument for descendants' do
+ relation = described_class.new(Group.where(id: Group.maximum(:id).succ), Group.where(id: child1.id)).all_groups
+
+ expect(relation).to include(child2)
+ end
end
end
diff --git a/spec/lib/gitlab/highlight_spec.rb b/spec/lib/gitlab/highlight_spec.rb
index c2bb9f9a166..fdc5b484ef1 100644
--- a/spec/lib/gitlab/highlight_spec.rb
+++ b/spec/lib/gitlab/highlight_spec.rb
@@ -15,7 +15,9 @@ describe Gitlab::Highlight, lib: true do
Gitlab::Highlight.new(blob.path, blob.data, repository: repository)
end
- before { project.change_head('gitattributes') }
+ before do
+ project.change_head('gitattributes')
+ end
describe 'basic language selection' do
let(:path) { 'custom-highlighting/test.gitlab-custom' }
diff --git a/spec/lib/gitlab/i18n_spec.rb b/spec/lib/gitlab/i18n_spec.rb
index a3dbeaa3753..0dba4132101 100644
--- a/spec/lib/gitlab/i18n_spec.rb
+++ b/spec/lib/gitlab/i18n_spec.rb
@@ -4,7 +4,9 @@ describe Gitlab::I18n, lib: true do
let(:user) { create(:user, preferred_language: 'es') }
describe '.locale=' do
- after { described_class.use_default_locale }
+ after do
+ described_class.use_default_locale
+ end
it 'sets the locale based on current user preferred language' do
described_class.locale = user.preferred_language
diff --git a/spec/lib/gitlab/import_export/all_models.yml b/spec/lib/gitlab/import_export/all_models.yml
index 412eb33b35b..a5f09f1856e 100644
--- a/spec/lib/gitlab/import_export/all_models.yml
+++ b/spec/lib/gitlab/import_export/all_models.yml
@@ -88,6 +88,9 @@ merge_requests:
- head_pipeline
merge_request_diff:
- merge_request
+- merge_request_diff_files
+merge_request_diff_files:
+- merge_request_diff
pipelines:
- project
- user
diff --git a/spec/lib/gitlab/import_export/project.json b/spec/lib/gitlab/import_export/project.json
index e3599d6fe59..98c117b4cd8 100644
--- a/spec/lib/gitlab/import_export/project.json
+++ b/spec/lib/gitlab/import_export/project.json
@@ -2821,9 +2821,11 @@
"committer_email": "dmitriy.zaporozhets@gmail.com"
}
],
- "utf8_st_diffs": [
+ "merge_request_diff_files": [
{
- "diff": "Binary files a/.DS_Store and /dev/null differ\n",
+ "merge_request_diff_id": 27,
+ "relative_order": 0,
+ "utf8_diff": "Binary files a/.DS_Store and /dev/null differ\n",
"new_path": ".DS_Store",
"old_path": ".DS_Store",
"a_mode": "100644",
@@ -2834,7 +2836,9 @@
"too_large": false
},
{
- "diff": "--- a/.gitignore\n+++ b/.gitignore\n@@ -17,3 +17,4 @@ rerun.txt\n pickle-email-*.html\n .project\n config/initializers/secret_token.rb\n+.DS_Store\n",
+ "merge_request_diff_id": 27,
+ "relative_order": 1,
+ "utf8_diff": "--- a/.gitignore\n+++ b/.gitignore\n@@ -17,3 +17,4 @@ rerun.txt\n pickle-email-*.html\n .project\n config/initializers/secret_token.rb\n+.DS_Store\n",
"new_path": ".gitignore",
"old_path": ".gitignore",
"a_mode": "100644",
@@ -2845,7 +2849,9 @@
"too_large": false
},
{
- "diff": "--- a/.gitmodules\n+++ b/.gitmodules\n@@ -1,3 +1,9 @@\n [submodule \"six\"]\n \tpath = six\n \turl = git://github.com/randx/six.git\n+[submodule \"gitlab-shell\"]\n+\tpath = gitlab-shell\n+\turl = https://github.com/gitlabhq/gitlab-shell.git\n+[submodule \"gitlab-grack\"]\n+\tpath = gitlab-grack\n+\turl = https://gitlab.com/gitlab-org/gitlab-grack.git\n",
+ "merge_request_diff_id": 27,
+ "relative_order": 2,
+ "utf8_diff": "--- a/.gitmodules\n+++ b/.gitmodules\n@@ -1,3 +1,9 @@\n [submodule \"six\"]\n \tpath = six\n \turl = git://github.com/randx/six.git\n+[submodule \"gitlab-shell\"]\n+\tpath = gitlab-shell\n+\turl = https://github.com/gitlabhq/gitlab-shell.git\n+[submodule \"gitlab-grack\"]\n+\tpath = gitlab-grack\n+\turl = https://gitlab.com/gitlab-org/gitlab-grack.git\n",
"new_path": ".gitmodules",
"old_path": ".gitmodules",
"a_mode": "100644",
@@ -2856,7 +2862,9 @@
"too_large": false
},
{
- "diff": "Binary files a/files/.DS_Store and /dev/null differ\n",
+ "merge_request_diff_id": 27,
+ "relative_order": 3,
+ "utf8_diff": "Binary files a/files/.DS_Store and /dev/null differ\n",
"new_path": "files/.DS_Store",
"old_path": "files/.DS_Store",
"a_mode": "100644",
@@ -2867,7 +2875,9 @@
"too_large": false
},
{
- "diff": "--- /dev/null\n+++ b/files/ruby/feature.rb\n@@ -0,0 +1,4 @@\n+# This file was changed in feature branch\n+# We put different code here to make merge conflict\n+class Conflict\n+end\n",
+ "merge_request_diff_id": 27,
+ "relative_order": 4,
+ "utf8_diff": "--- /dev/null\n+++ b/files/ruby/feature.rb\n@@ -0,0 +1,4 @@\n+# This file was changed in feature branch\n+# We put different code here to make merge conflict\n+class Conflict\n+end\n",
"new_path": "files/ruby/feature.rb",
"old_path": "files/ruby/feature.rb",
"a_mode": "0",
@@ -2878,7 +2888,9 @@
"too_large": false
},
{
- "diff": "--- a/files/ruby/popen.rb\n+++ b/files/ruby/popen.rb\n@@ -6,12 +6,18 @@ module Popen\n \n def popen(cmd, path=nil)\n unless cmd.is_a?(Array)\n- raise \"System commands must be given as an array of strings\"\n+ raise RuntimeError, \"System commands must be given as an array of strings\"\n end\n \n path ||= Dir.pwd\n- vars = { \"PWD\" =\u003e path }\n- options = { chdir: path }\n+\n+ vars = {\n+ \"PWD\" =\u003e path\n+ }\n+\n+ options = {\n+ chdir: path\n+ }\n \n unless File.directory?(path)\n FileUtils.mkdir_p(path)\n@@ -19,6 +25,7 @@ module Popen\n \n @cmd_output = \"\"\n @cmd_status = 0\n+\n Open3.popen3(vars, *cmd, options) do |stdin, stdout, stderr, wait_thr|\n @cmd_output \u003c\u003c stdout.read\n @cmd_output \u003c\u003c stderr.read\n",
+ "merge_request_diff_id": 27,
+ "relative_order": 5,
+ "utf8_diff": "--- a/files/ruby/popen.rb\n+++ b/files/ruby/popen.rb\n@@ -6,12 +6,18 @@ module Popen\n \n def popen(cmd, path=nil)\n unless cmd.is_a?(Array)\n- raise \"System commands must be given as an array of strings\"\n+ raise RuntimeError, \"System commands must be given as an array of strings\"\n end\n \n path ||= Dir.pwd\n- vars = { \"PWD\" =\u003e path }\n- options = { chdir: path }\n+\n+ vars = {\n+ \"PWD\" =\u003e path\n+ }\n+\n+ options = {\n+ chdir: path\n+ }\n \n unless File.directory?(path)\n FileUtils.mkdir_p(path)\n@@ -19,6 +25,7 @@ module Popen\n \n @cmd_output = \"\"\n @cmd_status = 0\n+\n Open3.popen3(vars, *cmd, options) do |stdin, stdout, stderr, wait_thr|\n @cmd_output \u003c\u003c stdout.read\n @cmd_output \u003c\u003c stderr.read\n",
"new_path": "files/ruby/popen.rb",
"old_path": "files/ruby/popen.rb",
"a_mode": "100644",
@@ -2889,7 +2901,9 @@
"too_large": false
},
{
- "diff": "--- a/files/ruby/regex.rb\n+++ b/files/ruby/regex.rb\n@@ -19,14 +19,12 @@ module Gitlab\n end\n \n def archive_formats_regex\n- #|zip|tar| tar.gz | tar.bz2 |\n- /(zip|tar|tar\\.gz|tgz|gz|tar\\.bz2|tbz|tbz2|tb2|bz2)/\n+ /(zip|tar|7z|tar\\.gz|tgz|gz|tar\\.bz2|tbz|tbz2|tb2|bz2)/\n end\n \n def git_reference_regex\n # Valid git ref regex, see:\n # https://www.kernel.org/pub/software/scm/git/docs/git-check-ref-format.html\n-\n %r{\n (?!\n (?# doesn't begins with)\n",
+ "merge_request_diff_id": 27,
+ "relative_order": 6,
+ "utf8_diff": "--- a/files/ruby/regex.rb\n+++ b/files/ruby/regex.rb\n@@ -19,14 +19,12 @@ module Gitlab\n end\n \n def archive_formats_regex\n- #|zip|tar| tar.gz | tar.bz2 |\n- /(zip|tar|tar\\.gz|tgz|gz|tar\\.bz2|tbz|tbz2|tb2|bz2)/\n+ /(zip|tar|7z|tar\\.gz|tgz|gz|tar\\.bz2|tbz|tbz2|tb2|bz2)/\n end\n \n def git_reference_regex\n # Valid git ref regex, see:\n # https://www.kernel.org/pub/software/scm/git/docs/git-check-ref-format.html\n-\n %r{\n (?!\n (?# doesn't begins with)\n",
"new_path": "files/ruby/regex.rb",
"old_path": "files/ruby/regex.rb",
"a_mode": "100644",
@@ -2900,7 +2914,9 @@
"too_large": false
},
{
- "diff": "--- /dev/null\n+++ b/gitlab-grack\n@@ -0,0 +1 @@\n+Subproject commit 645f6c4c82fd3f5e06f67134450a570b795e55a6\n",
+ "merge_request_diff_id": 27,
+ "relative_order": 7,
+ "utf8_diff": "--- /dev/null\n+++ b/gitlab-grack\n@@ -0,0 +1 @@\n+Subproject commit 645f6c4c82fd3f5e06f67134450a570b795e55a6\n",
"new_path": "gitlab-grack",
"old_path": "gitlab-grack",
"a_mode": "0",
@@ -2911,7 +2927,9 @@
"too_large": false
},
{
- "diff": "--- /dev/null\n+++ b/gitlab-shell\n@@ -0,0 +1 @@\n+Subproject commit 79bceae69cb5750d6567b223597999bfa91cb3b9\n",
+ "merge_request_diff_id": 27,
+ "relative_order": 8,
+ "utf8_diff": "--- /dev/null\n+++ b/gitlab-shell\n@@ -0,0 +1 @@\n+Subproject commit 79bceae69cb5750d6567b223597999bfa91cb3b9\n",
"new_path": "gitlab-shell",
"old_path": "gitlab-shell",
"a_mode": "0",
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 14338515892..c11b15a811b 100644
--- a/spec/lib/gitlab/import_export/project_tree_restorer_spec.rb
+++ b/spec/lib/gitlab/import_export/project_tree_restorer_spec.rb
@@ -86,8 +86,13 @@ describe Gitlab::ImportExport::ProjectTreeRestorer, services: true do
it 'has the correct data for merge request st_diffs' do
# makes sure we are renaming the custom method +utf8_st_diffs+ into +st_diffs+
+ # one MergeRequestDiff uses the new format, where st_diffs is expected to be nil
- expect(MergeRequestDiff.where.not(st_diffs: nil).count).to eq(9)
+ expect(MergeRequestDiff.where.not(st_diffs: nil).count).to eq(8)
+ end
+
+ it 'has the correct data for merge request diff files' do
+ expect(MergeRequestDiffFile.where.not(diff: nil).count).to eq(9)
end
it 'has the correct time for merge request st_commits' do
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 5aeb29b7fec..e52f79513f1 100644
--- a/spec/lib/gitlab/import_export/project_tree_saver_spec.rb
+++ b/spec/lib/gitlab/import_export/project_tree_saver_spec.rb
@@ -83,6 +83,10 @@ describe Gitlab::ImportExport::ProjectTreeSaver, services: true do
expect(saved_project_json['merge_requests'].first['merge_request_diff']['utf8_st_diffs']).not_to be_nil
end
+ it 'has merge request diff files' do
+ expect(saved_project_json['merge_requests'].first['merge_request_diff']['merge_request_diff_files']).not_to be_empty
+ end
+
it 'has merge requests comments' do
expect(saved_project_json['merge_requests'].first['notes']).not_to be_empty
end
@@ -145,6 +149,12 @@ describe Gitlab::ImportExport::ProjectTreeSaver, services: true do
expect(project_tree_saver.save).to be true
end
+ it 'does not complain about non UTF-8 characters in MR diff files' do
+ ActiveRecord::Base.connection.execute("UPDATE merge_request_diff_files SET diff = '---\n- :diff: !binary |-\n LS0tIC9kZXYvbnVsbAorKysgYi9pbWFnZXMvbnVjb3IucGRmCkBAIC0wLDAg\n KzEsMTY3OSBAQAorJVBERi0xLjUNJeLjz9MNCisxIDAgb2JqDTw8L01ldGFk\n YXR'")
+
+ expect(project_tree_saver.save).to be true
+ end
+
context 'group members' do
let(:user2) { create(:user, email: 'group@member.com') }
let(:member_emails) do
diff --git a/spec/lib/gitlab/import_export/safe_model_attributes.yml b/spec/lib/gitlab/import_export/safe_model_attributes.yml
index 50ff6ecc1e0..fadd3ad1330 100644
--- a/spec/lib/gitlab/import_export/safe_model_attributes.yml
+++ b/spec/lib/gitlab/import_export/safe_model_attributes.yml
@@ -172,6 +172,17 @@ MergeRequestDiff:
- real_size
- head_commit_sha
- start_commit_sha
+MergeRequestDiffFile:
+- merge_request_diff_id
+- relative_order
+- new_file
+- renamed_file
+- deleted_file
+- new_path
+- old_path
+- a_mode
+- b_mode
+- too_large
Ci::Pipeline:
- id
- project_id
diff --git a/spec/lib/gitlab/kubernetes_spec.rb b/spec/lib/gitlab/kubernetes_spec.rb
index 91f9d06b85a..e8c599a95ee 100644
--- a/spec/lib/gitlab/kubernetes_spec.rb
+++ b/spec/lib/gitlab/kubernetes_spec.rb
@@ -1,6 +1,7 @@
require 'spec_helper'
describe Gitlab::Kubernetes do
+ include KubernetesHelpers
include described_class
describe '#container_exec_url' do
@@ -36,4 +37,13 @@ describe Gitlab::Kubernetes do
it { expect(result.query).to match(/\Acontainer=container\+1&/) }
end
end
+
+ describe '#filter_by_label' do
+ it 'returns matching labels' do
+ matching_items = [kube_pod(app: 'foo')]
+ items = matching_items + [kube_pod]
+
+ expect(filter_by_label(items, app: 'foo')).to eq(matching_items)
+ end
+ end
end
diff --git a/spec/lib/gitlab/ldap/adapter_spec.rb b/spec/lib/gitlab/ldap/adapter_spec.rb
index 563c074017a..9454878b057 100644
--- a/spec/lib/gitlab/ldap/adapter_spec.rb
+++ b/spec/lib/gitlab/ldap/adapter_spec.rb
@@ -74,13 +74,17 @@ describe Gitlab::LDAP::Adapter, lib: true do
subject { adapter.dn_matches_filter?(:dn, :filter) }
context "when the search result is non-empty" do
- before { allow(adapter).to receive(:ldap_search).and_return([:foo]) }
+ before do
+ allow(adapter).to receive(:ldap_search).and_return([:foo])
+ end
it { is_expected.to be_truthy }
end
context "when the search result is empty" do
- before { allow(adapter).to receive(:ldap_search).and_return([]) }
+ before do
+ allow(adapter).to receive(:ldap_search).and_return([])
+ end
it { is_expected.to be_falsey }
end
@@ -91,13 +95,17 @@ describe Gitlab::LDAP::Adapter, lib: true do
context "when the search is successful" do
context "and the result is non-empty" do
- before { allow(ldap).to receive(:search).and_return([:foo]) }
+ before do
+ allow(ldap).to receive(:search).and_return([:foo])
+ end
it { is_expected.to eq [:foo] }
end
context "and the result is empty" do
- before { allow(ldap).to receive(:search).and_return([]) }
+ before do
+ allow(ldap).to receive(:search).and_return([])
+ end
it { is_expected.to eq [] }
end
diff --git a/spec/lib/gitlab/ldap/user_spec.rb b/spec/lib/gitlab/ldap/user_spec.rb
index a0eda685ca3..f0a1dd22fee 100644
--- a/spec/lib/gitlab/ldap/user_spec.rb
+++ b/spec/lib/gitlab/ldap/user_spec.rb
@@ -173,7 +173,9 @@ describe Gitlab::LDAP::User, lib: true do
context 'signup' do
context 'dont block on create' do
- before { configure_block(false) }
+ before do
+ configure_block(false)
+ end
it do
ldap_user.save
@@ -183,7 +185,9 @@ describe Gitlab::LDAP::User, lib: true do
end
context 'block on create' do
- before { configure_block(true) }
+ before do
+ configure_block(true)
+ end
it do
ldap_user.save
@@ -200,7 +204,9 @@ describe Gitlab::LDAP::User, lib: true do
end
context 'dont block on create' do
- before { configure_block(false) }
+ before do
+ configure_block(false)
+ end
it do
ldap_user.save
@@ -210,7 +216,9 @@ describe Gitlab::LDAP::User, lib: true do
end
context 'block on create' do
- before { configure_block(true) }
+ before do
+ configure_block(true)
+ end
it do
ldap_user.save
diff --git a/spec/lib/gitlab/middleware/rails_queue_duration_spec.rb b/spec/lib/gitlab/middleware/rails_queue_duration_spec.rb
index 168090d5b5c..88107536c9e 100644
--- a/spec/lib/gitlab/middleware/rails_queue_duration_spec.rb
+++ b/spec/lib/gitlab/middleware/rails_queue_duration_spec.rb
@@ -6,7 +6,9 @@ describe Gitlab::Middleware::RailsQueueDuration do
let(:env) { {} }
let(:transaction) { double(:transaction) }
- before { expect(app).to receive(:call).with(env).and_return('yay') }
+ before do
+ expect(app).to receive(:call).with(env).and_return('yay')
+ end
describe '#call' do
it 'calls the app when metrics are disabled' do
@@ -15,7 +17,9 @@ describe Gitlab::Middleware::RailsQueueDuration do
end
context 'when metrics are enabled' do
- before { allow(Gitlab::Metrics).to receive(:current_transaction).and_return(transaction) }
+ before do
+ allow(Gitlab::Metrics).to receive(:current_transaction).and_return(transaction)
+ end
it 'calls the app when metrics are enabled but no timing header is found' do
expect(middleware.call(env)).to eq('yay')
diff --git a/spec/lib/gitlab/o_auth/auth_hash_spec.rb b/spec/lib/gitlab/o_auth/auth_hash_spec.rb
index 8aaeb5779d3..19ab17419fc 100644
--- a/spec/lib/gitlab/o_auth/auth_hash_spec.rb
+++ b/spec/lib/gitlab/o_auth/auth_hash_spec.rb
@@ -55,7 +55,9 @@ describe Gitlab::OAuth::AuthHash, lib: true do
end
context 'email not provided' do
- before { info_hash.delete(:email) }
+ before do
+ info_hash.delete(:email)
+ end
it 'generates a temp email' do
expect( auth_hash.email).to start_with('temp-email-for-oauth')
@@ -63,7 +65,9 @@ describe Gitlab::OAuth::AuthHash, lib: true do
end
context 'username not provided' do
- before { info_hash.delete(:nickname) }
+ before do
+ info_hash.delete(:nickname)
+ end
it 'takes the first part of the email as username' do
expect(auth_hash.username).to eql 'onur.kucuk_ABC-123'
@@ -71,7 +75,9 @@ describe Gitlab::OAuth::AuthHash, lib: true do
end
context 'name not provided' do
- before { info_hash.delete(:name) }
+ before do
+ info_hash.delete(:name)
+ end
it 'concats first and lastname as the name' do
expect(auth_hash.name).to eql name_utf8
diff --git a/spec/lib/gitlab/o_auth/user_spec.rb b/spec/lib/gitlab/o_auth/user_spec.rb
index 8943d1aa488..ea29cb9caf1 100644
--- a/spec/lib/gitlab/o_auth/user_spec.rb
+++ b/spec/lib/gitlab/o_auth/user_spec.rb
@@ -112,7 +112,9 @@ describe Gitlab::OAuth::User, lib: true do
end
context 'with new allow_single_sign_on enabled syntax' do
- before { stub_omniauth_config(allow_single_sign_on: ['twitter']) }
+ before do
+ stub_omniauth_config(allow_single_sign_on: ['twitter'])
+ end
it "creates a user from Omniauth" do
oauth_user.save
@@ -125,7 +127,9 @@ describe Gitlab::OAuth::User, lib: true do
end
context "with old allow_single_sign_on enabled syntax" do
- before { stub_omniauth_config(allow_single_sign_on: true) }
+ before do
+ stub_omniauth_config(allow_single_sign_on: true)
+ end
it "creates a user from Omniauth" do
oauth_user.save
@@ -138,14 +142,20 @@ describe Gitlab::OAuth::User, lib: true do
end
context 'with new allow_single_sign_on disabled syntax' do
- before { stub_omniauth_config(allow_single_sign_on: []) }
+ before do
+ stub_omniauth_config(allow_single_sign_on: [])
+ end
+
it 'throws an error' do
expect{ oauth_user.save }.to raise_error StandardError
end
end
context 'with old allow_single_sign_on disabled (Default)' do
- before { stub_omniauth_config(allow_single_sign_on: false) }
+ before do
+ stub_omniauth_config(allow_single_sign_on: false)
+ end
+
it 'throws an error' do
expect{ oauth_user.save }.to raise_error StandardError
end
@@ -153,21 +163,30 @@ describe Gitlab::OAuth::User, lib: true do
end
context "with auto_link_ldap_user disabled (default)" do
- before { stub_omniauth_config(auto_link_ldap_user: false) }
+ before do
+ stub_omniauth_config(auto_link_ldap_user: false)
+ end
+
include_examples "to verify compliance with allow_single_sign_on"
end
context "with auto_link_ldap_user enabled" do
- before { stub_omniauth_config(auto_link_ldap_user: true) }
+ before do
+ stub_omniauth_config(auto_link_ldap_user: true)
+ end
context "and no LDAP provider defined" do
- before { stub_ldap_config(providers: []) }
+ before do
+ stub_ldap_config(providers: [])
+ end
include_examples "to verify compliance with allow_single_sign_on"
end
context "and at least one LDAP provider is defined" do
- before { stub_ldap_config(providers: %w(ldapmain)) }
+ before do
+ stub_ldap_config(providers: %w(ldapmain))
+ end
context "and a corresponding LDAP person" do
before do
@@ -238,7 +257,9 @@ describe Gitlab::OAuth::User, lib: true do
end
context "and no corresponding LDAP person" do
- before { allow(Gitlab::LDAP::Person).to receive(:find_by_uid).and_return(nil) }
+ before do
+ allow(Gitlab::LDAP::Person).to receive(:find_by_uid).and_return(nil)
+ end
include_examples "to verify compliance with allow_single_sign_on"
end
@@ -248,11 +269,16 @@ describe Gitlab::OAuth::User, lib: true do
describe 'blocking' do
let(:provider) { 'twitter' }
- before { stub_omniauth_config(allow_single_sign_on: ['twitter']) }
+
+ before do
+ stub_omniauth_config(allow_single_sign_on: ['twitter'])
+ end
context 'signup with omniauth only' do
context 'dont block on create' do
- before { stub_omniauth_config(block_auto_created_users: false) }
+ before do
+ stub_omniauth_config(block_auto_created_users: false)
+ end
it do
oauth_user.save
@@ -262,7 +288,9 @@ describe Gitlab::OAuth::User, lib: true do
end
context 'block on create' do
- before { stub_omniauth_config(block_auto_created_users: true) }
+ before do
+ stub_omniauth_config(block_auto_created_users: true)
+ end
it do
oauth_user.save
@@ -284,7 +312,9 @@ describe Gitlab::OAuth::User, lib: true do
context "and no account for the LDAP user" do
context 'dont block on create (LDAP)' do
- before { allow_any_instance_of(Gitlab::LDAP::Config).to receive_messages(block_auto_created_users: false) }
+ before do
+ allow_any_instance_of(Gitlab::LDAP::Config).to receive_messages(block_auto_created_users: false)
+ end
it do
oauth_user.save
@@ -294,7 +324,9 @@ describe Gitlab::OAuth::User, lib: true do
end
context 'block on create (LDAP)' do
- before { allow_any_instance_of(Gitlab::LDAP::Config).to receive_messages(block_auto_created_users: true) }
+ before do
+ allow_any_instance_of(Gitlab::LDAP::Config).to receive_messages(block_auto_created_users: true)
+ end
it do
oauth_user.save
@@ -308,7 +340,9 @@ describe Gitlab::OAuth::User, lib: true do
let!(:existing_user) { create(:omniauth_user, email: 'john@example.com', extern_uid: 'uid=user1,ou=People,dc=example', provider: 'ldapmain', username: 'john') }
context 'dont block on create (LDAP)' do
- before { allow_any_instance_of(Gitlab::LDAP::Config).to receive_messages(block_auto_created_users: false) }
+ before do
+ allow_any_instance_of(Gitlab::LDAP::Config).to receive_messages(block_auto_created_users: false)
+ end
it do
oauth_user.save
@@ -318,7 +352,9 @@ describe Gitlab::OAuth::User, lib: true do
end
context 'block on create (LDAP)' do
- before { allow_any_instance_of(Gitlab::LDAP::Config).to receive_messages(block_auto_created_users: true) }
+ before do
+ allow_any_instance_of(Gitlab::LDAP::Config).to receive_messages(block_auto_created_users: true)
+ end
it do
oauth_user.save
@@ -336,7 +372,9 @@ describe Gitlab::OAuth::User, lib: true do
end
context 'dont block on create' do
- before { stub_omniauth_config(block_auto_created_users: false) }
+ before do
+ stub_omniauth_config(block_auto_created_users: false)
+ end
it do
oauth_user.save
@@ -346,7 +384,9 @@ describe Gitlab::OAuth::User, lib: true do
end
context 'block on create' do
- before { stub_omniauth_config(block_auto_created_users: true) }
+ before do
+ stub_omniauth_config(block_auto_created_users: true)
+ end
it do
oauth_user.save
@@ -356,7 +396,9 @@ describe Gitlab::OAuth::User, lib: true do
end
context 'dont block on create (LDAP)' do
- before { allow_any_instance_of(Gitlab::LDAP::Config).to receive_messages(block_auto_created_users: false) }
+ before do
+ allow_any_instance_of(Gitlab::LDAP::Config).to receive_messages(block_auto_created_users: false)
+ end
it do
oauth_user.save
@@ -366,7 +408,9 @@ describe Gitlab::OAuth::User, lib: true do
end
context 'block on create (LDAP)' do
- before { allow_any_instance_of(Gitlab::LDAP::Config).to receive_messages(block_auto_created_users: true) }
+ before do
+ allow_any_instance_of(Gitlab::LDAP::Config).to receive_messages(block_auto_created_users: true)
+ end
it do
oauth_user.save
diff --git a/spec/lib/gitlab/slash_commands/command_definition_spec.rb b/spec/lib/gitlab/quick_actions/command_definition_spec.rb
index 5b9173d3d3f..f44a562dc63 100644
--- a/spec/lib/gitlab/slash_commands/command_definition_spec.rb
+++ b/spec/lib/gitlab/quick_actions/command_definition_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe Gitlab::SlashCommands::CommandDefinition do
+describe Gitlab::QuickActions::CommandDefinition do
subject { described_class.new(:command) }
describe "#all_names" do
diff --git a/spec/lib/gitlab/slash_commands/dsl_spec.rb b/spec/lib/gitlab/quick_actions/dsl_spec.rb
index 33b49a5ddf9..a4bb3f911d7 100644
--- a/spec/lib/gitlab/slash_commands/dsl_spec.rb
+++ b/spec/lib/gitlab/quick_actions/dsl_spec.rb
@@ -1,9 +1,9 @@
require 'spec_helper'
-describe Gitlab::SlashCommands::Dsl do
+describe Gitlab::QuickActions::Dsl do
before :all do
DummyClass = Struct.new(:project) do
- include Gitlab::SlashCommands::Dsl # rubocop:disable RSpec/DescribedClass
+ include Gitlab::QuickActions::Dsl # rubocop:disable RSpec/DescribedClass
desc 'A command with no args'
command :no_args, :none do
diff --git a/spec/lib/gitlab/slash_commands/extractor_spec.rb b/spec/lib/gitlab/quick_actions/extractor_spec.rb
index d7f77486b3e..9d32938e155 100644
--- a/spec/lib/gitlab/slash_commands/extractor_spec.rb
+++ b/spec/lib/gitlab/quick_actions/extractor_spec.rb
@@ -1,9 +1,9 @@
require 'spec_helper'
-describe Gitlab::SlashCommands::Extractor do
+describe Gitlab::QuickActions::Extractor do
let(:definitions) do
Class.new do
- include Gitlab::SlashCommands::Dsl
+ include Gitlab::QuickActions::Dsl
command(:reopen, :open) { }
command(:assign) { }
diff --git a/spec/lib/gitlab/redis_spec.rb b/spec/lib/gitlab/redis_spec.rb
index 8b77c925705..593aa5038ad 100644
--- a/spec/lib/gitlab/redis_spec.rb
+++ b/spec/lib/gitlab/redis_spec.rb
@@ -108,11 +108,18 @@ describe Gitlab::Redis do
end
describe '.with' do
- before { clear_pool }
- after { clear_pool }
+ before do
+ clear_pool
+ end
+
+ after do
+ clear_pool
+ end
context 'when running not on sidekiq workers' do
- before { allow(Sidekiq).to receive(:server?).and_return(false) }
+ before do
+ allow(Sidekiq).to receive(:server?).and_return(false)
+ end
it 'instantiates a connection pool with size 5' do
expect(ConnectionPool).to receive(:new).with(size: 5).and_call_original
diff --git a/spec/lib/gitlab/repo_path_spec.rb b/spec/lib/gitlab/repo_path_spec.rb
index f9025397107..efea4f429bf 100644
--- a/spec/lib/gitlab/repo_path_spec.rb
+++ b/spec/lib/gitlab/repo_path_spec.rb
@@ -4,24 +4,44 @@ describe ::Gitlab::RepoPath do
describe '.parse' do
set(:project) { create(:project) }
- it 'parses a full repository path' do
- expect(described_class.parse(project.repository.path)).to eq([project, false])
- end
+ context 'a repository storage path' do
+ it 'parses a full repository path' do
+ expect(described_class.parse(project.repository.path)).to eq([project, false, nil])
+ end
- it 'parses a full wiki path' do
- expect(described_class.parse(project.wiki.repository.path)).to eq([project, true])
+ it 'parses a full wiki path' do
+ expect(described_class.parse(project.wiki.repository.path)).to eq([project, true, nil])
+ end
end
- it 'parses a relative repository path' do
- expect(described_class.parse(project.full_path + '.git')).to eq([project, false])
- end
+ context 'a relative path' do
+ it 'parses a relative repository path' do
+ expect(described_class.parse(project.full_path + '.git')).to eq([project, false, nil])
+ end
- it 'parses a relative wiki path' do
- expect(described_class.parse(project.full_path + '.wiki.git')).to eq([project, true])
- end
+ it 'parses a relative wiki path' do
+ expect(described_class.parse(project.full_path + '.wiki.git')).to eq([project, true, nil])
+ end
+
+ it 'parses a relative path starting with /' do
+ expect(described_class.parse('/' + project.full_path + '.git')).to eq([project, false, nil])
+ end
+
+ context 'of a redirected project' do
+ let(:redirect) { project.route.create_redirect('foo/bar') }
+
+ it 'parses a relative repository path' do
+ expect(described_class.parse(redirect.path + '.git')).to eq([project, false, 'foo/bar'])
+ end
+
+ it 'parses a relative wiki path' do
+ expect(described_class.parse(redirect.path + '.wiki.git')).to eq([project, true, 'foo/bar.wiki'])
+ end
- it 'parses a relative path starting with /' do
- expect(described_class.parse('/' + project.full_path + '.git')).to eq([project, false])
+ it 'parses a relative path starting with /' do
+ expect(described_class.parse('/' + redirect.path + '.git')).to eq([project, false, 'foo/bar'])
+ end
+ end
end
end
@@ -43,4 +63,33 @@ describe ::Gitlab::RepoPath do
)
end
end
+
+ describe '.find_project' do
+ let(:project) { create(:empty_project) }
+ let(:redirect) { project.route.create_redirect('foo/bar/baz') }
+
+ context 'when finding a project by its canonical path' do
+ context 'when the cases match' do
+ it 'returns the project and false' do
+ expect(described_class.find_project(project.full_path)).to eq([project, false])
+ end
+ end
+
+ context 'when the cases do not match' do
+ # This is slightly different than web behavior because on the web it is
+ # easy and safe to redirect someone to the correctly-cased URL. For git
+ # requests, we should accept wrongly-cased URLs because it is a pain to
+ # block people's git operations and force them to update remote URLs.
+ it 'returns the project and false' do
+ expect(described_class.find_project(project.full_path.upcase)).to eq([project, false])
+ end
+ end
+ end
+
+ context 'when finding a project via a redirect' do
+ it 'returns the project and true' do
+ expect(described_class.find_project(redirect.path)).to eq([project, true])
+ end
+ end
+ end
end
diff --git a/spec/lib/gitlab/saml/user_spec.rb b/spec/lib/gitlab/saml/user_spec.rb
index b106d156b75..a4d2367b72a 100644
--- a/spec/lib/gitlab/saml/user_spec.rb
+++ b/spec/lib/gitlab/saml/user_spec.rb
@@ -31,11 +31,17 @@ describe Gitlab::Saml::User, lib: true do
allow(Gitlab::Saml::Config).to receive_messages({ options: { name: 'saml', groups_attribute: 'groups', external_groups: groups, args: {} } })
end
- before { stub_basic_saml_config }
+ before do
+ stub_basic_saml_config
+ end
describe 'account exists on server' do
- before { stub_omniauth_config({ allow_single_sign_on: ['saml'], auto_link_saml_user: true }) }
+ before do
+ stub_omniauth_config({ allow_single_sign_on: ['saml'], auto_link_saml_user: true })
+ end
+
let!(:existing_user) { create(:user, email: 'john@mail.com', username: 'john') }
+
context 'and should bind with SAML' do
it 'adds the SAML identity to the existing user' do
saml_user.save
@@ -57,7 +63,10 @@ describe Gitlab::Saml::User, lib: true do
end
end
- before { stub_saml_group_config(%w(Interns)) }
+ before do
+ stub_saml_group_config(%w(Interns))
+ end
+
context 'are defined but the user does not belong there' do
it 'does not mark the user as external' do
saml_user.save
@@ -80,7 +89,9 @@ describe Gitlab::Saml::User, lib: true do
describe 'no account exists on server' do
shared_examples 'to verify compliance with allow_single_sign_on' do
context 'with allow_single_sign_on enabled' do
- before { stub_omniauth_config(allow_single_sign_on: ['saml']) }
+ before do
+ stub_omniauth_config(allow_single_sign_on: ['saml'])
+ end
it 'creates a user from SAML' do
saml_user.save
@@ -93,14 +104,20 @@ describe Gitlab::Saml::User, lib: true do
end
context 'with allow_single_sign_on default (["saml"])' do
- before { stub_omniauth_config(allow_single_sign_on: ['saml']) }
+ before do
+ stub_omniauth_config(allow_single_sign_on: ['saml'])
+ end
+
it 'does not throw an error' do
expect{ saml_user.save }.not_to raise_error
end
end
context 'with allow_single_sign_on disabled' do
- before { stub_omniauth_config(allow_single_sign_on: false) }
+ before do
+ stub_omniauth_config(allow_single_sign_on: false)
+ end
+
it 'throws an error' do
expect{ saml_user.save }.to raise_error StandardError
end
@@ -128,15 +145,22 @@ describe Gitlab::Saml::User, lib: true do
end
context 'with auto_link_ldap_user disabled (default)' do
- before { stub_omniauth_config({ auto_link_ldap_user: false, auto_link_saml_user: false, allow_single_sign_on: ['saml'] }) }
+ before do
+ stub_omniauth_config({ auto_link_ldap_user: false, auto_link_saml_user: false, allow_single_sign_on: ['saml'] })
+ end
+
include_examples 'to verify compliance with allow_single_sign_on'
end
context 'with auto_link_ldap_user enabled' do
- before { stub_omniauth_config({ auto_link_ldap_user: true, auto_link_saml_user: false }) }
+ before do
+ stub_omniauth_config({ auto_link_ldap_user: true, auto_link_saml_user: false })
+ end
context 'and at least one LDAP provider is defined' do
- before { stub_ldap_config(providers: %w(ldapmain)) }
+ before do
+ stub_ldap_config(providers: %w(ldapmain))
+ end
context 'and a corresponding LDAP person' do
before do
@@ -239,11 +263,15 @@ describe Gitlab::Saml::User, lib: true do
end
describe 'blocking' do
- before { stub_omniauth_config({ allow_single_sign_on: ['saml'], auto_link_saml_user: true }) }
+ before do
+ stub_omniauth_config({ allow_single_sign_on: ['saml'], auto_link_saml_user: true })
+ end
context 'signup with SAML only' do
context 'dont block on create' do
- before { stub_omniauth_config(block_auto_created_users: false) }
+ before do
+ stub_omniauth_config(block_auto_created_users: false)
+ end
it 'does not block the user' do
saml_user.save
@@ -253,7 +281,9 @@ describe Gitlab::Saml::User, lib: true do
end
context 'block on create' do
- before { stub_omniauth_config(block_auto_created_users: true) }
+ before do
+ stub_omniauth_config(block_auto_created_users: true)
+ end
it 'blocks user' do
saml_user.save
@@ -270,7 +300,9 @@ describe Gitlab::Saml::User, lib: true do
end
context 'dont block on create' do
- before { stub_omniauth_config(block_auto_created_users: false) }
+ before do
+ stub_omniauth_config(block_auto_created_users: false)
+ end
it do
saml_user.save
@@ -280,7 +312,9 @@ describe Gitlab::Saml::User, lib: true do
end
context 'block on create' do
- before { stub_omniauth_config(block_auto_created_users: true) }
+ before do
+ stub_omniauth_config(block_auto_created_users: true)
+ end
it do
saml_user.save
diff --git a/spec/lib/gitlab/serializer/pagination_spec.rb b/spec/lib/gitlab/serializer/pagination_spec.rb
index 519eb1b274f..1bc6536439e 100644
--- a/spec/lib/gitlab/serializer/pagination_spec.rb
+++ b/spec/lib/gitlab/serializer/pagination_spec.rb
@@ -22,7 +22,9 @@ describe Gitlab::Serializer::Pagination do
let(:params) { { page: 1, per_page: 2 } }
context 'when a multiple resources are present in relation' do
- before { create_list(:user, 3) }
+ before do
+ create_list(:user, 3)
+ end
it 'correctly paginates the resource' do
expect(subject.count).to be 2
diff --git a/spec/lib/gitlab/chat_commands/command_spec.rb b/spec/lib/gitlab/slash_commands/command_spec.rb
index 13e6953147b..28d7f9858c3 100644
--- a/spec/lib/gitlab/chat_commands/command_spec.rb
+++ b/spec/lib/gitlab/slash_commands/command_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe Gitlab::ChatCommands::Command, service: true do
+describe Gitlab::SlashCommands::Command, service: true do
let(:project) { create(:empty_project) }
let(:user) { create(:user) }
@@ -93,19 +93,19 @@ describe Gitlab::ChatCommands::Command, service: true do
context 'IssueShow is triggered' do
let(:params) { { text: 'issue show 123' } }
- it { is_expected.to eq(Gitlab::ChatCommands::IssueShow) }
+ it { is_expected.to eq(Gitlab::SlashCommands::IssueShow) }
end
context 'IssueCreate is triggered' do
let(:params) { { text: 'issue create my title' } }
- it { is_expected.to eq(Gitlab::ChatCommands::IssueNew) }
+ it { is_expected.to eq(Gitlab::SlashCommands::IssueNew) }
end
context 'IssueSearch is triggered' do
let(:params) { { text: 'issue search my query' } }
- it { is_expected.to eq(Gitlab::ChatCommands::IssueSearch) }
+ it { is_expected.to eq(Gitlab::SlashCommands::IssueSearch) }
end
end
end
diff --git a/spec/lib/gitlab/chat_commands/deploy_spec.rb b/spec/lib/gitlab/slash_commands/deploy_spec.rb
index 46dbdeae37c..d919f7260db 100644
--- a/spec/lib/gitlab/chat_commands/deploy_spec.rb
+++ b/spec/lib/gitlab/slash_commands/deploy_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe Gitlab::ChatCommands::Deploy, service: true do
+describe Gitlab::SlashCommands::Deploy, service: true do
describe '#execute' do
let(:project) { create(:empty_project) }
let(:user) { create(:user) }
diff --git a/spec/lib/gitlab/chat_commands/issue_new_spec.rb b/spec/lib/gitlab/slash_commands/issue_new_spec.rb
index 84c22328064..4de50d4a8bb 100644
--- a/spec/lib/gitlab/chat_commands/issue_new_spec.rb
+++ b/spec/lib/gitlab/slash_commands/issue_new_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe Gitlab::ChatCommands::IssueNew, service: true do
+describe Gitlab::SlashCommands::IssueNew, service: true do
describe '#execute' do
let(:project) { create(:empty_project) }
let(:user) { create(:user) }
diff --git a/spec/lib/gitlab/chat_commands/issue_search_spec.rb b/spec/lib/gitlab/slash_commands/issue_search_spec.rb
index 551ccb79a58..06fff0afc50 100644
--- a/spec/lib/gitlab/chat_commands/issue_search_spec.rb
+++ b/spec/lib/gitlab/slash_commands/issue_search_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe Gitlab::ChatCommands::IssueSearch, service: true do
+describe Gitlab::SlashCommands::IssueSearch, service: true do
describe '#execute' do
let!(:issue) { create(:issue, project: project, title: 'find me') }
let!(:confidential) { create(:issue, :confidential, project: project, title: 'mepmep find') }
diff --git a/spec/lib/gitlab/chat_commands/issue_show_spec.rb b/spec/lib/gitlab/slash_commands/issue_show_spec.rb
index 1f20d0a44ce..1899f664ccd 100644
--- a/spec/lib/gitlab/chat_commands/issue_show_spec.rb
+++ b/spec/lib/gitlab/slash_commands/issue_show_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe Gitlab::ChatCommands::IssueShow, service: true do
+describe Gitlab::SlashCommands::IssueShow, service: true do
describe '#execute' do
let(:issue) { create(:issue, project: project) }
let(:project) { create(:empty_project) }
diff --git a/spec/lib/gitlab/chat_commands/presenters/access_spec.rb b/spec/lib/gitlab/slash_commands/presenters/access_spec.rb
index ae41d75ab0c..ef3d217f7be 100644
--- a/spec/lib/gitlab/chat_commands/presenters/access_spec.rb
+++ b/spec/lib/gitlab/slash_commands/presenters/access_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe Gitlab::ChatCommands::Presenters::Access do
+describe Gitlab::SlashCommands::Presenters::Access do
describe '#access_denied' do
subject { described_class.new.access_denied }
diff --git a/spec/lib/gitlab/chat_commands/presenters/deploy_spec.rb b/spec/lib/gitlab/slash_commands/presenters/deploy_spec.rb
index dc2dd300072..dee3c77db27 100644
--- a/spec/lib/gitlab/chat_commands/presenters/deploy_spec.rb
+++ b/spec/lib/gitlab/slash_commands/presenters/deploy_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe Gitlab::ChatCommands::Presenters::Deploy do
+describe Gitlab::SlashCommands::Presenters::Deploy do
let(:build) { create(:ci_build) }
describe '#present' do
diff --git a/spec/lib/gitlab/chat_commands/presenters/issue_new_spec.rb b/spec/lib/gitlab/slash_commands/presenters/issue_new_spec.rb
index 17fcdbc2452..7f81ebb47db 100644
--- a/spec/lib/gitlab/chat_commands/presenters/issue_new_spec.rb
+++ b/spec/lib/gitlab/slash_commands/presenters/issue_new_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe Gitlab::ChatCommands::Presenters::IssueNew do
+describe Gitlab::SlashCommands::Presenters::IssueNew do
let(:project) { create(:empty_project) }
let(:issue) { create(:issue, project: project) }
let(:attachment) { subject[:attachments].first }
diff --git a/spec/lib/gitlab/chat_commands/presenters/issue_search_spec.rb b/spec/lib/gitlab/slash_commands/presenters/issue_search_spec.rb
index ec6d3e34a96..7e57a0addcb 100644
--- a/spec/lib/gitlab/chat_commands/presenters/issue_search_spec.rb
+++ b/spec/lib/gitlab/slash_commands/presenters/issue_search_spec.rb
@@ -1,10 +1,12 @@
require 'spec_helper'
-describe Gitlab::ChatCommands::Presenters::IssueSearch do
+describe Gitlab::SlashCommands::Presenters::IssueSearch do
let(:project) { create(:empty_project) }
let(:message) { subject[:text] }
- before { create_list(:issue, 2, project: project) }
+ before do
+ create_list(:issue, 2, project: project)
+ end
subject { described_class.new(project.issues).present }
diff --git a/spec/lib/gitlab/chat_commands/presenters/issue_show_spec.rb b/spec/lib/gitlab/slash_commands/presenters/issue_show_spec.rb
index 3916fc704a4..2a6ed860737 100644
--- a/spec/lib/gitlab/chat_commands/presenters/issue_show_spec.rb
+++ b/spec/lib/gitlab/slash_commands/presenters/issue_show_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe Gitlab::ChatCommands::Presenters::IssueShow do
+describe Gitlab::SlashCommands::Presenters::IssueShow do
let(:project) { create(:empty_project) }
let(:issue) { create(:issue, project: project) }
let(:attachment) { subject[:attachments].first }
diff --git a/spec/lib/gitlab/template/issue_template_spec.rb b/spec/lib/gitlab/template/issue_template_spec.rb
index 329d1d74970..bf45c8d16d6 100644
--- a/spec/lib/gitlab/template/issue_template_spec.rb
+++ b/spec/lib/gitlab/template/issue_template_spec.rb
@@ -52,7 +52,10 @@ describe Gitlab::Template::IssueTemplate do
context 'when repo is bare or empty' do
let(:empty_project) { create(:empty_project) }
- before { empty_project.add_user(user, Gitlab::Access::MASTER) }
+
+ before do
+ empty_project.add_user(user, Gitlab::Access::MASTER)
+ end
it "returns empty array" do
templates = subject.by_category('', empty_project)
@@ -77,7 +80,9 @@ describe Gitlab::Template::IssueTemplate do
context "when repo is empty" do
let(:empty_project) { create(:empty_project) }
- before { empty_project.add_user(user, Gitlab::Access::MASTER) }
+ before do
+ empty_project.add_user(user, Gitlab::Access::MASTER)
+ end
it "raises file not found" do
issue_template = subject.new('.gitlab/issue_templates/not_existent.md', empty_project)
diff --git a/spec/lib/gitlab/template/merge_request_template_spec.rb b/spec/lib/gitlab/template/merge_request_template_spec.rb
index 2b0056d9bab..8479f92c8df 100644
--- a/spec/lib/gitlab/template/merge_request_template_spec.rb
+++ b/spec/lib/gitlab/template/merge_request_template_spec.rb
@@ -52,7 +52,10 @@ describe Gitlab::Template::MergeRequestTemplate do
context 'when repo is bare or empty' do
let(:empty_project) { create(:empty_project) }
- before { empty_project.add_user(user, Gitlab::Access::MASTER) }
+
+ before do
+ empty_project.add_user(user, Gitlab::Access::MASTER)
+ end
it "returns empty array" do
templates = subject.by_category('', empty_project)
@@ -77,7 +80,9 @@ describe Gitlab::Template::MergeRequestTemplate do
context "when repo is empty" do
let(:empty_project) { create(:empty_project) }
- before { empty_project.add_user(user, Gitlab::Access::MASTER) }
+ before do
+ empty_project.add_user(user, Gitlab::Access::MASTER)
+ end
it "raises file not found" do
issue_template = subject.new('.gitlab/merge_request_templates/not_existent.md', empty_project)
diff --git a/spec/lib/gitlab/workhorse_spec.rb b/spec/lib/gitlab/workhorse_spec.rb
index b1999409170..ad19998dff4 100644
--- a/spec/lib/gitlab/workhorse_spec.rb
+++ b/spec/lib/gitlab/workhorse_spec.rb
@@ -212,7 +212,7 @@ describe Gitlab::Workhorse, lib: true do
it 'includes a Repository param' do
repo_param = { Repository: {
- path: repo_path,
+ path: '', # deprecated field; grpc automatically creates it anyway
storage_name: 'default',
relative_path: project.full_path + '.git'
} }
diff --git a/spec/lib/json_web_token/rsa_token_spec.rb b/spec/lib/json_web_token/rsa_token_spec.rb
index 18726754517..e7022bd06f8 100644
--- a/spec/lib/json_web_token/rsa_token_spec.rb
+++ b/spec/lib/json_web_token/rsa_token_spec.rb
@@ -15,11 +15,15 @@ describe JSONWebToken::RSAToken do
let(:rsa_token) { described_class.new(nil) }
let(:rsa_encoded) { rsa_token.encoded }
- before { allow_any_instance_of(described_class).to receive(:key).and_return(rsa_key) }
+ before do
+ allow_any_instance_of(described_class).to receive(:key).and_return(rsa_key)
+ end
context 'token' do
context 'for valid key to be validated' do
- before { rsa_token['key'] = 'value' }
+ before do
+ rsa_token['key'] = 'value'
+ end
subject { JWT.decode(rsa_encoded, rsa_key) }
diff --git a/spec/lib/json_web_token/token_spec.rb b/spec/lib/json_web_token/token_spec.rb
index 3d955e4d774..d7e7560d962 100644
--- a/spec/lib/json_web_token/token_spec.rb
+++ b/spec/lib/json_web_token/token_spec.rb
@@ -3,7 +3,10 @@ describe JSONWebToken::Token do
context 'custom parameters' do
let(:value) { 'value' }
- before { token[:key] = value }
+
+ before do
+ token[:key] = value
+ end
it { expect(token[:key]).to eq(value) }
it { expect(token.payload).to include(key: value) }
diff --git a/spec/mailers/notify_spec.rb b/spec/mailers/notify_spec.rb
index ec6f6c42eac..980b24370d0 100644
--- a/spec/mailers/notify_spec.rb
+++ b/spec/mailers/notify_spec.rb
@@ -130,8 +130,13 @@ describe Notify do
end
context 'with a preferred language' do
- before { Gitlab::I18n.locale = :es }
- after { Gitlab::I18n.use_default_locale }
+ before do
+ Gitlab::I18n.locale = :es
+ end
+
+ after do
+ Gitlab::I18n.use_default_locale
+ end
it 'always generates the email using the default language' do
is_expected.to have_body_text('foo, bar, and baz')
@@ -581,7 +586,9 @@ describe Notify do
let(:project) { create(:project, :repository) }
let(:commit) { project.commit }
- before(:each) { allow(note).to receive(:noteable).and_return(commit) }
+ before do
+ allow(note).to receive(:noteable).and_return(commit)
+ end
subject { described_class.note_commit_email(recipient.id, note.id) }
@@ -603,7 +610,10 @@ describe Notify do
describe 'on a merge request' do
let(:merge_request) { create(:merge_request, source_project: project, target_project: project) }
let(:note_on_merge_request_path) { namespace_project_merge_request_path(project.namespace, project, merge_request, anchor: "note_#{note.id}") }
- before(:each) { allow(note).to receive(:noteable).and_return(merge_request) }
+
+ before do
+ allow(note).to receive(:noteable).and_return(merge_request)
+ end
subject { described_class.note_merge_request_email(recipient.id, note.id) }
@@ -625,7 +635,10 @@ describe Notify do
describe 'on an issue' do
let(:issue) { create(:issue, project: project) }
let(:note_on_issue_path) { namespace_project_issue_path(project.namespace, project, issue, anchor: "note_#{note.id}") }
- before(:each) { allow(note).to receive(:noteable).and_return(issue) }
+
+ before do
+ allow(note).to receive(:noteable).and_return(issue)
+ end
subject { described_class.note_issue_email(recipient.id, note.id) }
@@ -687,7 +700,9 @@ describe Notify do
let(:commit) { project.commit }
let(:note) { create(:discussion_note_on_commit, commit_id: commit.id, project: project, author: note_author) }
- before(:each) { allow(note).to receive(:noteable).and_return(commit) }
+ before do
+ allow(note).to receive(:noteable).and_return(commit)
+ end
subject { described_class.note_commit_email(recipient.id, note.id) }
@@ -711,7 +726,10 @@ describe Notify do
let(:merge_request) { create(:merge_request, source_project: project, target_project: project) }
let(:note) { create(:discussion_note_on_merge_request, noteable: merge_request, project: project, author: note_author) }
let(:note_on_merge_request_path) { namespace_project_merge_request_path(project.namespace, project, merge_request, anchor: "note_#{note.id}") }
- before(:each) { allow(note).to receive(:noteable).and_return(merge_request) }
+
+ before do
+ allow(note).to receive(:noteable).and_return(merge_request)
+ end
subject { described_class.note_merge_request_email(recipient.id, note.id) }
@@ -735,7 +753,10 @@ describe Notify do
let(:issue) { create(:issue, project: project) }
let(:note) { create(:discussion_note_on_issue, noteable: issue, project: project, author: note_author) }
let(:note_on_issue_path) { namespace_project_issue_path(project.namespace, project, issue, anchor: "note_#{note.id}") }
- before(:each) { allow(note).to receive(:noteable).and_return(issue) }
+
+ before do
+ allow(note).to receive(:noteable).and_return(issue)
+ end
subject { described_class.note_issue_email(recipient.id, note.id) }
diff --git a/spec/migrations/README.md b/spec/migrations/README.md
new file mode 100644
index 00000000000..05d4f35db72
--- /dev/null
+++ b/spec/migrations/README.md
@@ -0,0 +1,87 @@
+# Testing migrations
+
+In order to reliably test a migration, we need to test it against a database
+schema that this migration has been written for. In order to achieve that we
+have some _migration helpers_ and RSpec test tag, called `:migration`.
+
+If you want to write a test for a migration consider adding `:migration` tag to
+the test signature, like `describe SomeMigrationClass, :migration`.
+
+## How does it work?
+
+Adding a `:migration` tag to a test signature injects a few before / after
+hooks to the test.
+
+The most important change is that adding a `:migration` tag adds a `before`
+hook that will revert all migrations to the point that a migration under test
+is not yet migrated.
+
+In other words, our custom RSpec hooks will find a previous migration, and
+migrate the database **down** to the previous migration version.
+
+With this approach you can test a migration against a database schema that this
+migration has been written for.
+
+Use `migrate!` helper to run the migration that is under test.
+
+The `after` hook will migrate the database **up** and reinstitutes the latest
+schema version, so that the process does not affect subsequent specs and
+ensures proper isolation.
+
+## Available helpers
+
+Use `table` helper to create a temporary `ActiveRecord::Base` derived model
+for a table.
+
+Use `migrate!` helper to run the migration that is under test. It will not only
+run migration, but will also bump the schema version in the `schema_migrations`
+table. It is necessary because in the `after` hook we trigger the rest of
+the migrations, and we need to know where to start.
+
+See `spec/support/migrations_helpers.rb` for all the available helpers.
+
+## An example
+
+```ruby
+require 'spec_helper'
+
+# Load a migration class.
+
+require Rails.root.join('db', 'post_migrate', '20170526185842_migrate_pipeline_stages.rb')
+
+describe MigratePipelineStages, :migration do
+
+ # Create test data - pipeline and CI/CD jobs.
+
+ let(:jobs) { table(:ci_builds) }
+ let(:stages) { table(:ci_stages) }
+ let(:pipelines) { table(:ci_pipelines) }
+ let(:projects) { table(:projects) }
+
+ before do
+ projects.create!(id: 123, name: 'gitlab1', path: 'gitlab1')
+ pipelines.create!(id: 1, project_id: 123, ref: 'master', sha: 'adf43c3a')
+ jobs.create!(id: 1, commit_id: 1, project_id: 123, stage_idx: 2, stage: 'build')
+ jobs.create!(id: 2, commit_id: 1, project_id: 123, stage_idx: 1, stage: 'test')
+ end
+
+ # Test the migration.
+
+ it 'correctly migrates pipeline stages' do
+ expect(stages.count).to be_zero
+
+ migrate!
+
+ expect(stages.count).to eq 2
+ expect(stages.all.pluck(:name)).to match_array %w[test build]
+ end
+end
+```
+
+## Best practices
+
+1. Use only one test example per migration unless there is a good reason to
+use more.
+1. Note that this type of tests do not run within the transaction, we use
+a truncation database cleanup strategy. Do not depend on transaction being
+present.
diff --git a/spec/migrations/convert_custom_notification_settings_to_columns_spec.rb b/spec/migrations/convert_custom_notification_settings_to_columns_spec.rb
new file mode 100644
index 00000000000..1396d12e5a9
--- /dev/null
+++ b/spec/migrations/convert_custom_notification_settings_to_columns_spec.rb
@@ -0,0 +1,118 @@
+require 'spec_helper'
+require Rails.root.join('db', 'post_migrate', '20170607121233_convert_custom_notification_settings_to_columns')
+
+describe ConvertCustomNotificationSettingsToColumns, :migration do
+ let(:settings_params) do
+ [
+ { level: 0, events: [:new_note] }, # disabled, single event
+ { level: 3, events: [:new_issue, :reopen_issue, :close_issue, :reassign_issue] }, # global, multiple events
+ { level: 5, events: described_class::EMAIL_EVENTS }, # custom, all events
+ { level: 5, events: [] } # custom, no events
+ ]
+ end
+
+ let(:notification_settings_before) do
+ settings_params.map do |params|
+ events = {}
+
+ params[:events].each do |event|
+ events[event] = true
+ end
+
+ user = create(:user)
+ create_params = { user_id: user.id, level: params[:level], events: events }
+ notification_setting = described_class::NotificationSetting.create(create_params)
+
+ [notification_setting, params]
+ end
+ end
+
+ let(:notification_settings_after) do
+ settings_params.map do |params|
+ events = {}
+
+ params[:events].each do |event|
+ events[event] = true
+ end
+
+ user = create(:user)
+ create_params = events.merge(user_id: user.id, level: params[:level])
+ notification_setting = described_class::NotificationSetting.create(create_params)
+
+ [notification_setting, params]
+ end
+ end
+
+ describe '#up' do
+ it 'migrates all settings where a custom event is enabled, even if they are not currently using the custom level' do
+ notification_settings_before
+
+ described_class.new.up
+
+ notification_settings_before.each do |(notification_setting, params)|
+ notification_setting.reload
+
+ expect(notification_setting.read_attribute_before_type_cast(:events)).to be_nil
+ expect(notification_setting.level).to eq(params[:level])
+
+ described_class::EMAIL_EVENTS.each do |event|
+ # We don't set the others to false, just let them default to nil
+ expected = params[:events].include?(event) || nil
+
+ expect(notification_setting.read_attribute(event)).to eq(expected)
+ end
+ end
+ end
+ end
+
+ describe '#down' do
+ it 'creates a custom events hash for all settings where at least one event is enabled' do
+ notification_settings_after
+
+ described_class.new.down
+
+ notification_settings_after.each do |(notification_setting, params)|
+ notification_setting.reload
+
+ expect(notification_setting.level).to eq(params[:level])
+
+ if params[:events].empty?
+ # We don't migrate empty settings
+ expect(notification_setting.events).to eq({})
+ else
+ described_class::EMAIL_EVENTS.each do |event|
+ expected = params[:events].include?(event)
+
+ expect(notification_setting.events[event]).to eq(expected)
+ expect(notification_setting.read_attribute(event)).to be_nil
+ end
+ end
+ end
+ end
+
+ it 'reverts the database to the state it was in before' do
+ notification_settings_before
+
+ described_class.new.up
+ described_class.new.down
+
+ notification_settings_before.each do |(notification_setting, params)|
+ notification_setting.reload
+
+ expect(notification_setting.level).to eq(params[:level])
+
+ if params[:events].empty?
+ # We don't migrate empty settings
+ expect(notification_setting.events).to eq({})
+ else
+ described_class::EMAIL_EVENTS.each do |event|
+ expected = params[:events].include?(event)
+
+ expect(notification_setting.events[event]).to eq(expected)
+ expect(notification_setting.read_attribute(event)).to be_nil
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/spec/migrations/rename_more_reserved_project_names_spec.rb b/spec/migrations/rename_more_reserved_project_names_spec.rb
index 36e82729c23..4bd8d4ac0d1 100644
--- a/spec/migrations/rename_more_reserved_project_names_spec.rb
+++ b/spec/migrations/rename_more_reserved_project_names_spec.rb
@@ -17,7 +17,9 @@ describe RenameMoreReservedProjectNames, truncate: true do
describe '#up' do
context 'when project repository exists' do
- before { project.create_repository }
+ before do
+ project.create_repository
+ end
context 'when no exception is raised' do
it 'renames project with reserved names' do
diff --git a/spec/migrations/rename_reserved_project_names_spec.rb b/spec/migrations/rename_reserved_project_names_spec.rb
index 4fb7ed36884..05e021c2e32 100644
--- a/spec/migrations/rename_reserved_project_names_spec.rb
+++ b/spec/migrations/rename_reserved_project_names_spec.rb
@@ -17,7 +17,9 @@ describe RenameReservedProjectNames, truncate: true do
describe '#up' do
context 'when project repository exists' do
- before { project.create_repository }
+ before do
+ project.create_repository
+ end
context 'when no exception is raised' do
it 'renames project with reserved names' do
diff --git a/spec/models/application_setting_spec.rb b/spec/models/application_setting_spec.rb
index fa229542f70..166a4474abf 100644
--- a/spec/models/application_setting_spec.rb
+++ b/spec/models/application_setting_spec.rb
@@ -78,7 +78,9 @@ describe ApplicationSetting, models: true do
# Upgraded databases will have this sort of content
context 'repository_storages is a String, not an Array' do
- before { setting.__send__(:raw_write_attribute, :repository_storages, 'default') }
+ before do
+ setting.__send__(:raw_write_attribute, :repository_storages, 'default')
+ end
it { expect(setting.repository_storages_before_type_cast).to eq('default') }
it { expect(setting.repository_storages).to eq(['default']) }
diff --git a/spec/models/broadcast_message_spec.rb b/spec/models/broadcast_message_spec.rb
index 219db365a91..333f4139a96 100644
--- a/spec/models/broadcast_message_spec.rb
+++ b/spec/models/broadcast_message_spec.rb
@@ -21,22 +21,29 @@ describe BroadcastMessage, models: true do
end
describe '.current' do
- it "returns last message if time match" do
+ it 'returns message if time match' do
message = create(:broadcast_message)
- expect(BroadcastMessage.current).to eq message
+ expect(BroadcastMessage.current).to include(message)
end
- it "returns nil if time not come" do
+ it 'returns multiple messages if time match' do
+ message1 = create(:broadcast_message)
+ message2 = create(:broadcast_message)
+
+ expect(BroadcastMessage.current).to contain_exactly(message1, message2)
+ end
+
+ it 'returns empty list if time not come' do
create(:broadcast_message, :future)
- expect(BroadcastMessage.current).to be_nil
+ expect(BroadcastMessage.current).to be_empty
end
- it "returns nil if time has passed" do
+ it 'returns empty list if time has passed' do
create(:broadcast_message, :expired)
- expect(BroadcastMessage.current).to be_nil
+ expect(BroadcastMessage.current).to be_empty
end
end
diff --git a/spec/models/ci/build_spec.rb b/spec/models/ci/build_spec.rb
index b0716e04d3d..3816422fec6 100644
--- a/spec/models/ci/build_spec.rb
+++ b/spec/models/ci/build_spec.rb
@@ -21,6 +21,18 @@ describe Ci::Build, :models do
it { is_expected.to respond_to(:has_trace?) }
it { is_expected.to respond_to(:trace) }
+ describe '.manual_actions' do
+ let!(:manual_but_created) { create(:ci_build, :manual, status: :created, pipeline: pipeline) }
+ let!(:manual_but_succeeded) { create(:ci_build, :manual, status: :success, pipeline: pipeline) }
+ let!(:manual_action) { create(:ci_build, :manual, pipeline: pipeline) }
+
+ subject { described_class.manual_actions }
+
+ it { is_expected.to include(manual_action) }
+ it { is_expected.to include(manual_but_succeeded) }
+ it { is_expected.not_to include(manual_but_created) }
+ end
+
describe '#actionize' do
context 'when build is a created' do
before do
@@ -95,12 +107,18 @@ describe Ci::Build, :models do
it { is_expected.to be_truthy }
context 'is expired' do
- before { build.update(artifacts_expire_at: Time.now - 7.days) }
+ before do
+ build.update(artifacts_expire_at: Time.now - 7.days)
+ end
+
it { is_expected.to be_falsy }
end
context 'is not expired' do
- before { build.update(artifacts_expire_at: Time.now + 7.days) }
+ before do
+ build.update(artifacts_expire_at: Time.now + 7.days)
+ end
+
it { is_expected.to be_truthy }
end
end
@@ -110,13 +128,17 @@ describe Ci::Build, :models do
subject { build.artifacts_expired? }
context 'is expired' do
- before { build.update(artifacts_expire_at: Time.now - 7.days) }
+ before do
+ build.update(artifacts_expire_at: Time.now - 7.days)
+ end
it { is_expected.to be_truthy }
end
context 'is not expired' do
- before { build.update(artifacts_expire_at: Time.now + 7.days) }
+ before do
+ build.update(artifacts_expire_at: Time.now + 7.days)
+ end
it { is_expected.to be_falsey }
end
@@ -141,7 +163,9 @@ describe Ci::Build, :models do
context 'when artifacts_expire_at is specified' do
let(:expire_at) { Time.now + 7.days }
- before { build.artifacts_expire_at = expire_at }
+ before do
+ build.artifacts_expire_at = expire_at
+ end
it { is_expected.to be_within(5).of(expire_at - Time.now) }
end
@@ -926,6 +950,10 @@ describe Ci::Build, :models do
context 'when other build is retried' do
let!(:retried_build) { Ci::Build.retry(other_build, user) }
+ before do
+ retried_build.success
+ end
+
it 'returns a retried build' do
is_expected.to contain_exactly(retried_build)
end
@@ -1071,7 +1099,9 @@ describe Ci::Build, :models do
describe '#has_expiring_artifacts?' do
context 'when artifacts have expiration date set' do
- before { build.update(artifacts_expire_at: 1.day.from_now) }
+ before do
+ build.update(artifacts_expire_at: 1.day.from_now)
+ end
it 'has expiring artifacts' do
expect(build).to have_expiring_artifacts
@@ -1079,7 +1109,9 @@ describe Ci::Build, :models do
end
context 'when artifacts do not have expiration date set' do
- before { build.update(artifacts_expire_at: nil) }
+ before do
+ build.update(artifacts_expire_at: nil)
+ end
it 'does not have expiring artifacts' do
expect(build).not_to have_expiring_artifacts
diff --git a/spec/models/ci/legacy_stage_spec.rb b/spec/models/ci/legacy_stage_spec.rb
index 48116c7e701..d43c33d3807 100644
--- a/spec/models/ci/legacy_stage_spec.rb
+++ b/spec/models/ci/legacy_stage_spec.rb
@@ -55,6 +55,17 @@ describe Ci::LegacyStage, :models do
expect(stage.groups.map(&:name))
.to eq %w[aaaaa rspec spinach]
end
+
+ context 'when a name is nil on legacy pipelines' do
+ before do
+ pipeline.builds.first.update_attribute(:name, nil)
+ end
+
+ it 'returns an array of three groups' do
+ expect(stage.groups.map(&:name))
+ .to eq ['', 'aaaaa', 'rspec', 'spinach']
+ end
+ end
end
describe '#statuses_count' do
diff --git a/spec/models/ci/pipeline_spec.rb b/spec/models/ci/pipeline_spec.rb
index b50c7700bd3..e86cbe8498a 100644
--- a/spec/models/ci/pipeline_spec.rb
+++ b/spec/models/ci/pipeline_spec.rb
@@ -1156,7 +1156,9 @@ describe Ci::Pipeline, models: true do
end
context 'when pipeline is not stuck' do
- before { create(:ci_runner, :shared, :online) }
+ before do
+ create(:ci_runner, :shared, :online)
+ end
it 'is not stuck' do
expect(pipeline).not_to be_stuck
diff --git a/spec/models/commit_spec.rb b/spec/models/commit_spec.rb
index ba247dcc5cf..6056d78da4e 100644
--- a/spec/models/commit_spec.rb
+++ b/spec/models/commit_spec.rb
@@ -206,19 +206,25 @@ eos
it { expect(commit.reverts_commit?(another_commit, user)).to be_falsy }
context 'commit has no description' do
- before { allow(commit).to receive(:description?).and_return(false) }
+ before do
+ allow(commit).to receive(:description?).and_return(false)
+ end
it { expect(commit.reverts_commit?(another_commit, user)).to be_falsy }
end
context "another_commit's description does not revert commit" do
- before { allow(commit).to receive(:description).and_return("Foo Bar") }
+ before do
+ allow(commit).to receive(:description).and_return("Foo Bar")
+ end
it { expect(commit.reverts_commit?(another_commit, user)).to be_falsy }
end
context "another_commit's description reverts commit" do
- before { allow(commit).to receive(:description).and_return("Foo #{another_commit.revert_description} Bar") }
+ before do
+ allow(commit).to receive(:description).and_return("Foo #{another_commit.revert_description} Bar")
+ end
it { expect(commit.reverts_commit?(another_commit, user)).to be_truthy }
end
diff --git a/spec/models/commit_status_spec.rb b/spec/models/commit_status_spec.rb
index c50b8bf7b13..9262ce08987 100644
--- a/spec/models/commit_status_spec.rb
+++ b/spec/models/commit_status_spec.rb
@@ -31,7 +31,10 @@ describe CommitStatus, :models do
describe '#author' do
subject { commit_status.author }
- before { commit_status.author = User.new }
+
+ before do
+ commit_status.author = User.new
+ end
it { is_expected.to eq(commit_status.user) }
end
@@ -50,14 +53,18 @@ describe CommitStatus, :models do
subject { commit_status.started? }
context 'without started_at' do
- before { commit_status.started_at = nil }
+ before do
+ commit_status.started_at = nil
+ end
it { is_expected.to be_falsey }
end
%w[running success failed].each do |status|
context "if commit status is #{status}" do
- before { commit_status.status = status }
+ before do
+ commit_status.status = status
+ end
it { is_expected.to be_truthy }
end
@@ -65,7 +72,9 @@ describe CommitStatus, :models do
%w[pending canceled].each do |status|
context "if commit status is #{status}" do
- before { commit_status.status = status }
+ before do
+ commit_status.status = status
+ end
it { is_expected.to be_falsey }
end
@@ -77,7 +86,9 @@ describe CommitStatus, :models do
%w[pending running].each do |state|
context "if commit_status.status is #{state}" do
- before { commit_status.status = state }
+ before do
+ commit_status.status = state
+ end
it { is_expected.to be_truthy }
end
@@ -85,7 +96,9 @@ describe CommitStatus, :models do
%w[success failed canceled].each do |state|
context "if commit_status.status is #{state}" do
- before { commit_status.status = state }
+ before do
+ commit_status.status = state
+ end
it { is_expected.to be_falsey }
end
@@ -97,7 +110,9 @@ describe CommitStatus, :models do
%w[success failed canceled].each do |state|
context "if commit_status.status is #{state}" do
- before { commit_status.status = state }
+ before do
+ commit_status.status = state
+ end
it { is_expected.to be_truthy }
end
@@ -105,7 +120,9 @@ describe CommitStatus, :models do
%w[pending running].each do |state|
context "if commit_status.status is #{state}" do
- before { commit_status.status = state }
+ before do
+ commit_status.status = state
+ end
it { is_expected.to be_falsey }
end
@@ -271,7 +288,9 @@ describe CommitStatus, :models do
subject { commit_status.before_sha }
context 'when no before_sha is set for pipeline' do
- before { pipeline.before_sha = nil }
+ before do
+ pipeline.before_sha = nil
+ end
it 'returns blank sha' do
is_expected.to eq(Gitlab::Git::BLANK_SHA)
@@ -280,7 +299,10 @@ describe CommitStatus, :models do
context 'for before_sha set for pipeline' do
let(:value) { '1234' }
- before { pipeline.before_sha = value }
+
+ before do
+ pipeline.before_sha = value
+ end
it 'returns the set value' do
is_expected.to eq(value)
diff --git a/spec/models/concerns/access_requestable_spec.rb b/spec/models/concerns/access_requestable_spec.rb
index 4829ef17a20..97b7e48bb3c 100644
--- a/spec/models/concerns/access_requestable_spec.rb
+++ b/spec/models/concerns/access_requestable_spec.rb
@@ -14,7 +14,9 @@ describe AccessRequestable do
let(:group) { create(:group, :public, :access_requestable) }
let(:user) { create(:user) }
- before { group.request_access(user) }
+ before do
+ group.request_access(user)
+ end
it { expect(group.requesters.exists?(user_id: user)).to be_truthy }
end
@@ -32,7 +34,9 @@ describe AccessRequestable do
let(:project) { create(:empty_project, :public, :access_requestable) }
let(:user) { create(:user) }
- before { project.request_access(user) }
+ before do
+ project.request_access(user)
+ end
it { expect(project.requesters.exists?(user_id: user)).to be_truthy }
end
diff --git a/spec/models/concerns/issuable_spec.rb b/spec/models/concerns/issuable_spec.rb
index 27890e33b49..1a9bda64191 100644
--- a/spec/models/concerns/issuable_spec.rb
+++ b/spec/models/concerns/issuable_spec.rb
@@ -200,7 +200,9 @@ describe Issuable do
let(:project) { issue.project }
context 'user is not a participant in the issue' do
- before { allow(issue).to receive(:participants).with(user).and_return([]) }
+ before do
+ allow(issue).to receive(:participants).with(user).and_return([])
+ end
it 'returns false when no subcription exists' do
expect(issue.subscribed?(user, project)).to be_falsey
@@ -220,7 +222,9 @@ describe Issuable do
end
context 'user is a participant in the issue' do
- before { allow(issue).to receive(:participants).with(user).and_return([user]) }
+ before do
+ allow(issue).to receive(:participants).with(user).and_return([user])
+ end
it 'returns false when no subcription exists' do
expect(issue.subscribed?(user, project)).to be_truthy
@@ -252,7 +256,9 @@ describe Issuable do
end
context "issue is assigned" do
- before { issue.assignees << user }
+ before do
+ issue.assignees << user
+ end
it "returns correct hook data" do
expect(data[:assignees].first).to eq(user.hook_attrs)
@@ -276,7 +282,9 @@ describe Issuable do
context 'issue has labels' do
let(:labels) { [create(:label), create(:label)] }
- before { issue.update_attribute(:labels, labels)}
+ before do
+ issue.update_attribute(:labels, labels)
+ end
it 'includes labels in the hook data' do
expect(data[:labels]).to eq(labels.map(&:hook_attrs))
diff --git a/spec/models/concerns/mentionable_spec.rb b/spec/models/concerns/mentionable_spec.rb
index e382c7120de..e2a29e0ae70 100644
--- a/spec/models/concerns/mentionable_spec.rb
+++ b/spec/models/concerns/mentionable_spec.rb
@@ -61,7 +61,9 @@ describe Issue, "Mentionable" do
end
context 'when the current user can see the issue' do
- before { private_project.team << [user, Gitlab::Access::DEVELOPER] }
+ before do
+ private_project.team << [user, Gitlab::Access::DEVELOPER]
+ end
it 'includes the reference' do
expect(referenced_issues(user)).to contain_exactly(private_issue, public_issue)
diff --git a/spec/models/concerns/milestoneish_spec.rb b/spec/models/concerns/milestoneish_spec.rb
index 675b730c557..cefe7fb6fea 100644
--- a/spec/models/concerns/milestoneish_spec.rb
+++ b/spec/models/concerns/milestoneish_spec.rb
@@ -19,12 +19,43 @@ describe Milestone, 'Milestoneish' do
let!(:closed_security_issue_3) { create(:issue, :confidential, :closed, project: project, author: author, milestone: milestone) }
let!(:closed_security_issue_4) { create(:issue, :confidential, :closed, project: project, assignees: [assignee], milestone: milestone) }
let!(:merge_request) { create(:merge_request, source_project: project, target_project: project, milestone: milestone) }
+ let(:label_1) { create(:label, title: 'label_1', project: project, priority: 1) }
+ let(:label_2) { create(:label, title: 'label_2', project: project, priority: 2) }
+ let(:label_3) { create(:label, title: 'label_3', project: project) }
before do
project.team << [member, :developer]
project.team << [guest, :guest]
end
+ describe '#sorted_issues' do
+ it 'sorts issues by label priority' do
+ issue.labels << label_1
+ security_issue_1.labels << label_2
+ closed_issue_1.labels << label_3
+
+ issues = milestone.sorted_issues(member)
+
+ expect(issues.first).to eq(issue)
+ expect(issues.second).to eq(security_issue_1)
+ expect(issues.third).not_to eq(closed_issue_1)
+ end
+ end
+
+ describe '#sorted_merge_requests' do
+ it 'sorts merge requests by label priority' do
+ merge_request_1 = create(:labeled_merge_request, labels: [label_2], source_project: project, source_branch: 'branch_1', milestone: milestone)
+ merge_request_2 = create(:labeled_merge_request, labels: [label_1], source_project: project, source_branch: 'branch_2', milestone: milestone)
+ merge_request_3 = create(:labeled_merge_request, labels: [label_3], source_project: project, source_branch: 'branch_3', milestone: milestone)
+
+ merge_requests = milestone.sorted_merge_requests
+
+ expect(merge_requests.first).to eq(merge_request_2)
+ expect(merge_requests.second).to eq(merge_request_1)
+ expect(merge_requests.third).to eq(merge_request_3)
+ end
+ end
+
describe '#closed_items_count' do
it 'does not count confidential issues for non project members' do
expect(milestone.closed_items_count(non_member)).to eq 2
diff --git a/spec/models/concerns/reactive_caching_spec.rb b/spec/models/concerns/reactive_caching_spec.rb
index a0765a264cf..808247ebfd5 100644
--- a/spec/models/concerns/reactive_caching_spec.rb
+++ b/spec/models/concerns/reactive_caching_spec.rb
@@ -40,7 +40,10 @@ describe ReactiveCaching, caching: true do
let(:instance) { CacheTest.new(666, &calculation) }
describe '#with_reactive_cache' do
- before { stub_reactive_cache }
+ before do
+ stub_reactive_cache
+ end
+
subject(:go!) { instance.result }
context 'when cache is empty' do
@@ -60,12 +63,17 @@ describe ReactiveCaching, caching: true do
end
context 'when the cache is full' do
- before { stub_reactive_cache(instance, 4) }
+ before do
+ stub_reactive_cache(instance, 4)
+ end
it { is_expected.to eq(2) }
context 'and expired' do
- before { invalidate_reactive_cache(instance) }
+ before do
+ invalidate_reactive_cache(instance)
+ end
+
it { is_expected.to be_nil }
end
end
@@ -84,7 +92,9 @@ describe ReactiveCaching, caching: true do
subject(:go!) { instance.exclusively_update_reactive_cache! }
context 'when the lease is free and lifetime is not exceeded' do
- before { stub_reactive_cache(instance, "preexisting") }
+ before do
+ stub_reactive_cache(instance, "preexisting")
+ end
it 'takes and releases the lease' do
expect_any_instance_of(Gitlab::ExclusiveLease).to receive(:try_obtain).and_return("000000")
@@ -106,7 +116,10 @@ describe ReactiveCaching, caching: true do
end
context 'and #calculate_reactive_cache raises an exception' do
- before { stub_reactive_cache(instance, "preexisting") }
+ before do
+ stub_reactive_cache(instance, "preexisting")
+ end
+
let(:calculation) { -> { raise "foo"} }
it 'leaves the cache untouched' do
diff --git a/spec/models/concerns/token_authenticatable_spec.rb b/spec/models/concerns/token_authenticatable_spec.rb
index 4b0bfa43abf..882afeccfc6 100644
--- a/spec/models/concerns/token_authenticatable_spec.rb
+++ b/spec/models/concerns/token_authenticatable_spec.rb
@@ -49,7 +49,10 @@ describe ApplicationSetting, 'TokenAuthenticatable' do
end
context 'token is generated' do
- before { subject.send("reset_#{token_field}!") }
+ before do
+ subject.send("reset_#{token_field}!")
+ end
+
it 'persists a new token' do
expect(subject.send(:read_attribute, token_field)).to be_a String
end
diff --git a/spec/models/deployment_spec.rb b/spec/models/deployment_spec.rb
index 6f0d2db23c7..aad215d5f41 100644
--- a/spec/models/deployment_spec.rb
+++ b/spec/models/deployment_spec.rb
@@ -102,7 +102,7 @@ describe Deployment, models: true do
end
context 'with other actions' do
- let!(:close_action) { create(:ci_build, pipeline: build.pipeline, name: 'close_app', when: :manual) }
+ 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') }
@@ -130,7 +130,7 @@ describe Deployment, models: true 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!(:close_action) { create(:ci_build, pipeline: build.pipeline, name: 'close_app', when: :manual) }
+ let!(:close_action) { create(:ci_build, :manual, pipeline: build.pipeline, name: 'close_app') }
it { is_expected.to be_truthy }
end
diff --git a/spec/models/diff_viewer/base_spec.rb b/spec/models/diff_viewer/base_spec.rb
new file mode 100644
index 00000000000..3755f4a56f3
--- /dev/null
+++ b/spec/models/diff_viewer/base_spec.rb
@@ -0,0 +1,150 @@
+require 'spec_helper'
+
+describe DiffViewer::Base, model: true do
+ include FakeBlobHelpers
+
+ let(:project) { create(:project, :repository) }
+ let(:commit) { project.commit('570e7b2abdd848b95f2f578043fc23bd6f6fd24d') }
+ let(:diff_file) { commit.diffs.diff_file_with_new_path('files/ruby/popen.rb') }
+
+ let(:viewer_class) do
+ Class.new(described_class) do
+ include DiffViewer::ServerSide
+
+ self.extensions = %w(jpg)
+ self.binary = true
+ self.collapse_limit = 1.megabyte
+ self.size_limit = 5.megabytes
+ end
+ end
+
+ let(:viewer) { viewer_class.new(diff_file) }
+
+ describe '.can_render?' do
+ context 'when the extension is supported' do
+ let(:commit) { project.commit('2f63565e7aac07bcdadb654e253078b727143ec4') }
+ let(:diff_file) { commit.diffs.diff_file_with_new_path('files/images/6049019_460s.jpg') }
+
+ context 'when the binaryness matches' do
+ it 'returns true' do
+ expect(viewer_class.can_render?(diff_file)).to be_truthy
+ end
+ end
+
+ context 'when the binaryness does not match' do
+ before do
+ allow(diff_file.old_blob).to receive(:binary?).and_return(false)
+ allow(diff_file.new_blob).to receive(:binary?).and_return(false)
+ end
+
+ it 'returns false' do
+ expect(viewer_class.can_render?(diff_file)).to be_falsey
+ end
+ end
+ end
+
+ context 'when the file type is supported' do
+ let(:commit) { project.commit('1a0b36b3cdad1d2ee32457c102a8c0b7056fa863') }
+ let(:diff_file) { commit.diffs.diff_file_with_new_path('LICENSE') }
+
+ before do
+ viewer_class.file_types = %i(license)
+ viewer_class.binary = false
+ end
+
+ context 'when the binaryness matches' do
+ it 'returns true' do
+ expect(viewer_class.can_render?(diff_file)).to be_truthy
+ end
+ end
+
+ context 'when the binaryness does not match' do
+ before do
+ allow(diff_file.old_blob).to receive(:binary?).and_return(true)
+ allow(diff_file.new_blob).to receive(:binary?).and_return(true)
+ end
+
+ it 'returns false' do
+ expect(viewer_class.can_render?(diff_file)).to be_falsey
+ end
+ end
+ end
+
+ context 'when the extension and file type are not supported' do
+ it 'returns false' do
+ expect(viewer_class.can_render?(diff_file)).to be_falsey
+ end
+ end
+
+ context 'when the file was renamed and only the old blob is supported' do
+ let(:commit) { project.commit('2f63565e7aac07bcdadb654e253078b727143ec4') }
+ let(:diff_file) { commit.diffs.diff_file_with_new_path('files/images/6049019_460s.jpg') }
+
+ before do
+ allow(diff_file).to receive(:renamed_file?).and_return(true)
+ allow(diff_file.new_blob).to receive(:extension).and_return('jpeg')
+ end
+
+ it 'returns false' do
+ expect(viewer_class.can_render?(diff_file)).to be_falsey
+ end
+ end
+ end
+
+ describe '#collapsed?' do
+ context 'when the combined blob size is larger than the collapse limit' do
+ before do
+ allow(diff_file.old_blob).to receive(:raw_size).and_return(512.kilobytes)
+ allow(diff_file.new_blob).to receive(:raw_size).and_return(513.kilobytes)
+ end
+
+ it 'returns true' do
+ expect(viewer.collapsed?).to be_truthy
+ end
+ end
+
+ context 'when the combined blob size is smaller than the collapse limit' do
+ it 'returns false' do
+ expect(viewer.collapsed?).to be_falsey
+ end
+ end
+ end
+
+ describe '#too_large?' do
+ context 'when the combined blob size is larger than the size limit' do
+ before do
+ allow(diff_file.old_blob).to receive(:raw_size).and_return(2.megabytes)
+ allow(diff_file.new_blob).to receive(:raw_size).and_return(4.megabytes)
+ end
+
+ it 'returns true' do
+ expect(viewer.too_large?).to be_truthy
+ end
+ end
+
+ context 'when the blob size is smaller than the size limit' do
+ it 'returns false' do
+ expect(viewer.too_large?).to be_falsey
+ end
+ end
+ end
+
+ describe '#render_error' do
+ context 'when the combined blob size is larger than the size limit' do
+ before do
+ allow(diff_file.old_blob).to receive(:raw_size).and_return(2.megabytes)
+ allow(diff_file.new_blob).to receive(:raw_size).and_return(4.megabytes)
+ end
+
+ it 'returns :too_large' do
+ expect(viewer.render_error).to eq(:too_large)
+ end
+ end
+
+ context 'when the combined blob size is smaller than the size limit' do
+ it 'returns nil' do
+ expect(viewer.render_error).to be_nil
+ end
+ end
+ end
+end
diff --git a/spec/models/diff_viewer/server_side_spec.rb b/spec/models/diff_viewer/server_side_spec.rb
new file mode 100644
index 00000000000..2d926e06936
--- /dev/null
+++ b/spec/models/diff_viewer/server_side_spec.rb
@@ -0,0 +1,36 @@
+require 'spec_helper'
+
+describe DiffViewer::ServerSide, model: true do
+ let(:project) { create(:project, :repository) }
+ let(:commit) { project.commit('570e7b2abdd848b95f2f578043fc23bd6f6fd24d') }
+ let(:diff_file) { commit.diffs.diff_file_with_new_path('files/ruby/popen.rb') }
+
+ let(:viewer_class) do
+ Class.new(DiffViewer::Base) do
+ include DiffViewer::ServerSide
+ end
+ end
+
+ subject { viewer_class.new(diff_file) }
+
+ describe '#prepare!' do
+ it 'loads all diff file data' do
+ expect(diff_file.old_blob).to receive(:load_all_data!)
+ expect(diff_file.new_blob).to receive(:load_all_data!)
+
+ subject.prepare!
+ end
+ end
+
+ describe '#render_error' do
+ context 'when the diff file is stored externally' do
+ before do
+ allow(diff_file).to receive(:stored_externally?).and_return(true)
+ end
+
+ it 'return :server_side_but_stored_externally' do
+ expect(subject.render_error).to eq(:server_side_but_stored_externally)
+ end
+ end
+ end
+end
diff --git a/spec/models/environment_spec.rb b/spec/models/environment_spec.rb
index fe69c8e351d..f8123cb518e 100644
--- a/spec/models/environment_spec.rb
+++ b/spec/models/environment_spec.rb
@@ -170,7 +170,7 @@ describe Environment, models: true do
context 'when matching action is defined' do
let(:build) { create(:ci_build) }
let!(:deployment) { create(:deployment, environment: environment, deployable: build, on_stop: 'close_app') }
- let!(:close_action) { create(:ci_build, pipeline: build.pipeline, name: 'close_app', when: :manual) }
+ let!(:close_action) { create(:ci_build, :manual, pipeline: build.pipeline, name: 'close_app') }
context 'when environment is available' do
before do
diff --git a/spec/models/generic_commit_status_spec.rb b/spec/models/generic_commit_status_spec.rb
index f4c3e6d503f..152e97e09bf 100644
--- a/spec/models/generic_commit_status_spec.rb
+++ b/spec/models/generic_commit_status_spec.rb
@@ -19,7 +19,10 @@ describe GenericCommitStatus, models: true do
describe '#context' do
subject { generic_commit_status.context }
- before { generic_commit_status.context = 'my_context' }
+
+ before do
+ generic_commit_status.context = 'my_context'
+ end
it { is_expected.to eq(generic_commit_status.name) }
end
@@ -39,7 +42,9 @@ describe GenericCommitStatus, models: true do
end
context 'when user has ability to see datails' do
- before { project.team << [user, :developer] }
+ before do
+ project.team << [user, :developer]
+ end
it 'details path points to an external URL' do
expect(status).to have_details
diff --git a/spec/models/group_spec.rb b/spec/models/group_spec.rb
index 3d437ca0fcc..449b7c2f7d7 100644
--- a/spec/models/group_spec.rb
+++ b/spec/models/group_spec.rb
@@ -143,14 +143,20 @@ describe Group, models: true do
describe '#add_user' do
let(:user) { create(:user) }
- before { group.add_user(user, GroupMember::MASTER) }
+
+ before do
+ group.add_user(user, GroupMember::MASTER)
+ end
it { expect(group.group_members.masters.map(&:user)).to include(user) }
end
describe '#add_users' do
let(:user) { create(:user) }
- before { group.add_users([user.id], GroupMember::GUEST) }
+
+ before do
+ group.add_users([user.id], GroupMember::GUEST)
+ end
it "updates the group permission" do
expect(group.group_members.guests.map(&:user)).to include(user)
@@ -162,7 +168,10 @@ describe Group, models: true do
describe '#avatar_type' do
let(:user) { create(:user) }
- before { group.add_user(user, GroupMember::MASTER) }
+
+ before do
+ group.add_user(user, GroupMember::MASTER)
+ end
it "is true if avatar is image" do
group.update_attribute(:avatar, 'uploads/avatar.png')
@@ -182,7 +191,9 @@ describe Group, models: true do
let(:avatar_path) { "/uploads/system/group/avatar/#{group.id}/dk.png" }
context 'when avatar file is uploaded' do
- before { group.add_master(user) }
+ before do
+ group.add_master(user)
+ end
it 'shows correct avatar url' do
expect(group.avatar_url).to eq(avatar_path)
@@ -222,7 +233,9 @@ describe Group, models: true do
end
describe '#has_owner?' do
- before { @members = setup_group_members(group) }
+ before do
+ @members = setup_group_members(group)
+ end
it { expect(group.has_owner?(@members[:owner])).to be_truthy }
it { expect(group.has_owner?(@members[:master])).to be_falsey }
@@ -233,7 +246,9 @@ describe Group, models: true do
end
describe '#has_master?' do
- before { @members = setup_group_members(group) }
+ before do
+ @members = setup_group_members(group)
+ end
it { expect(group.has_master?(@members[:owner])).to be_falsey }
it { expect(group.has_master?(@members[:master])).to be_truthy }
diff --git a/spec/models/issue_spec.rb b/spec/models/issue_spec.rb
index bb4e70db2e9..12e7d646382 100644
--- a/spec/models/issue_spec.rb
+++ b/spec/models/issue_spec.rb
@@ -245,7 +245,9 @@ describe Issue, models: true do
let(:project) { create(:empty_project) }
let(:issue) { create(:issue, project: project) }
- before { project.team << [user, :reporter] }
+ before do
+ project.team << [user, :reporter]
+ end
it { is_expected.to eq true }
@@ -259,12 +261,18 @@ describe Issue, models: true do
let(:to_project) { create(:empty_project) }
context 'destination project allowed' do
- before { to_project.team << [user, :reporter] }
+ before do
+ to_project.team << [user, :reporter]
+ end
+
it { is_expected.to eq true }
end
context 'destination project not allowed' do
- before { to_project.team << [user, :guest] }
+ before do
+ to_project.team << [user, :guest]
+ end
+
it { is_expected.to eq false }
end
end
@@ -549,7 +557,9 @@ describe Issue, models: true do
end
context 'when the user is the project owner' do
- before { project.team << [user, :master] }
+ before do
+ project.team << [user, :master]
+ end
it 'returns true for a regular issue' do
issue = build(:issue, project: project)
diff --git a/spec/models/merge_request_diff_file_spec.rb b/spec/models/merge_request_diff_file_spec.rb
new file mode 100644
index 00000000000..7276f5b5061
--- /dev/null
+++ b/spec/models/merge_request_diff_file_spec.rb
@@ -0,0 +1,11 @@
+require 'rails_helper'
+
+describe MergeRequestDiffFile, type: :model do
+ describe '#utf8_diff' do
+ it 'does not raise error when a hash value is in binary' do
+ subject.diff = "\x05\x00\x68\x65\x6c\x6c\x6f"
+
+ expect { subject.utf8_diff }.not_to raise_error
+ end
+ end
+end
diff --git a/spec/models/merge_request_diff_spec.rb b/spec/models/merge_request_diff_spec.rb
index ed9fde57bf7..4ad4abaa572 100644
--- a/spec/models/merge_request_diff_spec.rb
+++ b/spec/models/merge_request_diff_spec.rb
@@ -36,7 +36,9 @@ describe MergeRequestDiff, models: true do
end
context 'when the raw diffs are empty' do
- before { mr_diff.update_attributes(st_diffs: '') }
+ before do
+ MergeRequestDiffFile.delete_all(merge_request_diff_id: mr_diff.id)
+ end
it 'returns an empty DiffCollection' do
expect(mr_diff.raw_diffs).to be_a(Gitlab::Git::DiffCollection)
@@ -45,7 +47,10 @@ describe MergeRequestDiff, models: true do
end
context 'when the raw diffs have invalid content' do
- before { mr_diff.update_attributes(st_diffs: ["--broken-diff"]) }
+ before do
+ MergeRequestDiffFile.delete_all(merge_request_diff_id: mr_diff.id)
+ mr_diff.update_attributes(st_diffs: ["--broken-diff"])
+ end
it 'returns an empty DiffCollection' do
expect(mr_diff.raw_diffs.to_a).to be_empty
diff --git a/spec/models/merge_request_spec.rb b/spec/models/merge_request_spec.rb
index 060754fab63..a56bc524a98 100644
--- a/spec/models/merge_request_spec.rb
+++ b/spec/models/merge_request_spec.rb
@@ -892,7 +892,9 @@ describe MergeRequest, models: true do
end
context 'when broken' do
- before { allow(subject).to receive(:broken?) { true } }
+ before do
+ allow(subject).to receive(:broken?) { true }
+ end
it 'becomes unmergeable' do
expect { subject.check_if_can_be_merged }.to change { subject.merge_status }.to('cannot_be_merged')
@@ -944,7 +946,9 @@ describe MergeRequest, models: true do
end
context 'when not open' do
- before { subject.close }
+ before do
+ subject.close
+ end
it 'returns false' do
expect(subject.mergeable_state?).to be_falsey
@@ -952,7 +956,9 @@ describe MergeRequest, models: true do
end
context 'when working in progress' do
- before { subject.title = 'WIP MR' }
+ before do
+ subject.title = 'WIP MR'
+ end
it 'returns false' do
expect(subject.mergeable_state?).to be_falsey
@@ -960,7 +966,9 @@ describe MergeRequest, models: true do
end
context 'when broken' do
- before { allow(subject).to receive(:broken?) { true } }
+ before do
+ allow(subject).to receive(:broken?) { true }
+ end
it 'returns false' do
expect(subject.mergeable_state?).to be_falsey
@@ -1389,7 +1397,7 @@ describe MergeRequest, models: true do
end
end
- describe '#mergeable_with_slash_command?' do
+ describe '#mergeable_with_quick_action?' do
def create_pipeline(status)
pipeline = create(:ci_pipeline_with_one_job,
project: project,
@@ -1413,21 +1421,21 @@ describe MergeRequest, models: true do
context 'when autocomplete_precheck is set to true' do
it 'is mergeable by developer' do
- expect(merge_request.mergeable_with_slash_command?(developer, autocomplete_precheck: true)).to be_truthy
+ expect(merge_request.mergeable_with_quick_action?(developer, autocomplete_precheck: true)).to be_truthy
end
it 'is not mergeable by normal user' do
- expect(merge_request.mergeable_with_slash_command?(user, autocomplete_precheck: true)).to be_falsey
+ expect(merge_request.mergeable_with_quick_action?(user, autocomplete_precheck: true)).to be_falsey
end
end
context 'when autocomplete_precheck is set to false' do
it 'is mergeable by developer' do
- expect(merge_request.mergeable_with_slash_command?(developer, last_diff_sha: mr_sha)).to be_truthy
+ expect(merge_request.mergeable_with_quick_action?(developer, last_diff_sha: mr_sha)).to be_truthy
end
it 'is not mergeable by normal user' do
- expect(merge_request.mergeable_with_slash_command?(user, last_diff_sha: mr_sha)).to be_falsey
+ expect(merge_request.mergeable_with_quick_action?(user, last_diff_sha: mr_sha)).to be_falsey
end
context 'closed MR' do
@@ -1436,7 +1444,7 @@ describe MergeRequest, models: true do
end
it 'is not mergeable' do
- expect(merge_request.mergeable_with_slash_command?(developer, last_diff_sha: mr_sha)).to be_falsey
+ expect(merge_request.mergeable_with_quick_action?(developer, last_diff_sha: mr_sha)).to be_falsey
end
end
@@ -1446,19 +1454,19 @@ describe MergeRequest, models: true do
end
it 'is not mergeable' do
- expect(merge_request.mergeable_with_slash_command?(developer, last_diff_sha: mr_sha)).to be_falsey
+ expect(merge_request.mergeable_with_quick_action?(developer, last_diff_sha: mr_sha)).to be_falsey
end
end
context 'sha differs from the MR diff_head_sha' do
it 'is not mergeable' do
- expect(merge_request.mergeable_with_slash_command?(developer, last_diff_sha: 'some other sha')).to be_falsey
+ expect(merge_request.mergeable_with_quick_action?(developer, last_diff_sha: 'some other sha')).to be_falsey
end
end
context 'sha is not provided' do
it 'is not mergeable' do
- expect(merge_request.mergeable_with_slash_command?(developer)).to be_falsey
+ expect(merge_request.mergeable_with_quick_action?(developer)).to be_falsey
end
end
@@ -1468,7 +1476,7 @@ describe MergeRequest, models: true do
end
it 'is mergeable' do
- expect(merge_request.mergeable_with_slash_command?(developer, last_diff_sha: mr_sha)).to be_truthy
+ expect(merge_request.mergeable_with_quick_action?(developer, last_diff_sha: mr_sha)).to be_truthy
end
end
@@ -1478,7 +1486,7 @@ describe MergeRequest, models: true do
end
it 'is not mergeable' do
- expect(merge_request.mergeable_with_slash_command?(developer, last_diff_sha: mr_sha)).to be_falsey
+ expect(merge_request.mergeable_with_quick_action?(developer, last_diff_sha: mr_sha)).to be_falsey
end
end
@@ -1488,7 +1496,7 @@ describe MergeRequest, models: true do
end
it 'is mergeable' do
- expect(merge_request.mergeable_with_slash_command?(developer, last_diff_sha: mr_sha)).to be_truthy
+ expect(merge_request.mergeable_with_quick_action?(developer, last_diff_sha: mr_sha)).to be_truthy
end
end
end
diff --git a/spec/models/milestone_spec.rb b/spec/models/milestone_spec.rb
index aa1ce89ffd7..20b96c08a8f 100644
--- a/spec/models/milestone_spec.rb
+++ b/spec/models/milestone_spec.rb
@@ -144,35 +144,6 @@ describe Milestone, models: true do
end
end
- describe '#sort_issues' do
- let(:milestone) { create(:milestone) }
-
- let(:issue1) { create(:issue, milestone: milestone, position: 1) }
- let(:issue2) { create(:issue, milestone: milestone, position: 2) }
- let(:issue3) { create(:issue, milestone: milestone, position: 3) }
- let(:issue4) { create(:issue, position: 42) }
-
- it 'sorts the given issues' do
- milestone.sort_issues([issue3.id, issue2.id, issue1.id])
-
- issue1.reload
- issue2.reload
- issue3.reload
-
- expect(issue1.position).to eq(3)
- expect(issue2.position).to eq(2)
- expect(issue3.position).to eq(1)
- end
-
- it 'ignores issues not part of the milestone' do
- milestone.sort_issues([issue3.id, issue2.id, issue1.id, issue4.id])
-
- issue4.reload
-
- expect(issue4.position).to eq(42)
- end
- end
-
describe '.search' do
let(:milestone) { create(:milestone, title: 'foo', description: 'bar') }
diff --git a/spec/models/note_spec.rb b/spec/models/note_spec.rb
index 7a01cef9b4b..d4d4fc86343 100644
--- a/spec/models/note_spec.rb
+++ b/spec/models/note_spec.rb
@@ -26,14 +26,18 @@ describe Note, models: true do
it { is_expected.to validate_presence_of(:project) }
context 'when note is on commit' do
- before { allow(subject).to receive(:for_commit?).and_return(true) }
+ before do
+ allow(subject).to receive(:for_commit?).and_return(true)
+ end
it { is_expected.to validate_presence_of(:commit_id) }
it { is_expected.not_to validate_presence_of(:noteable_id) }
end
context 'when note is not on commit' do
- before { allow(subject).to receive(:for_commit?).and_return(false) }
+ before do
+ allow(subject).to receive(:for_commit?).and_return(false)
+ end
it { is_expected.not_to validate_presence_of(:commit_id) }
it { is_expected.to validate_presence_of(:noteable_id) }
diff --git a/spec/models/notification_setting_spec.rb b/spec/models/notification_setting_spec.rb
index d58673413c8..cc235ad467e 100644
--- a/spec/models/notification_setting_spec.rb
+++ b/spec/models/notification_setting_spec.rb
@@ -55,4 +55,34 @@ RSpec.describe NotificationSetting, type: :model do
expect(user.notification_settings.for_projects.map(&:project)).to all(have_attributes(pending_delete: false))
end
end
+
+ describe 'event_enabled?' do
+ before do
+ subject.update!(user: create(:user))
+ end
+
+ context 'for an event with a matching column name' do
+ before do
+ subject.update!(events: { new_note: true }.to_json)
+ end
+
+ it 'returns the value of the column' do
+ subject.update!(new_note: false)
+
+ expect(subject.event_enabled?(:new_note)).to be(false)
+ end
+
+ context 'when the column has a nil value' do
+ it 'returns the value from the events hash' do
+ expect(subject.event_enabled?(:new_note)).to be(false)
+ end
+ end
+ end
+
+ context 'for an event without a matching column name' do
+ it 'returns false' do
+ expect(subject.event_enabled?(:foo_event)).to be(false)
+ end
+ end
+ end
end
diff --git a/spec/models/project_services/bamboo_service_spec.rb b/spec/models/project_services/bamboo_service_spec.rb
index 4014d6129ee..e62fd69e567 100644
--- a/spec/models/project_services/bamboo_service_spec.rb
+++ b/spec/models/project_services/bamboo_service_spec.rb
@@ -24,7 +24,9 @@ describe BambooService, models: true, caching: true do
describe 'Validations' do
context 'when service is active' do
- before { subject.active = true }
+ before do
+ subject.active = true
+ end
it { is_expected.to validate_presence_of(:build_key) }
it { is_expected.to validate_presence_of(:bamboo_url) }
@@ -60,7 +62,9 @@ describe BambooService, models: true, caching: true do
end
context 'when service is inactive' do
- before { subject.active = false }
+ before do
+ subject.active = false
+ end
it { is_expected.not_to validate_presence_of(:build_key) }
it { is_expected.not_to validate_presence_of(:bamboo_url) }
diff --git a/spec/models/project_services/bugzilla_service_spec.rb b/spec/models/project_services/bugzilla_service_spec.rb
index 739cc72b2ff..5f17bbde390 100644
--- a/spec/models/project_services/bugzilla_service_spec.rb
+++ b/spec/models/project_services/bugzilla_service_spec.rb
@@ -8,7 +8,9 @@ describe BugzillaService, models: true do
describe 'Validations' do
context 'when service is active' do
- before { subject.active = true }
+ before do
+ subject.active = true
+ end
it { is_expected.to validate_presence_of(:project_url) }
it { is_expected.to validate_presence_of(:issues_url) }
@@ -19,7 +21,9 @@ describe BugzillaService, models: true do
end
context 'when service is inactive' do
- before { subject.active = false }
+ before do
+ subject.active = false
+ end
it { is_expected.not_to validate_presence_of(:project_url) }
it { is_expected.not_to validate_presence_of(:issues_url) }
diff --git a/spec/models/project_services/buildkite_service_spec.rb b/spec/models/project_services/buildkite_service_spec.rb
index 05b602d8106..dd529597067 100644
--- a/spec/models/project_services/buildkite_service_spec.rb
+++ b/spec/models/project_services/buildkite_service_spec.rb
@@ -23,7 +23,9 @@ describe BuildkiteService, models: true, caching: true do
describe 'Validations' do
context 'when service is active' do
- before { subject.active = true }
+ before do
+ subject.active = true
+ end
it { is_expected.to validate_presence_of(:project_url) }
it { is_expected.to validate_presence_of(:token) }
@@ -31,7 +33,9 @@ describe BuildkiteService, models: true, caching: true do
end
context 'when service is inactive' do
- before { subject.active = false }
+ before do
+ subject.active = false
+ end
it { is_expected.not_to validate_presence_of(:project_url) }
it { is_expected.not_to validate_presence_of(:token) }
diff --git a/spec/models/project_services/campfire_service_spec.rb b/spec/models/project_services/campfire_service_spec.rb
index 953e664fb66..de55627dd27 100644
--- a/spec/models/project_services/campfire_service_spec.rb
+++ b/spec/models/project_services/campfire_service_spec.rb
@@ -8,13 +8,17 @@ describe CampfireService, models: true do
describe 'Validations' do
context 'when service is active' do
- before { subject.active = true }
+ before do
+ subject.active = true
+ end
it { is_expected.to validate_presence_of(:token) }
end
context 'when service is inactive' do
- before { subject.active = false }
+ before do
+ subject.active = false
+ end
it { is_expected.not_to validate_presence_of(:token) }
end
diff --git a/spec/models/project_services/chat_message/wiki_page_message_spec.rb b/spec/models/project_services/chat_message/wiki_page_message_spec.rb
index 4ca1b8aa7b7..17355c1e6f1 100644
--- a/spec/models/project_services/chat_message/wiki_page_message_spec.rb
+++ b/spec/models/project_services/chat_message/wiki_page_message_spec.rb
@@ -23,7 +23,9 @@ describe ChatMessage::WikiPageMessage, models: true do
context 'without markdown' do
describe '#pretext' do
context 'when :action == "create"' do
- before { args[:object_attributes][:action] = 'create' }
+ before do
+ args[:object_attributes][:action] = 'create'
+ end
it 'returns a message that a new wiki page was created' do
expect(subject.pretext).to eq(
@@ -33,7 +35,9 @@ describe ChatMessage::WikiPageMessage, models: true do
end
context 'when :action == "update"' do
- before { args[:object_attributes][:action] = 'update' }
+ before do
+ args[:object_attributes][:action] = 'update'
+ end
it 'returns a message that a wiki page was updated' do
expect(subject.pretext).to eq(
@@ -47,7 +51,9 @@ describe ChatMessage::WikiPageMessage, models: true do
let(:color) { '#345' }
context 'when :action == "create"' do
- before { args[:object_attributes][:action] = 'create' }
+ before do
+ args[:object_attributes][:action] = 'create'
+ end
it 'returns the attachment for a new wiki page' do
expect(subject.attachments).to eq([
@@ -60,7 +66,9 @@ describe ChatMessage::WikiPageMessage, models: true do
end
context 'when :action == "update"' do
- before { args[:object_attributes][:action] = 'update' }
+ before do
+ args[:object_attributes][:action] = 'update'
+ end
it 'returns the attachment for an updated wiki page' do
expect(subject.attachments).to eq([
@@ -81,7 +89,9 @@ describe ChatMessage::WikiPageMessage, models: true do
describe '#pretext' do
context 'when :action == "create"' do
- before { args[:object_attributes][:action] = 'create' }
+ before do
+ args[:object_attributes][:action] = 'create'
+ end
it 'returns a message that a new wiki page was created' do
expect(subject.pretext).to eq(
@@ -90,7 +100,9 @@ describe ChatMessage::WikiPageMessage, models: true do
end
context 'when :action == "update"' do
- before { args[:object_attributes][:action] = 'update' }
+ before do
+ args[:object_attributes][:action] = 'update'
+ end
it 'returns a message that a wiki page was updated' do
expect(subject.pretext).to eq(
@@ -101,7 +113,9 @@ describe ChatMessage::WikiPageMessage, models: true do
describe '#attachments' do
context 'when :action == "create"' do
- before { args[:object_attributes][:action] = 'create' }
+ before do
+ args[:object_attributes][:action] = 'create'
+ end
it 'returns the attachment for a new wiki page' do
expect(subject.attachments).to eq('Wiki page description')
@@ -109,7 +123,9 @@ describe ChatMessage::WikiPageMessage, models: true do
end
context 'when :action == "update"' do
- before { args[:object_attributes][:action] = 'update' }
+ before do
+ args[:object_attributes][:action] = 'update'
+ end
it 'returns the attachment for an updated wiki page' do
expect(subject.attachments).to eq('Wiki page description')
@@ -119,7 +135,9 @@ describe ChatMessage::WikiPageMessage, models: true do
describe '#activity' do
context 'when :action == "create"' do
- before { args[:object_attributes][:action] = 'create' }
+ before do
+ args[:object_attributes][:action] = 'create'
+ end
it 'returns the attachment for a new wiki page' do
expect(subject.activity).to eq({
@@ -132,7 +150,9 @@ describe ChatMessage::WikiPageMessage, models: true do
end
context 'when :action == "update"' do
- before { args[:object_attributes][:action] = 'update' }
+ before do
+ args[:object_attributes][:action] = 'update'
+ end
it 'returns the attachment for an updated wiki page' do
expect(subject.activity).to eq({
diff --git a/spec/models/project_services/custom_issue_tracker_service_spec.rb b/spec/models/project_services/custom_issue_tracker_service_spec.rb
index 63320931e76..9e574762232 100644
--- a/spec/models/project_services/custom_issue_tracker_service_spec.rb
+++ b/spec/models/project_services/custom_issue_tracker_service_spec.rb
@@ -8,7 +8,9 @@ describe CustomIssueTrackerService, models: true do
describe 'Validations' do
context 'when service is active' do
- before { subject.active = true }
+ before do
+ subject.active = true
+ end
it { is_expected.to validate_presence_of(:project_url) }
it { is_expected.to validate_presence_of(:issues_url) }
@@ -19,7 +21,9 @@ describe CustomIssueTrackerService, models: true do
end
context 'when service is inactive' do
- before { subject.active = false }
+ before do
+ subject.active = false
+ end
it { is_expected.not_to validate_presence_of(:project_url) }
it { is_expected.not_to validate_presence_of(:issues_url) }
diff --git a/spec/models/project_services/drone_ci_service_spec.rb b/spec/models/project_services/drone_ci_service_spec.rb
index 044737c6026..1400175427f 100644
--- a/spec/models/project_services/drone_ci_service_spec.rb
+++ b/spec/models/project_services/drone_ci_service_spec.rb
@@ -10,7 +10,9 @@ describe DroneCiService, models: true, caching: true do
describe 'validations' do
context 'active' do
- before { subject.active = true }
+ before do
+ subject.active = true
+ end
it { is_expected.to validate_presence_of(:token) }
it { is_expected.to validate_presence_of(:drone_url) }
@@ -18,7 +20,9 @@ describe DroneCiService, models: true, caching: true do
end
context 'inactive' do
- before { subject.active = false }
+ before do
+ subject.active = false
+ end
it { is_expected.not_to validate_presence_of(:token) }
it { is_expected.not_to validate_presence_of(:drone_url) }
diff --git a/spec/models/project_services/emails_on_push_service_spec.rb b/spec/models/project_services/emails_on_push_service_spec.rb
index e6f78898c82..d9b7010e5e5 100644
--- a/spec/models/project_services/emails_on_push_service_spec.rb
+++ b/spec/models/project_services/emails_on_push_service_spec.rb
@@ -3,13 +3,17 @@ require 'spec_helper'
describe EmailsOnPushService do
describe 'Validations' do
context 'when service is active' do
- before { subject.active = true }
+ before do
+ subject.active = true
+ end
it { is_expected.to validate_presence_of(:recipients) }
end
context 'when service is inactive' do
- before { subject.active = false }
+ before do
+ subject.active = false
+ end
it { is_expected.not_to validate_presence_of(:recipients) }
end
diff --git a/spec/models/project_services/external_wiki_service_spec.rb b/spec/models/project_services/external_wiki_service_spec.rb
index bdeea1db1e3..291fc645a1c 100644
--- a/spec/models/project_services/external_wiki_service_spec.rb
+++ b/spec/models/project_services/external_wiki_service_spec.rb
@@ -9,14 +9,18 @@ describe ExternalWikiService, models: true do
describe 'Validations' do
context 'when service is active' do
- before { subject.active = true }
+ before do
+ subject.active = true
+ end
it { is_expected.to validate_presence_of(:external_wiki_url) }
it_behaves_like 'issue tracker service URL attribute', :external_wiki_url
end
context 'when service is inactive' do
- before { subject.active = false }
+ before do
+ subject.active = false
+ end
it { is_expected.not_to validate_presence_of(:external_wiki_url) }
end
diff --git a/spec/models/project_services/flowdock_service_spec.rb b/spec/models/project_services/flowdock_service_spec.rb
index a97e8c6e4ce..56ace04dd58 100644
--- a/spec/models/project_services/flowdock_service_spec.rb
+++ b/spec/models/project_services/flowdock_service_spec.rb
@@ -8,13 +8,17 @@ describe FlowdockService, models: true do
describe 'Validations' do
context 'when service is active' do
- before { subject.active = true }
+ before do
+ subject.active = true
+ end
it { is_expected.to validate_presence_of(:token) }
end
context 'when service is inactive' do
- before { subject.active = false }
+ before do
+ subject.active = false
+ end
it { is_expected.not_to validate_presence_of(:token) }
end
diff --git a/spec/models/project_services/gemnasium_service_spec.rb b/spec/models/project_services/gemnasium_service_spec.rb
index a13fbae03eb..65c9e714bd1 100644
--- a/spec/models/project_services/gemnasium_service_spec.rb
+++ b/spec/models/project_services/gemnasium_service_spec.rb
@@ -8,14 +8,18 @@ describe GemnasiumService, models: true do
describe 'Validations' do
context 'when service is active' do
- before { subject.active = true }
+ before do
+ subject.active = true
+ end
it { is_expected.to validate_presence_of(:token) }
it { is_expected.to validate_presence_of(:api_key) }
end
context 'when service is inactive' do
- before { subject.active = false }
+ before do
+ subject.active = false
+ end
it { is_expected.not_to validate_presence_of(:token) }
it { is_expected.not_to validate_presence_of(:api_key) }
diff --git a/spec/models/project_services/hipchat_service_spec.rb b/spec/models/project_services/hipchat_service_spec.rb
index 1200ae7eb22..c7c8e9651ab 100644
--- a/spec/models/project_services/hipchat_service_spec.rb
+++ b/spec/models/project_services/hipchat_service_spec.rb
@@ -8,13 +8,17 @@ describe HipchatService, models: true do
describe 'Validations' do
context 'when service is active' do
- before { subject.active = true }
+ before do
+ subject.active = true
+ end
it { is_expected.to validate_presence_of(:token) }
end
context 'when service is inactive' do
- before { subject.active = false }
+ before do
+ subject.active = false
+ end
it { is_expected.not_to validate_presence_of(:token) }
end
diff --git a/spec/models/project_services/irker_service_spec.rb b/spec/models/project_services/irker_service_spec.rb
index d5a16226d9d..a5c4938b54e 100644
--- a/spec/models/project_services/irker_service_spec.rb
+++ b/spec/models/project_services/irker_service_spec.rb
@@ -10,13 +10,17 @@ describe IrkerService, models: true do
describe 'Validations' do
context 'when service is active' do
- before { subject.active = true }
+ before do
+ subject.active = true
+ end
it { is_expected.to validate_presence_of(:recipients) }
end
context 'when service is inactive' do
- before { subject.active = false }
+ before do
+ subject.active = false
+ end
it { is_expected.not_to validate_presence_of(:recipients) }
end
diff --git a/spec/models/project_services/jira_service_spec.rb b/spec/models/project_services/jira_service_spec.rb
index 0ee050196e4..e2b8226124f 100644
--- a/spec/models/project_services/jira_service_spec.rb
+++ b/spec/models/project_services/jira_service_spec.rb
@@ -10,7 +10,9 @@ describe JiraService, models: true do
describe 'Validations' do
context 'when service is active' do
- before { subject.active = true }
+ before do
+ subject.active = true
+ end
it { is_expected.to validate_presence_of(:url) }
it { is_expected.to validate_presence_of(:project_key) }
@@ -18,7 +20,9 @@ describe JiraService, models: true do
end
context 'when service is inactive' do
- before { subject.active = false }
+ before do
+ subject.active = false
+ end
it { is_expected.not_to validate_presence_of(:url) }
end
diff --git a/spec/models/project_services/kubernetes_service_spec.rb b/spec/models/project_services/kubernetes_service_spec.rb
index 0dcf4a4b5d6..858ad595dbf 100644
--- a/spec/models/project_services/kubernetes_service_spec.rb
+++ b/spec/models/project_services/kubernetes_service_spec.rb
@@ -7,31 +7,15 @@ describe KubernetesService, models: true, caching: true do
let(:project) { build_stubbed(:kubernetes_project) }
let(:service) { project.kubernetes_service }
- # We use Kubeclient to interactive with the Kubernetes API. It will
- # GET /api/v1 for a list of resources the API supports. This must be stubbed
- # in addition to any other HTTP requests we expect it to perform.
- let(:discovery_url) { service.api_url + '/api/v1' }
- let(:discovery_response) { { body: kube_discovery_body.to_json } }
-
- let(:pods_url) { service.api_url + "/api/v1/namespaces/#{service.actual_namespace}/pods" }
- let(:pods_response) { { body: kube_pods_body(kube_pod).to_json } }
-
- def stub_kubeclient_discover
- WebMock.stub_request(:get, discovery_url).to_return(discovery_response)
- end
-
- def stub_kubeclient_pods
- stub_kubeclient_discover
- WebMock.stub_request(:get, pods_url).to_return(pods_response)
- end
-
describe "Associations" do
it { is_expected.to belong_to :project }
end
describe 'Validations' do
context 'when service is active' do
- before { subject.active = true }
+ before do
+ subject.active = true
+ end
it { is_expected.not_to validate_presence_of(:namespace) }
it { is_expected.to validate_presence_of(:api_url) }
@@ -66,7 +50,9 @@ describe KubernetesService, models: true, caching: true do
end
context 'when service is inactive' do
- before { subject.active = false }
+ before do
+ subject.active = false
+ end
it { is_expected.not_to validate_presence_of(:api_url) }
it { is_expected.not_to validate_presence_of(:token) }
@@ -87,7 +73,9 @@ describe KubernetesService, models: true, caching: true do
end
context 'as template' do
- before { subject.template = true }
+ before do
+ subject.template = true
+ end
it 'sets the namespace to the default' do
expect(kube_namespace).not_to be_nil
@@ -96,7 +84,9 @@ describe KubernetesService, models: true, caching: true do
end
context 'with associated project' do
- before { subject.project = project }
+ before do
+ subject.project = project
+ end
it 'sets the namespace to the default' do
expect(kube_namespace).not_to be_nil
@@ -111,6 +101,34 @@ describe KubernetesService, models: true, caching: true do
it "returns the default namespace" do
is_expected.to eq(service.send(:default_namespace))
end
+
+ context 'when namespace is specified' do
+ before do
+ service.namespace = 'my-namespace'
+ end
+
+ it "returns the user-namespace" do
+ is_expected.to eq('my-namespace')
+ end
+ end
+
+ context 'when service is not assigned to project' do
+ before do
+ service.project = nil
+ end
+
+ it "does not return namespace" do
+ is_expected.to be_nil
+ end
+ end
+ end
+
+ describe '#actual_namespace' do
+ subject { service.actual_namespace }
+
+ it "returns the default namespace" do
+ is_expected.to eq(service.send(:default_namespace))
+ end
context 'when namespace is specified' do
before do
@@ -134,6 +152,8 @@ describe KubernetesService, models: true, caching: true do
end
describe '#test' do
+ let(:discovery_url) { 'https://kubernetes.example.com/api/v1' }
+
before do
stub_kubeclient_discover
end
@@ -142,7 +162,8 @@ describe KubernetesService, models: true, caching: true do
let(:discovery_url) { 'https://kubernetes.example.com/prefix/api/v1' }
it 'tests with the prefix' do
- service.api_url = 'https://kubernetes.example.com/prefix/'
+ service.api_url = 'https://kubernetes.example.com/prefix'
+ stub_kubeclient_discover
expect(service.test[:success]).to be_truthy
expect(WebMock).to have_requested(:get, discovery_url).once
@@ -170,9 +191,9 @@ describe KubernetesService, models: true, caching: true do
end
context 'failure' do
- let(:discovery_response) { { status: 404 } }
-
it 'fails to read the discovery endpoint' do
+ WebMock.stub_request(:get, service.api_url + '/api/v1').to_return(status: 404)
+
expect(service.test[:success]).to be_falsy
expect(WebMock).to have_requested(:get, discovery_url).once
end
@@ -188,7 +209,9 @@ describe KubernetesService, models: true, caching: true do
end
context 'namespace is provided' do
- before { subject.namespace = 'my-project' }
+ before do
+ subject.namespace = 'my-project'
+ end
it 'sets the variables' do
expect(subject.predefined_variables).to include(
@@ -258,27 +281,36 @@ describe KubernetesService, models: true, caching: true do
end
describe '#calculate_reactive_cache' do
- before { stub_kubeclient_pods }
subject { service.calculate_reactive_cache }
context 'when service is inactive' do
- before { service.active = false }
+ before do
+ service.active = false
+ end
it { is_expected.to be_nil }
end
context 'when kubernetes responds with valid pods' do
+ before do
+ stub_kubeclient_pods
+ end
+
it { is_expected.to eq(pods: [kube_pod]) }
end
- context 'when kubernetes responds with 500' do
- let(:pods_response) { { status: 500 } }
+ context 'when kubernetes responds with 500s' do
+ before do
+ stub_kubeclient_pods(status: 500)
+ end
it { expect { subject }.to raise_error(KubeException) }
end
- context 'when kubernetes responds with 404' do
- let(:pods_response) { { status: 404 } }
+ context 'when kubernetes responds with 404s' do
+ before do
+ stub_kubeclient_pods(status: 404)
+ end
it { is_expected.to eq(pods: []) }
end
diff --git a/spec/models/project_services/microsoft_teams_service_spec.rb b/spec/models/project_services/microsoft_teams_service_spec.rb
index facc034f69c..bd50a2d1470 100644
--- a/spec/models/project_services/microsoft_teams_service_spec.rb
+++ b/spec/models/project_services/microsoft_teams_service_spec.rb
@@ -11,14 +11,18 @@ describe MicrosoftTeamsService, models: true do
describe 'Validations' do
context 'when service is active' do
- before { subject.active = true }
+ before do
+ subject.active = true
+ end
it { is_expected.to validate_presence_of(:webhook) }
it_behaves_like 'issue tracker service URL attribute', :webhook
end
context 'when service is inactive' do
- before { subject.active = false }
+ before do
+ subject.active = false
+ end
it { is_expected.not_to validate_presence_of(:webhook) }
end
diff --git a/spec/models/project_services/pivotaltracker_service_spec.rb b/spec/models/project_services/pivotaltracker_service_spec.rb
index a76e909d04d..f4c1a9c94b6 100644
--- a/spec/models/project_services/pivotaltracker_service_spec.rb
+++ b/spec/models/project_services/pivotaltracker_service_spec.rb
@@ -8,13 +8,17 @@ describe PivotaltrackerService, models: true do
describe 'Validations' do
context 'when service is active' do
- before { subject.active = true }
+ before do
+ subject.active = true
+ end
it { is_expected.to validate_presence_of(:token) }
end
context 'when service is inactive' do
- before { subject.active = false }
+ before do
+ subject.active = false
+ end
it { is_expected.not_to validate_presence_of(:token) }
end
diff --git a/spec/models/project_services/prometheus_service_spec.rb b/spec/models/project_services/prometheus_service_spec.rb
index 1f9d3c07b51..71b53732164 100644
--- a/spec/models/project_services/prometheus_service_spec.rb
+++ b/spec/models/project_services/prometheus_service_spec.rb
@@ -14,13 +14,17 @@ describe PrometheusService, models: true, caching: true do
describe 'Validations' do
context 'when service is active' do
- before { subject.active = true }
+ before do
+ subject.active = true
+ end
it { is_expected.to validate_presence_of(:api_url) }
end
context 'when service is inactive' do
- before { subject.active = false }
+ before do
+ subject.active = false
+ end
it { is_expected.not_to validate_presence_of(:api_url) }
end
diff --git a/spec/models/project_services/pushover_service_spec.rb b/spec/models/project_services/pushover_service_spec.rb
index a7e7594a7d5..9171d9604ee 100644
--- a/spec/models/project_services/pushover_service_spec.rb
+++ b/spec/models/project_services/pushover_service_spec.rb
@@ -8,7 +8,9 @@ describe PushoverService, models: true do
describe 'Validations' do
context 'when service is active' do
- before { subject.active = true }
+ before do
+ subject.active = true
+ end
it { is_expected.to validate_presence_of(:api_key) }
it { is_expected.to validate_presence_of(:user_key) }
@@ -16,7 +18,9 @@ describe PushoverService, models: true do
end
context 'when service is inactive' do
- before { subject.active = false }
+ before do
+ subject.active = false
+ end
it { is_expected.not_to validate_presence_of(:api_key) }
it { is_expected.not_to validate_presence_of(:user_key) }
diff --git a/spec/models/project_services/redmine_service_spec.rb b/spec/models/project_services/redmine_service_spec.rb
index 0a7b237a051..6631d9040b1 100644
--- a/spec/models/project_services/redmine_service_spec.rb
+++ b/spec/models/project_services/redmine_service_spec.rb
@@ -8,7 +8,9 @@ describe RedmineService, models: true do
describe 'Validations' do
context 'when service is active' do
- before { subject.active = true }
+ before do
+ subject.active = true
+ end
it { is_expected.to validate_presence_of(:project_url) }
it { is_expected.to validate_presence_of(:issues_url) }
@@ -19,7 +21,9 @@ describe RedmineService, models: true do
end
context 'when service is inactive' do
- before { subject.active = false }
+ before do
+ subject.active = false
+ end
it { is_expected.not_to validate_presence_of(:project_url) }
it { is_expected.not_to validate_presence_of(:issues_url) }
diff --git a/spec/models/project_services/teamcity_service_spec.rb b/spec/models/project_services/teamcity_service_spec.rb
index 77b18e1c7d0..7349eb4149a 100644
--- a/spec/models/project_services/teamcity_service_spec.rb
+++ b/spec/models/project_services/teamcity_service_spec.rb
@@ -24,7 +24,9 @@ describe TeamcityService, models: true, caching: true do
describe 'Validations' do
context 'when service is active' do
- before { subject.active = true }
+ before do
+ subject.active = true
+ end
it { is_expected.to validate_presence_of(:build_type) }
it { is_expected.to validate_presence_of(:teamcity_url) }
@@ -60,7 +62,9 @@ describe TeamcityService, models: true, caching: true do
end
context 'when service is inactive' do
- before { subject.active = false }
+ before do
+ subject.active = false
+ end
it { is_expected.not_to validate_presence_of(:build_type) }
it { is_expected.not_to validate_presence_of(:teamcity_url) }
diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb
index 454eeb58ecd..63333b7af1f 100644
--- a/spec/models/project_spec.rb
+++ b/spec/models/project_spec.rb
@@ -1005,13 +1005,17 @@ describe Project, models: true do
subject { project.shared_runners_enabled }
context 'are enabled' do
- before { stub_application_setting(shared_runners_enabled: true) }
+ before do
+ stub_application_setting(shared_runners_enabled: true)
+ end
it { is_expected.to be_truthy }
end
context 'are disabled' do
- before { stub_application_setting(shared_runners_enabled: false) }
+ before do
+ stub_application_setting(shared_runners_enabled: false)
+ end
it { is_expected.to be_falsey }
end
@@ -1107,7 +1111,9 @@ describe Project, models: true do
subject { project.pages_deployed? }
context 'if public folder does exist' do
- before { allow(Dir).to receive(:exist?).with(project.public_pages_path).and_return(true) }
+ before do
+ allow(Dir).to receive(:exist?).with(project.public_pages_path).and_return(true)
+ end
it { is_expected.to be_truthy }
end
@@ -1365,7 +1371,9 @@ describe Project, models: true do
subject { project.container_registry_url }
- before { stub_container_registry_config(**registry_settings) }
+ before do
+ stub_container_registry_config(**registry_settings)
+ end
context 'for enabled registry' do
let(:registry_settings) do
@@ -1389,7 +1397,9 @@ describe Project, models: true do
let(:project) { create(:empty_project) }
context 'when container registry is enabled' do
- before { stub_container_registry_config(enabled: true) }
+ before do
+ stub_container_registry_config(enabled: true)
+ end
context 'when tags are present for multi-level registries' do
before do
@@ -1427,7 +1437,9 @@ describe Project, models: true do
end
context 'when container registry is disabled' do
- before { stub_container_registry_config(enabled: false) }
+ before do
+ stub_container_registry_config(enabled: false)
+ end
it 'should not have image tags' do
expect(project).not_to have_container_registry_tags
@@ -1945,7 +1957,9 @@ describe Project, models: true do
describe '#parent_changed?' do
let(:project) { create(:empty_project) }
- before { project.namespace_id = 7 }
+ before do
+ project.namespace_id = 7
+ end
it { expect(project.parent_changed?).to be_truthy }
end
diff --git a/spec/models/project_team_spec.rb b/spec/models/project_team_spec.rb
index 497e3cdf415..ea3cd5fe10a 100644
--- a/spec/models/project_team_spec.rb
+++ b/spec/models/project_team_spec.rb
@@ -240,7 +240,9 @@ describe ProjectTeam, models: true do
it { expect(project.team.max_member_access(requester.id)).to eq(Gitlab::Access::NO_ACCESS) }
context 'but share_with_group_lock is true' do
- before { project.namespace.update(share_with_group_lock: true) }
+ before do
+ project.namespace.update(share_with_group_lock: true)
+ end
it { expect(project.team.max_member_access(master.id)).to eq(Gitlab::Access::NO_ACCESS) }
it { expect(project.team.max_member_access(reporter.id)).to eq(Gitlab::Access::NO_ACCESS) }
diff --git a/spec/models/project_wiki_spec.rb b/spec/models/project_wiki_spec.rb
index 224067f58dd..3f5f4eea4a1 100644
--- a/spec/models/project_wiki_spec.rb
+++ b/spec/models/project_wiki_spec.rb
@@ -8,7 +8,10 @@ describe ProjectWiki, models: true do
let(:project_wiki) { ProjectWiki.new(project, user) }
subject { project_wiki }
- before { project_wiki.wiki }
+
+ before do
+ project_wiki.wiki
+ end
describe "#path_with_namespace" do
it "returns the project path with namespace with the .wiki extension" do
diff --git a/spec/models/route_spec.rb b/spec/models/route_spec.rb
index c1fe1b06c52..1754253e0f2 100644
--- a/spec/models/route_spec.rb
+++ b/spec/models/route_spec.rb
@@ -9,7 +9,10 @@ describe Route, models: true do
end
describe 'validations' do
- before { route }
+ before do
+ expect(route).to be_persisted
+ end
+
it { is_expected.to validate_presence_of(:source) }
it { is_expected.to validate_presence_of(:path) }
it { is_expected.to validate_uniqueness_of(:path) }
@@ -59,7 +62,9 @@ describe Route, models: true do
context 'path update' do
context 'when route name is set' do
- before { route.update_attributes(path: 'bar') }
+ before do
+ route.update_attributes(path: 'bar')
+ end
it 'updates children routes with new path' do
expect(described_class.exists?(path: 'bar')).to be_truthy
diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb
index d5bd9946ab6..f6b7120e2c9 100644
--- a/spec/models/user_spec.rb
+++ b/spec/models/user_spec.rb
@@ -451,6 +451,40 @@ describe User, models: true do
end
end
+ describe '#ensure_user_rights_and_limits' do
+ describe 'with external user' do
+ let(:user) { create(:user, external: true) }
+
+ it 'receives callback when external changes' do
+ expect(user).to receive(:ensure_user_rights_and_limits)
+
+ user.update_attributes(external: false)
+ end
+
+ it 'ensures correct rights and limits for user' do
+ stub_config_setting(default_can_create_group: true)
+
+ expect { user.update_attributes(external: false) }.to change { user.can_create_group }.to(true)
+ .and change { user.projects_limit }.to(current_application_settings.default_projects_limit)
+ end
+ end
+
+ describe 'without external user' do
+ let(:user) { create(:user, external: false) }
+
+ it 'receives callback when external changes' do
+ expect(user).to receive(:ensure_user_rights_and_limits)
+
+ user.update_attributes(external: true)
+ end
+
+ it 'ensures correct rights and limits for user' do
+ expect { user.update_attributes(external: true) }.to change { user.can_create_group }.to(false)
+ .and change { user.projects_limit }.to(0)
+ end
+ end
+ end
+
describe 'rss token' do
it 'ensures an rss token on read' do
user = create(:user, rss_token: nil)
@@ -1584,7 +1618,9 @@ describe User, models: true do
end
context 'user is member of the top group' do
- before { group.add_owner(user) }
+ before do
+ group.add_owner(user)
+ end
if Group.supports_nested_groups?
it 'returns all groups' do
@@ -1602,7 +1638,9 @@ describe User, models: true do
end
context 'user is member of the first child (internal node), branch 1', :nested_groups do
- before { nested_group_1.add_owner(user) }
+ before do
+ nested_group_1.add_owner(user)
+ end
it 'returns the groups in the hierarchy' do
is_expected.to match_array [
@@ -1613,7 +1651,9 @@ describe User, models: true do
end
context 'user is member of the first child (internal node), branch 2', :nested_groups do
- before { nested_group_2.add_owner(user) }
+ before do
+ nested_group_2.add_owner(user)
+ end
it 'returns the groups in the hierarchy' do
is_expected.to match_array [
@@ -1624,7 +1664,9 @@ describe User, models: true do
end
context 'user is member of the last child (leaf node)', :nested_groups do
- before { nested_group_1_1.add_owner(user) }
+ before do
+ nested_group_1_1.add_owner(user)
+ end
it 'returns the groups in the hierarchy' do
is_expected.to match_array [
diff --git a/spec/policies/ci/build_policy_spec.rb b/spec/policies/ci/build_policy_spec.rb
index 3f4ce222b60..48a139d4b83 100644
--- a/spec/policies/ci/build_policy_spec.rb
+++ b/spec/policies/ci/build_policy_spec.rb
@@ -10,7 +10,9 @@ describe Ci::BuildPolicy, :models do
end
shared_context 'public pipelines disabled' do
- before { project.update_attribute(:public_builds, false) }
+ before do
+ project.update_attribute(:public_builds, false)
+ end
end
describe '#rules' do
@@ -54,7 +56,9 @@ describe Ci::BuildPolicy, :models do
let(:project) { create(:empty_project, :public) }
context 'team member is a guest' do
- before { project.team << [user, :guest] }
+ before do
+ project.team << [user, :guest]
+ end
context 'when public builds are enabled' do
it 'includes ability to read build' do
@@ -72,7 +76,9 @@ describe Ci::BuildPolicy, :models do
end
context 'team member is a reporter' do
- before { project.team << [user, :reporter] }
+ before do
+ project.team << [user, :reporter]
+ end
context 'when public builds are enabled' do
it 'includes ability to read build' do
diff --git a/spec/policies/project_policy_spec.rb b/spec/policies/project_policy_spec.rb
index 0d3af1f4499..848fd547e10 100644
--- a/spec/policies/project_policy_spec.rb
+++ b/spec/policies/project_policy_spec.rb
@@ -139,6 +139,18 @@ describe ProjectPolicy, models: true do
is_expected.not_to include(:read_build, :read_pipeline)
end
end
+
+ context 'when builds are disabled' do
+ before do
+ project.project_feature.update(
+ builds_access_level: ProjectFeature::DISABLED)
+ end
+
+ it do
+ is_expected.not_to include(:read_build)
+ is_expected.to include(:read_pipeline)
+ end
+ end
end
context 'reporter' do
diff --git a/spec/policies/project_snippet_policy_spec.rb b/spec/policies/project_snippet_policy_spec.rb
index ddbed5f781e..d2b2528c57a 100644
--- a/spec/policies/project_snippet_policy_spec.rb
+++ b/spec/policies/project_snippet_policy_spec.rb
@@ -78,7 +78,9 @@ describe ProjectSnippetPolicy, models: true do
context 'project team member external user' do
subject { abilities(external_user, :internal) }
- before { project.team << [external_user, :developer] }
+ before do
+ project.team << [external_user, :developer]
+ end
it do
is_expected.to include(:read_project_snippet)
@@ -120,7 +122,9 @@ describe ProjectSnippetPolicy, models: true do
context 'project team member normal user' do
subject { abilities(regular_user, :private) }
- before { project.team << [regular_user, :developer] }
+ before do
+ project.team << [regular_user, :developer]
+ end
it do
is_expected.to include(:read_project_snippet)
@@ -131,7 +135,9 @@ describe ProjectSnippetPolicy, models: true do
context 'project team member external user' do
subject { abilities(external_user, :private) }
- before { project.team << [external_user, :developer] }
+ before do
+ project.team << [external_user, :developer]
+ end
it do
is_expected.to include(:read_project_snippet)
diff --git a/spec/requests/api/award_emoji_spec.rb b/spec/requests/api/award_emoji_spec.rb
index bbdef0aeb1b..6d822b5cb4f 100644
--- a/spec/requests/api/award_emoji_spec.rb
+++ b/spec/requests/api/award_emoji_spec.rb
@@ -9,7 +9,9 @@ describe API::AwardEmoji do
let!(:downvote) { create(:award_emoji, :downvote, awardable: merge_request, user: user) }
let!(:note) { create(:note, project: project, noteable: issue) }
- before { project.team << [user, :master] }
+ before do
+ project.team << [user, :master]
+ end
describe "GET /projects/:id/awardable/:awardable_id/award_emoji" do
context 'on an issue' do
diff --git a/spec/requests/api/commit_statuses_spec.rb b/spec/requests/api/commit_statuses_spec.rb
index 6b637a03b6f..b8ca73c321c 100644
--- a/spec/requests/api/commit_statuses_spec.rb
+++ b/spec/requests/api/commit_statuses_spec.rb
@@ -34,7 +34,9 @@ describe API::CommitStatuses do
let!(:status6) { create_status(master, status: 'success') }
context 'latest commit statuses' do
- before { get api(get_url, reporter) }
+ before do
+ get api(get_url, reporter)
+ end
it 'returns latest commit statuses' do
expect(response).to have_http_status(200)
@@ -48,7 +50,9 @@ describe API::CommitStatuses do
end
context 'all commit statuses' do
- before { get api(get_url, reporter), all: 1 }
+ before do
+ get api(get_url, reporter), all: 1
+ end
it 'returns all commit statuses' do
expect(response).to have_http_status(200)
@@ -61,7 +65,9 @@ describe API::CommitStatuses do
end
context 'latest commit statuses for specific ref' do
- before { get api(get_url, reporter), ref: 'develop' }
+ before do
+ get api(get_url, reporter), ref: 'develop'
+ end
it 'returns latest commit statuses for specific ref' do
expect(response).to have_http_status(200)
@@ -72,7 +78,9 @@ describe API::CommitStatuses do
end
context 'latest commit statues for specific name' do
- before { get api(get_url, reporter), name: 'coverage' }
+ before do
+ get api(get_url, reporter), name: 'coverage'
+ end
it 'return latest commit statuses for specific name' do
expect(response).to have_http_status(200)
@@ -85,7 +93,9 @@ describe API::CommitStatuses do
end
context 'ci commit does not exist' do
- before { get api(get_url, reporter) }
+ before do
+ get api(get_url, reporter)
+ end
it 'returns empty array' do
expect(response.status).to eq 200
@@ -95,7 +105,9 @@ describe API::CommitStatuses do
end
context "guest user" do
- before { get api(get_url, guest) }
+ before do
+ get api(get_url, guest)
+ end
it "does not return project commits" do
expect(response).to have_http_status(403)
@@ -103,7 +115,9 @@ describe API::CommitStatuses do
end
context "unauthorized user" do
- before { get api(get_url) }
+ before do
+ get api(get_url)
+ end
it "does not return project commits" do
expect(response).to have_http_status(401)
@@ -209,7 +223,9 @@ describe API::CommitStatuses do
end
context 'when status is invalid' do
- before { post api(post_url, developer), state: 'invalid' }
+ before do
+ post api(post_url, developer), state: 'invalid'
+ end
it 'does not create commit status' do
expect(response).to have_http_status(400)
@@ -217,7 +233,9 @@ describe API::CommitStatuses do
end
context 'when request without a state made' do
- before { post api(post_url, developer) }
+ before do
+ post api(post_url, developer)
+ end
it 'does not create commit status' do
expect(response).to have_http_status(400)
@@ -226,7 +244,10 @@ describe API::CommitStatuses do
context 'when commit SHA is invalid' do
let(:sha) { 'invalid_sha' }
- before { post api(post_url, developer), state: 'running' }
+
+ before do
+ post api(post_url, developer), state: 'running'
+ end
it 'returns not found error' do
expect(response).to have_http_status(404)
@@ -248,7 +269,9 @@ describe API::CommitStatuses do
end
context 'reporter user' do
- before { post api(post_url, reporter), state: 'running' }
+ before do
+ post api(post_url, reporter), state: 'running'
+ end
it 'does not create commit status' do
expect(response).to have_http_status(403)
@@ -256,7 +279,9 @@ describe API::CommitStatuses do
end
context 'guest user' do
- before { post api(post_url, guest), state: 'running' }
+ before do
+ post api(post_url, guest), state: 'running'
+ end
it 'does not create commit status' do
expect(response).to have_http_status(403)
@@ -264,7 +289,9 @@ describe API::CommitStatuses do
end
context 'unauthorized user' do
- before { post api(post_url) }
+ before do
+ post api(post_url)
+ end
it 'does not create commit status' do
expect(response).to have_http_status(401)
diff --git a/spec/requests/api/commits_spec.rb b/spec/requests/api/commits_spec.rb
index b0c265b6453..0dad547735d 100644
--- a/spec/requests/api/commits_spec.rb
+++ b/spec/requests/api/commits_spec.rb
@@ -9,11 +9,15 @@ describe API::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 do
+ project.team << [user, :reporter]
+ end
describe "List repository commits" do
context "authorized user" do
- before { project.team << [user2, :reporter] }
+ before do
+ project.team << [user2, :reporter]
+ end
it "returns project commits" do
commit = project.repository.commit
@@ -514,7 +518,9 @@ describe API::Commits do
describe "Get the diff of a commit" do
context "authorized user" do
- before { project.team << [user2, :reporter] }
+ before do
+ project.team << [user2, :reporter]
+ end
it "returns the diff of the selected commit" do
get api("/projects/#{project.id}/repository/commits/#{project.repository.commit.id}/diff", user)
diff --git a/spec/requests/api/deploy_keys_spec.rb b/spec/requests/api/deploy_keys_spec.rb
index 4d9cd5f3a27..9c260f88f56 100644
--- a/spec/requests/api/deploy_keys_spec.rb
+++ b/spec/requests/api/deploy_keys_spec.rb
@@ -41,7 +41,9 @@ describe API::DeployKeys do
end
describe 'GET /projects/:id/deploy_keys' do
- before { deploy_key }
+ before do
+ deploy_key
+ end
it 'returns array of ssh keys' do
get api("/projects/#{project.id}/deploy_keys", admin)
@@ -161,7 +163,9 @@ describe API::DeployKeys do
end
describe 'DELETE /projects/:id/deploy_keys/:key_id' do
- before { deploy_key }
+ before do
+ deploy_key
+ end
it 'deletes existing key' do
expect do
diff --git a/spec/requests/api/files_spec.rb b/spec/requests/api/files_spec.rb
index d325c6eff9d..c5ec8be4f21 100644
--- a/spec/requests/api/files_spec.rb
+++ b/spec/requests/api/files_spec.rb
@@ -13,7 +13,9 @@ describe API::Files do
let(:author_email) { 'user@example.org' }
let(:author_name) { 'John Doe' }
- before { project.team << [user, :developer] }
+ before do
+ project.team << [user, :developer]
+ end
def route(file_path = nil)
"/projects/#{project.id}/repository/files/#{file_path}"
diff --git a/spec/requests/api/helpers_spec.rb b/spec/requests/api/helpers_spec.rb
index ed392acc607..191c60aba31 100644
--- a/spec/requests/api/helpers_spec.rb
+++ b/spec/requests/api/helpers_spec.rb
@@ -55,40 +55,62 @@ describe API::Helpers do
subject { current_user }
describe "Warden authentication" do
- before { doorkeeper_guard_returns false }
+ before do
+ doorkeeper_guard_returns false
+ end
context "with invalid credentials" do
context "GET request" do
- before { env['REQUEST_METHOD'] = 'GET' }
+ before do
+ env['REQUEST_METHOD'] = 'GET'
+ end
+
it { is_expected.to be_nil }
end
end
context "with valid credentials" do
- before { warden_authenticate_returns user }
+ before do
+ warden_authenticate_returns user
+ end
context "GET request" do
- before { env['REQUEST_METHOD'] = 'GET' }
+ before do
+ env['REQUEST_METHOD'] = 'GET'
+ end
+
it { is_expected.to eq(user) }
end
context "HEAD request" do
- before { env['REQUEST_METHOD'] = 'HEAD' }
+ before do
+ env['REQUEST_METHOD'] = 'HEAD'
+ end
+
it { is_expected.to eq(user) }
end
context "PUT request" do
- before { env['REQUEST_METHOD'] = 'PUT' }
+ before do
+ env['REQUEST_METHOD'] = 'PUT'
+ end
+
it { is_expected.to be_nil }
end
context "POST request" do
- before { env['REQUEST_METHOD'] = 'POST' }
+ before do
+ env['REQUEST_METHOD'] = 'POST'
+ end
+
it { is_expected.to be_nil }
end
context "DELETE request" do
- before { env['REQUEST_METHOD'] = 'DELETE' }
+ before do
+ env['REQUEST_METHOD'] = 'DELETE'
+ end
+
it { is_expected.to be_nil }
end
end
diff --git a/spec/requests/api/internal_spec.rb b/spec/requests/api/internal_spec.rb
index cf232e7ff69..6deaea956e0 100644
--- a/spec/requests/api/internal_spec.rb
+++ b/spec/requests/api/internal_spec.rb
@@ -15,21 +15,43 @@ describe API::Internal do
end
end
- describe "GET /internal/broadcast_message" do
- context "broadcast message exists" do
- let!(:broadcast_message) { create(:broadcast_message, starts_at: Time.now.yesterday, ends_at: Time.now.tomorrow ) }
+ describe 'GET /internal/broadcast_message' do
+ context 'broadcast message exists' do
+ let!(:broadcast_message) { create(:broadcast_message, starts_at: 1.day.ago, ends_at: 1.day.from_now ) }
- it do
- get api("/internal/broadcast_message"), secret_token: secret_token
+ it 'returns one broadcast message' do
+ get api('/internal/broadcast_message'), secret_token: secret_token
expect(response).to have_http_status(200)
- expect(json_response["message"]).to eq(broadcast_message.message)
+ expect(json_response['message']).to eq(broadcast_message.message)
end
end
- context "broadcast message doesn't exist" do
- it do
- get api("/internal/broadcast_message"), secret_token: secret_token
+ context 'broadcast message does not exist' do
+ it 'returns nothing' do
+ get api('/internal/broadcast_message'), secret_token: secret_token
+
+ expect(response).to have_http_status(200)
+ expect(json_response).to be_empty
+ end
+ end
+ end
+
+ describe 'GET /internal/broadcast_messages' do
+ context 'broadcast message(s) exist' do
+ let!(:broadcast_message) { create(:broadcast_message, starts_at: 1.day.ago, ends_at: 1.day.from_now ) }
+
+ it 'returns active broadcast message(s)' do
+ get api('/internal/broadcast_messages'), secret_token: secret_token
+
+ expect(response).to have_http_status(200)
+ expect(json_response[0]['message']).to eq(broadcast_message.message)
+ end
+ end
+
+ context 'broadcast message does not exist' do
+ it 'returns nothing' do
+ get api('/internal/broadcast_messages'), secret_token: secret_token
expect(response).to have_http_status(200)
expect(json_response).to be_empty
@@ -299,8 +321,6 @@ describe API::Internal do
end
context "archived project" do
- let(:personal_project) { create(:empty_project, namespace: user.namespace) }
-
before do
project.team << [user, :developer]
project.archive!
@@ -423,6 +443,42 @@ describe API::Internal do
expect(json_response['status']).to be_truthy
end
end
+
+ 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
+
+ before do
+ project.team << [user, :developer]
+ project.path = 'new_path'
+ project.save!
+ end
+
+ it 'rejects the push' do
+ push_with_path(key, old_path_to_repo)
+
+ expect(response).to have_http_status(200)
+ expect(json_response['status']).to be_falsey
+ expect(json_response['message']).to eq(project_moved_message)
+ end
+
+ it 'rejects the SSH pull' do
+ pull_with_path(key, old_path_to_repo)
+
+ expect(response).to have_http_status(200)
+ expect(json_response['status']).to be_falsey
+ expect(json_response['message']).to eq(project_moved_message)
+ end
+ end
end
describe 'GET /internal/merge_request_urls' do
@@ -565,6 +621,17 @@ describe API::Internal do
)
end
+ def pull_with_path(key, path_to_repo, protocol = 'ssh')
+ post(
+ api("/internal/allowed"),
+ key_id: key.id,
+ project: path_to_repo,
+ action: 'git-upload-pack',
+ secret_token: secret_token,
+ protocol: protocol
+ )
+ end
+
def push(key, project, protocol = 'ssh', env: nil)
post(
api("/internal/allowed"),
@@ -578,6 +645,19 @@ describe API::Internal do
)
end
+ def push_with_path(key, path_to_repo, protocol = 'ssh', env: nil)
+ post(
+ api("/internal/allowed"),
+ changes: 'd14d6c0abdd253381df51a723d58691b2ee1ab08 570e7b2abdd848b95f2f578043fc23bd6f6fd24d refs/heads/master',
+ key_id: key.id,
+ project: path_to_repo,
+ action: 'git-receive-pack',
+ secret_token: secret_token,
+ protocol: protocol,
+ env: env
+ )
+ end
+
def archive(key, project)
post(
api("/internal/allowed"),
diff --git a/spec/requests/api/jobs_spec.rb b/spec/requests/api/jobs_spec.rb
index e5e5872dc1f..8d647eb1c7e 100644
--- a/spec/requests/api/jobs_spec.rb
+++ b/spec/requests/api/jobs_spec.rb
@@ -11,7 +11,7 @@ describe API::Jobs, :api do
ref: project.default_branch)
end
- let!(:build) { create(:ci_build, pipeline: pipeline) }
+ let!(:job) { create(:ci_build, pipeline: pipeline) }
let(:user) { create(:user) }
let(:api_user) { user }
@@ -42,13 +42,13 @@ describe API::Jobs, :api do
end
it 'returns pipeline data' do
- json_build = json_response.first
+ json_job = json_response.first
- expect(json_build['pipeline']).not_to be_empty
- expect(json_build['pipeline']['id']).to eq build.pipeline.id
- expect(json_build['pipeline']['ref']).to eq build.pipeline.ref
- expect(json_build['pipeline']['sha']).to eq build.pipeline.sha
- expect(json_build['pipeline']['status']).to eq build.pipeline.status
+ expect(json_job['pipeline']).not_to be_empty
+ expect(json_job['pipeline']['id']).to eq job.pipeline.id
+ expect(json_job['pipeline']['ref']).to eq job.pipeline.ref
+ expect(json_job['pipeline']['sha']).to eq job.pipeline.sha
+ expect(json_job['pipeline']['status']).to eq job.pipeline.status
end
context 'filter project with one scope element' do
@@ -79,7 +79,7 @@ describe API::Jobs, :api do
context 'unauthorized user' do
let(:api_user) { nil }
- it 'does not return project builds' do
+ it 'does not return project jobs' do
expect(response).to have_http_status(401)
end
end
@@ -105,13 +105,13 @@ describe API::Jobs, :api do
end
it 'returns pipeline data' do
- json_build = json_response.first
+ json_job = json_response.first
- expect(json_build['pipeline']).not_to be_empty
- expect(json_build['pipeline']['id']).to eq build.pipeline.id
- expect(json_build['pipeline']['ref']).to eq build.pipeline.ref
- expect(json_build['pipeline']['sha']).to eq build.pipeline.sha
- expect(json_build['pipeline']['status']).to eq build.pipeline.status
+ expect(json_job['pipeline']).not_to be_empty
+ expect(json_job['pipeline']['id']).to eq job.pipeline.id
+ expect(json_job['pipeline']['ref']).to eq job.pipeline.ref
+ expect(json_job['pipeline']['sha']).to eq job.pipeline.sha
+ expect(json_job['pipeline']['status']).to eq job.pipeline.status
end
context 'filter jobs with one scope element' do
@@ -140,7 +140,7 @@ describe API::Jobs, :api do
context 'jobs in different pipelines' do
let!(:pipeline2) { create(:ci_empty_pipeline, project: project) }
- let!(:build2) { create(:ci_build, pipeline: pipeline2) }
+ let!(:job2) { create(:ci_build, pipeline: pipeline2) }
it 'excludes jobs from other pipelines' do
json_response.each { |job| expect(job['pipeline']['id']).to eq(pipeline.id) }
@@ -159,7 +159,7 @@ describe API::Jobs, :api do
describe 'GET /projects/:id/jobs/:job_id' do
before do
- get api("/projects/#{project.id}/jobs/#{build.id}", api_user)
+ get api("/projects/#{project.id}/jobs/#{job.id}", api_user)
end
context 'authorized user' do
@@ -169,12 +169,13 @@ describe API::Jobs, :api do
end
it 'returns pipeline data' do
- json_build = json_response
- expect(json_build['pipeline']).not_to be_empty
- expect(json_build['pipeline']['id']).to eq build.pipeline.id
- expect(json_build['pipeline']['ref']).to eq build.pipeline.ref
- expect(json_build['pipeline']['sha']).to eq build.pipeline.sha
- expect(json_build['pipeline']['status']).to eq build.pipeline.status
+ json_job = json_response
+
+ expect(json_job['pipeline']).not_to be_empty
+ expect(json_job['pipeline']['id']).to eq job.pipeline.id
+ expect(json_job['pipeline']['ref']).to eq job.pipeline.ref
+ expect(json_job['pipeline']['sha']).to eq job.pipeline.sha
+ expect(json_job['pipeline']['status']).to eq job.pipeline.status
end
end
@@ -189,11 +190,11 @@ describe API::Jobs, :api do
describe 'GET /projects/:id/jobs/:job_id/artifacts' do
before do
- get api("/projects/#{project.id}/jobs/#{build.id}/artifacts", api_user)
+ get api("/projects/#{project.id}/jobs/#{job.id}/artifacts", api_user)
end
context 'job with artifacts' do
- let(:build) { create(:ci_build, :artifacts, pipeline: pipeline) }
+ let(:job) { create(:ci_build, :artifacts, pipeline: pipeline) }
context 'authorized user' do
let(:download_headers) do
@@ -204,7 +205,7 @@ describe API::Jobs, :api do
it 'returns specific job artifacts' do
expect(response).to have_http_status(200)
expect(response.headers).to include(download_headers)
- expect(response.body).to match_file(build.artifacts_file.file.file)
+ expect(response.body).to match_file(job.artifacts_file.file.file)
end
end
@@ -224,14 +225,14 @@ describe API::Jobs, :api do
describe 'GET /projects/:id/artifacts/:ref_name/download?job=name' do
let(:api_user) { reporter }
- let(:build) { create(:ci_build, :artifacts, pipeline: pipeline) }
+ let(:job) { create(:ci_build, :artifacts, pipeline: pipeline) }
before do
- build.success
+ job.success
end
- def get_for_ref(ref = pipeline.ref, job = build.name)
- get api("/projects/#{project.id}/jobs/artifacts/#{ref}/download", api_user), job: job
+ def get_for_ref(ref = pipeline.ref, job_name = job.name)
+ get api("/projects/#{project.id}/jobs/artifacts/#{ref}/download", api_user), job: job_name
end
context 'when not logged in' do
@@ -285,7 +286,7 @@ describe API::Jobs, :api do
let(:download_headers) do
{ 'Content-Transfer-Encoding' => 'binary',
'Content-Disposition' =>
- "attachment; filename=#{build.artifacts_file.filename}" }
+ "attachment; filename=#{job.artifacts_file.filename}" }
end
it { expect(response).to have_http_status(200) }
@@ -321,16 +322,16 @@ describe API::Jobs, :api do
end
describe 'GET /projects/:id/jobs/:job_id/trace' do
- let(:build) { create(:ci_build, :trace, pipeline: pipeline) }
+ let(:job) { create(:ci_build, :trace, pipeline: pipeline) }
before do
- get api("/projects/#{project.id}/jobs/#{build.id}/trace", api_user)
+ get api("/projects/#{project.id}/jobs/#{job.id}/trace", api_user)
end
context 'authorized user' do
it 'returns specific job trace' do
expect(response).to have_http_status(200)
- expect(response.body).to eq(build.trace.raw)
+ expect(response.body).to eq(job.trace.raw)
end
end
@@ -345,7 +346,7 @@ describe API::Jobs, :api do
describe 'POST /projects/:id/jobs/:job_id/cancel' do
before do
- post api("/projects/#{project.id}/jobs/#{build.id}/cancel", api_user)
+ post api("/projects/#{project.id}/jobs/#{job.id}/cancel", api_user)
end
context 'authorized user' do
@@ -375,10 +376,10 @@ describe API::Jobs, :api do
end
describe 'POST /projects/:id/jobs/:job_id/retry' do
- let(:build) { create(:ci_build, :canceled, pipeline: pipeline) }
+ let(:job) { create(:ci_build, :canceled, pipeline: pipeline) }
before do
- post api("/projects/#{project.id}/jobs/#{build.id}/retry", api_user)
+ post api("/projects/#{project.id}/jobs/#{job.id}/retry", api_user)
end
context 'authorized user' do
@@ -410,28 +411,29 @@ describe API::Jobs, :api do
describe 'POST /projects/:id/jobs/:job_id/erase' do
before do
- post api("/projects/#{project.id}/jobs/#{build.id}/erase", user)
+ post api("/projects/#{project.id}/jobs/#{job.id}/erase", user)
end
context 'job is erasable' do
- let(:build) { create(:ci_build, :trace, :artifacts, :success, project: project, pipeline: pipeline) }
+ let(:job) { create(:ci_build, :trace, :artifacts, :success, project: project, pipeline: pipeline) }
it 'erases job content' do
expect(response).to have_http_status(201)
- expect(build).not_to have_trace
- expect(build.artifacts_file.exists?).to be_falsy
- expect(build.artifacts_metadata.exists?).to be_falsy
+ expect(job).not_to have_trace
+ expect(job.artifacts_file.exists?).to be_falsy
+ expect(job.artifacts_metadata.exists?).to be_falsy
end
it 'updates job' do
- build.reload
- expect(build.erased_at).to be_truthy
- expect(build.erased_by).to eq(user)
+ job.reload
+
+ expect(job.erased_at).to be_truthy
+ expect(job.erased_by).to eq(user)
end
end
context 'job is not erasable' do
- let(:build) { create(:ci_build, :trace, project: project, pipeline: pipeline) }
+ let(:job) { create(:ci_build, :trace, project: project, pipeline: pipeline) }
it 'responds with forbidden' do
expect(response).to have_http_status(403)
@@ -439,25 +441,25 @@ describe API::Jobs, :api do
end
end
- describe 'POST /projects/:id/jobs/:build_id/artifacts/keep' do
+ describe 'POST /projects/:id/jobs/:job_id/artifacts/keep' do
before do
- post api("/projects/#{project.id}/jobs/#{build.id}/artifacts/keep", user)
+ post api("/projects/#{project.id}/jobs/#{job.id}/artifacts/keep", user)
end
context 'artifacts did not expire' do
- let(:build) do
+ let(:job) do
create(:ci_build, :trace, :artifacts, :success,
project: project, pipeline: pipeline, artifacts_expire_at: Time.now + 7.days)
end
it 'keeps artifacts' do
expect(response).to have_http_status(200)
- expect(build.reload.artifacts_expire_at).to be_nil
+ expect(job.reload.artifacts_expire_at).to be_nil
end
end
context 'no artifacts' do
- let(:build) { create(:ci_build, project: project, pipeline: pipeline) }
+ let(:job) { create(:ci_build, project: project, pipeline: pipeline) }
it 'responds with not found' do
expect(response).to have_http_status(404)
@@ -467,18 +469,18 @@ describe API::Jobs, :api do
describe 'POST /projects/:id/jobs/:job_id/play' do
before do
- post api("/projects/#{project.id}/jobs/#{build.id}/play", api_user)
+ post api("/projects/#{project.id}/jobs/#{job.id}/play", api_user)
end
context 'on an playable job' do
- let(:build) { create(:ci_build, :manual, project: project, pipeline: pipeline) }
+ let(:job) { create(:ci_build, :manual, project: project, pipeline: pipeline) }
context 'when user is authorized to trigger a manual action' do
it 'plays the job' do
expect(response).to have_http_status(200)
expect(json_response['user']['id']).to eq(user.id)
- expect(json_response['id']).to eq(build.id)
- expect(build.reload).to be_pending
+ expect(json_response['id']).to eq(job.id)
+ expect(job.reload).to be_pending
end
end
@@ -487,7 +489,7 @@ describe API::Jobs, :api do
let(:api_user) { create(:user) }
it 'does not trigger a manual action' do
- expect(build.reload).to be_manual
+ expect(job.reload).to be_manual
expect(response).to have_http_status(404)
end
end
@@ -496,7 +498,7 @@ describe API::Jobs, :api do
let(:api_user) { reporter }
it 'does not trigger a manual action' do
- expect(build.reload).to be_manual
+ expect(job.reload).to be_manual
expect(response).to have_http_status(403)
end
end
diff --git a/spec/requests/api/keys_spec.rb b/spec/requests/api/keys_spec.rb
index ab957c72984..f534332ca6c 100644
--- a/spec/requests/api/keys_spec.rb
+++ b/spec/requests/api/keys_spec.rb
@@ -4,11 +4,9 @@ describe API::Keys do
let(:user) { create(:user) }
let(:admin) { create(:admin) }
let(:key) { create(:key, user: user) }
- let(:email) { create(:email, user: user) }
+ let(:email) { create(:email, user: user) }
describe 'GET /keys/:uid' do
- before { admin }
-
context 'when unauthenticated' do
it 'returns authentication error' do
get api("/keys/#{key.id}")
diff --git a/spec/requests/api/labels_spec.rb b/spec/requests/api/labels_spec.rb
index 0c6b55c1630..f7e2f1908bb 100644
--- a/spec/requests/api/labels_spec.rb
+++ b/spec/requests/api/labels_spec.rb
@@ -339,7 +339,9 @@ describe API::Labels do
end
context "when user is already subscribed to label" do
- before { label1.subscribe(user, project) }
+ before do
+ label1.subscribe(user, project)
+ end
it "returns 304" do
post api("/projects/#{project.id}/labels/#{label1.id}/subscribe", user)
@@ -358,7 +360,9 @@ describe API::Labels do
end
describe "POST /projects/:id/labels/:label_id/unsubscribe" do
- before { label1.subscribe(user, project) }
+ before do
+ label1.subscribe(user, project)
+ end
context "when label_id is a label title" do
it "unsubscribes from the label" do
@@ -381,7 +385,9 @@ describe API::Labels do
end
context "when user is already unsubscribed from label" do
- before { label1.unsubscribe(user, project) }
+ before do
+ label1.unsubscribe(user, project)
+ end
it "returns 304" do
post api("/projects/#{project.id}/labels/#{label1.id}/unsubscribe", user)
diff --git a/spec/requests/api/merge_requests_spec.rb b/spec/requests/api/merge_requests_spec.rb
index 16e5efb2f5b..452ff4aba8e 100644
--- a/spec/requests/api/merge_requests_spec.rb
+++ b/spec/requests/api/merge_requests_spec.rb
@@ -334,14 +334,13 @@ describe API::MergeRequests do
target_branch: 'master',
author: user,
labels: 'label, label2',
- milestone_id: milestone.id,
- remove_source_branch: true
+ milestone_id: milestone.id
expect(response).to have_http_status(201)
expect(json_response['title']).to eq('Test merge_request')
expect(json_response['labels']).to eq(%w(label label2))
expect(json_response['milestone']['id']).to eq(milestone.id)
- expect(json_response['force_remove_source_branch']).to be_truthy
+ expect(json_response['force_remove_source_branch']).to be_falsy
end
it "returns 422 when source_branch equals target_branch" do
@@ -404,6 +403,27 @@ describe API::MergeRequests do
expect(response).to have_http_status(409)
end
end
+
+ context 'accepts remove_source_branch parameter' do
+ let(:params) do
+ { title: 'Test merge_request',
+ source_branch: 'markdown',
+ target_branch: 'master',
+ author: user }
+ end
+
+ it 'sets force_remove_source_branch to false' do
+ post api("/projects/#{project.id}/merge_requests", user), params.merge(remove_source_branch: false)
+
+ expect(json_response['force_remove_source_branch']).to be_falsy
+ end
+
+ it 'sets force_remove_source_branch to true' do
+ post api("/projects/#{project.id}/merge_requests", user), params.merge(remove_source_branch: true)
+
+ expect(json_response['force_remove_source_branch']).to be_truthy
+ end
+ end
end
context 'forked projects' do
diff --git a/spec/requests/api/milestones_spec.rb b/spec/requests/api/milestones_spec.rb
index dd74351a2b1..ab5ea3e8f2c 100644
--- a/spec/requests/api/milestones_spec.rb
+++ b/spec/requests/api/milestones_spec.rb
@@ -5,8 +5,13 @@ describe API::Milestones do
let!(:project) { create(:empty_project, namespace: user.namespace ) }
let!(:closed_milestone) { create(:closed_milestone, project: project, title: 'version1', description: 'closed milestone') }
let!(:milestone) { create(:milestone, project: project, title: 'version2', description: 'open milestone') }
+ let(:label_1) { create(:label, title: 'label_1', project: project, priority: 1) }
+ let(:label_2) { create(:label, title: 'label_2', project: project, priority: 2) }
+ let(:label_3) { create(:label, title: 'label_3', project: project) }
- before { project.team << [user, :developer] }
+ before do
+ project.team << [user, :developer]
+ end
describe 'GET /projects/:id/milestones' do
it 'returns project milestones' do
@@ -226,6 +231,18 @@ describe API::Milestones do
expect(json_response.first['milestone']['title']).to eq(milestone.title)
end
+ it 'returns project issues sorted by label priority' do
+ issue_1 = create(:labeled_issue, project: project, milestone: milestone, labels: [label_3])
+ issue_2 = create(:labeled_issue, project: project, milestone: milestone, labels: [label_1])
+ issue_3 = create(:labeled_issue, project: project, milestone: milestone, labels: [label_2])
+
+ get api("/projects/#{project.id}/milestones/#{milestone.id}/issues", user)
+
+ expect(json_response.first['id']).to eq(issue_2.id)
+ expect(json_response.second['id']).to eq(issue_3.id)
+ expect(json_response.third['id']).to eq(issue_1.id)
+ end
+
it 'matches V4 response schema for a list of issues' do
get api("/projects/#{project.id}/milestones/#{milestone.id}/issues", user)
@@ -242,8 +259,8 @@ describe API::Milestones do
describe 'confidential issues' do
let(:public_project) { create(:empty_project, :public) }
let(:milestone) { create(:milestone, project: public_project) }
- let(:issue) { create(:issue, project: public_project, position: 2) }
- let(:confidential_issue) { create(:issue, confidential: true, project: public_project, position: 1) }
+ let(:issue) { create(:issue, project: public_project) }
+ let(:confidential_issue) { create(:issue, confidential: true, project: public_project) }
before do
public_project.team << [user, :developer]
@@ -283,7 +300,10 @@ describe API::Milestones do
expect(json_response.map { |issue| issue['id'] }).to include(issue.id)
end
- it 'returns issues ordered by position asc' do
+ it 'returns issues ordered by label priority' do
+ issue.labels << label_2
+ confidential_issue.labels << label_1
+
get api("/projects/#{public_project.id}/milestones/#{milestone.id}/issues", user)
expect(response).to have_http_status(200)
@@ -297,8 +317,8 @@ describe API::Milestones do
end
describe 'GET /projects/:id/milestones/:milestone_id/merge_requests' do
- let(:merge_request) { create(:merge_request, source_project: project, position: 2) }
- let(:another_merge_request) { create(:merge_request, :simple, source_project: project, position: 1) }
+ let(:merge_request) { create(:merge_request, source_project: project) }
+ let(:another_merge_request) { create(:merge_request, :simple, source_project: project) }
before do
milestone.merge_requests << merge_request
@@ -316,6 +336,18 @@ describe API::Milestones do
expect(json_response.first['milestone']['title']).to eq(milestone.title)
end
+ it 'returns project merge_requests sorted by label priority' do
+ merge_request_1 = create(:labeled_merge_request, source_branch: 'branch_1', source_project: project, milestone: milestone, labels: [label_2])
+ merge_request_2 = create(:labeled_merge_request, source_branch: 'branch_2', source_project: project, milestone: milestone, labels: [label_1])
+ merge_request_3 = create(:labeled_merge_request, source_branch: 'branch_3', source_project: project, milestone: milestone, labels: [label_3])
+
+ get api("/projects/#{project.id}/milestones/#{milestone.id}/merge_requests", user)
+
+ expect(json_response.first['id']).to eq(merge_request_2.id)
+ expect(json_response.second['id']).to eq(merge_request_1.id)
+ expect(json_response.third['id']).to eq(merge_request_3.id)
+ end
+
it 'returns a 404 error if milestone id not found' do
get api("/projects/#{project.id}/milestones/1234/merge_requests", user)
@@ -337,6 +369,8 @@ describe API::Milestones do
it 'returns merge_requests ordered by position asc' do
milestone.merge_requests << another_merge_request
+ another_merge_request.labels << label_1
+ merge_request.labels << label_2
get api("/projects/#{project.id}/milestones/#{milestone.id}/merge_requests", user)
diff --git a/spec/requests/api/notes_spec.rb b/spec/requests/api/notes_spec.rb
index 6afcd237c3c..03f2b5950ee 100644
--- a/spec/requests/api/notes_spec.rb
+++ b/spec/requests/api/notes_spec.rb
@@ -28,7 +28,9 @@ describe API::Notes do
system: true
end
- before { project.team << [user, :reporter] }
+ before do
+ project.team << [user, :reporter]
+ end
describe "GET /projects/:id/noteable/:noteable_id/notes" do
context "when noteable is an Issue" do
@@ -58,7 +60,9 @@ describe API::Notes do
end
context "and issue is confidential" do
- before { ext_issue.update_attributes(confidential: true) }
+ before do
+ ext_issue.update_attributes(confidential: true)
+ end
it "returns 404" do
get api("/projects/#{ext_proj.id}/issues/#{ext_issue.iid}/notes", user)
@@ -150,7 +154,9 @@ describe API::Notes do
end
context "when issue is confidential" do
- before { issue.update_attributes(confidential: true) }
+ before do
+ issue.update_attributes(confidential: true)
+ end
it "returns 404" do
get api("/projects/#{project.id}/issues/#{issue.iid}/notes/#{issue_note.id}", private_user)
diff --git a/spec/requests/api/pipelines_spec.rb b/spec/requests/api/pipelines_spec.rb
index 9e6957e9922..258085e503f 100644
--- a/spec/requests/api/pipelines_spec.rb
+++ b/spec/requests/api/pipelines_spec.rb
@@ -10,7 +10,9 @@ describe API::Pipelines do
ref: project.default_branch, user: user)
end
- before { project.team << [user, :master] }
+ before do
+ project.team << [user, :master]
+ end
describe 'GET /projects/:id/pipelines ' do
context 'authorized user' do
@@ -285,7 +287,9 @@ describe API::Pipelines do
describe 'POST /projects/:id/pipeline ' do
context 'authorized user' do
context 'with gitlab-ci.yml' do
- before { stub_ci_pipeline_to_return_yaml_file }
+ before do
+ stub_ci_pipeline_to_return_yaml_file
+ end
it 'creates and returns a new pipeline' do
expect do
@@ -419,7 +423,9 @@ describe API::Pipelines do
context 'user without proper access rights' do
let!(:reporter) { create(:user) }
- before { project.team << [reporter, :reporter] }
+ before do
+ project.team << [reporter, :reporter]
+ end
it 'rejects the action' do
post api("/projects/#{project.id}/pipelines/#{pipeline.id}/cancel", reporter)
diff --git a/spec/requests/api/projects_spec.rb b/spec/requests/api/projects_spec.rb
index 3e831373514..d92262a4c99 100644
--- a/spec/requests/api/projects_spec.rb
+++ b/spec/requests/api/projects_spec.rb
@@ -476,8 +476,9 @@ describe API::Projects do
end
describe 'POST /projects/user/:id' do
- before { project }
- before { admin }
+ before do
+ expect(project).to be_persisted
+ end
it 'creates new project without path but with name and return 201' do
expect { post api("/projects/user/#{user.id}", admin), name: 'Foo Project' }.to change {Project.count}.by(1)
@@ -581,7 +582,9 @@ describe API::Projects do
end
describe "POST /projects/:id/uploads" do
- before { project }
+ before do
+ project
+ end
it "uploads the file and returns its info" do
post api("/projects/#{project.id}/uploads", user), file: fixture_file_upload(Rails.root + "spec/fixtures/dk.png", "image/png")
@@ -729,7 +732,9 @@ describe API::Projects do
describe 'permissions' do
context 'all projects' do
- before { project.team << [user, :master] }
+ before do
+ project.team << [user, :master]
+ end
it 'contains permission information' do
get api("/projects", user)
@@ -756,7 +761,9 @@ describe API::Projects do
context 'group project' do
let(:project2) { create(:empty_project, group: create(:group)) }
- before { project2.group.add_owner(user) }
+ before do
+ project2.group.add_owner(user)
+ end
it 'sets the owner and return 200' do
get api("/projects/#{project2.id}", user)
@@ -822,7 +829,9 @@ describe API::Projects do
end
describe 'GET /projects/:id/snippets' do
- before { snippet }
+ before do
+ snippet
+ end
it 'returns an array of project snippets' do
get api("/projects/#{project.id}/snippets", user)
@@ -879,7 +888,9 @@ describe API::Projects do
end
describe 'DELETE /projects/:id/snippets/:snippet_id' do
- before { snippet }
+ before do
+ snippet
+ end
it 'deletes existing project snippet' do
expect do
@@ -1074,14 +1085,16 @@ describe API::Projects do
end
describe 'PUT /projects/:id' do
- before { project }
- before { user }
- before { user3 }
- before { user4 }
- before { project3 }
- before { project4 }
- before { project_member2 }
- before { project_member }
+ before do
+ expect(project).to be_persisted
+ expect(user).to be_persisted
+ expect(user3).to be_persisted
+ expect(user4).to be_persisted
+ expect(project3).to be_persisted
+ expect(project4).to be_persisted
+ expect(project_member2).to be_persisted
+ expect(project_member).to be_persisted
+ end
it 'returns 400 when nothing sent' do
project_param = {}
diff --git a/spec/requests/api/runner_spec.rb b/spec/requests/api/runner_spec.rb
index 9556c99dea1..d554c242916 100644
--- a/spec/requests/api/runner_spec.rb
+++ b/spec/requests/api/runner_spec.rb
@@ -190,17 +190,23 @@ describe API::Runner do
pipeline: pipeline, name: 'spinach', stage: 'test', stage_idx: 0, commands: "ls\ndate")
end
- before { project.runners << runner }
+ before do
+ project.runners << runner
+ end
describe 'POST /api/v4/jobs/request' do
let!(:last_update) {}
let!(:new_update) { }
let(:user_agent) { 'gitlab-runner 9.0.0 (9-0-stable; go1.7.4; linux/amd64)' }
- before { stub_container_registry_config(enabled: false) }
+ before do
+ stub_container_registry_config(enabled: false)
+ end
shared_examples 'no jobs available' do
- before { request_job }
+ before do
+ request_job
+ end
context 'when runner sends version in User-Agent' do
context 'for stable version' do
@@ -277,7 +283,9 @@ describe API::Runner do
end
context 'when jobs are finished' do
- before { job.success }
+ before do
+ job.success
+ end
it_behaves_like 'no jobs available'
end
@@ -356,8 +364,11 @@ describe API::Runner do
expect(json_response['token']).to eq(job.token)
expect(json_response['job_info']).to eq(expected_job_info)
expect(json_response['git_info']).to eq(expected_git_info)
- expect(json_response['image']).to eq({ 'name' => 'ruby:2.1' })
- expect(json_response['services']).to eq([{ 'name' => 'postgres' }])
+ expect(json_response['image']).to eq({ 'name' => 'ruby:2.1', 'entrypoint' => '/bin/sh' })
+ expect(json_response['services']).to eq([{ 'name' => 'postgres', 'entrypoint' => nil,
+ 'alias' => nil, 'command' => nil },
+ { 'name' => 'docker:dind', 'entrypoint' => '/bin/sh',
+ 'alias' => 'docker', 'command' => 'sleep 30' }])
expect(json_response['steps']).to eq(expected_steps)
expect(json_response['artifacts']).to eq(expected_artifacts)
expect(json_response['cache']).to eq(expected_cache)
@@ -505,10 +516,14 @@ describe API::Runner do
end
context 'when job has no tags' do
- before { job.update(tags: []) }
+ before do
+ job.update(tags: [])
+ end
context 'when runner is allowed to pick untagged jobs' do
- before { runner.update_column(:run_untagged, true) }
+ before do
+ runner.update_column(:run_untagged, true)
+ end
it 'picks job' do
request_job
@@ -518,7 +533,9 @@ describe API::Runner do
end
context 'when runner is not allowed to pick untagged jobs' do
- before { runner.update_column(:run_untagged, false) }
+ before do
+ runner.update_column(:run_untagged, false)
+ end
it_behaves_like 'no jobs available'
end
@@ -558,7 +575,9 @@ describe API::Runner do
end
context 'when registry is enabled' do
- before { stub_container_registry_config(enabled: true, host_port: registry_url) }
+ before do
+ stub_container_registry_config(enabled: true, host_port: registry_url)
+ end
it 'sends registry credentials key' do
request_job
@@ -569,7 +588,9 @@ describe API::Runner do
end
context 'when registry is disabled' do
- before { stub_container_registry_config(enabled: false, host_port: registry_url) }
+ before do
+ stub_container_registry_config(enabled: false, host_port: registry_url)
+ end
it 'does not send registry credentials' do
request_job
@@ -591,7 +612,9 @@ describe API::Runner do
describe 'PUT /api/v4/jobs/:id' do
let(:job) { create(:ci_build, :pending, :trace, pipeline: pipeline, runner_id: runner.id) }
- before { job.run! }
+ before do
+ job.run!
+ end
context 'when status is given' do
it 'mark job as succeeded' do
@@ -646,7 +669,9 @@ describe API::Runner do
let(:headers_with_range) { headers.merge({ 'Content-Range' => '11-20' }) }
let(:update_interval) { 10.seconds.to_i }
- before { initial_patch_the_trace }
+ before do
+ initial_patch_the_trace
+ end
context 'when request is valid' do
it 'gets correct response' do
@@ -788,7 +813,9 @@ describe API::Runner do
let(:file_upload) { fixture_file_upload(Rails.root + 'spec/fixtures/banana_sample.gif', 'image/gif') }
let(:file_upload2) { fixture_file_upload(Rails.root + 'spec/fixtures/dk.png', 'image/gif') }
- before { job.run! }
+ before do
+ job.run!
+ end
describe 'POST /api/v4/jobs/:id/artifacts/authorize' do
context 'when using token as parameter' do
@@ -894,13 +921,17 @@ describe API::Runner do
end
context 'when uses regular file post' do
- before { upload_artifacts(file_upload, headers_with_token, false) }
+ before do
+ upload_artifacts(file_upload, headers_with_token, false)
+ end
it_behaves_like 'successful artifacts upload'
end
context 'when uses accelerated file post' do
- before { upload_artifacts(file_upload, headers_with_token, true) }
+ before do
+ upload_artifacts(file_upload, headers_with_token, true)
+ end
it_behaves_like 'successful artifacts upload'
end
@@ -1054,7 +1085,9 @@ describe API::Runner do
allow(ArtifactUploader).to receive(:artifacts_upload_path).and_return(@tmpdir)
end
- after { FileUtils.remove_entry @tmpdir }
+ after do
+ FileUtils.remove_entry @tmpdir
+ end
it' "fails to post artifacts for outside of tmp path"' do
upload_artifacts(file_upload, headers_with_token)
@@ -1076,7 +1109,9 @@ describe API::Runner do
describe 'GET /api/v4/jobs/:id/artifacts' do
let(:token) { job.token }
- before { download_artifact }
+ before do
+ download_artifact
+ end
context 'when job has artifacts' do
let(:job) { create(:ci_build, :artifacts) }
diff --git a/spec/requests/api/settings_spec.rb b/spec/requests/api/settings_spec.rb
index 2398ae6219c..ede48b1c888 100644
--- a/spec/requests/api/settings_spec.rb
+++ b/spec/requests/api/settings_spec.rb
@@ -40,7 +40,10 @@ describe API::Settings, 'Settings' do
plantuml_url: 'http://plantuml.example.com',
default_snippet_visibility: 'internal',
restricted_visibility_levels: ['public'],
- default_artifacts_expire_in: '2 days'
+ default_artifacts_expire_in: '2 days',
+ help_page_text: 'custom help text',
+ help_page_hide_commercial_content: true,
+ help_page_support_url: 'http://example.com/help'
expect(response).to have_http_status(200)
expect(json_response['default_projects_limit']).to eq(3)
expect(json_response['signin_enabled']).to be_falsey
@@ -53,6 +56,9 @@ describe API::Settings, 'Settings' do
expect(json_response['default_snippet_visibility']).to eq('internal')
expect(json_response['restricted_visibility_levels']).to eq(['public'])
expect(json_response['default_artifacts_expire_in']).to eq('2 days')
+ expect(json_response['help_page_text']).to eq('custom help text')
+ expect(json_response['help_page_hide_commercial_content']).to be_truthy
+ expect(json_response['help_page_support_url']).to eq('http://example.com/help')
end
end
diff --git a/spec/requests/api/system_hooks_spec.rb b/spec/requests/api/system_hooks_spec.rb
index 2eb191d6049..f65b475fe44 100644
--- a/spec/requests/api/system_hooks_spec.rb
+++ b/spec/requests/api/system_hooks_spec.rb
@@ -5,7 +5,9 @@ describe API::SystemHooks do
let(:admin) { create(:admin) }
let!(:hook) { create(:system_hook, url: "http://example.com") }
- before { stub_request(:post, hook.url) }
+ before do
+ stub_request(:post, hook.url)
+ end
describe "GET /hooks" do
context "when no user" do
diff --git a/spec/requests/api/templates_spec.rb b/spec/requests/api/templates_spec.rb
index cb55985e3f5..f8af9295842 100644
--- a/spec/requests/api/templates_spec.rb
+++ b/spec/requests/api/templates_spec.rb
@@ -2,14 +2,18 @@ require 'spec_helper'
describe API::Templates do
context 'the Template Entity' do
- before { get api('/templates/gitignores/Ruby') }
+ before do
+ get api('/templates/gitignores/Ruby')
+ end
it { expect(json_response['name']).to eq('Ruby') }
it { expect(json_response['content']).to include('*.gem') }
end
context 'the TemplateList Entity' do
- before { get api('/templates/gitignores') }
+ before do
+ get api('/templates/gitignores')
+ end
it { expect(json_response.first['name']).not_to be_nil }
it { expect(json_response.first['content']).to be_nil }
@@ -47,7 +51,9 @@ describe API::Templates do
end
context 'the License Template Entity' do
- before { get api('/templates/licenses/mit') }
+ before do
+ get api('/templates/licenses/mit')
+ end
it 'returns a license template' do
expect(json_response['key']).to eq('mit')
diff --git a/spec/requests/api/users_spec.rb b/spec/requests/api/users_spec.rb
index ec51b96c86b..bc869ea1108 100644
--- a/spec/requests/api/users_spec.rb
+++ b/spec/requests/api/users_spec.rb
@@ -160,7 +160,9 @@ describe API::Users do
end
describe "POST /users" do
- before { admin }
+ before do
+ admin
+ end
it "creates user" do
expect do
@@ -349,7 +351,9 @@ describe API::Users do
describe "PUT /users/:id" do
let!(:admin_user) { create(:admin) }
- before { admin }
+ before do
+ admin
+ end
it "updates user with new bio" do
put api("/users/#{user.id}", admin), { bio: 'new test bio' }
@@ -373,6 +377,16 @@ describe API::Users do
expect(user.reload.organization).to eq('GitLab')
end
+ it 'updates user with avatar' do
+ put api("/users/#{user.id}", admin), { avatar: fixture_file_upload(Rails.root + 'spec/fixtures/banana_sample.gif', 'image/gif') }
+
+ user.reload
+
+ expect(user.avatar).to be_present
+ expect(response).to have_http_status(200)
+ expect(json_response['avatar_url']).to include(user.avatar_path)
+ end
+
it 'updates user with his own email' do
put api("/users/#{user.id}", admin), email: user.email
expect(response).to have_http_status(200)
@@ -502,7 +516,9 @@ describe API::Users do
end
describe "POST /users/:id/keys" do
- before { admin }
+ before do
+ admin
+ end
it "does not create invalid ssh key" do
post api("/users/#{user.id}/keys", admin), { title: "invalid key" }
@@ -532,7 +548,9 @@ describe API::Users do
end
describe 'GET /user/:id/keys' do
- before { admin }
+ before do
+ admin
+ end
context 'when unauthenticated' do
it 'returns authentication error' do
@@ -563,7 +581,9 @@ describe API::Users do
end
describe 'DELETE /user/:id/keys/:key_id' do
- before { admin }
+ before do
+ admin
+ end
context 'when unauthenticated' do
it 'returns authentication error' do
@@ -601,7 +621,9 @@ describe API::Users do
end
describe "POST /users/:id/emails" do
- before { admin }
+ before do
+ admin
+ end
it "does not create invalid email" do
post api("/users/#{user.id}/emails", admin), {}
@@ -625,7 +647,9 @@ describe API::Users do
end
describe 'GET /user/:id/emails' do
- before { admin }
+ before do
+ admin
+ end
context 'when unauthenticated' do
it 'returns authentication error' do
@@ -662,7 +686,9 @@ describe API::Users do
end
describe 'DELETE /user/:id/emails/:email_id' do
- before { admin }
+ before do
+ admin
+ end
context 'when unauthenticated' do
it 'returns authentication error' do
@@ -708,7 +734,10 @@ describe API::Users do
describe "DELETE /users/:id" do
let!(:namespace) { user.namespace }
let!(:issue) { create(:issue, author: user) }
- before { admin }
+
+ before do
+ admin
+ end
it "deletes user" do
Sidekiq::Testing.inline! { delete api("/users/#{user.id}", admin) }
@@ -1068,7 +1097,10 @@ describe API::Users do
end
describe 'POST /users/:id/block' do
- before { admin }
+ before do
+ admin
+ end
+
it 'blocks existing user' do
post api("/users/#{user.id}/block", admin)
expect(response).to have_http_status(201)
@@ -1096,7 +1128,10 @@ describe API::Users do
describe 'POST /users/:id/unblock' do
let(:blocked_user) { create(:user, state: 'blocked') }
- before { admin }
+
+ before do
+ admin
+ end
it 'unblocks existing user' do
post api("/users/#{user.id}/unblock", admin)
diff --git a/spec/requests/ci/api/builds_spec.rb b/spec/requests/ci/api/builds_spec.rb
index 286de277ae7..83c675792f4 100644
--- a/spec/requests/ci/api/builds_spec.rb
+++ b/spec/requests/ci/api/builds_spec.rb
@@ -137,6 +137,18 @@ describe Ci::API::Builds do
end
end
end
+
+ context 'when docker configuration options are used' do
+ let!(:build) { create(:ci_build, :extended_options, pipeline: pipeline, name: 'spinach', stage: 'test', stage_idx: 0) }
+
+ it 'starts a build' do
+ register_builds info: { platform: :darwin }
+
+ expect(response).to have_http_status(201)
+ expect(json_response['options']['image']).to eq('ruby:2.1')
+ expect(json_response['options']['services']).to eq(['postgres', 'docker:dind'])
+ end
+ end
end
context 'when builds are finished' do
@@ -229,7 +241,9 @@ describe Ci::API::Builds do
end
context 'when runner is allowed to pick untagged builds' do
- before { runner.update_column(:run_untagged, true) }
+ before do
+ runner.update_column(:run_untagged, true)
+ end
it 'picks build' do
register_builds
@@ -455,7 +469,9 @@ describe Ci::API::Builds do
let(:token) { build.token }
let(:headers_with_token) { headers.merge(Ci::API::Helpers::BUILD_TOKEN_HEADER => token) }
- before { build.run! }
+ before do
+ build.run!
+ end
describe "POST /builds/:id/artifacts/authorize" do
context "authorizes posting artifact to running build" do
@@ -511,7 +527,9 @@ describe Ci::API::Builds do
end
context 'authorization token is invalid' do
- before { post authorize_url, { token: 'invalid', filesize: 100 } }
+ before do
+ post authorize_url, { token: 'invalid', filesize: 100 }
+ end
it 'responds with forbidden' do
expect(response).to have_http_status(403)
diff --git a/spec/requests/ci/api/runners_spec.rb b/spec/requests/ci/api/runners_spec.rb
index 0b9733221d8..78b2be350cd 100644
--- a/spec/requests/ci/api/runners_spec.rb
+++ b/spec/requests/ci/api/runners_spec.rb
@@ -12,7 +12,9 @@ describe Ci::API::Runners do
describe "POST /runners/register" do
context 'when runner token is provided' do
- before { post ci_api("/runners/register"), token: registration_token }
+ before do
+ post ci_api("/runners/register"), token: registration_token
+ end
it 'creates runner with default values' do
expect(response).to have_http_status 201
@@ -69,7 +71,10 @@ describe Ci::API::Runners do
context 'when project token is provided' do
let(:project) { FactoryGirl.create(:empty_project) }
- before { post ci_api("/runners/register"), token: project.runners_token }
+
+ before do
+ post ci_api("/runners/register"), token: project.runners_token
+ end
it 'creates runner' do
expect(response).to have_http_status 201
diff --git a/spec/requests/git_http_spec.rb b/spec/requests/git_http_spec.rb
index 6a83024d0d5..b08148eca3c 100644
--- a/spec/requests/git_http_spec.rb
+++ b/spec/requests/git_http_spec.rb
@@ -316,6 +316,26 @@ describe 'Git HTTP requests', lib: true do
it_behaves_like 'pushes require Basic HTTP Authentication'
end
end
+
+ context 'and the user requests a redirected path' do
+ let!(:redirect) { project.route.create_redirect('foo/bar') }
+ let(:path) { "#{redirect.path}.git" }
+ let(:project_moved_message) do
+ <<-MSG.strip_heredoc
+ Project '#{redirect.path}' was moved to '#{project.full_path}'.
+
+ Please update your Git remote and try again:
+
+ git remote set-url origin #{project.http_url_to_repo}
+ MSG
+ end
+
+ it 'downloads get status 404 with "project was moved" message' do
+ clone_get(path, {})
+ expect(response).to have_http_status(:not_found)
+ expect(response.body).to match(project_moved_message)
+ end
+ end
end
context "when the project is private" do
@@ -505,6 +525,33 @@ describe 'Git HTTP requests', lib: true do
Rack::Attack::Allow2Ban.reset(ip, options)
end
end
+
+ context 'and the user requests a redirected path' do
+ let!(:redirect) { project.route.create_redirect('foo/bar') }
+ let(:path) { "#{redirect.path}.git" }
+ let(:project_moved_message) do
+ <<-MSG.strip_heredoc
+ Project '#{redirect.path}' was moved to '#{project.full_path}'.
+
+ Please update your Git remote and try again:
+
+ git remote set-url origin #{project.http_url_to_repo}
+ MSG
+ end
+
+ it 'downloads get status 404 with "project was moved" message' do
+ clone_get(path, env)
+ expect(response).to have_http_status(:not_found)
+ expect(response.body).to match(project_moved_message)
+ end
+
+ it 'uploads get status 404 with "project was moved" message' do
+ upload(path, env) do |response|
+ expect(response).to have_http_status(:not_found)
+ expect(response.body).to match(project_moved_message)
+ end
+ end
+ end
end
context "when the user doesn't have access to the project" do
@@ -627,7 +674,9 @@ describe 'Git HTTP requests', lib: true do
let(:path) { "/#{project.path_with_namespace}/info/refs" }
context "when no params are added" do
- before { get path }
+ before do
+ get path
+ end
it "redirects to the .git suffix version" do
expect(response).to redirect_to("/#{project.path_with_namespace}.git/info/refs")
@@ -636,7 +685,10 @@ describe 'Git HTTP requests', lib: true do
context "when the upload-pack service is requested" do
let(:params) { { service: 'git-upload-pack' } }
- before { get path, params }
+
+ before do
+ get path, params
+ end
it "redirects to the .git suffix version" do
expect(response).to redirect_to("/#{project.path_with_namespace}.git/info/refs?service=#{params[:service]}")
@@ -645,7 +697,10 @@ describe 'Git HTTP requests', lib: true do
context "when the receive-pack service is requested" do
let(:params) { { service: 'git-receive-pack' } }
- before { get path, params }
+
+ before do
+ get path, params
+ end
it "redirects to the .git suffix version" do
expect(response).to redirect_to("/#{project.path_with_namespace}.git/info/refs?service=#{params[:service]}")
@@ -654,7 +709,10 @@ describe 'Git HTTP requests', lib: true do
context "when the params are anything else" do
let(:params) { { service: 'git-implode-pack' } }
- before { get path, params }
+
+ before do
+ get path, params
+ end
it "redirects to the sign-in page" do
expect(response).to redirect_to(new_user_session_path)
@@ -669,7 +727,7 @@ describe 'Git HTTP requests', lib: true do
end
context "POST git-receive-pack" do
- it "failes to find a route" do
+ it "fails to find a route" do
expect { push_post(project.path_with_namespace) }.to raise_error(ActionController::RoutingError)
end
end
@@ -695,7 +753,9 @@ describe 'Git HTTP requests', lib: true do
end
context "when the file does not exist" do
- before { get "/#{project.path_with_namespace}/blob/master/info/refs" }
+ before do
+ get "/#{project.path_with_namespace}/blob/master/info/refs"
+ end
it "returns not found" do
expect(response).to have_http_status(:not_found)
diff --git a/spec/requests/jwt_controller_spec.rb b/spec/requests/jwt_controller_spec.rb
index 54d7cf5f10d..5e4cf05748e 100644
--- a/spec/requests/jwt_controller_spec.rb
+++ b/spec/requests/jwt_controller_spec.rb
@@ -6,7 +6,9 @@ describe JwtController do
let(:service_name) { 'test' }
let(:parameters) { { service: service_name } }
- before { stub_const('JwtController::SERVICES', service_name => service_class) }
+ before do
+ stub_const('JwtController::SERVICES', service_name => service_class)
+ end
context 'existing service' do
subject! { get '/jwt/auth', parameters }
diff --git a/spec/routing/project_routing_spec.rb b/spec/routing/project_routing_spec.rb
index 0a6778ae2ef..95d40138fea 100644
--- a/spec/routing/project_routing_spec.rb
+++ b/spec/routing/project_routing_spec.rb
@@ -93,13 +93,17 @@ describe 'project routing' do
end
context 'name with dot' do
- before { allow(Project).to receive(:find_by_full_path).with('gitlab/gitlabhq.keys', any_args).and_return(true) }
+ before do
+ allow(Project).to receive(:find_by_full_path).with('gitlab/gitlabhq.keys', any_args).and_return(true)
+ end
it { expect(get('/gitlab/gitlabhq.keys')).to route_to('projects#show', namespace_id: 'gitlab', id: 'gitlabhq.keys') }
end
context 'with nested group' do
- before { allow(Project).to receive(:find_by_full_path).with('gitlab/subgroup/gitlabhq', any_args).and_return(true) }
+ before do
+ allow(Project).to receive(:find_by_full_path).with('gitlab/subgroup/gitlabhq', any_args).and_return(true)
+ end
it { expect(get('/gitlab/subgroup/gitlabhq')).to route_to('projects#show', namespace_id: 'gitlab/subgroup', id: 'gitlabhq') }
end
diff --git a/spec/routing/routing_spec.rb b/spec/routing/routing_spec.rb
index a62af13cf0c..a45839b16f5 100644
--- a/spec/routing/routing_spec.rb
+++ b/spec/routing/routing_spec.rb
@@ -286,7 +286,9 @@ end
describe "Groups", "routing" do
let(:name) { 'complex.group-namegit' }
- before { allow_any_instance_of(GroupUrlConstrainer).to receive(:matches?).and_return(true) }
+ before do
+ allow_any_instance_of(GroupUrlConstrainer).to receive(:matches?).and_return(true)
+ end
it "to #show" do
expect(get("/groups/#{name}")).to route_to('groups#show', id: name)
diff --git a/spec/rubocop/cop/migration/add_timestamps_spec.rb b/spec/rubocop/cop/migration/add_timestamps_spec.rb
new file mode 100644
index 00000000000..18df62dec3e
--- /dev/null
+++ b/spec/rubocop/cop/migration/add_timestamps_spec.rb
@@ -0,0 +1,90 @@
+require 'spec_helper'
+
+require 'rubocop'
+require 'rubocop/rspec/support'
+
+require_relative '../../../../rubocop/cop/migration/add_timestamps'
+
+describe RuboCop::Cop::Migration::AddTimestamps do
+ include CopHelper
+
+ subject(:cop) { described_class.new }
+ let(:migration_with_add_timestamps) do
+ %q(
+ class Users < ActiveRecord::Migration
+ DOWNTIME = false
+
+ def change
+ add_column(:users, :username, :text)
+ add_timestamps(:users)
+ end
+ end
+ )
+ end
+
+ let(:migration_without_add_timestamps) do
+ %q(
+ class Users < ActiveRecord::Migration
+ DOWNTIME = false
+
+ def change
+ add_column(:users, :username, :text)
+ end
+ end
+ )
+ end
+
+ let(:migration_with_add_timestamps_with_timezone) do
+ %q(
+ class Users < ActiveRecord::Migration
+ DOWNTIME = false
+
+ def change
+ add_column(:users, :username, :text)
+ add_timestamps_with_timezone(:users)
+ end
+ end
+ )
+ end
+
+ context 'in migration' do
+ before do
+ allow(cop).to receive(:in_migration?).and_return(true)
+ end
+
+ it 'registers an offense when the "add_timestamps" method is used' do
+ inspect_source(cop, migration_with_add_timestamps)
+
+ aggregate_failures do
+ expect(cop.offenses.size).to eq(1)
+ expect(cop.offenses.map(&:line)).to eq([7])
+ end
+ end
+
+ it 'does not register an offense when the "add_timestamps" method is not used' do
+ inspect_source(cop, migration_without_add_timestamps)
+
+ aggregate_failures do
+ expect(cop.offenses.size).to eq(0)
+ end
+ 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)
+
+ aggregate_failures do
+ expect(cop.offenses.size).to eq(0)
+ end
+ end
+ end
+
+ 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)
+
+ expect(cop.offenses.size).to eq(0)
+ end
+ end
+end
diff --git a/spec/rubocop/cop/migration/datetime_spec.rb b/spec/rubocop/cop/migration/datetime_spec.rb
new file mode 100644
index 00000000000..388b086ce6a
--- /dev/null
+++ b/spec/rubocop/cop/migration/datetime_spec.rb
@@ -0,0 +1,90 @@
+require 'spec_helper'
+
+require 'rubocop'
+require 'rubocop/rspec/support'
+
+require_relative '../../../../rubocop/cop/migration/datetime'
+
+describe RuboCop::Cop::Migration::Datetime do
+ include CopHelper
+
+ subject(:cop) { described_class.new }
+ let(:migration_with_datetime) do
+ %q(
+ class Users < ActiveRecord::Migration
+ DOWNTIME = false
+
+ def change
+ add_column(:users, :username, :text)
+ add_column(:users, :last_sign_in, :datetime)
+ end
+ end
+ )
+ end
+
+ let(:migration_without_datetime) do
+ %q(
+ class Users < ActiveRecord::Migration
+ DOWNTIME = false
+
+ def change
+ add_column(:users, :username, :text)
+ end
+ end
+ )
+ end
+
+ let(:migration_with_datetime_with_timezone) do
+ %q(
+ class Users < ActiveRecord::Migration
+ DOWNTIME = false
+
+ def change
+ add_column(:users, :username, :text)
+ add_column(:users, :last_sign_in, :datetime_with_timezone)
+ end
+ end
+ )
+ end
+
+ context 'in migration' do
+ before do
+ allow(cop).to receive(:in_migration?).and_return(true)
+ end
+
+ it 'registers an offense when the ":datetime" data type is used' do
+ inspect_source(cop, migration_with_datetime)
+
+ aggregate_failures do
+ expect(cop.offenses.size).to eq(1)
+ expect(cop.offenses.map(&:line)).to eq([7])
+ end
+ end
+
+ it 'does not register an offense when the ":datetime" data type is not used' do
+ inspect_source(cop, migration_without_datetime)
+
+ aggregate_failures do
+ expect(cop.offenses.size).to eq(0)
+ end
+ 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)
+
+ aggregate_failures do
+ expect(cop.offenses.size).to eq(0)
+ end
+ end
+ end
+
+ context 'outside of migration' do
+ it 'registers no offense' do
+ inspect_source(cop, migration_with_datetime)
+ inspect_source(cop, migration_without_datetime)
+ inspect_source(cop, migration_with_datetime_with_timezone)
+
+ expect(cop.offenses.size).to eq(0)
+ end
+ end
+end
diff --git a/spec/rubocop/cop/migration/timestamps_spec.rb b/spec/rubocop/cop/migration/timestamps_spec.rb
new file mode 100644
index 00000000000..cdf1423d0c5
--- /dev/null
+++ b/spec/rubocop/cop/migration/timestamps_spec.rb
@@ -0,0 +1,99 @@
+require 'spec_helper'
+
+require 'rubocop'
+require 'rubocop/rspec/support'
+
+require_relative '../../../../rubocop/cop/migration/timestamps'
+
+describe RuboCop::Cop::Migration::Timestamps do
+ include CopHelper
+
+ subject(:cop) { described_class.new }
+ let(:migration_with_timestamps) do
+ %q(
+ class Users < ActiveRecord::Migration
+ DOWNTIME = false
+
+ def change
+ create_table :users do |t|
+ t.string :username, null: false
+ t.timestamps null: true
+ t.string :password
+ end
+ end
+ end
+ )
+ end
+
+ let(:migration_without_timestamps) do
+ %q(
+ class Users < ActiveRecord::Migration
+ DOWNTIME = false
+
+ def change
+ create_table :users do |t|
+ t.string :username, null: false
+ t.string :password
+ end
+ end
+ end
+ )
+ end
+
+ let(:migration_with_timestamps_with_timezone) do
+ %q(
+ class Users < ActiveRecord::Migration
+ DOWNTIME = false
+
+ def change
+ create_table :users do |t|
+ t.string :username, null: false
+ t.timestamps_with_timezone null: true
+ t.string :password
+ end
+ end
+ end
+ )
+ end
+
+ context 'in migration' do
+ before do
+ allow(cop).to receive(:in_migration?).and_return(true)
+ end
+
+ it 'registers an offense when the "timestamps" method is used' do
+ inspect_source(cop, migration_with_timestamps)
+
+ aggregate_failures do
+ expect(cop.offenses.size).to eq(1)
+ expect(cop.offenses.map(&:line)).to eq([8])
+ end
+ end
+
+ it 'does not register an offense when the "timestamps" method is not used' do
+ inspect_source(cop, migration_without_timestamps)
+
+ aggregate_failures do
+ expect(cop.offenses.size).to eq(0)
+ end
+ end
+
+ it 'does not register an offense when the "timestamps_with_timezone" method is used' do
+ inspect_source(cop, migration_with_timestamps_with_timezone)
+
+ aggregate_failures do
+ expect(cop.offenses.size).to eq(0)
+ end
+ end
+ end
+
+ 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)
+
+ expect(cop.offenses.size).to eq(0)
+ end
+ end
+end
diff --git a/spec/rubocop/cop/rspec/single_line_hook_spec.rb b/spec/rubocop/cop/rspec/single_line_hook_spec.rb
new file mode 100644
index 00000000000..6cf0831d3ad
--- /dev/null
+++ b/spec/rubocop/cop/rspec/single_line_hook_spec.rb
@@ -0,0 +1,66 @@
+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/serializers/build_details_entity_spec.rb b/spec/serializers/build_details_entity_spec.rb
index e2511e8968c..b92c1c28ba8 100644
--- a/spec/serializers/build_details_entity_spec.rb
+++ b/spec/serializers/build_details_entity_spec.rb
@@ -3,8 +3,8 @@ require 'spec_helper'
describe BuildDetailsEntity do
set(:user) { create(:admin) }
- it 'inherits from BuildEntity' do
- expect(described_class).to be < BuildEntity
+ it 'inherits from JobEntity' do
+ expect(described_class).to be < JobEntity
end
describe '#as_json' do
@@ -29,7 +29,7 @@ describe BuildDetailsEntity do
it 'contains the needed key value pairs' do
expect(subject).to include(:coverage, :erased_at, :duration)
- expect(subject).to include(:artifacts, :runner, :pipeline)
+ expect(subject).to include(:runner, :pipeline)
expect(subject).to include(:raw_path, :merge_request)
expect(subject).to include(:new_issue_path)
end
diff --git a/spec/serializers/environment_serializer_spec.rb b/spec/serializers/environment_serializer_spec.rb
index d2ad6c44702..4c52a00b442 100644
--- a/spec/serializers/environment_serializer_spec.rb
+++ b/spec/serializers/environment_serializer_spec.rb
@@ -62,7 +62,9 @@ describe EnvironmentSerializer do
subject { serializer.represent(resource) }
context 'when there is a single environment' do
- before { create(:environment, name: 'staging') }
+ before do
+ create(:environment, name: 'staging')
+ end
it 'represents one standalone environment' do
expect(subject.count).to eq 1
@@ -138,7 +140,9 @@ describe EnvironmentSerializer do
context 'when resource is paginatable relation' do
context 'when there is a single environment object in relation' do
- before { create(:environment) }
+ before do
+ create(:environment)
+ end
it 'serializes environments' do
expect(subject.first).to have_key :id
@@ -146,7 +150,9 @@ describe EnvironmentSerializer do
end
context 'when multiple environment objects are serialized' do
- before { create_list(:environment, 3) }
+ before do
+ create_list(:environment, 3)
+ end
it 'serializes appropriate number of objects' do
expect(subject.count).to be 2
diff --git a/spec/serializers/build_entity_spec.rb b/spec/serializers/job_entity_spec.rb
index e51ff9fc709..5ca7bf2fcaf 100644
--- a/spec/serializers/build_entity_spec.rb
+++ b/spec/serializers/job_entity_spec.rb
@@ -1,9 +1,9 @@
require 'spec_helper'
-describe BuildEntity do
+describe JobEntity do
let(:user) { create(:user) }
- let(:build) { create(:ci_build) }
- let(:project) { build.project }
+ let(:job) { create(:ci_build) }
+ let(:project) { job.project }
let(:request) { double('request') }
before do
@@ -12,12 +12,12 @@ describe BuildEntity do
end
let(:entity) do
- described_class.new(build, request: request)
+ described_class.new(job, request: request)
end
subject { entity.as_json }
- it 'contains paths to build page action' do
+ it 'contains paths to job page action' do
expect(subject).to include(:build_path)
end
@@ -27,7 +27,7 @@ describe BuildEntity do
end
it 'contains whether it is playable' do
- expect(subject[:playable]).to eq build.playable?
+ expect(subject[:playable]).to eq job.playable?
end
it 'contains timestamps' do
@@ -39,9 +39,9 @@ describe BuildEntity do
expect(subject[:status]).to include :icon, :favicon, :text, :label
end
- context 'when build is retryable' do
+ context 'when job is retryable' do
before do
- build.update(status: :failed)
+ job.update(status: :failed)
end
it 'contains cancel path' do
@@ -49,9 +49,9 @@ describe BuildEntity do
end
end
- context 'when build is cancelable' do
+ context 'when job is cancelable' do
before do
- build.update(status: :running)
+ job.update(status: :running)
end
it 'contains cancel path' do
@@ -59,7 +59,7 @@ describe BuildEntity do
end
end
- context 'when build is a regular build' do
+ context 'when job is a regular job' do
it 'does not contain path to play action' do
expect(subject).not_to include(:play_path)
end
@@ -69,8 +69,8 @@ describe BuildEntity do
end
end
- context 'when build is a manual action' do
- let(:build) { create(:ci_build, :manual) }
+ context 'when job is a manual action' do
+ let(:job) { create(:ci_build, :manual) }
context 'when user is allowed to trigger action' do
before do
@@ -99,4 +99,25 @@ describe BuildEntity do
end
end
end
+
+ context 'when job is generic commit status' do
+ let(:job) { create(:generic_commit_status, target_url: 'http://google.com') }
+
+ it 'contains paths to target action' do
+ expect(subject).to include(:build_path)
+ end
+
+ it 'does not contain paths to other action paths' do
+ expect(subject).not_to include(:retry_path, :cancel_path, :play_path)
+ end
+
+ it 'contains timestamps' do
+ expect(subject).to include(:created_at, :updated_at)
+ end
+
+ it 'contains details' do
+ expect(subject).to include :status
+ expect(subject[:status]).to include :icon, :favicon, :text, :label
+ end
+ end
end
diff --git a/spec/serializers/pipeline_details_entity_spec.rb b/spec/serializers/pipeline_details_entity_spec.rb
index 03cc5ae9b63..d28dec9592a 100644
--- a/spec/serializers/pipeline_details_entity_spec.rb
+++ b/spec/serializers/pipeline_details_entity_spec.rb
@@ -51,7 +51,9 @@ describe PipelineDetailsEntity do
end
context 'user has ability to retry pipeline' do
- before { project.team << [user, :developer] }
+ before do
+ project.team << [user, :developer]
+ end
it 'retryable flag is true' do
expect(subject[:flags][:retryable]).to eq true
@@ -77,7 +79,9 @@ describe PipelineDetailsEntity do
end
context 'user has ability to cancel pipeline' do
- before { project.add_developer(user) }
+ before do
+ project.add_developer(user)
+ end
it 'cancelable flag is true' do
expect(subject[:flags][:cancelable]).to eq true
@@ -91,6 +95,20 @@ describe PipelineDetailsEntity do
end
end
+ context 'when pipeline has commit statuses' do
+ let(:pipeline) { create(:ci_empty_pipeline) }
+
+ before do
+ create(:generic_commit_status, pipeline: pipeline)
+ end
+
+ it 'contains stages' do
+ expect(subject).to include(:details)
+ expect(subject[:details]).to include(:stages)
+ expect(subject[:details][:stages].first).to include(name: 'external')
+ end
+ end
+
context 'when pipeline has YAML errors' do
let(:pipeline) do
create(:ci_pipeline, config: { rspec: { invalid: :value } })
diff --git a/spec/serializers/pipeline_entity_spec.rb b/spec/serializers/pipeline_entity_spec.rb
index a059c2cc736..46650f3a80d 100644
--- a/spec/serializers/pipeline_entity_spec.rb
+++ b/spec/serializers/pipeline_entity_spec.rb
@@ -51,7 +51,9 @@ describe PipelineEntity do
end
context 'user has ability to retry pipeline' do
- before { project.team << [user, :developer] }
+ before do
+ project.team << [user, :developer]
+ end
it 'contains retry path' do
expect(subject[:retry_path]).to be_present
@@ -77,7 +79,9 @@ describe PipelineEntity do
end
context 'user has ability to cancel pipeline' do
- before { project.add_developer(user) }
+ before do
+ project.add_developer(user)
+ end
it 'contains cancel path' do
expect(subject[:cancel_path]).to be_present
diff --git a/spec/serializers/pipeline_serializer_spec.rb b/spec/serializers/pipeline_serializer_spec.rb
index 34b19fb9fc4..44813656aff 100644
--- a/spec/serializers/pipeline_serializer_spec.rb
+++ b/spec/serializers/pipeline_serializer_spec.rb
@@ -69,7 +69,9 @@ describe PipelineSerializer do
let(:pagination) { { page: 1, per_page: 2 } }
context 'when a single pipeline object is present in relation' do
- before { create(:ci_empty_pipeline) }
+ before do
+ create(:ci_empty_pipeline)
+ end
it 'serializes pipeline relation' do
expect(subject.first).to have_key :id
@@ -77,7 +79,9 @@ describe PipelineSerializer do
end
context 'when a multiple pipeline objects are being serialized' do
- before { create_list(:ci_empty_pipeline, 3) }
+ before do
+ create_list(:ci_empty_pipeline, 3)
+ end
it 'serializes appropriate number of objects' do
expect(subject.count).to be 2
diff --git a/spec/serializers/stage_entity_spec.rb b/spec/serializers/stage_entity_spec.rb
index 64b3217b809..40e303f7b89 100644
--- a/spec/serializers/stage_entity_spec.rb
+++ b/spec/serializers/stage_entity_spec.rb
@@ -54,6 +54,17 @@ describe StageEntity do
it 'exposes the group key' do
expect(subject).to include :groups
end
+
+ context 'and contains commit status' do
+ before do
+ create(:generic_commit_status, pipeline: pipeline, stage: 'test')
+ end
+
+ it 'contains commit status' do
+ groups = subject[:groups].map { |group| group[:name] }
+ expect(groups).to include('generic')
+ end
+ end
end
end
end
diff --git a/spec/services/auth/container_registry_authentication_service_spec.rb b/spec/services/auth/container_registry_authentication_service_spec.rb
index e273dfe1552..60cb7a9440f 100644
--- a/spec/services/auth/container_registry_authentication_service_spec.rb
+++ b/spec/services/auth/container_registry_authentication_service_spec.rb
@@ -34,7 +34,9 @@ describe Auth::ContainerRegistryAuthenticationService, services: true do
end
context 'for changed configuration' do
- before { stub_application_setting(container_registry_token_expire_delay: expire_delay) }
+ before do
+ stub_application_setting(container_registry_token_expire_delay: expire_delay)
+ end
it { expect(expires_at).to be_within(2.seconds).of(Time.now + expire_delay.minutes) }
end
@@ -117,7 +119,9 @@ describe Auth::ContainerRegistryAuthenticationService, services: true do
end
context 'allow developer to push images' do
- before { project.team << [current_user, :developer] }
+ before do
+ project.team << [current_user, :developer]
+ end
let(:current_params) do
{ scope: "repository:#{project.path_with_namespace}:push" }
@@ -128,7 +132,9 @@ describe Auth::ContainerRegistryAuthenticationService, services: true do
end
context 'allow reporter to pull images' do
- before { project.team << [current_user, :reporter] }
+ before do
+ project.team << [current_user, :reporter]
+ end
context 'when pulling from root level repository' do
let(:current_params) do
@@ -141,7 +147,9 @@ describe Auth::ContainerRegistryAuthenticationService, services: true do
end
context 'return a least of privileges' do
- before { project.team << [current_user, :reporter] }
+ before do
+ project.team << [current_user, :reporter]
+ end
let(:current_params) do
{ scope: "repository:#{project.path_with_namespace}:push,pull" }
@@ -152,7 +160,9 @@ describe Auth::ContainerRegistryAuthenticationService, services: true do
end
context 'disallow guest to pull or push images' do
- before { project.team << [current_user, :guest] }
+ before do
+ project.team << [current_user, :guest]
+ end
let(:current_params) do
{ scope: "repository:#{project.path_with_namespace}:pull,push" }
@@ -355,7 +365,9 @@ describe Auth::ContainerRegistryAuthenticationService, services: true do
context 'for project without container registry' do
let(:project) { create(:empty_project, :public, container_registry_enabled: false) }
- before { project.update(container_registry_enabled: false) }
+ before do
+ project.update(container_registry_enabled: false)
+ end
context 'disallow when pulling' do
let(:current_params) do
diff --git a/spec/services/ci/update_build_queue_service_spec.rb b/spec/services/ci/update_build_queue_service_spec.rb
index c44e6b2a48b..efefa8e8eca 100644
--- a/spec/services/ci/update_build_queue_service_spec.rb
+++ b/spec/services/ci/update_build_queue_service_spec.rb
@@ -9,7 +9,9 @@ describe Ci::UpdateBuildQueueService, :services do
let(:runner) { create(:ci_runner) }
context 'when there are runner that can pick build' do
- before { build.project.runners << runner }
+ before do
+ build.project.runners << runner
+ end
it 'ticks runner queue value' do
expect { subject.execute(build) }
@@ -36,7 +38,9 @@ describe Ci::UpdateBuildQueueService, :services do
end
context 'when there are no runners that can pick build' do
- before { build.tag_list = [:docker] }
+ before do
+ build.tag_list = [:docker]
+ end
it 'does not tick runner queue value' do
expect { subject.execute(build) }
diff --git a/spec/services/create_deployment_service_spec.rb b/spec/services/create_deployment_service_spec.rb
index 5398b5c3f7e..6cf4342ad4c 100644
--- a/spec/services/create_deployment_service_spec.rb
+++ b/spec/services/create_deployment_service_spec.rb
@@ -204,7 +204,9 @@ describe CreateDeploymentService, services: true do
let(:merge_request) { create(:merge_request, target_branch: 'master', source_branch: 'feature', source_project: project) }
context "while updating the 'first_deployed_to_production_at' time" do
- before { merge_request.mark_as_merged }
+ before do
+ merge_request.mark_as_merged
+ end
context "for merge requests merged before the current deploy" do
it "sets the time if the deploy's environment is 'production'" do
diff --git a/spec/services/groups/create_service_spec.rb b/spec/services/groups/create_service_spec.rb
index bcb62429275..fbd9026640c 100644
--- a/spec/services/groups/create_service_spec.rb
+++ b/spec/services/groups/create_service_spec.rb
@@ -14,7 +14,9 @@ describe Groups::CreateService, '#execute', services: true do
end
context "cannot create group with restricted visibility level" do
- before { allow_any_instance_of(ApplicationSetting).to receive(:restricted_visibility_levels).and_return([Gitlab::VisibilityLevel::PUBLIC]) }
+ before do
+ allow_any_instance_of(ApplicationSetting).to receive(:restricted_visibility_levels).and_return([Gitlab::VisibilityLevel::PUBLIC])
+ end
it { is_expected.not_to be_persisted }
end
@@ -25,7 +27,9 @@ describe Groups::CreateService, '#execute', services: true do
let!(:service) { described_class.new(user, group_params.merge(parent_id: group.id)) }
context 'as group owner' do
- before { group.add_owner(user) }
+ before do
+ group.add_owner(user)
+ end
it { is_expected.to be_persisted }
end
diff --git a/spec/services/issues/create_service_spec.rb b/spec/services/issues/create_service_spec.rb
index dab1a3469f7..ae9d2b2855d 100644
--- a/spec/services/issues/create_service_spec.rb
+++ b/spec/services/issues/create_service_spec.rb
@@ -155,7 +155,9 @@ describe Issues::CreateService, services: true do
context 'issue create service' do
context 'assignees' do
- before { project.team << [user, :master] }
+ before do
+ project.team << [user, :master]
+ end
it 'removes assignee when user id is invalid' do
opts = { title: 'Title', description: 'Description', assignee_ids: [-1] }
@@ -204,9 +206,9 @@ describe Issues::CreateService, services: true do
end
end
- it_behaves_like 'new issuable record that supports slash commands'
+ it_behaves_like 'new issuable record that supports quick actions'
- context 'Slash commands' do
+ context 'Quick actions' do
context 'with assignee and milestone in params and command' do
let(:opts) do
{
diff --git a/spec/services/issues/move_service_spec.rb b/spec/services/issues/move_service_spec.rb
index 9f8346d52bb..d1dd1466d95 100644
--- a/spec/services/issues/move_service_spec.rb
+++ b/spec/services/issues/move_service_spec.rb
@@ -251,12 +251,18 @@ describe Issues::MoveService, services: true do
end
context 'user is reporter only in new project' do
- before { new_project.team << [user, :reporter] }
+ before do
+ new_project.team << [user, :reporter]
+ end
+
it { expect { move }.to raise_error(StandardError, /permissions/) }
end
context 'user is reporter only in old project' do
- before { old_project.team << [user, :reporter] }
+ before do
+ old_project.team << [user, :reporter]
+ end
+
it { expect { move }.to raise_error(StandardError, /permissions/) }
end
diff --git a/spec/services/issues/update_service_spec.rb b/spec/services/issues/update_service_spec.rb
index a78866a2c32..c26642f5015 100644
--- a/spec/services/issues/update_service_spec.rb
+++ b/spec/services/issues/update_service_spec.rb
@@ -295,7 +295,9 @@ describe Issues::UpdateService, services: true do
end
context 'when issue has the `label` label' do
- before { issue.labels << label }
+ before do
+ issue.labels << label
+ end
it 'does not send notifications for existing labels' do
opts = { label_ids: [label.id, label2.id] }
@@ -329,7 +331,9 @@ describe Issues::UpdateService, services: true do
it { expect(issue.tasks?).to eq(true) }
context 'when tasks are marked as completed' do
- before { update_issue(description: "- [x] Task 1\n- [X] Task 2") }
+ before do
+ update_issue(description: "- [x] Task 1\n- [X] Task 2")
+ end
it 'creates system note about task status change' do
note1 = find_note('marked the task **Task 1** as completed')
@@ -417,7 +421,9 @@ describe Issues::UpdateService, services: true do
context 'when remove_label_ids and label_ids are passed' do
let(:params) { { label_ids: [], remove_label_ids: [label.id] } }
- before { issue.update_attributes(labels: [label, label3]) }
+ before do
+ issue.update_attributes(labels: [label, label3])
+ end
it 'ignores the label_ids parameter' do
expect(result.label_ids).not_to be_empty
@@ -431,7 +437,9 @@ describe Issues::UpdateService, services: true do
context 'when add_label_ids and remove_label_ids are passed' do
let(:params) { { add_label_ids: [label3.id], remove_label_ids: [label.id] } }
- before { issue.update_attributes(labels: [label]) }
+ before do
+ issue.update_attributes(labels: [label])
+ end
it 'adds the passed labels' do
expect(result.label_ids).to include(label3.id)
diff --git a/spec/services/members/create_service_spec.rb b/spec/services/members/create_service_spec.rb
index 5ce8e17976b..5a05ab3ea50 100644
--- a/spec/services/members/create_service_spec.rb
+++ b/spec/services/members/create_service_spec.rb
@@ -5,7 +5,9 @@ describe Members::CreateService, services: true do
let(:user) { create(:user) }
let(:project_user) { create(:user) }
- before { project.team << [user, :master] }
+ before do
+ project.team << [user, :master]
+ end
it 'adds user to members' do
params = { user_ids: project_user.id.to_s, access_level: Gitlab::Access::GUEST }
diff --git a/spec/services/merge_requests/build_service_spec.rb b/spec/services/merge_requests/build_service_spec.rb
index 6f9d1208b1d..01ef52396d7 100644
--- a/spec/services/merge_requests/build_service_spec.rb
+++ b/spec/services/merge_requests/build_service_spec.rb
@@ -206,7 +206,9 @@ describe MergeRequests::BuildService, services: true do
context 'branch starts with external issue IID followed by a hyphen' do
let(:source_branch) { '12345-fix-issue' }
- before { allow(project).to receive(:default_issues_tracker?).and_return(false) }
+ before do
+ allow(project).to receive(:default_issues_tracker?).and_return(false)
+ end
it 'sets the title to: Resolves External Issue $issue-iid' do
expect(merge_request.title).to eq('Resolve External Issue 12345')
diff --git a/spec/services/merge_requests/create_service_spec.rb b/spec/services/merge_requests/create_service_spec.rb
index 2963f62cc7d..2026b7f6510 100644
--- a/spec/services/merge_requests/create_service_spec.rb
+++ b/spec/services/merge_requests/create_service_spec.rb
@@ -108,7 +108,7 @@ describe MergeRequests::CreateService, services: true do
end
end
- it_behaves_like 'new issuable record that supports slash commands' do
+ it_behaves_like 'new issuable record that supports quick actions' do
let(:default_params) do
{
source_branch: 'feature',
@@ -117,7 +117,7 @@ describe MergeRequests::CreateService, services: true do
end
end
- context 'Slash commands' do
+ context 'Quick actions' do
context 'with assignee and milestone in params and command' do
let(:merge_request) { described_class.new(project, user, opts).execute }
let(:milestone) { create(:milestone, project: project) }
@@ -150,7 +150,9 @@ describe MergeRequests::CreateService, services: true do
context 'asssignee_id' do
let(:assignee) { create(:user) }
- before { project.team << [user, :master] }
+ before do
+ project.team << [user, :master]
+ end
it 'removes assignee_id when user id is invalid' do
opts = { title: 'Title', description: 'Description', assignee_id: -1 }
diff --git a/spec/services/merge_requests/merge_service_spec.rb b/spec/services/merge_requests/merge_service_spec.rb
index d96f819e66a..b3b188a805f 100644
--- a/spec/services/merge_requests/merge_service_spec.rb
+++ b/spec/services/merge_requests/merge_service_spec.rb
@@ -81,7 +81,9 @@ describe MergeRequests::MergeService, services: true do
end
context "when jira_issue_transition_id is not present" do
- before { allow_any_instance_of(JIRA::Resource::Issue).to receive(:resolution).and_return(nil) }
+ before do
+ allow_any_instance_of(JIRA::Resource::Issue).to receive(:resolution).and_return(nil)
+ end
it "does not close issue" do
allow(jira_tracker).to receive_messages(jira_issue_transition_id: nil)
diff --git a/spec/services/merge_requests/update_service_spec.rb b/spec/services/merge_requests/update_service_spec.rb
index 091c193aaa6..fd46020bbdb 100644
--- a/spec/services/merge_requests/update_service_spec.rb
+++ b/spec/services/merge_requests/update_service_spec.rb
@@ -356,7 +356,9 @@ describe MergeRequests::UpdateService, services: true do
end
context 'when issue has the `label` label' do
- before { merge_request.labels << label }
+ before do
+ merge_request.labels << label
+ end
it 'does not send notifications for existing labels' do
opts = { label_ids: [label.id, label2.id] }
@@ -388,12 +390,16 @@ describe MergeRequests::UpdateService, services: true do
end
context 'when MergeRequest has tasks' do
- before { update_merge_request({ description: "- [ ] Task 1\n- [ ] Task 2" }) }
+ before do
+ update_merge_request({ description: "- [ ] Task 1\n- [ ] Task 2" })
+ end
it { expect(@merge_request.tasks?).to eq(true) }
context 'when tasks are marked as completed' do
- before { update_merge_request({ description: "- [x] Task 1\n- [X] Task 2" }) }
+ before do
+ update_merge_request({ description: "- [x] Task 1\n- [X] Task 2" })
+ end
it 'creates system note about task status change' do
note1 = find_note('marked the task **Task 1** as completed')
diff --git a/spec/services/notes/slash_commands_service_spec.rb b/spec/services/notes/quick_actions_service_spec.rb
index c9954dc3603..9a98499826f 100644
--- a/spec/services/notes/slash_commands_service_spec.rb
+++ b/spec/services/notes/quick_actions_service_spec.rb
@@ -1,15 +1,17 @@
require 'spec_helper'
-describe Notes::SlashCommandsService, services: true do
+describe Notes::QuickActionsService, services: true do
shared_context 'note on noteable' do
let(:project) { create(:empty_project) }
let(:master) { create(:user).tap { |u| project.team << [u, :master] } }
let(:assignee) { create(:user) }
- before { project.team << [assignee, :master] }
+ before do
+ project.team << [assignee, :master]
+ end
end
- shared_examples 'note on noteable that does not support slash commands' do
+ shared_examples 'note on noteable that does not support quick actions' do
include_context 'note on noteable'
before do
@@ -43,7 +45,7 @@ describe Notes::SlashCommandsService, services: true do
end
end
- shared_examples 'note on noteable that supports slash commands' do
+ shared_examples 'note on noteable that supports quick actions' do
include_context 'note on noteable'
before do
@@ -208,15 +210,15 @@ describe Notes::SlashCommandsService, services: true do
describe '#execute' do
let(:service) { described_class.new(project, master) }
- it_behaves_like 'note on noteable that supports slash commands' do
+ it_behaves_like 'note on noteable that supports quick actions' do
let(:note) { build(:note_on_issue, project: project) }
end
- it_behaves_like 'note on noteable that supports slash commands' do
+ it_behaves_like 'note on noteable that supports quick actions' do
let(:note) { build(:note_on_merge_request, project: project) }
end
- it_behaves_like 'note on noteable that does not support slash commands' do
+ it_behaves_like 'note on noteable that does not support quick actions' do
let(:note) { build(:note_on_commit, project: project) }
end
end
diff --git a/spec/services/notification_service_spec.rb b/spec/services/notification_service_spec.rb
index de3bbc6b6a1..f1e00c1163b 100644
--- a/spec/services/notification_service_spec.rb
+++ b/spec/services/notification_service_spec.rb
@@ -1539,8 +1539,7 @@ describe NotificationService, services: true do
# When resource is nil it means global notification
def update_custom_notification(event, user, resource: nil, value: true)
setting = user.notification_settings_for(resource)
- setting.events[event] = value
- setting.save
+ setting.update!(event => value)
end
def add_users_with_subscription(project, issuable)
diff --git a/spec/services/pages_service_spec.rb b/spec/services/pages_service_spec.rb
index aa63fe3a5c1..cf38c7c75e5 100644
--- a/spec/services/pages_service_spec.rb
+++ b/spec/services/pages_service_spec.rb
@@ -10,10 +10,14 @@ describe PagesService, services: true do
end
context 'execute asynchronously for pages job' do
- before { build.name = 'pages' }
+ before do
+ build.name = 'pages'
+ end
context 'on success' do
- before { build.success }
+ before do
+ build.success
+ end
it 'executes worker' do
expect(PagesWorker).to receive(:perform_async)
@@ -23,7 +27,9 @@ describe PagesService, services: true do
%w(pending running failed canceled).each do |status|
context "on #{status}" do
- before { build.status = status }
+ before do
+ build.status = status
+ end
it 'does not execute worker' do
expect(PagesWorker).not_to receive(:perform_async)
diff --git a/spec/services/preview_markdown_service_spec.rb b/spec/services/preview_markdown_service_spec.rb
index b2fb5c91313..4fd9cb23ae1 100644
--- a/spec/services/preview_markdown_service_spec.rb
+++ b/spec/services/preview_markdown_service_spec.rb
@@ -19,24 +19,24 @@ describe PreviewMarkdownService do
end
end
- context 'new note with slash commands' do
+ context 'new note with quick actions' do
let(:issue) { create(:issue, project: project) }
let(:params) do
{
text: "Please do it\n/assign #{user.to_reference}",
- slash_commands_target_type: 'Issue',
- slash_commands_target_id: issue.id
+ quick_actions_target_type: 'Issue',
+ quick_actions_target_id: issue.id
}
end
let(:service) { described_class.new(project, user, params) }
- it 'removes slash commands from text' do
+ it 'removes quick actions from text' do
result = service.execute
expect(result[:text]).to eq 'Please do it'
end
- it 'explains slash commands effect' do
+ it 'explains quick actions effect' do
result = service.execute
expect(result[:commands]).to eq "Assigns #{user.to_reference}."
@@ -47,21 +47,21 @@ describe PreviewMarkdownService do
let(:params) do
{
text: "My work\n/estimate 2y",
- slash_commands_target_type: 'MergeRequest'
+ quick_actions_target_type: 'MergeRequest'
}
end
let(:service) { described_class.new(project, user, params) }
- it 'removes slash commands from text' do
+ it 'removes quick actions from text' do
result = service.execute
expect(result[:text]).to eq 'My work'
end
- it 'explains slash commands effect' do
+ it 'explains quick actions effect' do
result = service.execute
expect(result[:commands]).to eq 'Sets time estimate to 2y.'
- end
+ end
end
end
diff --git a/spec/services/projects/transfer_service_spec.rb b/spec/services/projects/transfer_service_spec.rb
index b957517c715..5d2f4cf17fb 100644
--- a/spec/services/projects/transfer_service_spec.rb
+++ b/spec/services/projects/transfer_service_spec.rb
@@ -59,12 +59,16 @@ describe Projects::TransferService, services: true do
context 'visibility level' do
let(:internal_group) { create(:group, :internal) }
- before { internal_group.add_owner(user) }
+ before do
+ internal_group.add_owner(user)
+ end
context 'when namespace visibility level < project visibility level' do
let(:public_project) { create(:project, :public, :repository, namespace: user.namespace) }
- before { transfer_project(public_project, user, internal_group) }
+ before do
+ transfer_project(public_project, user, internal_group)
+ end
it { expect(public_project.visibility_level).to eq(internal_group.visibility_level) }
end
@@ -72,7 +76,9 @@ describe Projects::TransferService, services: true do
context 'when namespace visibility level > project visibility level' do
let(:private_project) { create(:project, :private, :repository, namespace: user.namespace) }
- before { transfer_project(private_project, user, internal_group) }
+ before do
+ transfer_project(private_project, user, internal_group)
+ end
it { expect(private_project.visibility_level).to eq(Gitlab::VisibilityLevel::PRIVATE) }
end
diff --git a/spec/services/slash_commands/interpret_service_spec.rb b/spec/services/quick_actions/interpret_service_spec.rb
index 4db491fd5f3..c9e63efbc14 100644
--- a/spec/services/slash_commands/interpret_service_spec.rb
+++ b/spec/services/quick_actions/interpret_service_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe SlashCommands::InterpretService, services: true do
+describe QuickActions::InterpretService, services: true do
let(:project) { create(:empty_project, :public) }
let(:developer) { create(:user) }
let(:developer2) { create(:user) }
@@ -378,7 +378,9 @@ describe SlashCommands::InterpretService, services: true do
context 'assign command with multiple assignees' do
let(:content) { "/assign @#{developer.username} @#{developer2.username}" }
- before{ project.team << [developer2, :developer] }
+ before do
+ project.team << [developer2, :developer]
+ end
context 'Issue' do
it 'fetches assignee and populates assignee_id if content contains /assign' do
@@ -798,7 +800,11 @@ describe SlashCommands::InterpretService, services: true do
context 'if the project has multiple boards' do
let(:issuable) { issue }
- before { create(:board, project: project) }
+
+ before do
+ create(:board, project: project)
+ end
+
it_behaves_like 'empty command'
end
diff --git a/spec/services/spam_service_spec.rb b/spec/services/spam_service_spec.rb
index 74cba8c014b..5e6e43b7a90 100644
--- a/spec/services/spam_service_spec.rb
+++ b/spec/services/spam_service_spec.rb
@@ -70,7 +70,9 @@ describe SpamService, services: true do
end
context 'when not indicated as spam by akismet' do
- before { allow(AkismetService).to receive(:new).and_return(double(is_spam?: false)) }
+ before do
+ allow(AkismetService).to receive(:new).and_return(double(is_spam?: false))
+ end
it 'returns false' do
expect(check_spam(issue, request, false)).to be_falsey
diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb
index 1979347a178..fdef6fd5221 100644
--- a/spec/spec_helper.rb
+++ b/spec/spec_helper.rb
@@ -44,6 +44,7 @@ RSpec.configure do |config|
config.include Devise::Test::ControllerHelpers, type: :controller
config.include Devise::Test::ControllerHelpers, type: :view
+ config.include Devise::Test::IntegrationHelpers, type: :feature
config.include Warden::Test::Helpers, type: :request
config.include LoginHelpers, type: :feature
config.include SearchHelpers, type: :feature
@@ -56,6 +57,7 @@ RSpec.configure do |config|
config.include StubGitlabCalls
config.include StubGitlabData
config.include ApiHelpers, :api
+ config.include Rails.application.routes.url_helpers, type: :routing
config.include MigrationsHelpers, :migration
config.infer_spec_type_from_file_location!
@@ -106,15 +108,13 @@ RSpec.configure do |config|
Sidekiq.redis(&:flushall)
end
- config.around(:example, :migration) do |example|
- begin
- ActiveRecord::Migrator
- .migrate(migrations_paths, previous_migration.version)
+ config.before(:example, :migration) do
+ ActiveRecord::Migrator
+ .migrate(migrations_paths, previous_migration.version)
+ end
- example.run
- ensure
- ActiveRecord::Migrator.migrate(migrations_paths)
- end
+ config.after(:example, :migration) do
+ ActiveRecord::Migrator.migrate(migrations_paths)
end
config.around(:each, :nested_groups) do |example|
diff --git a/spec/support/capybara.rb b/spec/support/capybara.rb
index c34e76fa72f..4aa81a03558 100644
--- a/spec/support/capybara.rb
+++ b/spec/support/capybara.rb
@@ -35,4 +35,13 @@ RSpec.configure do |config|
TestEnv.eager_load_driver_server
$capybara_server_already_started = true
end
+
+ config.after(:each, :js) do
+ # capybara/rspec already calls Capybara.reset_sessions! in an `after` hook,
+ # but `block_and_wait_for_requests_complete` is called before it so by
+ # calling it explicitely here, we prevent any new requests from being fired
+ # See https://github.com/teamcapybara/capybara/blob/ffb41cfad620de1961bb49b1562a9fa9b28c0903/lib/capybara/rspec.rb#L20-L25
+ Capybara.reset_sessions!
+ block_and_wait_for_requests_complete
+ end
end
diff --git a/spec/support/chat_slash_commands_shared_examples.rb b/spec/support/chat_slash_commands_shared_examples.rb
index 4dfa29849ee..978b0b9cc30 100644
--- a/spec/support/chat_slash_commands_shared_examples.rb
+++ b/spec/support/chat_slash_commands_shared_examples.rb
@@ -87,7 +87,7 @@ RSpec.shared_examples 'chat slash commands service' do
end
it 'triggers the command' do
- expect_any_instance_of(Gitlab::ChatCommands::Command).to receive(:execute)
+ expect_any_instance_of(Gitlab::SlashCommands::Command).to receive(:execute)
subject.trigger(params)
end
diff --git a/spec/support/features/issuable_slash_commands_shared_examples.rb b/spec/support/features/issuable_slash_commands_shared_examples.rb
index fa82dc5e9f9..50869099bb7 100644
--- a/spec/support/features/issuable_slash_commands_shared_examples.rb
+++ b/spec/support/features/issuable_slash_commands_shared_examples.rb
@@ -1,8 +1,8 @@
# Specifications for behavior common to all objects with executable attributes.
# It takes a `issuable_type`, and expect an `issuable`.
-shared_examples 'issuable record that supports slash commands in its description and notes' do |issuable_type|
- include SlashCommandsHelpers
+shared_examples 'issuable record that supports quick actions in its description and notes' do |issuable_type|
+ include QuickActionsHelpers
let(:master) { create(:user) }
let(:assignee) { create(:user, username: 'bob') }
@@ -17,7 +17,7 @@ shared_examples 'issuable record that supports slash commands in its description
project.team << [master, :master]
project.team << [assignee, :developer]
project.team << [guest, :guest]
- login_with(master)
+ gitlab_sign_in(master)
end
after do
@@ -105,8 +105,8 @@ shared_examples 'issuable record that supports slash commands in its description
context "when current user cannot close #{issuable_type}" do
before do
- logout
- login_with(guest)
+ gitlab_sign_out
+ gitlab_sign_in(guest)
visit public_send("namespace_project_#{issuable_type}_path", project.namespace, project, issuable)
end
@@ -140,8 +140,8 @@ shared_examples 'issuable record that supports slash commands in its description
context "when current user cannot reopen #{issuable_type}" do
before do
- logout
- login_with(guest)
+ gitlab_sign_out
+ gitlab_sign_in(guest)
visit public_send("namespace_project_#{issuable_type}_path", project.namespace, project, issuable)
end
@@ -170,8 +170,8 @@ shared_examples 'issuable record that supports slash commands in its description
context "when current user cannot change title of #{issuable_type}" do
before do
- logout
- login_with(guest)
+ gitlab_sign_out
+ gitlab_sign_in(guest)
visit public_send("namespace_project_#{issuable_type}_path", project.namespace, project, issuable)
end
@@ -260,7 +260,7 @@ shared_examples 'issuable record that supports slash commands in its description
end
describe "preview of note on #{issuable_type}" do
- it 'removes slash commands from note and explains them' do
+ it 'removes quick actions from note and explains them' do
visit public_send("namespace_project_#{issuable_type}_path", project.namespace, project, issuable)
page.within('.js-main-target-form') do
diff --git a/spec/support/kubernetes_helpers.rb b/spec/support/kubernetes_helpers.rb
index 9280fad4ace..c92f78b324c 100644
--- a/spec/support/kubernetes_helpers.rb
+++ b/spec/support/kubernetes_helpers.rb
@@ -1,7 +1,26 @@
module KubernetesHelpers
include Gitlab::Kubernetes
- def kube_discovery_body
+ def kube_response(body)
+ { body: body.to_json }
+ end
+
+ def kube_pods_response
+ kube_response(kube_pods_body)
+ end
+
+ def stub_kubeclient_discover
+ WebMock.stub_request(:get, service.api_url + '/api/v1').to_return(kube_response(kube_v1_discovery_body))
+ end
+
+ def stub_kubeclient_pods(response = nil)
+ stub_kubeclient_discover
+ pods_url = service.api_url + "/api/v1/namespaces/#{service.actual_namespace}/pods"
+
+ WebMock.stub_request(:get, pods_url).to_return(response || kube_pods_response)
+ end
+
+ def kube_v1_discovery_body
{
"kind" => "APIResourceList",
"resources" => [
@@ -10,17 +29,19 @@ module KubernetesHelpers
}
end
- def kube_pods_body(*pods)
- { "kind" => "PodList",
- "items" => [kube_pod] }
+ def kube_pods_body
+ {
+ "kind" => "PodList",
+ "items" => [kube_pod]
+ }
end
# This is a partial response, it will have many more elements in reality but
# these are the ones we care about at the moment
- def kube_pod(app: "valid-pod-label")
+ def kube_pod(name: "kube-pod", app: "valid-pod-label")
{
"metadata" => {
- "name" => "kube-pod",
+ "name" => name,
"creationTimestamp" => "2016-11-25T19:55:19Z",
"labels" => { "app" => app }
},
diff --git a/spec/support/login_helpers.rb b/spec/support/login_helpers.rb
index e6da852e728..879386b5437 100644
--- a/spec/support/login_helpers.rb
+++ b/spec/support/login_helpers.rb
@@ -6,15 +6,15 @@ module LoginHelpers
# Examples:
#
# # Create a user automatically
- # login_as(:user)
+ # gitlab_sign_in(:user)
#
# # Create an admin automatically
- # login_as(:admin)
+ # gitlab_sign_in(:admin)
#
# # Provide an existing User record
# user = create(:user)
- # login_as(user)
- def login_as(user_or_role)
+ # gitlab_sign_in(user)
+ def gitlab_sign_in(user_or_role, **kwargs)
@user =
if user_or_role.is_a?(User)
user_or_role
@@ -22,26 +22,44 @@ module LoginHelpers
create(user_or_role)
end
- login_with(@user)
+ gitlab_sign_in_with(@user, **kwargs)
end
- # Internal: Login as the specified user
+ def gitlab_sign_in_via(provider, user, uid)
+ mock_auth_hash(provider, uid, user.email)
+ visit new_user_session_path
+ click_link provider
+ end
+
+ # Requires Javascript driver.
+ def gitlab_sign_out
+ find(".header-user-dropdown-toggle").click
+ click_link "Sign out"
+ # check the sign_in button
+ expect(page).to have_button('Sign in')
+ end
+
+ # Logout without JavaScript driver
+ def gitlab_sign_out_direct
+ page.driver.submit :delete, '/users/sign_out', {}
+ end
+
+ private
+
+ # Private: Login as the specified user
#
# user - User instance to login with
# remember - Whether or not to check "Remember me" (default: false)
- def login_with(user, remember: false)
+ def gitlab_sign_in_with(user, remember: false)
visit new_user_session_path
+
fill_in "user_login", with: user.email
fill_in "user_password", with: "12345678"
check 'user_remember_me' if remember
+
click_button "Sign in"
- Thread.current[:current_user] = user
- end
- def login_via(provider, user, uid)
- mock_auth_hash(provider, uid, user.email)
- visit new_user_session_path
- click_link provider
+ Thread.current[:current_user] = user
end
def mock_auth_hash(provider, uid, email)
@@ -71,17 +89,4 @@ module LoginHelpers
})
Rails.application.env_config['omniauth.auth'] = OmniAuth.config.mock_auth[:saml]
end
-
- # Requires Javascript driver.
- def logout
- find(".header-user-dropdown-toggle").click
- click_link "Sign out"
- # check the sign_in button
- expect(page).to have_button('Sign in')
- end
-
- # Logout without JavaScript driver
- def logout_direct
- page.driver.submit :delete, '/users/sign_out', {}
- end
end
diff --git a/spec/support/matchers/gitaly_matchers.rb b/spec/support/matchers/gitaly_matchers.rb
index ed14bcec9f2..ebfabcd8f24 100644
--- a/spec/support/matchers/gitaly_matchers.rb
+++ b/spec/support/matchers/gitaly_matchers.rb
@@ -1,5 +1,10 @@
-RSpec::Matchers.define :gitaly_request_with_repo_path do |path|
- match { |actual| actual.repository.path == path }
+RSpec::Matchers.define :gitaly_request_with_path do |storage_name, relative_path|
+ match do |actual|
+ repository = actual.repository
+
+ repository.storage_name == storage_name &&
+ repository.relative_path == relative_path
+ end
end
RSpec::Matchers.define :gitaly_request_with_params do |params|
diff --git a/spec/support/milestone_tabs_examples.rb b/spec/support/milestone_tabs_examples.rb
index 7cfc1e06975..70b499198bf 100644
--- a/spec/support/milestone_tabs_examples.rb
+++ b/spec/support/milestone_tabs_examples.rb
@@ -15,7 +15,9 @@ shared_examples 'milestone tabs' do
describe '#merge_requests' do
context 'as html' do
- before { go(:merge_requests, format: 'html') }
+ before do
+ go(:merge_requests, format: 'html')
+ end
it 'redirects to milestone#show' do
expect(response).to redirect_to(milestone_path)
@@ -23,7 +25,9 @@ shared_examples 'milestone tabs' do
end
context 'as json' do
- before { go(:merge_requests, format: 'json') }
+ before do
+ go(:merge_requests, format: 'json')
+ end
it 'renders the merge requests tab template to a string' do
expect(response).to render_template('shared/milestones/_merge_requests_tab')
@@ -34,7 +38,9 @@ shared_examples 'milestone tabs' do
describe '#participants' do
context 'as html' do
- before { go(:participants, format: 'html') }
+ before do
+ go(:participants, format: 'html')
+ end
it 'redirects to milestone#show' do
expect(response).to redirect_to(milestone_path)
@@ -42,7 +48,9 @@ shared_examples 'milestone tabs' do
end
context 'as json' do
- before { go(:participants, format: 'json') }
+ before do
+ go(:participants, format: 'json')
+ end
it 'renders the participants tab template to a string' do
expect(response).to render_template('shared/milestones/_participants_tab')
@@ -53,7 +61,9 @@ shared_examples 'milestone tabs' do
describe '#labels' do
context 'as html' do
- before { go(:labels, format: 'html') }
+ before do
+ go(:labels, format: 'html')
+ end
it 'redirects to milestone#show' do
expect(response).to redirect_to(milestone_path)
@@ -61,7 +71,9 @@ shared_examples 'milestone tabs' do
end
context 'as json' do
- before { go(:labels, format: 'json') }
+ before do
+ go(:labels, format: 'json')
+ end
it 'renders the labels tab template to a string' do
expect(response).to render_template('shared/milestones/_labels_tab')
diff --git a/spec/support/project_features_apply_to_issuables_shared_examples.rb b/spec/support/project_features_apply_to_issuables_shared_examples.rb
index f8b7d0527ba..81b51509e0b 100644
--- a/spec/support/project_features_apply_to_issuables_shared_examples.rb
+++ b/spec/support/project_features_apply_to_issuables_shared_examples.rb
@@ -18,7 +18,7 @@ shared_examples 'project features apply to issuables' do |klass|
before do
_ = issuable
- login_as(user) if user
+ gitlab_sign_in(user) if user
visit path
end
diff --git a/spec/support/slash_commands_helpers.rb b/spec/support/quick_actions_helpers.rb
index 4bfe481115f..d2aaae7518f 100644
--- a/spec/support/slash_commands_helpers.rb
+++ b/spec/support/quick_actions_helpers.rb
@@ -1,4 +1,4 @@
-module SlashCommandsHelpers
+module QuickActionsHelpers
def write_note(text)
Sidekiq::Testing.fake! do
page.within('.js-main-target-form') do
diff --git a/spec/support/reference_parser_shared_examples.rb b/spec/support/reference_parser_shared_examples.rb
index 8eb74635a60..bd83cb88058 100644
--- a/spec/support/reference_parser_shared_examples.rb
+++ b/spec/support/reference_parser_shared_examples.rb
@@ -3,7 +3,9 @@ RSpec.shared_examples "referenced feature visibility" do |*related_features|
related_features.map { |feature| (feature + "_access_level").to_sym }
end
- before { link['data-project'] = project.id.to_s }
+ before do
+ link['data-project'] = project.id.to_s
+ end
context "when feature is disabled" do
it "does not create reference" do
@@ -13,7 +15,9 @@ RSpec.shared_examples "referenced feature visibility" do |*related_features|
end
context "when feature is enabled only for team members" do
- before { set_features_fields_to(ProjectFeature::PRIVATE) }
+ before do
+ set_features_fields_to(ProjectFeature::PRIVATE)
+ end
it "does not create reference for non member" do
non_member = create(:user)
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 1dd3663b944..9399745f900 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
@@ -1,7 +1,7 @@
# Specifications for behavior common to all objects with executable attributes.
# It can take a `default_params`.
-shared_examples 'new issuable record that supports slash commands' do
+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(:assignee) { create(:user) }
@@ -11,7 +11,9 @@ shared_examples 'new issuable record that supports slash commands' do
let(:params) { base_params.merge(defined?(default_params) ? default_params : {}).merge(example_params) }
let(:issuable) { described_class.new(project, user, params).execute }
- before { project.team << [assignee, :master] }
+ before do
+ project.team << [assignee, :master]
+ end
context 'with labels in command only' do
let(:example_params) do
diff --git a/spec/support/services/issuable_update_service_shared_examples.rb b/spec/support/services/issuable_update_service_shared_examples.rb
index 8947f20562f..ffbce6c42bf 100644
--- a/spec/support/services/issuable_update_service_shared_examples.rb
+++ b/spec/support/services/issuable_update_service_shared_examples.rb
@@ -4,7 +4,9 @@ shared_examples 'issuable update service' do
end
context 'changing state' do
- before { expect(project).to receive(:execute_hooks).once }
+ before do
+ expect(project).to receive(:execute_hooks).once
+ end
context 'to reopened' do
it 'executes hooks only once' do
diff --git a/spec/support/slack_mattermost_notifications_shared_examples.rb b/spec/support/slack_mattermost_notifications_shared_examples.rb
index 7e35ebb6c97..a7deb038703 100644
--- a/spec/support/slack_mattermost_notifications_shared_examples.rb
+++ b/spec/support/slack_mattermost_notifications_shared_examples.rb
@@ -11,14 +11,18 @@ RSpec.shared_examples 'slack or mattermost notifications' do
describe 'Validations' do
context 'when service is active' do
- before { subject.active = true }
+ before do
+ subject.active = true
+ end
it { is_expected.to validate_presence_of(:webhook) }
it_behaves_like 'issue tracker service URL attribute', :webhook
end
context 'when service is inactive' do
- before { subject.active = false }
+ before do
+ subject.active = false
+ end
it { is_expected.not_to validate_presence_of(:webhook) }
end
diff --git a/spec/support/time_tracking_shared_examples.rb b/spec/support/time_tracking_shared_examples.rb
index b407b8097d2..0fa74f911f6 100644
--- a/spec/support/time_tracking_shared_examples.rb
+++ b/spec/support/time_tracking_shared_examples.rb
@@ -54,7 +54,7 @@ shared_examples 'issuable time tracker' do
it 'shows the help state when icon is clicked' do
page.within '.time-tracking-component-wrap' do
find('.help-button').click
- expect(page).to have_content 'Track time with slash commands'
+ expect(page).to have_content 'Track time with quick actions'
expect(page).to have_content 'Learn more'
end
end
@@ -64,7 +64,7 @@ shared_examples 'issuable time tracker' do
find('.help-button').click
find('.close-help-button').click
- expect(page).not_to have_content 'Track time with slash commands'
+ expect(page).not_to have_content 'Track time with quick actions'
expect(page).not_to have_content 'Learn more'
end
end
@@ -78,8 +78,8 @@ shared_examples 'issuable time tracker' do
end
end
-def submit_time(slash_command)
- fill_in 'note[note]', with: slash_command
+def submit_time(quick_action)
+ fill_in 'note[note]', with: quick_action
find('.js-comment-submit-button').trigger('click')
wait_for_requests
end
diff --git a/spec/support/unique_ip_check_shared_examples.rb b/spec/support/unique_ip_check_shared_examples.rb
index 7cf5a65eeed..1986d202c4a 100644
--- a/spec/support/unique_ip_check_shared_examples.rb
+++ b/spec/support/unique_ip_check_shared_examples.rb
@@ -31,7 +31,9 @@ end
shared_examples 'user login operation with unique ip limit' do
include_context 'unique ips sign in limit' do
- before { current_application_settings.update!(unique_ips_limit_per_user: 1) }
+ before do
+ current_application_settings.update!(unique_ips_limit_per_user: 1)
+ end
it 'allows user authenticating from the same ip' do
expect { operation_from_ip('ip') }.not_to raise_error
@@ -47,7 +49,9 @@ end
shared_examples 'user login request with unique ip limit' do |success_status = 200|
include_context 'unique ips sign in limit' do
- before { current_application_settings.update!(unique_ips_limit_per_user: 1) }
+ before do
+ current_application_settings.update!(unique_ips_limit_per_user: 1)
+ end
it 'allows user authenticating from the same ip' do
expect(request_from_ip('ip')).to have_http_status(success_status)
diff --git a/spec/support/updating_mentions_shared_examples.rb b/spec/support/updating_mentions_shared_examples.rb
index e0c59a5c280..eeec3e1d79b 100644
--- a/spec/support/updating_mentions_shared_examples.rb
+++ b/spec/support/updating_mentions_shared_examples.rb
@@ -2,7 +2,9 @@ RSpec.shared_examples 'updating mentions' do |service_class|
let(:mentioned_user) { create(:user) }
let(:service_class) { service_class }
- before { project.team << [mentioned_user, :developer] }
+ before do
+ project.team << [mentioned_user, :developer]
+ end
def update_mentionable(opts)
reset_delivered_emails!
@@ -15,7 +17,9 @@ RSpec.shared_examples 'updating mentions' do |service_class|
end
context 'in title' do
- before { update_mentionable(title: mentioned_user.to_reference) }
+ before do
+ update_mentionable(title: mentioned_user.to_reference)
+ end
it 'emails only the newly-mentioned user' do
should_only_email(mentioned_user)
@@ -23,7 +27,9 @@ RSpec.shared_examples 'updating mentions' do |service_class|
end
context 'in description' do
- before { update_mentionable(description: mentioned_user.to_reference) }
+ before do
+ update_mentionable(description: mentioned_user.to_reference)
+ end
it 'emails only the newly-mentioned user' do
should_only_email(mentioned_user)
diff --git a/spec/support/wait_for_requests.rb b/spec/support/wait_for_requests.rb
index 05ec9026141..b5c3c0f55b8 100644
--- a/spec/support/wait_for_requests.rb
+++ b/spec/support/wait_for_requests.rb
@@ -7,7 +7,7 @@ module WaitForRequests
def block_and_wait_for_requests_complete
Gitlab::Testing::RequestBlockerMiddleware.block_requests!
wait_for('pending requests complete') do
- Gitlab::Testing::RequestBlockerMiddleware.num_active_requests.zero?
+ Gitlab::Testing::RequestBlockerMiddleware.num_active_requests.zero? && finished_all_requests?
end
ensure
Gitlab::Testing::RequestBlockerMiddleware.allow_requests!
@@ -40,22 +40,16 @@ module WaitForRequests
end
def finished_all_vue_resource_requests?
- page.evaluate_script('window.activeVueResources || 0').zero?
+ Capybara.page.evaluate_script('window.activeVueResources || 0').zero?
end
def finished_all_ajax_requests?
- return true if page.evaluate_script('typeof jQuery === "undefined"')
+ return true if Capybara.page.evaluate_script('typeof jQuery === "undefined"')
- page.evaluate_script('jQuery.active').zero?
+ Capybara.page.evaluate_script('jQuery.active').zero?
end
def javascript_test?
Capybara.current_driver == Capybara.javascript_driver
end
end
-
-RSpec.configure do |config|
- config.after(:each, :js) do
- block_and_wait_for_requests_complete
- end
-end
diff --git a/spec/tasks/gitlab/backup_rake_spec.rb b/spec/tasks/gitlab/backup_rake_spec.rb
index 0ff1a988a9e..1e5f55a738a 100644
--- a/spec/tasks/gitlab/backup_rake_spec.rb
+++ b/spec/tasks/gitlab/backup_rake_spec.rb
@@ -241,6 +241,10 @@ describe 'gitlab:app namespace rake task' do
project_a
project_b
+ # Avoid asking gitaly about the root ref (which will fail beacuse of the
+ # mocked storages)
+ allow_any_instance_of(Repository).to receive(:empty_repo?).and_return(false)
+
# We only need a backup of the repositories for this test
ENV["SKIP"] = "db,uploads,builds,artifacts,lfs,registry"
create_backup
diff --git a/spec/tasks/gitlab/gitaly_rake_spec.rb b/spec/tasks/gitlab/gitaly_rake_spec.rb
index 4a636decafd..cfa6c9ca8ce 100644
--- a/spec/tasks/gitlab/gitaly_rake_spec.rb
+++ b/spec/tasks/gitlab/gitaly_rake_spec.rb
@@ -79,8 +79,14 @@ describe 'gitlab:gitaly namespace rake task' do
describe 'storage_config' do
it 'prints storage configuration in a TOML format' do
config = {
- 'default' => { 'path' => '/path/to/default' },
- 'nfs_01' => { 'path' => '/path/to/nfs_01' }
+ 'default' => {
+ 'path' => '/path/to/default',
+ 'gitaly_address' => 'unix:/path/to/my.socket'
+ },
+ 'nfs_01' => {
+ 'path' => '/path/to/nfs_01',
+ 'gitaly_address' => 'unix:/path/to/my.socket'
+ }
}
allow(Gitlab.config.repositories).to receive(:storages).and_return(config)
@@ -89,6 +95,7 @@ describe 'gitlab:gitaly namespace rake task' do
expected_output = <<~TOML
# Gitaly storage configuration generated from #{Gitlab.config.source} on #{Time.current.to_s(:long)}
# This is in TOML format suitable for use in Gitaly's config.toml file.
+ socket_path = "/path/to/my.socket"
[[storage]]
name = "default"
path = "/path/to/default"
diff --git a/spec/views/profiles/show.html.haml_spec.rb b/spec/views/profiles/show.html.haml_spec.rb
new file mode 100644
index 00000000000..e89a8cb9626
--- /dev/null
+++ b/spec/views/profiles/show.html.haml_spec.rb
@@ -0,0 +1,19 @@
+require 'spec_helper'
+
+describe 'profiles/show' do
+ let(:user) { create(:user) }
+
+ before do
+ assign(:user, user)
+ allow(controller).to receive(:current_user).and_return(user)
+ end
+
+ context 'when the profile page is opened' do
+ it 'displays the correct elements' do
+ render
+
+ expect(rendered).to have_field('user_name', user.name)
+ expect(rendered).to have_field('user_id', user.id)
+ end
+ end
+end
diff --git a/spec/views/projects/diffs/_viewer.html.haml_spec.rb b/spec/views/projects/diffs/_viewer.html.haml_spec.rb
new file mode 100644
index 00000000000..32469202508
--- /dev/null
+++ b/spec/views/projects/diffs/_viewer.html.haml_spec.rb
@@ -0,0 +1,71 @@
+require 'spec_helper'
+
+describe 'projects/diffs/_viewer.html.haml', :view do
+ include FakeBlobHelpers
+
+ let(:project) { create(:project, :repository) }
+ let(:commit) { project.commit('570e7b2abdd848b95f2f578043fc23bd6f6fd24d') }
+ let(:diff_file) { commit.diffs.diff_file_with_new_path('files/ruby/popen.rb') }
+
+ let(:viewer_class) do
+ Class.new(DiffViewer::Base) do
+ include DiffViewer::Rich
+
+ self.partial_name = 'text'
+ end
+ end
+
+ let(:viewer) { viewer_class.new(diff_file) }
+
+ before do
+ assign(:project, project)
+
+ controller.params[:controller] = 'projects/commit'
+ controller.params[:action] = 'show'
+ controller.params[:namespace_id] = project.namespace.to_param
+ controller.params[:project_id] = project.to_param
+ controller.params[:id] = commit.id
+ end
+
+ def render_view
+ render partial: 'projects/diffs/viewer', locals: { viewer: viewer }
+ end
+
+ context 'when there is a render error' do
+ before do
+ allow(viewer).to receive(:render_error).and_return(:too_large)
+ end
+
+ it 'renders the error' do
+ render_view
+
+ expect(view).to render_template('projects/diffs/_render_error')
+ end
+ end
+
+ context 'when the viewer is collapsed' do
+ before do
+ allow(diff_file).to receive(:collapsed?).and_return(true)
+ end
+
+ it 'renders the collapsed view' do
+ render_view
+
+ expect(view).to render_template('projects/diffs/_collapsed')
+ end
+ end
+
+ context 'when there is no render error' do
+ it 'prepares the viewer' do
+ expect(viewer).to receive(:prepare!)
+
+ render_view
+ end
+
+ it 'renders the viewer' do
+ render_view
+
+ expect(view).to render_template('projects/diffs/viewers/_text')
+ end
+ end
+end
diff --git a/spec/views/shared/notes/_form.html.haml_spec.rb b/spec/views/shared/notes/_form.html.haml_spec.rb
index d7d0a5bf56a..cae6bee2776 100644
--- a/spec/views/shared/notes/_form.html.haml_spec.rb
+++ b/spec/views/shared/notes/_form.html.haml_spec.rb
@@ -20,8 +20,8 @@ describe 'shared/notes/_form' do
context "with a note on #{noteable}" do
let(:note) { build(:"note_on_#{noteable}", project: project) }
- it 'says that markdown and slash commands are supported' do
- expect(rendered).to have_content('Markdown and slash commands are supported')
+ it 'says that markdown and quick actions are supported' do
+ expect(rendered).to have_content('Markdown and quick actions are supported')
end
end
end
@@ -29,7 +29,7 @@ describe 'shared/notes/_form' do
context 'with a note on a commit' do
let(:note) { build(:note_on_commit, project: project) }
- it 'says that only markdown is supported, not slash commands' do
+ it 'says that only markdown is supported, not quick actions' do
expect(rendered).to have_content('Markdown is supported')
end
end
diff --git a/spec/workers/emails_on_push_worker_spec.rb b/spec/workers/emails_on_push_worker_spec.rb
index a0ed85cc0b3..5b6b38e0f76 100644
--- a/spec/workers/emails_on_push_worker_spec.rb
+++ b/spec/workers/emails_on_push_worker_spec.rb
@@ -71,7 +71,9 @@ describe EmailsOnPushWorker do
end
context "when there are no errors in sending" do
- before { perform }
+ before do
+ perform
+ end
it "sends a mail with the correct subject" do
expect(email.subject).to include('adds bar folder and branch-test text file')
diff --git a/spec/workers/expire_build_artifacts_worker_spec.rb b/spec/workers/expire_build_artifacts_worker_spec.rb
index 73cbadc13d9..b47b4a02a68 100644
--- a/spec/workers/expire_build_artifacts_worker_spec.rb
+++ b/spec/workers/expire_build_artifacts_worker_spec.rb
@@ -5,10 +5,14 @@ describe ExpireBuildArtifactsWorker do
let(:worker) { described_class.new }
- before { Sidekiq::Worker.clear_all }
+ before do
+ Sidekiq::Worker.clear_all
+ end
describe '#perform' do
- before { build }
+ before do
+ build
+ end
subject! do
Sidekiq::Testing.fake! { worker.perform }
diff --git a/spec/workers/git_garbage_collect_worker_spec.rb b/spec/workers/git_garbage_collect_worker_spec.rb
index 8c5303b61cc..f443bb2c9b4 100644
--- a/spec/workers/git_garbage_collect_worker_spec.rb
+++ b/spec/workers/git_garbage_collect_worker_spec.rb
@@ -23,7 +23,9 @@ describe GitGarbageCollectWorker do
end
shared_examples 'gc tasks' do
- before { allow(subject).to receive(:bitmaps_enabled?).and_return(bitmaps_enabled) }
+ before do
+ allow(subject).to receive(:bitmaps_enabled?).and_return(bitmaps_enabled)
+ end
it 'incremental repack adds a new packfile' do
create_objects(project)
diff --git a/spec/workers/post_receive_spec.rb b/spec/workers/post_receive_spec.rb
index 44163c735ba..6c4df7350ea 100644
--- a/spec/workers/post_receive_spec.rb
+++ b/spec/workers/post_receive_spec.rb
@@ -32,7 +32,7 @@ describe PostReceive do
context "with an absolute path as the project identifier" do
it "searches the project by full path" do
- expect(Project).to receive(:find_by_full_path).with(project.full_path).and_call_original
+ expect(Project).to receive(:find_by_full_path).with(project.full_path, follow_redirects: true).and_call_original
described_class.new.perform(pwd(project), key_id, base64_changes)
end
@@ -89,7 +89,9 @@ describe PostReceive do
end
context "does not create a Ci::Pipeline" do
- before { stub_ci_pipeline_yaml_file(nil) }
+ before do
+ stub_ci_pipeline_yaml_file(nil)
+ end
it { expect{ subject }.not_to change{ Ci::Pipeline.count } }
end
diff --git a/spec/workers/stuck_ci_jobs_worker_spec.rb b/spec/workers/stuck_ci_jobs_worker_spec.rb
index 8434b0c8e5b..549635f7f33 100644
--- a/spec/workers/stuck_ci_jobs_worker_spec.rb
+++ b/spec/workers/stuck_ci_jobs_worker_spec.rb
@@ -34,7 +34,9 @@ describe StuckCiJobsWorker do
let(:status) { 'pending' }
context 'when job is not stuck' do
- before { allow_any_instance_of(Ci::Build).to receive(:stuck?).and_return(false) }
+ before do
+ allow_any_instance_of(Ci::Build).to receive(:stuck?).and_return(false)
+ end
context 'when job was not updated for more than 1 day ago' do
let(:updated_at) { 2.days.ago }
@@ -53,7 +55,9 @@ describe StuckCiJobsWorker do
end
context 'when job is stuck' do
- before { allow_any_instance_of(Ci::Build).to receive(:stuck?).and_return(true) }
+ before do
+ allow_any_instance_of(Ci::Build).to receive(:stuck?).and_return(true)
+ end
context 'when job was not updated for more than 1 hour ago' do
let(:updated_at) { 2.hours.ago }
@@ -93,7 +97,9 @@ describe StuckCiJobsWorker do
let(:status) { 'running' }
let(:updated_at) { 2.days.ago }
- before { job.project.update(pending_delete: true) }
+ before do
+ job.project.update(pending_delete: true)
+ end
it 'does not drop job' do
expect_any_instance_of(Ci::Build).not_to receive(:drop)
diff --git a/yarn.lock b/yarn.lock
index 1db64aead8d..b902d5235d0 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -882,20 +882,20 @@ bn.js@^4.0.0, bn.js@^4.1.0, bn.js@^4.1.1, bn.js@^4.4.0:
version "4.11.6"
resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.11.6.tgz#53344adb14617a13f6e8dd2ce28905d1c0ba3215"
-body-parser@^1.12.4:
- version "1.16.0"
- resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.16.0.tgz#924a5e472c6229fb9d69b85a20d5f2532dec788b"
+body-parser@^1.16.1:
+ version "1.17.2"
+ resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.17.2.tgz#f8892abc8f9e627d42aedafbca66bf5ab99104ee"
dependencies:
bytes "2.4.0"
content-type "~1.0.2"
- debug "2.6.0"
+ debug "2.6.7"
depd "~1.1.0"
- http-errors "~1.5.1"
+ http-errors "~1.6.1"
iconv-lite "0.4.15"
on-finished "~2.3.0"
- qs "6.2.1"
+ qs "6.4.0"
raw-body "~2.2.0"
- type-is "~1.6.14"
+ type-is "~1.6.15"
boom@2.x.x:
version "2.10.1"
@@ -1265,14 +1265,6 @@ concat-map@0.0.1:
version "0.0.1"
resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b"
-concat-stream@1.5.0:
- version "1.5.0"
- resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.5.0.tgz#53f7d43c51c5e43f81c8fdd03321c631be68d611"
- dependencies:
- inherits "~2.0.1"
- readable-stream "~2.0.0"
- typedarray "~0.0.5"
-
concat-stream@^1.4.6:
version "1.6.0"
resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.0.tgz#0aac662fd52be78964d5532f694784e70110acf7"
@@ -1305,12 +1297,12 @@ connect-history-api-fallback@^1.3.0:
version "1.3.0"
resolved "https://registry.yarnpkg.com/connect-history-api-fallback/-/connect-history-api-fallback-1.3.0.tgz#e51d17f8f0ef0db90a64fdb47de3051556e9f169"
-connect@^3.3.5:
- version "3.5.0"
- resolved "https://registry.yarnpkg.com/connect/-/connect-3.5.0.tgz#b357525a0b4c1f50599cd983e1d9efeea9677198"
+connect@^3.6.0:
+ version "3.6.2"
+ resolved "https://registry.yarnpkg.com/connect/-/connect-3.6.2.tgz#694e8d20681bfe490282c8ab886be98f09f42fe7"
dependencies:
- debug "~2.2.0"
- finalhandler "0.5.0"
+ debug "2.6.7"
+ finalhandler "1.0.3"
parseurl "~1.3.1"
utils-merge "1.0.0"
@@ -1538,10 +1530,6 @@ de-indent@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/de-indent/-/de-indent-1.0.2.tgz#b2038e846dc33baa5796128d0804b455b8c1e21d"
-debug@0.7.4:
- version "0.7.4"
- resolved "https://registry.yarnpkg.com/debug/-/debug-0.7.4.tgz#06e1ea8082c2cb14e39806e22e2f6f757f92af39"
-
debug@2.2.0, debug@~2.2.0:
version "2.2.0"
resolved "https://registry.yarnpkg.com/debug/-/debug-2.2.0.tgz#f87057e995b1a1f6ae6a4960664137bc56f039da"
@@ -1554,18 +1542,18 @@ debug@2.3.3:
dependencies:
ms "0.7.2"
-debug@2.6.0, debug@^2.1.0, debug@^2.1.1, debug@^2.2.0:
- version "2.6.0"
- resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.0.tgz#bc596bcabe7617f11d9fa15361eded5608b8499b"
- dependencies:
- ms "0.7.2"
-
debug@2.6.7:
version "2.6.7"
resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.7.tgz#92bad1f6d05bbb6bba22cca88bcd0ec894c2861e"
dependencies:
ms "2.0.0"
+debug@^2.1.0, debug@^2.1.1, debug@^2.2.0:
+ version "2.6.0"
+ resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.0.tgz#bc596bcabe7617f11d9fa15361eded5608b8499b"
+ dependencies:
+ ms "0.7.2"
+
decamelize@^1.0.0, decamelize@^1.1.1, decamelize@^1.1.2:
version "1.2.0"
resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290"
@@ -1778,9 +1766,9 @@ end-of-stream@1.0.0:
dependencies:
once "~1.3.0"
-engine.io-client@1.8.2:
- version "1.8.2"
- resolved "https://registry.yarnpkg.com/engine.io-client/-/engine.io-client-1.8.2.tgz#c38767547f2a7d184f5752f6f0ad501006703766"
+engine.io-client@1.8.3:
+ version "1.8.3"
+ resolved "https://registry.yarnpkg.com/engine.io-client/-/engine.io-client-1.8.3.tgz#1798ed93451246453d4c6f635d7a201fe940d5ab"
dependencies:
component-emitter "1.2.1"
component-inherit "0.0.3"
@@ -1791,7 +1779,7 @@ engine.io-client@1.8.2:
parsejson "0.0.3"
parseqs "0.0.5"
parseuri "0.0.5"
- ws "1.1.1"
+ ws "1.1.2"
xmlhttprequest-ssl "1.5.3"
yeast "0.1.2"
@@ -1806,16 +1794,16 @@ engine.io-parser@1.3.2:
has-binary "0.1.7"
wtf-8 "1.0.0"
-engine.io@1.8.2:
- version "1.8.2"
- resolved "https://registry.yarnpkg.com/engine.io/-/engine.io-1.8.2.tgz#6b59be730b348c0125b0a4589de1c355abcf7a7e"
+engine.io@1.8.3:
+ version "1.8.3"
+ resolved "https://registry.yarnpkg.com/engine.io/-/engine.io-1.8.3.tgz#8de7f97895d20d39b85f88eeee777b2bd42b13d4"
dependencies:
accepts "1.3.3"
base64id "1.0.0"
cookie "0.3.1"
debug "2.3.3"
engine.io-parser "1.3.2"
- ws "1.1.1"
+ ws "1.1.2"
enhanced-resolve@^3.0.0:
version "3.1.0"
@@ -1884,10 +1872,6 @@ es6-promise@^3.0.2, es6-promise@~3.0.2:
version "3.0.2"
resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-3.0.2.tgz#010d5858423a5f118979665f46486a95c6ee2bb6"
-es6-promise@~4.0.3:
- version "4.0.5"
- resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-4.0.5.tgz#7882f30adde5b240ccfa7f7d78c548330951ae42"
-
es6-set@~0.1.3:
version "0.1.4"
resolved "https://registry.yarnpkg.com/es6-set/-/es6-set-0.1.4.tgz#9516b6761c2964b92ff479456233a247dc707ce8"
@@ -2219,15 +2203,6 @@ extglob@^0.3.1:
dependencies:
is-extglob "^1.0.0"
-extract-zip@~1.5.0:
- version "1.5.0"
- resolved "https://registry.yarnpkg.com/extract-zip/-/extract-zip-1.5.0.tgz#92ccf6d81ef70a9fa4c1747114ccef6d8688a6c4"
- dependencies:
- concat-stream "1.5.0"
- debug "0.7.4"
- mkdirp "0.5.0"
- yauzl "2.4.1"
-
extsprintf@1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.0.2.tgz#e1080e0658e300b06294990cc70e1502235fd550"
@@ -2258,12 +2233,6 @@ faye-websocket@~0.7.3:
dependencies:
websocket-driver ">=0.3.6"
-fd-slicer@~1.0.1:
- version "1.0.1"
- resolved "https://registry.yarnpkg.com/fd-slicer/-/fd-slicer-1.0.1.tgz#8b5bcbd9ec327c5041bf9ab023fd6750f1177e65"
- dependencies:
- pend "~1.2.0"
-
figures@^1.3.5:
version "1.7.0"
resolved "https://registry.yarnpkg.com/figures/-/figures-1.7.0.tgz#cbe1e3affcf1cd44b80cadfed28dc793a9701d2e"
@@ -2313,17 +2282,7 @@ fill-range@^2.1.0:
repeat-element "^1.1.2"
repeat-string "^1.5.2"
-finalhandler@0.5.0:
- version "0.5.0"
- resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-0.5.0.tgz#e9508abece9b6dba871a6942a1d7911b91911ac7"
- dependencies:
- debug "~2.2.0"
- escape-html "~1.0.3"
- on-finished "~2.3.0"
- statuses "~1.3.0"
- unpipe "~1.0.0"
-
-finalhandler@~1.0.3:
+finalhandler@1.0.3, finalhandler@~1.0.3:
version "1.0.3"
resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.0.3.tgz#ef47e77950e999780e86022a560e3217e0d0cc89"
dependencies:
@@ -2407,13 +2366,11 @@ from@~0:
version "0.1.7"
resolved "https://registry.yarnpkg.com/from/-/from-0.1.7.tgz#83c60afc58b9c56997007ed1a768b3ab303a44fe"
-fs-extra@~1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-1.0.0.tgz#cd3ce5f7e7cb6145883fcae3191e9877f8587950"
+fs-access@^1.0.0:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/fs-access/-/fs-access-1.0.1.tgz#d6a87f262271cefebec30c553407fb995da8777a"
dependencies:
- graceful-fs "^4.1.2"
- jsonfile "^2.1.0"
- klaw "^1.0.0"
+ null-check "^1.0.0"
fs.realpath@^1.0.0:
version "1.0.0"
@@ -2551,7 +2508,7 @@ got@^3.2.0:
read-all-stream "^3.0.0"
timed-out "^2.0.0"
-graceful-fs@^4.1.11, graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.1.9:
+graceful-fs@^4.1.11, graceful-fs@^4.1.2:
version "4.1.11"
resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.1.11.tgz#0e8bdfe4d1ddb8854d64e04ea7c00e2a026e5658"
@@ -2628,13 +2585,6 @@ hash.js@^1.0.0:
dependencies:
inherits "^2.0.1"
-hasha@~2.2.0:
- version "2.2.0"
- resolved "https://registry.yarnpkg.com/hasha/-/hasha-2.2.0.tgz#78d7cbfc1e6d66303fe79837365984517b2f6ee1"
- dependencies:
- is-stream "^1.0.1"
- pinkie-promise "^2.0.0"
-
hawk@~3.1.3:
version "3.1.3"
resolved "https://registry.yarnpkg.com/hawk/-/hawk-3.1.3.tgz#078444bd7c1640b0fe540d2c9b73d59678e8e1c4"
@@ -2695,7 +2645,7 @@ http-deceiver@^1.2.4:
version "1.2.7"
resolved "https://registry.yarnpkg.com/http-deceiver/-/http-deceiver-1.2.7.tgz#fa7168944ab9a519d337cb0bec7284dc3e723d87"
-http-errors@~1.5.0, http-errors@~1.5.1:
+http-errors@~1.5.0:
version "1.5.1"
resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.5.1.tgz#788c0d2c1de2c81b9e6e8c01843b6b97eb920750"
dependencies:
@@ -2987,7 +2937,7 @@ is-resolvable@^1.0.0:
dependencies:
tryit "^1.0.1"
-is-stream@^1.0.0, is-stream@^1.0.1:
+is-stream@^1.0.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44"
@@ -3124,9 +3074,9 @@ istanbul@^0.4.5:
which "^1.1.1"
wordwrap "^1.0.0"
-jasmine-core@^2.5.2:
- version "2.5.2"
- resolved "https://registry.yarnpkg.com/jasmine-core/-/jasmine-core-2.5.2.tgz#6f61bd79061e27f43e6f9355e44b3c6cab6ff297"
+jasmine-core@^2.6.3:
+ version "2.6.3"
+ resolved "https://registry.yarnpkg.com/jasmine-core/-/jasmine-core-2.6.3.tgz#45072950e4a42b1e322fe55c001100a465d77815"
jasmine-jquery@^2.1.1:
version "2.1.1"
@@ -3225,12 +3175,6 @@ json5@^0.5.0, json5@^0.5.1:
version "0.5.1"
resolved "https://registry.yarnpkg.com/json5/-/json5-0.5.1.tgz#1eade7acc012034ad84e2396767ead9fa5495821"
-jsonfile@^2.1.0:
- version "2.4.0"
- resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-2.4.0.tgz#3736a2b428b87bbda0cc83b53fa3d633a35c2ae8"
- optionalDependencies:
- graceful-fs "^4.1.6"
-
jsonify@~0.0.0:
version "0.0.0"
resolved "https://registry.yarnpkg.com/jsonify/-/jsonify-0.0.0.tgz#2c74b6ee41d93ca51b7b5aaee8f503631d252a73"
@@ -3261,6 +3205,13 @@ jszip@^3.1.3:
pako "~1.0.2"
readable-stream "~2.0.6"
+karma-chrome-launcher@^2.1.1:
+ version "2.1.1"
+ resolved "https://registry.yarnpkg.com/karma-chrome-launcher/-/karma-chrome-launcher-2.1.1.tgz#216879c68ac04d8d5140e99619ba04b59afd46cf"
+ dependencies:
+ fs-access "^1.0.0"
+ which "^1.2.1"
+
karma-coverage-istanbul-reporter@^0.2.0:
version "0.2.0"
resolved "https://registry.yarnpkg.com/karma-coverage-istanbul-reporter/-/karma-coverage-istanbul-reporter-0.2.0.tgz#5766263338adeb0026f7e4ac7a89a5f056c5642c"
@@ -3277,13 +3228,6 @@ karma-mocha-reporter@^2.2.2:
dependencies:
chalk "1.1.3"
-karma-phantomjs-launcher@^1.0.2:
- version "1.0.2"
- resolved "https://registry.yarnpkg.com/karma-phantomjs-launcher/-/karma-phantomjs-launcher-1.0.2.tgz#19e1041498fd75563ed86730a22c1fe579fa8fb1"
- dependencies:
- lodash "^4.0.1"
- phantomjs-prebuilt "^2.1.7"
-
karma-sourcemap-loader@^0.3.7:
version "0.3.7"
resolved "https://registry.yarnpkg.com/karma-sourcemap-loader/-/karma-sourcemap-loader-0.3.7.tgz#91322c77f8f13d46fed062b042e1009d4c4505d8"
@@ -3300,16 +3244,16 @@ karma-webpack@^2.0.2:
source-map "^0.1.41"
webpack-dev-middleware "^1.0.11"
-karma@^1.4.1:
- version "1.4.1"
- resolved "https://registry.yarnpkg.com/karma/-/karma-1.4.1.tgz#41981a71d54237606b0a3ea8c58c90773f41650e"
+karma@^1.7.0:
+ version "1.7.0"
+ resolved "https://registry.yarnpkg.com/karma/-/karma-1.7.0.tgz#6f7a1a406446fa2e187ec95398698f4cee476269"
dependencies:
bluebird "^3.3.0"
- body-parser "^1.12.4"
+ body-parser "^1.16.1"
chokidar "^1.4.1"
colors "^1.1.0"
combine-lists "^1.0.0"
- connect "^3.3.5"
+ connect "^3.6.0"
core-js "^2.2.0"
di "^0.0.1"
dom-serialize "^2.2.0"
@@ -3321,20 +3265,16 @@ karma@^1.4.1:
lodash "^3.8.0"
log4js "^0.6.31"
mime "^1.3.4"
- minimatch "^3.0.0"
+ minimatch "^3.0.2"
optimist "^0.6.1"
qjobs "^1.1.4"
range-parser "^1.2.0"
- rimraf "^2.3.3"
+ rimraf "^2.6.0"
safe-buffer "^5.0.1"
- socket.io "1.7.2"
+ socket.io "1.7.3"
source-map "^0.5.3"
- tmp "0.0.28"
- useragent "^2.1.10"
-
-kew@~0.7.0:
- version "0.7.0"
- resolved "https://registry.yarnpkg.com/kew/-/kew-0.7.0.tgz#79d93d2d33363d6fdd2970b335d9141ad591d79b"
+ tmp "0.0.31"
+ useragent "^2.1.12"
kind-of@^3.0.2:
version "3.1.0"
@@ -3342,12 +3282,6 @@ kind-of@^3.0.2:
dependencies:
is-buffer "^1.0.2"
-klaw@^1.0.0:
- version "1.3.1"
- resolved "https://registry.yarnpkg.com/klaw/-/klaw-1.3.1.tgz#4088433b46b3b1ba259d78785d8e96f73ba02439"
- optionalDependencies:
- graceful-fs "^4.1.9"
-
latest-version@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/latest-version/-/latest-version-1.0.1.tgz#72cfc46e3e8d1be651e1ebb54ea9f6ea96f374bb"
@@ -3556,7 +3490,7 @@ lodash@^3.8.0:
version "3.10.1"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-3.10.1.tgz#5bf45e8e49ba4189e17d482789dfd15bd140b7b6"
-lodash@^4.0.0, lodash@^4.0.1, lodash@^4.14.0, lodash@^4.17.2, lodash@^4.17.4, lodash@^4.2.0, lodash@^4.3.0, lodash@^4.5.0:
+lodash@^4.0.0, lodash@^4.14.0, lodash@^4.17.2, lodash@^4.17.4, lodash@^4.2.0, lodash@^4.3.0, lodash@^4.5.0:
version "4.17.4"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.4.tgz#78203a4d1c328ae1d86dca6460e369b57f4055ae"
@@ -3704,12 +3638,6 @@ minimist@^1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284"
-mkdirp@0.5.0:
- version "0.5.0"
- resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.0.tgz#1d73076a6df986cd9344e15e71fcc05a4c9abf12"
- dependencies:
- minimist "0.0.8"
-
mkdirp@0.5.x, "mkdirp@>=0.5 0", mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@~0.5.0, mkdirp@~0.5.1:
version "0.5.1"
resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903"
@@ -3907,6 +3835,10 @@ npmlog@^4.0.1:
gauge "~2.7.1"
set-blocking "~2.0.0"
+null-check@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/null-check/-/null-check-1.0.0.tgz#977dffd7176012b9ec30d2a39db5cf72a0439edd"
+
num2fraction@^1.2.2:
version "1.2.2"
resolved "https://registry.yarnpkg.com/num2fraction/-/num2fraction-1.2.2.tgz#6f682b6a027a4e9ddfa4564cd2589d1d4e669ede"
@@ -4165,24 +4097,6 @@ pdfjs-dist@^1.8.252:
node-ensure "^0.0.0"
worker-loader "^0.8.0"
-pend@~1.2.0:
- version "1.2.0"
- resolved "https://registry.yarnpkg.com/pend/-/pend-1.2.0.tgz#7a57eb550a6783f9115331fcf4663d5c8e007a50"
-
-phantomjs-prebuilt@^2.1.7:
- version "2.1.14"
- resolved "https://registry.yarnpkg.com/phantomjs-prebuilt/-/phantomjs-prebuilt-2.1.14.tgz#d53d311fcfb7d1d08ddb24014558f1188c516da0"
- dependencies:
- es6-promise "~4.0.3"
- extract-zip "~1.5.0"
- fs-extra "~1.0.0"
- hasha "~2.2.0"
- kew "~0.7.0"
- progress "~1.1.8"
- request "~2.79.0"
- request-progress "~2.0.1"
- which "~1.2.10"
-
pify@^2.0.0:
version "2.3.0"
resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c"
@@ -4518,7 +4432,7 @@ process@^0.11.0, process@~0.11.0:
version "0.11.9"
resolved "https://registry.yarnpkg.com/process/-/process-0.11.9.tgz#7bd5ad21aa6253e7da8682264f1e11d11c0318c1"
-progress@^1.1.8, progress@~1.1.8:
+progress@^1.1.8:
version "1.1.8"
resolved "https://registry.yarnpkg.com/progress/-/progress-1.1.8.tgz#e260c78f6161cdd9b0e56cc3e0a85de17c7a57be"
@@ -4573,10 +4487,6 @@ qjobs@^1.1.4:
version "1.1.5"
resolved "https://registry.yarnpkg.com/qjobs/-/qjobs-1.1.5.tgz#659de9f2cf8dcc27a1481276f205377272382e73"
-qs@6.2.1:
- version "6.2.1"
- resolved "https://registry.yarnpkg.com/qs/-/qs-6.2.1.tgz#ce03c5ff0935bc1d9d69a9f14cbd18e568d67625"
-
qs@6.4.0:
version "6.4.0"
resolved "https://registry.yarnpkg.com/qs/-/qs-6.4.0.tgz#13e26d28ad6b0ffaa91312cd3bf708ed351e7233"
@@ -4701,7 +4611,7 @@ readable-stream@^2.0.0, "readable-stream@^2.0.0 || ^1.1.13", readable-stream@^2.
string_decoder "~0.10.x"
util-deprecate "~1.0.1"
-readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.0.5, readable-stream@~2.0.0, readable-stream@~2.0.6:
+readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.0.5, readable-stream@~2.0.6:
version "2.0.6"
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.0.6.tgz#8f90341e68a53ccc928788dacfcd11b36eb9b78e"
dependencies:
@@ -4855,13 +4765,7 @@ repeating@^2.0.0:
dependencies:
is-finite "^1.0.0"
-request-progress@~2.0.1:
- version "2.0.1"
- resolved "https://registry.yarnpkg.com/request-progress/-/request-progress-2.0.1.tgz#5d36bb57961c673aa5b788dbc8141fdf23b44e08"
- dependencies:
- throttleit "^1.0.0"
-
-request@^2.79.0, request@~2.79.0:
+request@^2.79.0:
version "2.79.0"
resolved "https://registry.yarnpkg.com/request/-/request-2.79.0.tgz#4dfe5bf6be8b8cdc37fcf93e04b65577722710de"
dependencies:
@@ -4934,7 +4838,13 @@ right-align@^0.1.1:
dependencies:
align-text "^0.1.1"
-rimraf@2, rimraf@^2.2.8, rimraf@^2.3.3, rimraf@^2.4.3, rimraf@^2.4.4, rimraf@~2.5.1, rimraf@~2.5.4:
+rimraf@2, rimraf@^2.2.8, rimraf@^2.4.3, rimraf@^2.4.4, rimraf@^2.6.0:
+ version "2.6.1"
+ resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.1.tgz#c2338ec643df7a1b7fe5c54fa86f57428a55f33d"
+ dependencies:
+ glob "^7.0.5"
+
+rimraf@~2.5.1, rimraf@~2.5.4:
version "2.5.4"
resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.5.4.tgz#96800093cbf1a0c86bd95b4625467535c29dfa04"
dependencies:
@@ -5094,15 +5004,15 @@ socket.io-adapter@0.5.0:
debug "2.3.3"
socket.io-parser "2.3.1"
-socket.io-client@1.7.2:
- version "1.7.2"
- resolved "https://registry.yarnpkg.com/socket.io-client/-/socket.io-client-1.7.2.tgz#39fdb0c3dd450e321b7e40cfd83612ec533dd644"
+socket.io-client@1.7.3:
+ version "1.7.3"
+ resolved "https://registry.yarnpkg.com/socket.io-client/-/socket.io-client-1.7.3.tgz#b30e86aa10d5ef3546601c09cde4765e381da377"
dependencies:
backo2 "1.0.2"
component-bind "1.0.0"
component-emitter "1.2.1"
debug "2.3.3"
- engine.io-client "1.8.2"
+ engine.io-client "1.8.3"
has-binary "0.1.7"
indexof "0.0.1"
object-component "0.0.3"
@@ -5119,16 +5029,16 @@ socket.io-parser@2.3.1:
isarray "0.0.1"
json3 "3.3.2"
-socket.io@1.7.2:
- version "1.7.2"
- resolved "https://registry.yarnpkg.com/socket.io/-/socket.io-1.7.2.tgz#83bbbdf2e79263b378900da403e7843e05dc3b71"
+socket.io@1.7.3:
+ version "1.7.3"
+ resolved "https://registry.yarnpkg.com/socket.io/-/socket.io-1.7.3.tgz#b8af9caba00949e568e369f1327ea9be9ea2461b"
dependencies:
debug "2.3.3"
- engine.io "1.8.2"
+ engine.io "1.8.3"
has-binary "0.1.7"
object-assign "4.1.0"
socket.io-adapter "0.5.0"
- socket.io-client "1.7.2"
+ socket.io-client "1.7.3"
socket.io-parser "2.3.1"
sockjs-client@1.0.1:
@@ -5269,7 +5179,7 @@ stats-webpack-plugin@^0.4.3:
version "0.4.3"
resolved "https://registry.yarnpkg.com/stats-webpack-plugin/-/stats-webpack-plugin-0.4.3.tgz#b2f618202f28dd04ab47d7ecf54ab846137b7aea"
-"statuses@>= 1.3.1 < 2", statuses@~1.3.0, statuses@~1.3.1:
+"statuses@>= 1.3.1 < 2", statuses@~1.3.1:
version "1.3.1"
resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.3.1.tgz#faf51b9eb74aaef3b3acf4ad5f61abf24cb7b93e"
@@ -5449,10 +5359,6 @@ three@^0.84.0:
version "0.84.0"
resolved "https://registry.yarnpkg.com/three/-/three-0.84.0.tgz#95be85a55a0fa002aa625ed559130957dcffd918"
-throttleit@^1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/throttleit/-/throttleit-1.0.0.tgz#9e785836daf46743145a5984b6268d828528ac6c"
-
through@2, through@^2.3.6, through@~2.3, through@~2.3.1:
version "2.3.8"
resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5"
@@ -5481,9 +5387,9 @@ tiny-emitter@^1.0.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/tiny-emitter/-/tiny-emitter-1.1.0.tgz#ab405a21ffed814a76c19739648093d70654fecb"
-tmp@0.0.28, tmp@0.0.x:
- version "0.0.28"
- resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.28.tgz#172735b7f614ea7af39664fa84cf0de4e515d120"
+tmp@0.0.31, tmp@0.0.x:
+ version "0.0.31"
+ resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.31.tgz#8f38ab9438e17315e5dbd8b3657e8bfb277ae4a7"
dependencies:
os-tmpdir "~1.0.1"
@@ -5541,14 +5447,14 @@ type-check@~0.3.2:
dependencies:
prelude-ls "~1.1.2"
-type-is@~1.6.14, type-is@~1.6.15:
+type-is@~1.6.15:
version "1.6.15"
resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.15.tgz#cab10fb4909e441c82842eafe1ad646c81804410"
dependencies:
media-typer "0.3.0"
mime-types "~2.1.15"
-typedarray@^0.0.6, typedarray@~0.0.5:
+typedarray@^0.0.6:
version "0.0.6"
resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777"
@@ -5653,9 +5559,9 @@ user-home@^2.0.0:
dependencies:
os-homedir "^1.0.0"
-useragent@^2.1.10:
- version "2.1.12"
- resolved "https://registry.yarnpkg.com/useragent/-/useragent-2.1.12.tgz#aa7da6cdc48bdc37ba86790871a7321d64edbaa2"
+useragent@^2.1.12:
+ version "2.1.13"
+ resolved "https://registry.yarnpkg.com/useragent/-/useragent-2.1.13.tgz#bba43e8aa24d5ceb83c2937473e102e21df74c10"
dependencies:
lru-cache "2.2.x"
tmp "0.0.x"
@@ -5883,7 +5789,7 @@ which-module@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/which-module/-/which-module-1.0.0.tgz#bba63ca861948994ff307736089e3b96026c2a4f"
-which@^1.1.1, which@~1.2.10:
+which@^1.1.1, which@^1.2.1:
version "1.2.12"
resolved "https://registry.yarnpkg.com/which/-/which-1.2.12.tgz#de67b5e450269f194909ef23ece4ebe416fa1192"
dependencies:
@@ -5942,9 +5848,9 @@ write@^0.2.1:
dependencies:
mkdirp "^0.5.1"
-ws@1.1.1:
- version "1.1.1"
- resolved "https://registry.yarnpkg.com/ws/-/ws-1.1.1.tgz#082ddb6c641e85d4bb451f03d52f06eabdb1f018"
+ws@1.1.2:
+ version "1.1.2"
+ resolved "https://registry.yarnpkg.com/ws/-/ws-1.1.2.tgz#8a244fa052401e08c9886cf44a85189e1fd4067f"
dependencies:
options ">=0.0.5"
ultron "1.0.x"
@@ -6015,12 +5921,6 @@ yargs@~3.10.0:
decamelize "^1.0.0"
window-size "0.1.0"
-yauzl@2.4.1:
- version "2.4.1"
- resolved "https://registry.yarnpkg.com/yauzl/-/yauzl-2.4.1.tgz#9528f442dab1b2284e58b4379bb194e22e0c4005"
- dependencies:
- fd-slicer "~1.0.1"
-
yeast@0.1.2:
version "0.1.2"
resolved "https://registry.yarnpkg.com/yeast/-/yeast-0.1.2.tgz#008e06d8094320c372dbc2f8ed76a0ca6c8ac419"