summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitlab-ci.yml39
-rw-r--r--CHANGELOG53
-rw-r--r--Gemfile77
-rw-r--r--Gemfile.lock187
-rw-r--r--Guardfile27
-rw-r--r--app/assets/images/authbuttons/google_64.pngbin3169 -> 5281 bytes
-rw-r--r--app/assets/images/authbuttons/twitter_64.pngbin3054 -> 4835 bytes
-rw-r--r--app/assets/javascripts/application.js.coffee25
-rw-r--r--app/assets/javascripts/calendar.js.coffee1
-rw-r--r--app/assets/javascripts/dropzone_input.js.coffee52
-rw-r--r--app/assets/javascripts/extensions/jquery.js.coffee22
-rw-r--r--app/assets/javascripts/gfm_auto_complete.js.coffee20
-rw-r--r--app/assets/javascripts/issue.js.coffee1
-rw-r--r--app/assets/javascripts/merge_request.js.coffee164
-rw-r--r--app/assets/javascripts/merge_request_widget.js.coffee58
-rw-r--r--app/assets/javascripts/notes.js.coffee10
-rw-r--r--app/assets/javascripts/profile.js.coffee4
-rw-r--r--app/assets/javascripts/shortcuts_issuable.coffee2
-rw-r--r--app/assets/javascripts/stat_graph_contributors.js.coffee1
-rw-r--r--app/assets/javascripts/stat_graph_contributors_graph.js.coffee4
-rw-r--r--app/assets/javascripts/zen_mode.js.coffee12
-rw-r--r--app/assets/stylesheets/base/layout.scss2
-rw-r--r--app/assets/stylesheets/base/variables.scss5
-rw-r--r--app/assets/stylesheets/generic/common.scss2
-rw-r--r--app/assets/stylesheets/generic/forms.scss8
-rw-r--r--app/assets/stylesheets/generic/header.scss247
-rw-r--r--app/assets/stylesheets/generic/lists.scss1
-rw-r--r--app/assets/stylesheets/generic/markdown_area.scss16
-rw-r--r--app/assets/stylesheets/generic/mobile.scss21
-rw-r--r--app/assets/stylesheets/generic/sidebar.scss25
-rw-r--r--app/assets/stylesheets/generic/typography.scss7
-rw-r--r--app/assets/stylesheets/generic/zen.scss15
-rw-r--r--app/assets/stylesheets/pages/dashboard.scss2
-rw-r--r--app/assets/stylesheets/pages/issues.scss6
-rw-r--r--app/assets/stylesheets/pages/merge_requests.scss22
-rw-r--r--app/assets/stylesheets/pages/note_form.scss6
-rw-r--r--app/assets/stylesheets/pages/profile.scss5
-rw-r--r--app/assets/stylesheets/pages/projects.scss33
-rw-r--r--app/assets/stylesheets/themes/gitlab-theme.scss13
-rw-r--r--app/controllers/admin/application_settings_controller.rb3
-rw-r--r--app/controllers/admin/deploy_keys_controller.rb7
-rw-r--r--app/controllers/admin/groups_controller.rb2
-rw-r--r--app/controllers/admin/projects_controller.rb2
-rw-r--r--app/controllers/admin/users_controller.rb6
-rw-r--r--app/controllers/application_controller.rb2
-rw-r--r--app/controllers/groups/group_members_controller.rb6
-rw-r--r--app/controllers/groups_controller.rb2
-rw-r--r--app/controllers/oauth/applications_controller.rb8
-rw-r--r--app/controllers/omniauth_callbacks_controller.rb5
-rw-r--r--app/controllers/projects/blob_controller.rb60
-rw-r--r--app/controllers/projects/deploy_keys_controller.rb4
-rw-r--r--app/controllers/projects/merge_requests_controller.rb33
-rw-r--r--app/controllers/projects/project_members_controller.rb8
-rw-r--r--app/controllers/projects_controller.rb29
-rw-r--r--app/controllers/registrations_controller.rb2
-rw-r--r--app/controllers/sessions_controller.rb16
-rw-r--r--app/helpers/application_helper.rb4
-rw-r--r--app/helpers/application_settings_helper.rb4
-rw-r--r--app/helpers/blob_helper.rb10
-rw-r--r--app/helpers/gitlab_markdown_helper.rb46
-rw-r--r--app/helpers/projects_helper.rb12
-rw-r--r--app/helpers/tab_helper.rb2
-rw-r--r--app/models/application_setting.rb12
-rw-r--r--app/models/commit.rb6
-rw-r--r--app/models/concerns/mentionable.rb8
-rw-r--r--app/models/concerns/taskable.rb1
-rw-r--r--app/models/group.rb4
-rw-r--r--app/models/merge_request.rb13
-rw-r--r--app/models/namespace.rb15
-rw-r--r--app/models/project.rb2
-rw-r--r--app/models/project_services/gitlab_ci_service.rb14
-rw-r--r--app/models/repository.rb53
-rw-r--r--app/models/user.rb20
-rw-r--r--app/services/delete_user_service.rb16
-rw-r--r--app/services/destroy_group_service.rb17
-rw-r--r--app/services/files/base_service.rb80
-rw-r--r--app/services/files/create_service.rb44
-rw-r--r--app/services/files/delete_service.rb33
-rw-r--r--app/services/files/update_service.rb36
-rw-r--r--app/services/git_push_service.rb3
-rw-r--r--app/services/issuable_base_service.rb6
-rw-r--r--app/services/merge_requests/update_service.rb14
-rw-r--r--app/services/projects/destroy_service.rb67
-rw-r--r--app/services/projects/participants_service.rb4
-rw-r--r--app/services/search/global_service.rb2
-rw-r--r--app/services/system_note_service.rb19
-rw-r--r--app/views/admin/application_settings/_form.html.haml19
-rw-r--r--app/views/admin/broadcast_messages/index.html.haml4
-rw-r--r--app/views/admin/deploy_keys/index.html.haml3
-rw-r--r--app/views/admin/deploy_keys/show.html.haml35
-rw-r--r--app/views/admin/groups/index.html.haml2
-rw-r--r--app/views/admin/users/index.html.haml9
-rw-r--r--app/views/admin/users/show.html.haml24
-rw-r--r--app/views/dashboard/_activities.html.haml1
-rw-r--r--app/views/dashboard/groups/index.html.haml13
-rw-r--r--app/views/devise/sessions/_new_ldap.html.haml5
-rw-r--r--app/views/events/event/_push.html.haml4
-rw-r--r--app/views/explore/groups/index.html.haml2
-rw-r--r--app/views/explore/projects/_filter.html.haml2
-rw-r--r--app/views/groups/group_members/_group_member.html.haml3
-rw-r--r--app/views/groups/group_members/index.html.haml2
-rw-r--r--app/views/groups/show.html.haml3
-rw-r--r--app/views/layouts/_head.html.haml2
-rw-r--r--app/views/layouts/_head_panel.html.haml42
-rw-r--r--app/views/layouts/_page.html.haml10
-rw-r--r--app/views/layouts/_public_head_panel.html.haml22
-rw-r--r--app/views/layouts/application.html.haml9
-rw-r--r--app/views/layouts/devise.html.haml2
-rw-r--r--app/views/layouts/errors.html.haml2
-rw-r--r--app/views/layouts/header/_default.html.haml46
-rw-r--r--app/views/layouts/header/_empty.html.haml (renamed from app/views/layouts/_empty_head_panel.html.haml)4
-rw-r--r--app/views/layouts/header/_public.html.haml14
-rw-r--r--app/views/layouts/nav/_project.html.haml7
-rw-r--r--app/views/layouts/nav/_project_settings.html.haml15
-rw-r--r--app/views/layouts/notify.html.haml2
-rw-r--r--app/views/layouts/profile.html.haml4
-rw-r--r--app/views/layouts/project.html.haml8
-rw-r--r--app/views/notify/repository_push_email.html.haml2
-rw-r--r--app/views/profiles/accounts/show.html.haml121
-rw-r--r--app/views/profiles/applications.html.haml62
-rw-r--r--app/views/profiles/design.html.haml80
-rw-r--r--app/views/profiles/emails/index.html.haml34
-rw-r--r--app/views/profiles/keys/_key.html.haml3
-rw-r--r--app/views/profiles/keys/_key_details.html.haml2
-rw-r--r--app/views/profiles/keys/index.html.haml2
-rw-r--r--app/views/profiles/notifications/show.html.haml2
-rw-r--r--app/views/profiles/passwords/edit.html.haml3
-rw-r--r--app/views/profiles/show.html.haml11
-rw-r--r--app/views/profiles/two_factor_auths/new.html.haml13
-rw-r--r--app/views/projects/_aside.html.haml156
-rw-r--r--app/views/projects/_home_panel.html.haml2
-rw-r--r--app/views/projects/_issuable_form.html.haml28
-rw-r--r--app/views/projects/_md_preview.html.haml37
-rw-r--r--app/views/projects/_section.html.haml1
-rw-r--r--app/views/projects/blame/show.html.haml2
-rw-r--r--app/views/projects/blob/_editor.html.haml4
-rw-r--r--app/views/projects/blob/new.html.haml11
-rw-r--r--app/views/projects/deploy_keys/_deploy_key.html.haml16
-rw-r--r--app/views/projects/deploy_keys/show.html.haml14
-rw-r--r--app/views/projects/diffs/_file.html.haml5
-rw-r--r--app/views/projects/edit.html.haml42
-rw-r--r--app/views/projects/issues/_form.html.haml2
-rw-r--r--app/views/projects/issues/_issue.html.haml2
-rw-r--r--app/views/projects/labels/index.html.haml5
-rw-r--r--app/views/projects/merge_requests/_form.html.haml2
-rw-r--r--app/views/projects/merge_requests/_merge_request.html.haml2
-rw-r--r--app/views/projects/merge_requests/_new_submit.html.haml8
-rw-r--r--app/views/projects/merge_requests/_show.html.haml30
-rw-r--r--app/views/projects/merge_requests/automerge.js.haml8
-rw-r--r--app/views/projects/merge_requests/index.html.haml7
-rw-r--r--app/views/projects/merge_requests/show/_mr_accept.html.haml89
-rw-r--r--app/views/projects/merge_requests/show/_mr_ci.html.haml34
-rw-r--r--app/views/projects/merge_requests/show/_remove_source_branch.html.haml17
-rw-r--r--app/views/projects/merge_requests/show/_state_widget.html.haml50
-rw-r--r--app/views/projects/merge_requests/widget/_closed.html.haml9
-rw-r--r--app/views/projects/merge_requests/widget/_heading.html.haml38
-rw-r--r--app/views/projects/merge_requests/widget/_locked.html.haml8
-rw-r--r--app/views/projects/merge_requests/widget/_merged.html.haml41
-rw-r--r--app/views/projects/merge_requests/widget/_open.html.haml29
-rw-r--r--app/views/projects/merge_requests/widget/_show.html.haml20
-rw-r--r--app/views/projects/merge_requests/widget/open/_accept.html.haml34
-rw-r--r--app/views/projects/merge_requests/widget/open/_archived.html.haml2
-rw-r--r--app/views/projects/merge_requests/widget/open/_check.html.haml7
-rw-r--r--app/views/projects/merge_requests/widget/open/_conflicts.html.haml9
-rw-r--r--app/views/projects/merge_requests/widget/open/_missing_branch.html.haml (renamed from app/views/projects/merge_requests/show/_no_accept.html.haml)0
-rw-r--r--app/views/projects/merge_requests/widget/open/_no_satellite.html.haml3
-rw-r--r--app/views/projects/merge_requests/widget/open/_not_allowed.html.haml2
-rw-r--r--app/views/projects/merge_requests/widget/open/_nothing.html.haml8
-rw-r--r--app/views/projects/merge_requests/widget/open/_reload.html.haml1
-rw-r--r--app/views/projects/merge_requests/widget/open/_wip.html.haml13
-rw-r--r--app/views/projects/milestones/_form.html.haml2
-rw-r--r--app/views/projects/notes/_form.html.haml7
-rw-r--r--app/views/projects/project_members/_project_member.html.haml5
-rw-r--r--app/views/projects/project_members/index.html.haml2
-rw-r--r--app/views/projects/update.js.haml2
-rw-r--r--app/views/projects/wikis/_form.html.haml3
-rw-r--r--app/views/search/_form.html.haml2
-rw-r--r--app/views/shared/_file_highlight.html.haml3
-rw-r--r--app/views/shared/_issuable_search_form.html.haml2
-rw-r--r--app/views/shared/_project.html.haml3
-rw-r--r--app/views/users/show.html.haml2
-rwxr-xr-xbin/guard16
-rw-r--r--config/aws.yml.example3
-rw-r--r--config/gitlab.yml.example27
-rw-r--r--config/initializers/1_settings.rb7
-rw-r--r--config/initializers/6_rack_profiler.rb3
-rw-r--r--config/initializers/7_omniauth.rb2
-rw-r--r--config/initializers/rack_attack.rb.example1
-rw-r--r--config/initializers/session_store.rb6
-rw-r--r--config/initializers/smtp_settings.rb.sample1
-rw-r--r--config/resque.yml.example3
-rw-r--r--config/routes.rb6
-rw-r--r--config/unicorn.rb.example3
-rw-r--r--db/migrate/20150529111607_add_user_oauth_applications_to_application_settings.rb5
-rw-r--r--db/migrate/20150529150354_add_after_sign_out_path_for_application_settings.rb5
-rw-r--r--db/migrate/20150609141121_add_session_expire_delay_for_application_settings.rb5
-rw-r--r--db/schema.rb9
-rw-r--r--doc/README.md69
-rw-r--r--doc/api/README.md1
-rw-r--r--doc/api/groups.md370
-rw-r--r--doc/api/merge_requests.md4
-rw-r--r--doc/api/namespaces.md44
-rw-r--r--doc/development/shell_commands.md30
-rw-r--r--doc/install/installation.md13
-rw-r--r--doc/integration/bitbucket.md54
-rw-r--r--doc/integration/ldap.md7
-rw-r--r--doc/integration/omniauth.md1
-rw-r--r--doc/integration/saml.md77
-rw-r--r--doc/operations/README.md1
-rw-r--r--doc/operations/unicorn.md86
-rw-r--r--doc/raketasks/maintenance.md3
-rw-r--r--doc/release/monthly.md9
-rw-r--r--doc/update/6.x-or-7.x-to-7.12.md (renamed from doc/update/6.x-or-7.x-to-7.11.md)25
-rw-r--r--doc/update/7.11-to-7.12.md129
-rw-r--r--doc/workflow/2fa.pngbin0 -> 23415 bytes
-rw-r--r--doc/workflow/2fa_auth.pngbin0 -> 15569 bytes
-rw-r--r--doc/workflow/README.md33
-rw-r--r--doc/workflow/import_projects_from_github.md13
-rw-r--r--doc/workflow/importing/README.md6
-rw-r--r--doc/workflow/importing/bitbucket_importer/bitbucket_import_grant_access.pngbin0 -> 30083 bytes
-rw-r--r--doc/workflow/importing/bitbucket_importer/bitbucket_import_new_project.pngbin0 -> 16502 bytes
-rw-r--r--doc/workflow/importing/bitbucket_importer/bitbucket_import_select_bitbucket.pngbin0 -> 46606 bytes
-rw-r--r--doc/workflow/importing/bitbucket_importer/bitbucket_import_select_project.pngbin0 -> 16121 bytes
-rw-r--r--doc/workflow/importing/github_importer/importer.png (renamed from doc/workflow/github_importer/importer.png)bin39335 -> 39335 bytes
-rw-r--r--doc/workflow/importing/github_importer/new_project_page.png (renamed from doc/workflow/github_importer/new_project_page.png)bin46276 -> 46276 bytes
-rw-r--r--doc/workflow/importing/gitlab_importer/importer.png (renamed from doc/workflow/gitlab_importer/importer.png)bin40778 -> 40778 bytes
-rw-r--r--doc/workflow/importing/gitlab_importer/new_project_page.png (renamed from doc/workflow/gitlab_importer/new_project_page.png)bin72663 -> 72663 bytes
-rw-r--r--doc/workflow/importing/import_projects_from_bitbucket.md26
-rw-r--r--doc/workflow/importing/import_projects_from_github.md18
-rw-r--r--doc/workflow/importing/import_projects_from_gitlab_com.md (renamed from doc/workflow/import_projects_from_gitlab_com.md)0
-rw-r--r--doc/workflow/importing/migrating_from_svn.md (renamed from doc/workflow/migrating_from_svn.md)0
-rw-r--r--doc/workflow/two_factor_authentication.md67
-rw-r--r--doc/workflow/wip_merge_requests.md13
-rw-r--r--doc/workflow/wip_merge_requests/blocked_accept_button.pngbin0 -> 65231 bytes
-rw-r--r--doc/workflow/wip_merge_requests/mark_as_wip.pngbin0 -> 41549 bytes
-rw-r--r--doc/workflow/wip_merge_requests/unmark_as_wip.pngbin0 -> 32151 bytes
-rw-r--r--doc_styleguide.md1
-rw-r--r--docker/README.md25
-rw-r--r--features/admin/deploy_keys.feature5
-rw-r--r--features/dashboard/group.feature3
-rw-r--r--features/project/active_tab.feature12
-rw-r--r--features/project/merge_requests.feature8
-rw-r--r--features/steps/admin/applications.rb22
-rw-r--r--features/steps/admin/broadcast_messages.rb10
-rw-r--r--features/steps/admin/deploy_keys.rb17
-rw-r--r--features/steps/admin/groups.rb22
-rw-r--r--features/steps/admin/logs.rb6
-rw-r--r--features/steps/admin/projects.rb12
-rw-r--r--features/steps/admin/settings.rb18
-rw-r--r--features/steps/admin/users.rb28
-rw-r--r--features/steps/dashboard/archived_projects.rb6
-rw-r--r--features/steps/dashboard/dashboard.rb26
-rw-r--r--features/steps/dashboard/event_filters.rb12
-rw-r--r--features/steps/dashboard/group.rb22
-rw-r--r--features/steps/dashboard/help.rb2
-rw-r--r--features/steps/dashboard/issues.rb4
-rw-r--r--features/steps/dashboard/merge_requests.rb4
-rw-r--r--features/steps/dashboard/new_project.rb12
-rw-r--r--features/steps/dashboard/starred_projects.rb4
-rw-r--r--features/steps/explore/groups.rb12
-rw-r--r--features/steps/explore/projects.rb50
-rw-r--r--features/steps/groups.rb76
-rw-r--r--features/steps/profile/emails.rb14
-rw-r--r--features/steps/profile/notifications.rb2
-rw-r--r--features/steps/profile/profile.rb106
-rw-r--r--features/steps/profile/ssh_keys.rb10
-rw-r--r--features/steps/project/active_tab.rb2
-rw-r--r--features/steps/project/archived.rb4
-rw-r--r--features/steps/project/commits/branches.rb22
-rw-r--r--features/steps/project/commits/commits.rb57
-rw-r--r--features/steps/project/commits/tags.rb26
-rw-r--r--features/steps/project/commits/user_lookup.rb6
-rw-r--r--features/steps/project/create.rb20
-rw-r--r--features/steps/project/deploy_keys.rb24
-rw-r--r--features/steps/project/fork.rb10
-rw-r--r--features/steps/project/forked_merge_requests.rb82
-rw-r--r--features/steps/project/graph.rb6
-rw-r--r--features/steps/project/hooks.rb14
-rw-r--r--features/steps/project/issues/filter_labels.rb18
-rw-r--r--features/steps/project/issues/issues.rb54
-rw-r--r--features/steps/project/issues/labels.rb38
-rw-r--r--features/steps/project/issues/milestones.rb14
-rw-r--r--features/steps/project/merge_requests.rb115
-rw-r--r--features/steps/project/network_graph.rb34
-rw-r--r--features/steps/project/project.rb34
-rw-r--r--features/steps/project/redirects.rb12
-rw-r--r--features/steps/project/services.rb66
-rw-r--r--features/steps/project/snippets.rb20
-rw-r--r--features/steps/project/source/browse_files.rb52
-rw-r--r--features/steps/project/source/git_blame.rb6
-rw-r--r--features/steps/project/source/markdown_render.rb116
-rw-r--r--features/steps/project/source/multiselect_blob.rb6
-rw-r--r--features/steps/project/source/search_code.rb6
-rw-r--r--features/steps/project/star.rb4
-rw-r--r--features/steps/project/team_management.rb40
-rw-r--r--features/steps/project/wiki.rb52
-rw-r--r--features/steps/search.rb18
-rw-r--r--features/steps/shared/active_tab.rb12
-rw-r--r--features/steps/shared/authentication.rb2
-rw-r--r--features/steps/shared/diff_note.rb60
-rw-r--r--features/steps/shared/group.rb4
-rw-r--r--features/steps/shared/markdown.rb10
-rw-r--r--features/steps/shared/note.rb52
-rw-r--r--features/steps/shared/paths.rb12
-rw-r--r--features/steps/shared/project.rb20
-rw-r--r--features/steps/shared/project_tab.rb8
-rw-r--r--features/steps/snippet_search.rb18
-rw-r--r--features/steps/snippets/discover.rb6
-rw-r--r--features/steps/snippets/public_snippets.rb4
-rw-r--r--features/steps/snippets/snippets.rb14
-rw-r--r--features/steps/snippets/user.rb18
-rw-r--r--features/steps/user.rb6
-rw-r--r--features/support/env.rb1
-rw-r--r--lib/api/commits.rb2
-rw-r--r--lib/api/files.rb50
-rw-r--r--lib/api/groups.rb2
-rw-r--r--lib/api/merge_requests.rb8
-rw-r--r--lib/api/namespaces.rb11
-rw-r--r--lib/api/users.rb2
-rw-r--r--lib/backup/manager.rb3
-rw-r--r--lib/gitlab/backend/shell.rb14
-rw-r--r--lib/gitlab/current_settings.rb3
-rw-r--r--lib/gitlab/gitorious_import.rb5
-rw-r--r--lib/gitlab/gitorious_import/client.rb2
-rw-r--r--lib/gitlab/gitorious_import/repository.rb2
-rw-r--r--lib/gitlab/markdown.rb3
-rw-r--r--lib/gitlab/markdown/reference_filter.rb14
-rw-r--r--lib/gitlab/markdown/sanitization_filter.rb59
-rw-r--r--lib/gitlab/o_auth/user.rb63
-rw-r--r--lib/gitlab/reference_extractor.rb54
-rw-r--r--lib/gitlab/satellite/files/delete_file_action.rb50
-rw-r--r--lib/gitlab/satellite/files/edit_file_action.rb68
-rw-r--r--lib/gitlab/satellite/files/file_action.rb25
-rw-r--r--lib/gitlab/satellite/files/new_file_action.rb67
-rw-r--r--lib/gitlab/upgrader.rb11
-rw-r--r--lib/redcarpet/render/gitlab_html.rb2
-rwxr-xr-xlib/support/init.d/gitlab3
-rwxr-xr-xlib/support/init.d/gitlab.default.example5
-rw-r--r--lib/support/nginx/gitlab8
-rw-r--r--lib/support/nginx/gitlab-ssl8
-rw-r--r--lib/tasks/gitlab/check.rake86
-rw-r--r--lib/tasks/gitlab/cleanup.rake2
-rw-r--r--lib/tasks/gitlab/shell.rake3
-rw-r--r--lib/tasks/gitlab/task_helpers.rake4
-rw-r--r--lib/tasks/gitlab/web_hook.rake4
-rw-r--r--lib/tasks/jasmine.rake2
-rw-r--r--spec/features/groups_spec.rb36
-rw-r--r--spec/features/markdown_spec.rb2
-rw-r--r--spec/features/projects_spec.rb55
-rw-r--r--spec/helpers/blob_helper_spec.rb33
-rw-r--r--spec/helpers/gitlab_markdown_helper_spec.rb6
-rw-r--r--spec/javascripts/extensions/array_spec.js.coffee12
-rw-r--r--spec/javascripts/extensions/jquery_spec.js.coffee34
-rw-r--r--spec/javascripts/fixtures/issuable.html.haml2
-rw-r--r--spec/javascripts/fixtures/issue_note.html.haml12
-rw-r--r--spec/javascripts/fixtures/issues_show.html.haml13
-rw-r--r--spec/javascripts/fixtures/merge_requests_show.html.haml13
-rw-r--r--spec/javascripts/fixtures/zen_mode.html.haml9
-rw-r--r--spec/javascripts/issue_spec.js.coffee30
-rw-r--r--spec/javascripts/merge_request_spec.js.coffee31
-rw-r--r--spec/javascripts/notes_spec.js.coffee19
-rw-r--r--spec/javascripts/shortcuts_issuable_spec.js.coffee7
-rw-r--r--spec/javascripts/spec_helper.coffee46
-rw-r--r--spec/javascripts/support/jasmine.yml15
-rw-r--r--spec/javascripts/support/jasmine_helper.rb15
-rw-r--r--spec/javascripts/zen_mode_spec.js.coffee52
-rw-r--r--spec/lib/gitlab/markdown/autolink_filter_spec.rb6
-rw-r--r--spec/lib/gitlab/markdown/commit_range_reference_filter_spec.rb2
-rw-r--r--spec/lib/gitlab/markdown/commit_reference_filter_spec.rb2
-rw-r--r--spec/lib/gitlab/markdown/emoji_filter_spec.rb4
-rw-r--r--spec/lib/gitlab/markdown/external_issue_reference_filter_spec.rb2
-rw-r--r--spec/lib/gitlab/markdown/external_link_filter_spec.rb4
-rw-r--r--spec/lib/gitlab/markdown/issue_reference_filter_spec.rb2
-rw-r--r--spec/lib/gitlab/markdown/label_reference_filter_spec.rb2
-rw-r--r--spec/lib/gitlab/markdown/merge_request_reference_filter_spec.rb2
-rw-r--r--spec/lib/gitlab/markdown/sanitization_filter_spec.rb33
-rw-r--r--spec/lib/gitlab/markdown/snippet_reference_filter_spec.rb2
-rw-r--r--spec/lib/gitlab/markdown/table_of_contents_filter_spec.rb4
-rw-r--r--spec/lib/gitlab/markdown/task_list_filter_spec.rb4
-rw-r--r--spec/lib/gitlab/markdown/user_reference_filter_spec.rb2
-rw-r--r--spec/lib/gitlab/o_auth/user_spec.rb166
-rw-r--r--spec/lib/gitlab/reference_extractor_spec.rb24
-rw-r--r--spec/lib/gitlab/upgrader_spec.rb15
-rw-r--r--spec/lib/repository_cache_spec.rb1
-rw-r--r--spec/models/application_setting_spec.rb1
-rw-r--r--spec/models/concerns/issuable_spec.rb2
-rw-r--r--spec/models/concerns/mentionable_spec.rb21
-rw-r--r--spec/models/deploy_keys_project_spec.rb8
-rw-r--r--spec/models/key_spec.rb4
-rw-r--r--spec/models/project_services/gitlab_ci_service_spec.rb15
-rw-r--r--spec/models/project_spec.rb8
-rw-r--r--spec/models/snippet_spec.rb4
-rw-r--r--spec/models/user_spec.rb22
-rw-r--r--spec/requests/api/commits_spec.rb3
-rw-r--r--spec/requests/api/files_spec.rb51
-rw-r--r--spec/requests/api/merge_requests_spec.rb4
-rw-r--r--spec/requests/api/namespaces_spec.rb29
-rw-r--r--spec/requests/api/projects_spec.rb9
-rw-r--r--spec/routing/project_routing_spec.rb34
-rw-r--r--spec/services/destroy_group_service_spec.rb44
-rw-r--r--spec/services/merge_requests/update_service_spec.rb11
-rw-r--r--spec/services/projects/destroy_service_spec.rb34
-rw-r--r--spec/services/system_note_service_spec.rb14
-rw-r--r--spec/spec_helper.rb13
-rw-r--r--spec/support/api_helpers.rb2
-rw-r--r--spec/support/coverage.rb8
-rw-r--r--spec/support/filter_spec_helper.rb (renamed from spec/support/reference_filter_spec_helper.rb)75
-rw-r--r--spec/support/matchers.rb2
-rw-r--r--spec/support/mentionable_shared_examples.rb21
-rw-r--r--spec/support/webmock.rb4
-rw-r--r--spec/teaspoon_env.rb178
-rwxr-xr-xvendor/assets/javascripts/jasmine-fixture.js433
412 files changed, 5059 insertions, 3779 deletions
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
new file mode 100644
index 00000000000..1411a9194b5
--- /dev/null
+++ b/.gitlab-ci.yml
@@ -0,0 +1,39 @@
+before_script:
+ - export PATH=$HOME/bin:/usr/local/bin:/usr/bin:/bin
+ - ruby -v
+ - which ruby
+ - gem install bundler
+ - which bundle
+ - echo $PATH
+ - cp config/database.yml.mysql config/database.yml
+ - cp config/gitlab.yml.example config/gitlab.yml
+ - ! 'sed "s/username\:.*$/username\: runner/" -i config/database.yml'
+ - ! 'sed "s/password\:.*$/password\: ''password''/" -i config/database.yml'
+ - sed "s/gitlabhq_test/gitlabhq_test_$((RANDOM/5000))/" -i config/database.yml
+ - touch log/application.log
+ - touch log/test.log
+ - bundle install --without postgres production --jobs $(nproc)
+ - bundle exec rake db:create RAILS_ENV=test
+jobs:
+- script:
+ - RAILS_ENV=test SIMPLECOV=true bundle exec rake spec
+ name: Rspec
+ runner: ruby,mysql
+- script:
+ - RAILS_ENV=test SIMPLECOV=true bundle exec rake spinach
+ name: Spinach
+ runner: ruby,mysql
+- script:
+ - RAILS_ENV=test SIMPLECOV=true bundle exec rake jasmine:ci
+ name: Jasmine
+ runner: ruby,mysql
+- script:
+ - bundle exec rubocop
+ name: Rubocop
+ runner: ruby,mysql
+- script:
+ - bundle exec rake brakeman
+ name: Brakeman
+ runner: ruby,mysql
+deploy_jobs: []
+skip_refs: ''
diff --git a/CHANGELOG b/CHANGELOG
index 46a052e1a19..c5a625ed1df 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,6 +1,18 @@
Please view this file on the master branch, on stable branches it's out of date.
v 7.12.0 (unreleased)
+ - Update oauth button logos for Twitter and Google to recommended assets
+ - Update browser gem to version 0.8.0 for IE11 support (Stan Hu)
+ - Fix timeout when rendering file with thousands of lines.
+ - Add "Remember me" checkbox to LDAP signin form.
+ - Add session expiration delay configuration through UI application settings
+ - Don't notify users mentioned in code blocks or blockquotes.
+ - Omit link to generate labels if user does not have access to create them (Stan Hu)
+ - Show warning when a comment will add 10 or more people to the discussion.
+ - Disable changing of the source branch in merge request update API (Stan Hu)
+ - Shorten merge request WIP text.
+ - Add option to disallow users from registering any application to use GitLab as an OAuth provider
+ - Support editing target branch of merge request (Stan Hu)
- Refactor permission checks with issues and merge requests project settings (Stan Hu)
- Fix Markdown preview not working in Edit Milestone page (Stan Hu)
- Fix Zen Mode not closing with ESC key (Stan Hu)
@@ -8,10 +20,13 @@ v 7.12.0 (unreleased)
- Add file attachment support in Milestone description (Stan Hu)
- Fix milestone "Browse Issues" button.
- Set milestone on new issue when creating issue from index with milestone filter active.
+ - Make namespace API available to all users (Stan Hu)
- Add web hook support for note events (Stan Hu)
- Disable "New Issue" and "New Merge Request" buttons when features are disabled in project settings (Stan Hu)
- Remove Rack Attack monkey patches and bump to version 4.3.0 (Stan Hu)
- Fix clone URL losing selection after a single click in Safari and Chrome (Stan Hu)
+ - Fix git blame syntax highlighting when different commits break up lines (Stan Hu)
+ - Add "Resend confirmation e-mail" link in profile settings (Stan Hu)
- Allow to configure location of the `.gitlab_shell_secret` file. (Jakub Jirutka)
- Disabled expansion of top/bottom blobs for new file diffs
- Update Asciidoctor gem to version 1.5.2. (Jakub Jirutka)
@@ -24,9 +39,29 @@ v 7.12.0 (unreleased)
- Consistently refer to MRs as either Accepted or Rejected.
- Add Accepted and Rejected tabs to MR lists.
- Prefix EmailsOnPush email subject with `[Git]`.
- - Group project contributions by both name and email.
+ - Group project contributions by both name and email.
- Clarify navigation labels for Project Settings and Group Settings.
- Move user avatar and logout button to sidebar
+ - You can not remove user if he/she is an only owner of group
+ - User should be able to leave group. If not - show him proper message
+ - User has ability to leave project
+ - Add SAML support as an omniauth provider
+ - Allow to configure a URL to show after sign out
+ - Add an option to automatically sign-in with an Omniauth provider
+ - Better performance for web editor (switched from satellites to rugged)
+ - GitLab CI service sends .gitlab-ci.yaml in each push call
+ - When remove project - move repository and schedule it removal
+ - Improve group removing logic
+ - Trigger create-hooks on backup restore task
+ - Add option to automatically link omniauth and LDAP identities
+
+v 7.11.4
+ - Fix missing bullets when creating lists
+ - Set rel="nofollow" on external links
+
+v 7.11.3
+ - no changes
+ - Fix upgrader script (Martins Polakovs)
v 7.11.2
- no changes
@@ -34,9 +69,12 @@ v 7.11.2
v 7.11.1
- no changes
-v 7.11.0
+v 7.11.0
- Fall back to Plaintext when Syntaxhighlighting doesn't work. Fixes some buggy lexers (Hannes Rosenögger)
- Get editing comments to work in Chrome 43 again.
+ - Allow special character in users bio. I.e.: I <3 GitLab
+
+v 7.11.0
- Fix broken view when viewing history of a file that includes a path that used to be another file (Stan Hu)
- Don't show duplicate deploy keys
- Fix commit time being displayed in the wrong timezone in some cases (Hannes Rosenögger)
@@ -108,6 +146,7 @@ v 7.10.4
- Fix DB error when trying to tag a repository (Stan Hu)
- Fix Error 500 when searching Wiki pages (Stan Hu)
- Unescape branch names in compare commit (Stan Hu)
+ - Order commit comments chronologically in API.
v 7.10.2
- Fix CI links on MR page
@@ -189,12 +228,12 @@ v 7.10.0
- Ability to skip some items from backup (database, respositories or uploads)
- Archive repositories in background worker.
- Import GitHub, Bitbucket or GitLab.com projects owned by authenticated user into current namespace.
- - Project labels are now available over the API under the "tag_list" field (Cristian Medina)
+ - Project labels are now available over the API under the "tag_list" field (Cristian Medina)
- Fixed link paths for HTTP and SSH on the admin project view (Jeremy Maziarz)
- Fix and improve help rendering (Sullivan Sénéchal)
- Fix final line in EmailsOnPush email diff being rendered as error.
- Prevent duplicate Buildkite service creation.
- - Fix git over ssh errors 'fatal: protocol error: bad line length character'
+ - Fix git over ssh errors 'fatal: protocol error: bad line length character'
- Automatically setup GitLab CI project for forks if origin project has GitLab CI enabled
- Bust group page project list cache when namespace name or path changes.
- Explicitly set image alt-attribute to prevent graphical glitches if gravatars could not be loaded
@@ -203,7 +242,7 @@ v 7.10.0
- Fix stuck Merge Request merging events from old installations (Ben Bodenmiller)
- Fix merge request comments on files with multiple commits
- Fix Resource Owner Password Authentication Flow
-
+
v 7.9.4
- Security: Fix project import URL regex to prevent arbitary local repos from being imported
- Fixed issue where only 25 commits would load in file listings
@@ -507,10 +546,10 @@ v 7.5.0
- Fix raw view for public snippets
- Use secret token with GitLab internal API.
- Add missing timestamps to 'members' table
-
+
v 7.4.5
- Bump gitlab_git to 7.0.0.rc12 (includes Rugged 0.21.2)
-
+
v 7.4.4
- No changes
diff --git a/Gemfile b/Gemfile
index 8eb1f04000c..645e0b5cf7c 100644
--- a/Gemfile
+++ b/Gemfile
@@ -1,18 +1,7 @@
source "https://rubygems.org"
-def darwin_only(require_as)
- RUBY_PLATFORM.include?('darwin') && require_as
-end
-
-def linux_only(require_as)
- RUBY_PLATFORM.include?('linux') && require_as
-end
-
gem "rails", "~> 4.1.0"
-# Make links from text
-gem 'rails_autolink', '~> 1.1'
-
# Default values for AR models
gem "default_value_for", "~> 3.0.0"
@@ -31,6 +20,7 @@ gem 'omniauth-shibboleth'
gem 'omniauth-kerberos', group: :kerberos
gem 'omniauth-gitlab'
gem 'omniauth-bitbucket'
+gem 'omniauth-saml'
gem 'doorkeeper', '2.1.3'
gem "rack-oauth2", "~> 1.0.5"
@@ -40,22 +30,30 @@ gem 'rqrcode-rails3'
gem 'attr_encrypted', '1.3.4'
# Browser detection
-gem "browser"
+gem "browser", '~> 0.8.0'
# Extracting information from a git repository
# Provide access to Gitlab::Git library
-gem "gitlab_git", '~> 7.1.13'
+gem "gitlab_git", '~> 7.2.3'
# Ruby/Rack Git Smart-HTTP Server Handler
+# GitLab fork with a lot of changes (improved thread-safety, better memory usage etc)
+# For full list of changes see https://github.com/SaitoWu/grack/compare/master...gitlabhq:master
gem 'gitlab-grack', '~> 2.0.2', require: 'grack'
# LDAP Auth
+# GitLab fork with several improvements to original library. For full list of changes
+# see https://github.com/intridea/omniauth-ldap/compare/master...gitlabhq:master
gem 'gitlab_omniauth-ldap', '1.2.1', require: "omniauth-ldap"
# Git Wiki
gem 'gollum-lib', '~> 4.0.2'
# Language detection
+# GitLab fork of linguist does not require pygments/python dependency.
+# New version of original gem also dropped pygments support but it has strict
+# dependency to unstable rugged version. We have internal issue for replacing
+# fork with original gem when we meet on same rugged version - https://dev.gitlab.org/gitlab/gitlabhq/issues/2052.
gem "gitlab-linguist", "~> 3.0.1", require: "linguist"
# API
@@ -83,7 +81,7 @@ gem "carrierwave"
gem 'dropzonejs-rails'
# for aws storage
-gem "fog", "~> 1.14"
+gem "fog", "~> 1.25.0"
gem "unf"
# Authorization
@@ -186,23 +184,23 @@ gem 'charlock_holmes'
gem "sass-rails", '~> 4.0.2'
gem "coffee-rails"
gem "uglifier"
-gem 'turbolinks'
+gem 'turbolinks', '~> 2.5.0'
gem 'jquery-turbolinks'
-gem 'select2-rails'
+gem 'addressable'
+gem 'bootstrap-sass', '~> 3.0'
+gem 'font-awesome-rails', '~> 4.2'
+gem 'gitlab_emoji', '~> 0.1'
+gem 'gon', '~> 5.0.0'
gem 'jquery-atwho-rails', '~> 1.0.0'
-gem "jquery-rails"
-gem "jquery-ui-rails"
-gem "jquery-scrollto-rails"
-gem "raphael-rails", "~> 2.1.2"
-gem 'bootstrap-sass', '~> 3.0'
-gem "font-awesome-rails", '~> 4.2'
-gem "gitlab_emoji", "~> 0.1"
-gem "gon", '~> 5.0.0'
+gem 'jquery-rails', '3.1.2'
+gem 'jquery-scrollto-rails'
+gem 'jquery-ui-rails'
gem 'nprogress-rails'
+gem 'raphael-rails', '~> 2.1.2'
gem 'request_store'
-gem "virtus"
-gem 'addressable'
+gem 'select2-rails'
+gem 'virtus'
group :development do
gem 'brakeman', require: false
@@ -241,32 +239,25 @@ group :development, :test do
# Generate Fake data
gem 'ffaker', '~> 2.0.0'
- # Guard
- gem 'guard-rspec'
- gem 'guard-spinach'
-
- # Notification
- gem 'rb-fsevent', require: darwin_only('rb-fsevent')
- gem 'growl', require: darwin_only('growl')
- gem 'rb-inotify', require: linux_only('rb-inotify')
-
# PhantomJS driver for Capybara
gem 'poltergeist', '~> 1.5.1'
- gem 'jasmine-rails'
+ gem 'teaspoon', '~> 1.0.0'
+ gem 'teaspoon-jasmine'
- gem "spring", '~> 1.3.1'
- gem "spring-commands-rspec", '1.0.4'
- gem "spring-commands-spinach", '1.0.0'
+ gem 'spring', '~> 1.3.1'
+ gem 'spring-commands-rspec', '~> 1.0.0'
+ gem 'spring-commands-spinach', '~> 1.0.0'
+ gem 'spring-commands-teaspoon', '~> 0.0.2'
gem "byebug"
end
group :test do
- gem "simplecov", require: false
- gem "shoulda-matchers", "~> 2.7.0"
+ gem 'simplecov', require: false
+ gem 'shoulda-matchers', '~> 2.8.0', require: false
gem 'email_spec'
- gem "webmock"
+ gem 'webmock', '~> 1.21.0'
gem 'test_after_commit'
end
@@ -277,4 +268,4 @@ end
gem "newrelic_rpm"
gem 'octokit', '3.7.0'
-gem "rugments"
+gem "rugments", "~> 1.0.0.beta7"
diff --git a/Gemfile.lock b/Gemfile.lock
index 80e4a44c1da..1de29ad8f8a 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -1,6 +1,7 @@
GEM
remote: https://rubygems.org/
specs:
+ CFPropertyList (2.3.1)
RedCloth (4.2.9)
ace-rails-ap (2.0.1)
actionmailer (4.1.9)
@@ -35,7 +36,7 @@ GEM
tzinfo (~> 1.1)
acts-as-taggable-on (3.5.0)
activerecord (>= 3.2, < 5)
- addressable (2.3.5)
+ addressable (2.3.8)
annotate (2.6.0)
activerecord (>= 2.3.0)
rake (>= 0.8.7)
@@ -75,7 +76,7 @@ GEM
ruby_parser (~> 3.5.0)
sass (~> 3.0)
terminal-table (~> 1.4)
- browser (0.7.2)
+ browser (0.8.0)
builder (3.2.2)
byebug (3.2.0)
columnize (~> 0.8)
@@ -101,13 +102,13 @@ GEM
coderay (1.1.0)
coercible (1.0.0)
descendants_tracker (~> 0.0.1)
- coffee-rails (4.0.1)
+ coffee-rails (4.1.0)
coffee-script (>= 2.2.0)
railties (>= 4.0.0, < 5.0)
- coffee-script (2.2.0)
+ coffee-script (2.4.1)
coffee-script-source
execjs
- coffee-script-source (1.6.3)
+ coffee-script-source (1.9.1.1)
colored (1.2)
colorize (0.5.8)
columnize (0.9.0)
@@ -118,8 +119,8 @@ GEM
simplecov (>= 0.7)
term-ansicolor
thor
- crack (0.4.1)
- safe_yaml (~> 0.9.0)
+ crack (0.4.2)
+ safe_yaml (~> 1.0.0)
creole (0.3.8)
d3_rails (3.5.5)
railties (>= 3.1.0)
@@ -163,7 +164,7 @@ GEM
erubis (2.7.0)
escape_utils (0.2.4)
eventmachine (1.0.4)
- excon (0.32.1)
+ excon (0.45.3)
execjs (2.5.2)
expression_parser (0.9.0)
factory_girl (4.3.0)
@@ -178,29 +179,69 @@ GEM
fastercsv (1.5.5)
ffaker (2.0.0)
ffi (1.9.8)
- fog (1.21.0)
- fog-brightbox
- fog-core (~> 1.21, >= 1.21.1)
+ fission (0.5.0)
+ CFPropertyList (~> 2.2)
+ fog (1.25.0)
+ fog-brightbox (~> 0.4)
+ fog-core (~> 1.25)
fog-json
+ fog-profitbricks
+ fog-radosgw (>= 0.0.2)
+ fog-sakuracloud (>= 0.0.4)
+ fog-softlayer
+ fog-terremark
+ fog-vmfusion
+ fog-voxel
+ fog-xml (~> 0.1.1)
+ ipaddress (~> 0.5)
nokogiri (~> 1.5, >= 1.5.11)
- fog-brightbox (0.0.1)
- fog-core
+ opennebula
+ fog-brightbox (0.7.1)
+ fog-core (~> 1.22)
fog-json
- fog-core (1.21.1)
+ inflecto (~> 0.0.2)
+ fog-core (1.30.0)
builder
- excon (~> 0.32)
- formatador (~> 0.2.0)
+ excon (~> 0.45)
+ formatador (~> 0.2)
mime-types
net-scp (~> 1.1)
net-ssh (>= 2.1.3)
- fog-json (1.0.0)
- multi_json (~> 1.0)
+ fog-json (1.0.2)
+ fog-core (~> 1.0)
+ multi_json (~> 1.10)
+ fog-profitbricks (0.0.3)
+ fog-core
+ fog-xml
+ nokogiri
+ fog-radosgw (0.0.4)
+ fog-core (>= 1.21.0)
+ fog-json
+ fog-xml (>= 0.0.1)
+ fog-sakuracloud (1.0.1)
+ fog-core
+ fog-json
+ fog-softlayer (0.4.6)
+ fog-core
+ fog-json
+ fog-terremark (0.1.0)
+ fog-core
+ fog-xml
+ fog-vmfusion (0.1.0)
+ fission
+ fog-core
+ fog-voxel (0.1.0)
+ fog-core
+ fog-xml
+ fog-xml (0.1.2)
+ fog-core
+ nokogiri (~> 1.5, >= 1.5.11)
font-awesome-rails (4.2.0.0)
railties (>= 3.2, < 5.0)
foreman (0.63.0)
dotenv (>= 0.7)
thor (>= 0.13.6)
- formatador (0.2.4)
+ formatador (0.2.5)
gemnasium-gitlab-service (0.2.6)
rugged (~> 0.21)
gemojione (2.0.0)
@@ -225,7 +266,7 @@ GEM
mime-types (~> 1.19)
gitlab_emoji (0.1.0)
gemojione (~> 2.0)
- gitlab_git (7.1.13)
+ gitlab_git (7.2.3)
activesupport (~> 4.0)
charlock_holmes (~> 0.6)
gitlab-linguist (~> 3.0)
@@ -261,19 +302,6 @@ GEM
grape-entity (0.4.2)
activesupport
multi_json (>= 1.3.2)
- growl (1.0.3)
- guard (2.2.4)
- formatador (>= 0.2.4)
- listen (~> 2.1)
- lumberjack (~> 1.0)
- pry (>= 0.9.12)
- thor (>= 0.18.1)
- guard-rspec (4.2.0)
- guard (>= 2.1.1)
- rspec (>= 2.14, < 4.0)
- guard-spinach (0.0.2)
- guard (>= 1.1)
- spinach
haml (4.0.5)
tilt
haml-rails (0.5.3)
@@ -300,14 +328,10 @@ GEM
i18n (0.7.0)
ice_cube (0.11.1)
ice_nine (0.10.0)
- jasmine-core (2.2.0)
- jasmine-rails (0.10.8)
- jasmine-core (>= 1.3, < 3.0)
- phantomjs (>= 1.9)
- railties (>= 3.2.0)
- sprockets-rails
+ inflecto (0.0.2)
+ ipaddress (0.8.0)
jquery-atwho-rails (1.0.1)
- jquery-rails (3.1.0)
+ jquery-rails (3.1.2)
railties (>= 3.0, < 5.0)
thor (>= 0.14, < 2.0)
jquery-scrollto-rails (1.4.3)
@@ -332,7 +356,8 @@ GEM
celluloid (~> 0.16.0)
rb-fsevent (>= 0.9.3)
rb-inotify (>= 0.9)
- lumberjack (1.0.4)
+ macaddr (1.7.1)
+ systemu (~> 2.6.2)
mail (2.6.3)
mime-types (>= 1.16, < 3)
method_source (0.8.2)
@@ -346,9 +371,9 @@ GEM
multipart-post (1.2.0)
mysql2 (0.3.16)
net-ldap (0.11)
- net-scp (1.1.2)
+ net-scp (1.2.1)
net-ssh (>= 2.6.5)
- net-ssh (2.8.0)
+ net-ssh (2.9.2)
newrelic_rpm (3.9.4.245)
nokogiri (1.6.6.2)
mini_portile (~> 0.6.0)
@@ -389,18 +414,24 @@ GEM
omniauth-oauth2 (1.1.1)
oauth2 (~> 0.8.0)
omniauth (~> 1.0)
+ omniauth-saml (1.3.1)
+ omniauth (~> 1.1)
+ ruby-saml (~> 0.8.1)
omniauth-shibboleth (1.1.1)
omniauth (>= 1.0.0)
omniauth-twitter (1.0.1)
multi_json (~> 1.3)
omniauth-oauth (~> 1.0)
+ opennebula (4.12.1)
+ json
+ nokogiri
+ rbvmomi
org-ruby (0.9.12)
rubypants (~> 0.2)
orm_adapter (0.5.0)
parser (2.2.0.2)
ast (>= 1.1, < 3.0)
pg (0.15.1)
- phantomjs (1.9.8.0)
poltergeist (1.5.1)
capybara (~> 2.1)
cliver (~> 0.3.1)
@@ -418,7 +449,7 @@ GEM
quiet_assets (1.0.2)
railties (>= 3.1, < 5.0)
racc (1.4.10)
- rack (1.5.2)
+ rack (1.5.3)
rack-accept (0.4.5)
rack (>= 0.4)
rack-attack (4.3.0)
@@ -450,8 +481,6 @@ GEM
sprockets-rails (~> 2.0)
rails-observers (0.1.2)
activemodel (~> 4.0)
- rails_autolink (1.1.6)
- rails (> 3.1)
railties (4.1.9)
actionpack (= 4.1.9)
activesupport (= 4.1.9)
@@ -464,6 +493,10 @@ GEM
rb-fsevent (0.9.4)
rb-inotify (0.9.5)
ffi (>= 0.5.0)
+ rbvmomi (1.8.2)
+ builder
+ nokogiri (>= 1.4.1)
+ trollop
rdoc (3.12.2)
json (~> 1.4)
redcarpet (3.2.3)
@@ -497,10 +530,6 @@ GEM
rqrcode (0.4.2)
rqrcode-rails3 (0.1.7)
rqrcode (>= 0.4.2)
- rspec (2.99.0)
- rspec-core (~> 2.99.0)
- rspec-expectations (~> 2.99.0)
- rspec-mocks (~> 2.99.0)
rspec-collection_matchers (1.1.2)
rspec-expectations (>= 2.99.0.beta1)
rspec-core (2.99.2)
@@ -523,6 +552,9 @@ GEM
rainbow (>= 1.99.1, < 3.0)
ruby-progressbar (~> 1.4)
ruby-progressbar (1.7.1)
+ ruby-saml (0.8.2)
+ nokogiri (>= 1.5.0)
+ uuid (~> 2.3)
ruby2ruby (2.1.3)
ruby_parser (~> 3.1)
sexp_processor (~> 4.0)
@@ -531,8 +563,8 @@ GEM
rubyntlm (0.5.0)
rubypants (0.2.0)
rugged (0.22.2)
- rugments (1.0.0.beta6)
- safe_yaml (0.9.7)
+ rugments (1.0.0.beta7)
+ safe_yaml (1.0.4)
sanitize (2.1.0)
nokogiri (>= 1.4.4)
sass (3.2.19)
@@ -554,7 +586,7 @@ GEM
thor (~> 0.14)
settingslogic (2.0.9)
sexp_processor (4.4.5)
- shoulda-matchers (2.7.0)
+ shoulda-matchers (2.8.0)
activesupport (>= 3.0.0)
sidekiq (3.3.0)
celluloid (>= 0.16.0)
@@ -594,6 +626,8 @@ GEM
spring (>= 0.9.1)
spring-commands-spinach (1.0.0)
spring (>= 0.9.1)
+ spring-commands-teaspoon (0.0.2)
+ spring (>= 0.9.1)
sprockets (2.11.0)
hike (~> 1.2)
multi_json (~> 1.0)
@@ -606,8 +640,13 @@ GEM
stamp (0.5.0)
state_machine (1.2.0)
stringex (2.5.2)
+ systemu (2.6.5)
task_list (1.0.2)
html-pipeline
+ teaspoon (1.0.2)
+ railties (>= 3.2.5, < 5)
+ teaspoon-jasmine (2.2.0)
+ teaspoon (>= 1.0.0)
temple (0.6.7)
term-ansicolor (1.2.2)
tins (~> 0.8)
@@ -633,7 +672,8 @@ GEM
multi_json (~> 1.7)
twitter-stream (~> 0.1)
tins (0.13.1)
- turbolinks (2.0.0)
+ trollop (2.1.2)
+ turbolinks (2.5.3)
coffee-rails
twitter-stream (0.1.16)
eventmachine (>= 0.12.8)
@@ -654,6 +694,8 @@ GEM
raindrops (~> 0.7)
unicorn-worker-killer (0.4.2)
unicorn (~> 4)
+ uuid (2.3.7)
+ macaddr (~> 1.0)
version_sorter (2.0.0)
virtus (1.0.1)
axiom-types (~> 0.0.5)
@@ -662,8 +704,8 @@ GEM
equalizer (~> 0.0.7)
warden (1.2.3)
rack (>= 1.0)
- webmock (1.16.0)
- addressable (>= 2.2.7)
+ webmock (1.21.0)
+ addressable (>= 2.3.6)
crack (>= 0.3.2)
websocket-driver (0.3.3)
wikicloth (0.8.1)
@@ -690,7 +732,7 @@ DEPENDENCIES
binding_of_caller
bootstrap-sass (~> 3.0)
brakeman
- browser
+ browser (~> 0.8.0)
byebug
cal-heatmap-rails (~> 0.0.1)
capybara (~> 2.2.1)
@@ -714,7 +756,7 @@ DEPENDENCIES
enumerize
factory_girl_rails
ffaker (~> 2.0.0)
- fog (~> 1.14)
+ fog (~> 1.25.0)
font-awesome-rails (~> 4.2)
foreman
gemnasium-gitlab-service (~> 0.2)
@@ -723,23 +765,19 @@ DEPENDENCIES
gitlab-grack (~> 2.0.2)
gitlab-linguist (~> 3.0.1)
gitlab_emoji (~> 0.1)
- gitlab_git (~> 7.1.13)
+ gitlab_git (~> 7.2.3)
gitlab_meta (= 7.0)
gitlab_omniauth-ldap (= 1.2.1)
gollum-lib (~> 4.0.2)
gon (~> 5.0.0)
grape (~> 0.6.1)
grape-entity (~> 0.4.2)
- growl
- guard-rspec
- guard-spinach
haml-rails
hipchat (~> 1.5.0)
html-pipeline (~> 1.11.0)
httparty
- jasmine-rails
jquery-atwho-rails (~> 1.0.0)
- jquery-rails
+ jquery-rails (= 3.1.2)
jquery-scrollto-rails
jquery-turbolinks
jquery-ui-rails
@@ -757,6 +795,7 @@ DEPENDENCIES
omniauth-gitlab
omniauth-google-oauth2
omniauth-kerberos
+ omniauth-saml
omniauth-shibboleth
omniauth-twitter
org-ruby (= 0.9.12)
@@ -769,10 +808,7 @@ DEPENDENCIES
rack-mini-profiler
rack-oauth2 (~> 1.0.5)
rails (~> 4.1.0)
- rails_autolink (~> 1.1)
raphael-rails (~> 2.1.2)
- rb-fsevent
- rb-inotify
rdoc (~> 3.6)
redcarpet (~> 3.2.3)
redis-rails
@@ -781,14 +817,14 @@ DEPENDENCIES
rqrcode-rails3
rspec-rails (= 2.99)
rubocop (= 0.28.0)
- rugments
+ rugments (~> 1.0.0.beta7)
sanitize (~> 2.0)
sass-rails (~> 4.0.2)
sdoc
seed-fu
select2-rails
settingslogic
- shoulda-matchers (~> 2.7.0)
+ shoulda-matchers (~> 2.8.0)
sidekiq (~> 3.3)
sidetiq (= 0.6.3)
simplecov
@@ -798,15 +834,18 @@ DEPENDENCIES
slim
spinach-rails
spring (~> 1.3.1)
- spring-commands-rspec (= 1.0.4)
- spring-commands-spinach (= 1.0.0)
+ spring-commands-rspec (~> 1.0.0)
+ spring-commands-spinach (~> 1.0.0)
+ spring-commands-teaspoon (~> 0.0.2)
stamp
state_machine
task_list (= 1.0.2)
+ teaspoon (~> 1.0.0)
+ teaspoon-jasmine
test_after_commit
thin
tinder (~> 1.9.2)
- turbolinks
+ turbolinks (~> 2.5.0)
uglifier
underscore-rails (~> 1.4.4)
unf
@@ -814,5 +853,5 @@ DEPENDENCIES
unicorn-worker-killer
version_sorter
virtus
- webmock
+ webmock (~> 1.21.0)
wikicloth (= 0.8.1)
diff --git a/Guardfile b/Guardfile
deleted file mode 100644
index 68ac3232b09..00000000000
--- a/Guardfile
+++ /dev/null
@@ -1,27 +0,0 @@
-# A sample Guardfile
-# More info at https://github.com/guard/guard#readme
-
-guard 'rspec', cmd: "spring rspec", all_on_start: false, all_after_pass: false do
- watch(%r{^spec/.+_spec\.rb$})
- watch(%r{^lib/(.+)\.rb$}) { |m| "spec/lib/#{m[1]}_spec.rb" }
- watch(%r{^lib/api/(.+)\.rb$}) { |m| "spec/requests/api/#{m[1]}_spec.rb" }
- watch('spec/spec_helper.rb') { "spec" }
-
- # Rails example
- watch(%r{^app/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" }
- watch(%r{^app/(.*)(\.erb|\.haml)$}) { |m| "spec/#{m[1]}#{m[2]}_spec.rb" }
- watch(%r{^app/controllers/(.+)_(controller)\.rb$}) { |m| ["spec/routing/#{m[1]}_routing_spec.rb", "spec/#{m[2]}s/#{m[1]}_#{m[2]}_spec.rb", "spec/acceptance/#{m[1]}_spec.rb"] }
- watch(%r{^spec/support/(.+)\.rb$}) { "spec" }
- watch('config/routes.rb') { "spec/routing" }
- watch('app/controllers/application_controller.rb') { "spec/controllers" }
-
- # Capybara request specs
- watch(%r{^app/views/(.+)/.*\.(erb|haml)$}) { |m| "spec/requests/#{m[1]}_spec.rb" }
-end
-
-guard 'spinach', command_prefix: 'spring' do
- watch(%r|^features/(.*)\.feature|)
- watch(%r|^features/steps/(.*)([^/]+)\.rb|) do |m|
- "features/#{m[1]}#{m[2]}.feature"
- end
-end
diff --git a/app/assets/images/authbuttons/google_64.png b/app/assets/images/authbuttons/google_64.png
index 94a0e089c6e..fb64f8bee68 100644
--- a/app/assets/images/authbuttons/google_64.png
+++ b/app/assets/images/authbuttons/google_64.png
Binary files differ
diff --git a/app/assets/images/authbuttons/twitter_64.png b/app/assets/images/authbuttons/twitter_64.png
index 5c9f14cb077..e3bd9169a34 100644
--- a/app/assets/images/authbuttons/twitter_64.png
+++ b/app/assets/images/authbuttons/twitter_64.png
Binary files differ
diff --git a/app/assets/javascripts/application.js.coffee b/app/assets/javascripts/application.js.coffee
index ea2a4b97101..6a3f7386d5b 100644
--- a/app/assets/javascripts/application.js.coffee
+++ b/app/assets/javascripts/application.js.coffee
@@ -49,8 +49,6 @@ window.slugify = (text) ->
window.ajaxGet = (url) ->
$.ajax({type: "GET", url: url, dataType: "script"})
-window.showAndHide = (selector) ->
-
window.split = (val) ->
return val.split( /,\s*/ )
@@ -92,15 +90,7 @@ window.disableButtonIfAnyEmptyField = (form, form_selector, button_selector) ->
window.sanitize = (str) ->
return str.replace(/<(?:.|\n)*?>/gm, '')
-window.linkify = (str) ->
- exp = /(\b(https?|ftp|file):\/\/[-A-Z0-9+&@#\/%?=~_|!:,.;]*[-A-Z0-9+&@#\/%=~_|])/ig
- return str.replace(exp,"<a href='$1'>$1</a>")
-
-window.simpleFormat = (str) ->
- linkify(sanitize(str).replace(/\n/g, '<br />'))
-
window.unbindEvents = ->
- $(document).unbind('scroll')
$(document).off('scroll')
window.shiftWindow = ->
@@ -177,6 +167,10 @@ $ ->
$(@).next('table').show()
$(@).remove()
+ $('.navbar-toggle').on 'click', ->
+ $('.header-content .title').toggle()
+ $('.header-content .navbar-collapse').toggle()
+
# Show/hide comments on diff
$("body").on "click", ".js-toggle-diff-comments", (e) ->
$(@).toggleClass('active')
@@ -192,14 +186,3 @@ $ ->
new ConfirmDangerModal(form, text)
new Aside()
-
-(($) ->
- # Disable an element and add the 'disabled' Bootstrap class
- $.fn.extend disable: ->
- $(@).attr('disabled', 'disabled').addClass('disabled')
-
- # Enable an element and remove the 'disabled' Bootstrap class
- $.fn.extend enable: ->
- $(@).removeAttr('disabled').removeClass('disabled')
-
-)(jQuery)
diff --git a/app/assets/javascripts/calendar.js.coffee b/app/assets/javascripts/calendar.js.coffee
index 44d75bd694f..4c4bc3d66ed 100644
--- a/app/assets/javascripts/calendar.js.coffee
+++ b/app/assets/javascripts/calendar.js.coffee
@@ -25,6 +25,7 @@ class @Calendar
30
]
legendCellPadding: 3
+ cellSize: $('.user-calendar').width() / 80
onClick: (date, count) ->
formated_date = date.getFullYear() + "-" + (date.getMonth()+1) + "-" + date.getDate()
$.ajax
diff --git a/app/assets/javascripts/dropzone_input.js.coffee b/app/assets/javascripts/dropzone_input.js.coffee
index fca2a290e2d..a7476146010 100644
--- a/app/assets/javascripts/dropzone_input.js.coffee
+++ b/app/assets/javascripts/dropzone_input.js.coffee
@@ -10,12 +10,17 @@ class @DropzoneInput
iconSpinner = "<i class=\"fa fa-spinner fa-spin div-dropzone-icon\"></i>"
btnAlert = "<button type=\"button\"" + alertAttr + ">&times;</button>"
project_uploads_path = window.project_uploads_path or null
+ markdown_preview_path = window.markdown_preview_path or null
max_file_size = gon.max_file_size or 10
form_textarea = $(form).find("textarea.markdown-area")
form_textarea.wrap "<div class=\"div-dropzone\"></div>"
- form_textarea.bind 'paste', (event) =>
+ form_textarea.on 'paste', (event) =>
handlePaste(event)
+ form_textarea.on "input", ->
+ hideReferencedUsers()
+ form_textarea.on "blur", ->
+ renderMarkdown()
form_dropzone = $(form).find('.div-dropzone')
form_dropzone.parent().addClass "div-dropzone-wrapper"
@@ -45,16 +50,7 @@ class @DropzoneInput
form.find(".md-write-holder").hide()
form.find(".md-preview-holder").show()
- preview = form.find(".js-md-preview")
- mdText = form.find(".markdown-area").val()
- if mdText.trim().length is 0
- preview.text "Nothing to preview."
- else
- preview.text "Loading..."
- $.post($(this).data("url"),
- md_text: mdText
- ).success (previewData) ->
- preview.html previewData
+ renderMarkdown()
# Write button
$(document).off "click", ".js-md-write-button"
@@ -133,6 +129,40 @@ class @DropzoneInput
child = $(dropzone[0]).children("textarea")
+ hideReferencedUsers = ->
+ referencedUsers = form.find(".referenced-users")
+ referencedUsers.hide()
+
+ renderReferencedUsers = (users) ->
+ referencedUsers = form.find(".referenced-users")
+
+ if referencedUsers.length
+ if users.length >= 10
+ referencedUsers.show()
+ referencedUsers.find(".js-referenced-users-count").text users.length
+ else
+ referencedUsers.hide()
+
+ renderMarkdown = ->
+ preview = form.find(".js-md-preview")
+ mdText = form.find(".markdown-area").val()
+ if mdText.trim().length is 0
+ preview.text "Nothing to preview."
+ hideReferencedUsers()
+ else
+ preview.text "Loading..."
+ $.ajax(
+ type: "POST",
+ url: markdown_preview_path,
+ data: {
+ text: mdText
+ },
+ dataType: "json"
+ ).success (data) ->
+ preview.html data.body
+
+ renderReferencedUsers data.references.users
+
formatLink = (link) ->
text = "[#{link.alt}](#{link.url})"
text = "!#{text}" if link.is_image
diff --git a/app/assets/javascripts/extensions/jquery.js.coffee b/app/assets/javascripts/extensions/jquery.js.coffee
index 40fb6cb9fc3..0a9db8eb5ef 100644
--- a/app/assets/javascripts/extensions/jquery.js.coffee
+++ b/app/assets/javascripts/extensions/jquery.js.coffee
@@ -1,13 +1,11 @@
-$.fn.showAndHide = ->
- $(@).show().
- delay(3000).
- fadeOut()
-
-$.fn.enableButton = ->
- $(@).removeAttr('disabled').
- removeClass('disabled')
-
-$.fn.disableButton = ->
- $(@).attr('disabled', 'disabled').
- addClass('disabled')
+# Disable an element and add the 'disabled' Bootstrap class
+$.fn.extend disable: ->
+ $(@)
+ .attr('disabled', 'disabled')
+ .addClass('disabled')
+# Enable an element and remove the 'disabled' Bootstrap class
+$.fn.extend enable: ->
+ $(@)
+ .removeAttr('disabled')
+ .removeClass('disabled')
diff --git a/app/assets/javascripts/gfm_auto_complete.js.coffee b/app/assets/javascripts/gfm_auto_complete.js.coffee
index 4eb3f3c03f3..7967892f856 100644
--- a/app/assets/javascripts/gfm_auto_complete.js.coffee
+++ b/app/assets/javascripts/gfm_auto_complete.js.coffee
@@ -10,7 +10,7 @@ GitLab.GfmAutoComplete =
# Team Members
Members:
- template: '<li>${username} <small>${name}</small></li>'
+ template: '<li>${username} <small>${title}</small></li>'
# Issues and MergeRequests
Issues:
@@ -34,7 +34,13 @@ GitLab.GfmAutoComplete =
searchKey: 'search'
callbacks:
beforeSave: (members) ->
- $.map members, (m) -> name: m.name, username: m.username, search: "#{m.username} #{m.name}"
+ $.map members, (m) ->
+ title = m.name
+ title += " (#{m.count})" if m.count
+
+ username: m.username
+ title: sanitize(title)
+ search: sanitize("#{m.username} #{m.name}")
input.atwho
at: '#'
@@ -44,7 +50,10 @@ GitLab.GfmAutoComplete =
insertTpl: '${atwho-at}${id}'
callbacks:
beforeSave: (issues) ->
- $.map issues, (i) -> id: i.iid, title: sanitize(i.title), search: "#{i.iid} #{i.title}"
+ $.map issues, (i) ->
+ id: i.iid
+ title: sanitize(i.title)
+ search: "#{i.iid} #{i.title}"
input.atwho
at: '!'
@@ -54,7 +63,10 @@ GitLab.GfmAutoComplete =
insertTpl: '${atwho-at}${id}'
callbacks:
beforeSave: (merges) ->
- $.map merges, (m) -> id: m.iid, title: sanitize(m.title), search: "#{m.iid} #{m.title}"
+ $.map merges, (m) ->
+ id: m.iid
+ title: sanitize(m.title)
+ search: "#{m.iid} #{m.title}"
input.one 'focus', =>
$.getJSON(@dataSource).done (data) ->
diff --git a/app/assets/javascripts/issue.js.coffee b/app/assets/javascripts/issue.js.coffee
index 86ad3d03bac..74d6b80be5e 100644
--- a/app/assets/javascripts/issue.js.coffee
+++ b/app/assets/javascripts/issue.js.coffee
@@ -1,4 +1,3 @@
-#= require jquery
#= require jquery.waitforimages
#= require task_list
diff --git a/app/assets/javascripts/merge_request.js.coffee b/app/assets/javascripts/merge_request.js.coffee
index 3937c428e24..25a7815dba2 100644
--- a/app/assets/javascripts/merge_request.js.coffee
+++ b/app/assets/javascripts/merge_request.js.coffee
@@ -1,5 +1,4 @@
-#= require jquery
-#= require bootstrap
+#= require jquery.waitforimages
#= require task_list
class @MergeRequest
@@ -12,12 +11,6 @@ class @MergeRequest
# commits_loaded - Boolean, have commits been pre-rendered server-side?
# (default: false)
#
- # check_enable - Boolean, whether to check automerge status
- # url_to_automerge_check - String, URL to use to check automerge status
- # current_status - String, current automerge status
- # ci_enable - Boolean, whether a CI service is enabled
- # url_to_ci_check - String, URL to use to check CI status
- #
constructor: (@opts) ->
@initContextWidget()
this.$el = $('.merge-request')
@@ -26,16 +19,11 @@ class @MergeRequest
@commits_loaded = @opts.commits_loaded or false
this.bindEvents()
- this.activateTabFromHash()
+ this.activateTabFromPath()
- this.initMergeWidget()
this.$('.show-all-commits').on 'click', =>
this.showAllCommits()
- modal = $('#modal_merge_info').modal(show: false)
-
- disableButtonIfEmptyField '#commit_message', '.accept_merge_request'
-
# Prevent duplicate event bindings
@disableTaskList()
@@ -64,104 +52,74 @@ class @MergeRequest
$(".context .inline-update").on "change", "#merge_request_assignee_id", ->
$(this).submit()
- initMergeWidget: ->
- this.showState( @opts.current_status )
-
- if this.$('.automerge_widget').length and @opts.check_enable
- $.get @opts.url_to_automerge_check, (data) =>
- this.showState( data.merge_status )
- , 'json'
-
- if @opts.ci_enable
- $.get @opts.url_to_ci_check, (data) =>
- this.showCiState data.status
- if data.coverage
- this.showCiCoverage data.coverage
- , 'json'
bindEvents: ->
this.$('.merge-request-tabs a[data-toggle="tab"]').on 'shown.bs.tab', (e) =>
$target = $(e.target)
-
- # Nothing else to be done if we're on the first tab
- return if $target.data('action') == 'notes'
-
- # Persist current tab selection via URL
- href = $target.attr('href')
- if href.substr(0,1) == '#'
- location.replace("#!#{href.substr(1)}")
+ tab_action = $target.data('action')
# Lazy-load diffs
- if $target.data('action') == 'diffs'
+ if tab_action == 'diffs'
this.loadDiff() unless @diffs_loaded
- $('.diff-header').trigger("sticky_kit:recalc")
+ $('.diff-header').trigger('sticky_kit:recalc')
- this.$('.accept_merge_request').on 'click', ->
- $('.automerge_widget.can_be_merged').hide()
- $('.merge-in-progress').show()
+ # Skip tab-persisting behavior on MergeRequests#new
+ unless @opts.action == 'new'
+ @setCurrentAction(tab_action)
- this.$('.remove_source_branch').on 'click', ->
- $('.remove_source_branch_widget').hide()
- $('.remove_source_branch_in_progress').show()
-
- this.$(".remove_source_branch").on "ajax:success", (e, data, status, xhr) ->
- location.reload()
-
- this.$(".remove_source_branch").on "ajax:error", (e, data, status, xhr) =>
- this.$('.remove_source_branch_widget').hide()
- this.$('.remove_source_branch_in_progress').hide()
- this.$('.remove_source_branch_widget.failed').show()
+ # Activate a tab based on the current URL path
+ #
+ # If the current action is 'show' or 'new' (i.e., initial page load),
+ # activates the first tab, otherwise activates the tab corresponding to the
+ # current action (diffs, commits).
+ activateTabFromPath: ->
+ if @opts.action == 'show' || @opts.action == 'new'
+ this.$('.merge-request-tabs a[data-toggle="tab"]:first').tab('show')
+ else
+ this.$(".merge-request-tabs a[data-action='#{@opts.action}']").tab('show')
- # Activates a tab section based on the `#!` URL hash
+ # Replaces the current Merge Request-specific action in the URL with a new one
#
- # If no hash value is present (i.e., on the initial page load), the first tab
- # is selected by default.
+ # If the action is "notes", the URL is reset to the standard
+ # `MergeRequests#show` route.
#
- # ... unless the current controller action is `diffs`, in which case that tab
- # is selected instead. Fun, right?
+ # Examples:
#
- # Note: We use a `#!` instead of a standard URL hash for two reasons:
+ # location.pathname # => "/namespace/project/merge_requests/1"
+ # setCurrentAction('diffs')
+ # location.pathname # => "/namespace/project/merge_requests/1/diffs"
#
- # 1. Prevents the hash acting like an anchor and scrolling the page.
- # 2. Prevents mutating browser history.
- activateTabFromHash: ->
- # Correct the hash if we came here directly via the `/diffs` path
- if location.hash == '' and @opts.action == 'diffs'
- location.replace('#!diffs')
-
- if location.hash == ''
- this.$('.merge-request-tabs a[data-toggle="tab"]:first').tab('show')
- else if location.hash.substr(0,2) == '#!'
- this.$(".merge-request-tabs a[href='##{location.hash.substr(2)}']").tab("show")
-
- showState: (state) ->
- $('.automerge_widget').hide()
- $('.automerge_widget.' + state).show()
-
- showCiState: (state) ->
- $('.ci_widget').hide()
- allowed_states = ["failed", "canceled", "running", "pending", "success"]
- if state in allowed_states
- $('.ci_widget.ci-' + state).show()
- switch state
- when "failed", "canceled"
- @setMergeButtonClass('btn-danger')
- when "running", "pending"
- @setMergeButtonClass('btn-warning')
- else
- $('.ci_widget.ci-error').show()
- @setMergeButtonClass('btn-danger')
+ # location.pathname # => "/namespace/project/merge_requests/1/diffs"
+ # setCurrentAction('notes')
+ # location.pathname # => "/namespace/project/merge_requests/1"
+ #
+ # location.pathname # => "/namespace/project/merge_requests/1/diffs"
+ # setCurrentAction('commits')
+ # location.pathname # => "/namespace/project/merge_requests/1/commits"
+ setCurrentAction: (action) ->
+ # Normalize action, just to be safe
+ action = 'notes' if action == 'show'
+
+ # Remove a trailing '/commits' or '/diffs'
+ new_state = location.pathname.replace(/\/(commits|diffs)\/?$/, '')
- showCiCoverage: (coverage) ->
- cov_html = $('<span>')
- cov_html.addClass('ci-coverage')
- cov_html.text('Coverage ' + coverage + '%')
- $('.ci_widget:visible').append(cov_html)
+ # Append the new action if we're on a tab other than 'notes'
+ unless action == 'notes'
+ new_state += "/#{action}"
+
+ # Ensure parameters and hash come along for the ride
+ new_state += location.search + location.hash
+
+ # Replace the current history state with the new one without breaking
+ # Turbolinks' history.
+ #
+ # See https://github.com/rails/turbolinks/issues/363
+ history.replaceState {turbolinks: true, url: new_state}, '', new_state
loadDiff: (event) ->
$.ajax
type: 'GET'
- url: this.$('.merge-request-tabs .diffs-tab a').data('source') + ".json"
+ url: this.$('.merge-request-tabs .diffs-tab a').attr('href') + ".json"
beforeSend: =>
this.$('.mr-loading-status .loading').show()
complete: =>
@@ -175,26 +133,6 @@ class @MergeRequest
this.$('.first-commits').remove()
this.$('.all-commits').removeClass 'hide'
- alreadyOrCannotBeMerged: ->
- this.$('.automerge_widget').hide()
- this.$('.merge-in-progress').hide()
- this.$('.automerge_widget.already_cannot_be_merged').show()
-
- setMergeButtonClass: (css_class) ->
- $('.accept_merge_request').removeClass("btn-create").addClass(css_class)
-
- mergeInProgress: ->
- $.ajax
- type: 'GET'
- url: $('.merge-request').data('url')
- success: (data) =>
- switch data.state
- when 'merged'
- location.reload()
- else
- setTimeout(merge_request.mergeInProgress, 3000)
- dataType: 'json'
-
initTaskList: ->
$('.merge-request-details .js-task-list-container').taskList('enable')
$(document).on 'tasklist:changed', '.merge-request-details .js-task-list-container', @updateTaskList
diff --git a/app/assets/javascripts/merge_request_widget.js.coffee b/app/assets/javascripts/merge_request_widget.js.coffee
new file mode 100644
index 00000000000..ca769e06a4e
--- /dev/null
+++ b/app/assets/javascripts/merge_request_widget.js.coffee
@@ -0,0 +1,58 @@
+class @MergeRequestWidget
+ # Initialize MergeRequestWidget behavior
+ #
+ # check_enable - Boolean, whether to check automerge status
+ # url_to_automerge_check - String, URL to use to check automerge status
+ # current_status - String, current automerge status
+ # ci_enable - Boolean, whether a CI service is enabled
+ # url_to_ci_check - String, URL to use to check CI status
+ #
+ constructor: (@opts) ->
+ modal = $('#modal_merge_info').modal(show: false)
+
+ mergeInProgress: ->
+ $.ajax
+ type: 'GET'
+ url: $('.merge-request').data('url')
+ success: (data) =>
+ switch data.state
+ when 'merged'
+ location.reload()
+ else
+ setTimeout(merge_request_widget.mergeInProgress, 3000)
+ dataType: 'json'
+
+ getMergeStatus: ->
+ $.get @opts.url_to_automerge_check, (data) ->
+ $('.mr-state-widget').replaceWith(data)
+
+ getCiStatus: ->
+ if @opts.ci_enable
+ $.get @opts.url_to_ci_check, (data) =>
+ this.showCiState data.status
+ if data.coverage
+ this.showCiCoverage data.coverage
+ , 'json'
+
+ showCiState: (state) ->
+ $('.ci_widget').hide()
+ allowed_states = ["failed", "canceled", "running", "pending", "success"]
+ if state in allowed_states
+ $('.ci_widget.ci-' + state).show()
+ switch state
+ when "failed", "canceled"
+ @setMergeButtonClass('btn-danger')
+ when "running", "pending"
+ @setMergeButtonClass('btn-warning')
+ else
+ $('.ci_widget.ci-error').show()
+ @setMergeButtonClass('btn-danger')
+
+ showCiCoverage: (coverage) ->
+ cov_html = $('<span>')
+ cov_html.addClass('ci-coverage')
+ cov_html.text('Coverage ' + coverage + '%')
+ $('.ci_widget:visible').append(cov_html)
+
+ setMergeButtonClass: (css_class) ->
+ $('.accept_merge_request').removeClass("btn-create").addClass(css_class)
diff --git a/app/assets/javascripts/notes.js.coffee b/app/assets/javascripts/notes.js.coffee
index f186fec2a0c..21656f59149 100644
--- a/app/assets/javascripts/notes.js.coffee
+++ b/app/assets/javascripts/notes.js.coffee
@@ -1,6 +1,4 @@
-#= require jquery
#= require autosave
-#= require bootstrap
#= require dropzone
#= require dropzone_input
#= require gfm_auto_complete
@@ -65,13 +63,11 @@ class @Notes
# fetch notes when tab becomes visible
$(document).on "visibilitychange", @visibilityChange
- @notes_forms = '.js-main-target-form textarea, .js-discussion-note-form textarea'
# Chrome doesn't fire keypress or keyup for Command+Enter, so we need keydown.
- $(document).on('keydown', @notes_forms, (e) ->
+ $(document).on 'keydown', '.js-note-text', (e) ->
return if e.originalEvent.repeat
if e.keyCode == 10 || ((e.metaKey || e.ctrlKey) && e.keyCode == 13)
- $(@).parents('form').submit()
- )
+ $(@).closest('form').submit()
cleanBinding: ->
$(document).off "ajax:success", ".js-main-target-form"
@@ -86,7 +82,7 @@ class @Notes
$(document).off "click", ".js-discussion-reply-button"
$(document).off "click", ".js-add-diff-note-button"
$(document).off "visibilitychange"
- $(document).off "keydown", @notes_forms
+ $(document).off "keydown", ".js-note-text"
$(document).off "keyup", ".js-note-text"
$(document).off "click", ".js-note-target-reopen"
$(document).off "click", ".js-note-target-close"
diff --git a/app/assets/javascripts/profile.js.coffee b/app/assets/javascripts/profile.js.coffee
index de356fbec77..40459a9a155 100644
--- a/app/assets/javascripts/profile.js.coffee
+++ b/app/assets/javascripts/profile.js.coffee
@@ -12,11 +12,11 @@ class @Profile
$(this).find('.update-failed').hide()
$('.update-username form').on 'ajax:complete', ->
- $(this).find('.btn-save').enableButton()
+ $(this).find('.btn-save').enable()
$(this).find('.loading-gif').hide()
$('.update-notifications').on 'ajax:complete', ->
- $(this).find('.btn-save').enableButton()
+ $(this).find('.btn-save').enable()
$('.js-choose-user-avatar-button').bind "click", ->
diff --git a/app/assets/javascripts/shortcuts_issuable.coffee b/app/assets/javascripts/shortcuts_issuable.coffee
index 6b534f29218..bb532194682 100644
--- a/app/assets/javascripts/shortcuts_issuable.coffee
+++ b/app/assets/javascripts/shortcuts_issuable.coffee
@@ -1,6 +1,4 @@
-#= require jquery
#= require mousetrap
-
#= require shortcuts_navigation
class @ShortcutsIssuable extends ShortcutsNavigation
diff --git a/app/assets/javascripts/stat_graph_contributors.js.coffee b/app/assets/javascripts/stat_graph_contributors.js.coffee
index ed12bdcef22..3be14cb43dd 100644
--- a/app/assets/javascripts/stat_graph_contributors.js.coffee
+++ b/app/assets/javascripts/stat_graph_contributors.js.coffee
@@ -1,5 +1,4 @@
#= require d3
-#= require jquery
#= require stat_graph_contributors_util
class @ContributorsStatGraph
diff --git a/app/assets/javascripts/stat_graph_contributors_graph.js.coffee b/app/assets/javascripts/stat_graph_contributors_graph.js.coffee
index 0e6fbdef3bc..b7a0e073766 100644
--- a/app/assets/javascripts/stat_graph_contributors_graph.js.coffee
+++ b/app/assets/javascripts/stat_graph_contributors_graph.js.coffee
@@ -50,7 +50,7 @@ class @ContributorsGraph
class @ContributorsMasterGraph extends ContributorsGraph
constructor: (@data) ->
- @width = $('.container').width() - 345
+ @width = $('.content').width() - 70
@height = 200
@x = null
@y = null
@@ -123,7 +123,7 @@ class @ContributorsMasterGraph extends ContributorsGraph
class @ContributorsAuthorGraph extends ContributorsGraph
constructor: (@data) ->
- @width = $('.container').width()/2 - 225
+ @width = $('.content').width()/2 - 100
@height = 200
@x = null
@y = null
diff --git a/app/assets/javascripts/zen_mode.js.coffee b/app/assets/javascripts/zen_mode.js.coffee
index dc6a84c6c52..8a0564a9098 100644
--- a/app/assets/javascripts/zen_mode.js.coffee
+++ b/app/assets/javascripts/zen_mode.js.coffee
@@ -1,3 +1,7 @@
+#= require dropzone
+#= require mousetrap
+#= require mousetrap/pause
+
class @ZenMode
constructor: ->
@active_zen_area = null
@@ -26,7 +30,7 @@ class @ZenMode
@exitZenMode()
$(document).on 'keydown', (e) =>
- if e.keyCode is $.ui.keyCode.ESCAPE
+ if e.keyCode is 27 # Esc
@exitZenMode()
e.preventDefault()
@@ -42,7 +46,9 @@ class @ZenMode
@active_checkbox.prop('checked', false)
@active_zen_area = null
@active_checkbox = null
- window.location.hash = ''
- window.scrollTo(window.pageXOffset, @scroll_position)
+ @restoreScroll(@scroll_position)
# Enable dropzone when leaving ZEN mode
Dropzone.forElement('.div-dropzone').enable()
+
+ restoreScroll: (y) ->
+ window.scrollTo(window.pageXOffset, y)
diff --git a/app/assets/stylesheets/base/layout.scss b/app/assets/stylesheets/base/layout.scss
index 62c11b06368..690d89a5c16 100644
--- a/app/assets/stylesheets/base/layout.scss
+++ b/app/assets/stylesheets/base/layout.scss
@@ -4,7 +4,7 @@ html {
&.touch .tooltip { display: none !important; }
body {
- padding-top: 46px;
+ padding-top: $header-height;
}
}
diff --git a/app/assets/stylesheets/base/variables.scss b/app/assets/stylesheets/base/variables.scss
index 596376c3970..08f153dfbc9 100644
--- a/app/assets/stylesheets/base/variables.scss
+++ b/app/assets/stylesheets/base/variables.scss
@@ -1,16 +1,19 @@
$style_color: #474D57;
-$hover: #FFF3EB;
+$hover: #FFFAF1;
$gl-text-color: #222222;
$gl-link-color: #446e9b;
$nprogress-color: #c0392b;
$gl-font-size: 14px;
$list-font-size: 15px;
+$sidebar_collapsed_width: 52px;
$sidebar_width: 230px;
$avatar_radius: 50%;
$code_font_size: 13px;
$code_line_height: 1.5;
$border-color: #E5E5E5;
$background-color: #f5f5f5;
+$header-height: 50px;
+
/*
* State colors:
diff --git a/app/assets/stylesheets/generic/common.scss b/app/assets/stylesheets/generic/common.scss
index b69c5c4b574..1419a9cded9 100644
--- a/app/assets/stylesheets/generic/common.scss
+++ b/app/assets/stylesheets/generic/common.scss
@@ -307,7 +307,7 @@ table {
}
.btn-sign-in {
- margin-top: 5px;
+ margin-top: 7px;
text-shadow: none;
}
diff --git a/app/assets/stylesheets/generic/forms.scss b/app/assets/stylesheets/generic/forms.scss
index 7e070b4f386..4282832e2bf 100644
--- a/app/assets/stylesheets/generic/forms.scss
+++ b/app/assets/stylesheets/generic/forms.scss
@@ -49,14 +49,6 @@ label {
width: 250px;
}
-.input-mx-250 {
- max-width: 250px;
-}
-
-.input-mn-300 {
- min-width: 300px;
-}
-
.custom-form-control {
width: 150px;
}
diff --git a/app/assets/stylesheets/generic/header.scss b/app/assets/stylesheets/generic/header.scss
index fe32b024f49..5e8701830e7 100644
--- a/app/assets/stylesheets/generic/header.scss
+++ b/app/assets/stylesheets/generic/header.scss
@@ -3,76 +3,40 @@
*
*/
header {
+ &.navbar-empty {
+ background: #FFF;
+ border-bottom: 1px solid #EEE;
+
+ .center-logo {
+ margin: 8px 0;
+ text-align: center;
+ }
+ }
+
&.navbar-gitlab {
z-index: 100;
margin-bottom: 0;
- min-height: 40px;
+ min-height: $header-height;
border: none;
width: 100%;
.container {
+ background: #FFF;
width: 100% !important;
padding: 0;
- padding-right: 35px;
- background: #FFF;
- border-bottom: 1px solid #EEE;
filter: none;
- .title {
- position: relative;
- float: left;
- margin: 0;
- margin-left: 25px;
- font-size: 18px;
- line-height: 44px;
- font-weight: bold;
- color: #444;
-
- @include str-truncated(37%);
-
- a {
- color: #444;
- &:hover {
- text-decoration: underline;
- }
- }
- }
-
- .app_logo {
- border-bottom: 1px solid transparent;
- margin-bottom: -1px;
-
- a {
- padding: 5px 8px;
-
- img {
- float: left;
- }
-
- h3 {
- width: 158px;
- float: left;
- margin: 0;
- margin-left: 20px;
- font-size: 18px;
- line-height: 34px;
- font-weight: normal;
- }
- }
- }
-
.nav > li > a {
color: #888;
font-size: 14px;
- line-height: 19px;
padding: 0;
background-color: #f5f5f5;
- margin: 9px 0;
+ margin: ($header-height - 28) / 2 0;
margin-left: 10px;
border-radius: 40px;
- height: 26px;
- width: 26px;
- line-height: 26px;
+ height: 28px;
+ width: 28px;
+ line-height: 28px;
text-align: center;
&:hover, &:focus, &:active {
@@ -80,93 +44,84 @@ header {
}
}
- /** NAV block with links and profile **/
- .nav {
- float: right;
- margin-right: 0;
- }
-
.navbar-toggle {
color: #666;
margin: 0;
border-radius: 0;
+ position: absolute;
+ right: 2px;
&:hover {
background-color: #EEE;
}
}
}
-
- .turbolink-spinner {
- font-size: 20px;
- margin-right: 10px;
- }
-
- @media (max-width: $screen-xs-max) {
- border-width: 0;
- font-size: 18px;
-
- .title {
- @include str-truncated(70%);
- }
-
- .navbar-collapse {
- margin-top: 47px;
- }
-
- .navbar-nav {
- margin: 5px 0;
-
- .visible-xs, .visable-sm {
- display: table-cell !important;
- }
- }
-
- li {
- display: table-cell;
- width: 1%;
-
- a {
- text-align: center;
- font-size: 18px !important;
- }
- }
- }
}
- /**
- *
- * Logo holder
- *
- */
- .app_logo {
+ .header-logo {
+ border-bottom: 1px solid transparent;
float: left;
- margin-right: 9px;
+ height: $header-height;
+ width: $sidebar_width;
a {
float: left;
- height: 46px;
+ height: $header-height;
width: 100%;
+ padding: ($header-height - 36 ) / 2 8px;
+
+ h3 {
+ width: 158px;
+ float: left;
+ margin: 0;
+ margin-left: 14px;
+ font-size: 18px;
+ line-height: $header-height - 14;
+ font-weight: normal;
+ }
img {
width: 36px;
height: 36px;
+ float: left;
}
}
+
&:hover {
background-color: #EEE;
}
}
- /**
- *
- * Search box
- *
- */
+ .header-content {
+ border-bottom: 1px solid #EEE;
+ padding-right: 35px;
+ height: $header-height;
+
+ .title {
+ position: relative;
+ float: left;
+ margin: 0;
+ margin-left: 35px;
+ font-size: 18px;
+ line-height: $header-height;
+ font-weight: bold;
+ color: #444;
+
+ @include str-truncated(37%);
+
+ a {
+ color: #444;
+ &:hover {
+ text-decoration: underline;
+ }
+ }
+ }
+ }
+
.search {
margin-right: 10px;
margin-left: 10px;
- margin-top: 8px;
+ margin-top: ($header-height - 28) / 2;
form {
margin: 0;
@@ -174,6 +129,7 @@ header {
}
.search-input {
+ width: 220px;
background-image: image-url("icon-search.png");
background-repeat: no-repeat;
background-position: 10px;
@@ -183,45 +139,80 @@ header {
font-size: 13px;
background-color: #f5f5f5;
border-color: #f5f5f5;
+
+ &:focus {
+ @include box-shadow(none);
+ outline: none;
+ border-color: #DDD;
+ background-color: #FFF;
+ }
}
}
}
-.search .search-input {
- width: 300px;
-}
+@mixin collapsed-header {
+ .header-logo {
+ width: $sidebar_collapsed_width;
-@media (max-width: 1200px) {
- .search .search-input {
- width: 200px;
+ h3 {
+ display: none;
+ }
}
-}
-@media (max-width: $screen-xs-max) {
- #nprogress .spinner {
- right: 35px !important;
+ .header-content {
+ .title {
+ margin-left: 30px;
+ }
}
}
@media (max-width: $screen-md-max) {
- .header-collapsed, .header-expanded {
- width: 52px;
+ header .container .title {
+ max-width: 43%;
+ }
- h3 {
- display: none;
- }
+ .header-collapsed, .header-expanded {
+ @include collapsed-header;
}
}
@media(min-width: $screen-md-max) {
.header-collapsed {
- width: 52px;
-
- h3 {
- display: none;
- }
+ @include collapsed-header;
}
.header-expanded {
}
}
+
+@media (max-width: $screen-xs-max) {
+ header .container {
+ font-size: 18px;
+
+ .title {
+ max-width: 70%;
+ }
+
+ .navbar-nav {
+ margin: 0px;
+ float: none !important;
+
+ .visible-xs, .visable-sm {
+ display: table-cell !important;
+ }
+ }
+
+ .navbar-collapse {
+ padding-left: 5px;
+
+ li {
+ display: table-cell;
+ width: 1%;
+
+ a {
+ margin-left: 8px !important;
+ }
+ }
+ }
+ }
+}
diff --git a/app/assets/stylesheets/generic/lists.scss b/app/assets/stylesheets/generic/lists.scss
index 08bf6e943d2..c502d953c75 100644
--- a/app/assets/stylesheets/generic/lists.scss
+++ b/app/assets/stylesheets/generic/lists.scss
@@ -39,7 +39,6 @@
&:hover {
background: $hover;
- border-bottom: 1px solid darken($hover, 10%);
}
&:last-child {
diff --git a/app/assets/stylesheets/generic/markdown_area.scss b/app/assets/stylesheets/generic/markdown_area.scss
index eb39b6bb7e9..f94677d1925 100644
--- a/app/assets/stylesheets/generic/markdown_area.scss
+++ b/app/assets/stylesheets/generic/markdown_area.scss
@@ -52,6 +52,22 @@
transition: opacity 200ms ease-in-out;
}
+.md-area {
+ position: relative;
+}
+
+.md-header ul {
+ float: left;
+}
+
+.referenced-users {
+ padding: 10px 0;
+ color: #999;
+ margin-left: 10px;
+ margin-top: 1px;
+ margin-right: 130px;
+}
+
.md-preview-holder {
background: #FFF;
border: 1px solid #ddd;
diff --git a/app/assets/stylesheets/generic/mobile.scss b/app/assets/stylesheets/generic/mobile.scss
index b7f6fac5223..f04c8eef904 100644
--- a/app/assets/stylesheets/generic/mobile.scss
+++ b/app/assets/stylesheets/generic/mobile.scss
@@ -19,6 +19,10 @@
}
}
+ .referenced-users {
+ margin-right: 0;
+ }
+
.issues-filters,
.dash-projects-filters,
.check-all-holder {
@@ -57,9 +61,24 @@
}
.container .title {
- margin-left: 6px !important;
+ margin-left: 15px !important;
max-width: 70% !important;
}
+
+ .issue-info, .merge-request-info {
+ display: none;
+ }
+
+ .issue-details {
+ .creator,
+ .page-title .btn-close {
+ display: none;
+ }
+ }
+
+ %ul.notes .note-role, .note-actions {
+ display: none;
+ }
}
@media (max-width: $screen-sm-max) {
diff --git a/app/assets/stylesheets/generic/sidebar.scss b/app/assets/stylesheets/generic/sidebar.scss
index a80b5850803..65e06e14c73 100644
--- a/app/assets/stylesheets/generic/sidebar.scss
+++ b/app/assets/stylesheets/generic/sidebar.scss
@@ -1,6 +1,4 @@
.page-with-sidebar {
- background: $background-color;
-
.sidebar-wrapper {
position: fixed;
top: 0;
@@ -88,7 +86,7 @@
.nav-sidebar {
margin-top: 29px;
position: fixed;
- top: 45px;
+ top: $header-height;
width: $sidebar_width;
}
}
@@ -102,13 +100,13 @@
padding-left: 50px;
.sidebar-wrapper {
- width: 52px;
+ width: $sidebar_collapsed_width;
.nav-sidebar {
margin-top: 29px;
position: fixed;
- top: 45px;
- width: 52px;
+ top: $header-height;
+ width: $sidebar_collapsed_width;
li a {
padding-left: 18px;
@@ -125,28 +123,20 @@
.collapse-nav a {
left: 0px;
- width: 52px;
+ width: $sidebar_collapsed_width;
}
.sidebar-user {
.username {
display: none;
}
-
- .avatar {
- margin-bottom: 10px;
- }
-
- .logout-holder {
- text-align: center;
- }
}
}
}
.collapse-nav a {
position: fixed;
- top: 46px;
+ top: $header-height;
left: 198px;
font-size: 13px;
background: transparent;
@@ -190,9 +180,8 @@
bottom: 0;
width: 100%;
padding: 10px;
- color: #fff;
- .avatar {
+ .username {
margin-top: 5px;
}
}
diff --git a/app/assets/stylesheets/generic/typography.scss b/app/assets/stylesheets/generic/typography.scss
index e5590897947..66767cb13cb 100644
--- a/app/assets/stylesheets/generic/typography.scss
+++ b/app/assets/stylesheets/generic/typography.scss
@@ -23,6 +23,13 @@ pre {
font-family: $monospace_font;
}
+code {
+ &.key-fingerprint {
+ background: $body-bg;
+ color: $text-color;
+ }
+}
+
/**
* Wiki typography
*
diff --git a/app/assets/stylesheets/generic/zen.scss b/app/assets/stylesheets/generic/zen.scss
index 26afc21a6ab..7ab01187a02 100644
--- a/app/assets/stylesheets/generic/zen.scss
+++ b/app/assets/stylesheets/generic/zen.scss
@@ -1,15 +1,14 @@
.zennable {
- position: relative;
-
- input {
+ .zen-toggle-comment {
display: none;
}
.zen-enter-link {
color: #888;
position: absolute;
- top: -26px;
+ top: 0px;
right: 4px;
+ line-height: 40px;
}
.zen-leave-link {
@@ -26,10 +25,12 @@
}
}
+ // Hide the Enter link when we're in Zen mode
input:checked ~ .zen-backdrop .zen-enter-link {
display: none;
}
+ // Show the Leave link when we're in Zen mode
input:checked ~ .zen-backdrop .zen-leave-link {
display: block;
position: absolute;
@@ -62,6 +63,9 @@
}
}
+ // Make the placeholder text in the standard textarea the same color as the
+ // background, effectively hiding it
+
.zen-backdrop textarea::-webkit-input-placeholder {
color: white;
}
@@ -78,6 +82,9 @@
color: white;
}
+ // Make the color of the placeholder text in the Zenned-out textarea darker,
+ // so it becomes visible
+
input:checked ~ .zen-backdrop textarea::-webkit-input-placeholder {
color: #999;
}
diff --git a/app/assets/stylesheets/pages/dashboard.scss b/app/assets/stylesheets/pages/dashboard.scss
index af9c83e5dc8..09e8d57a100 100644
--- a/app/assets/stylesheets/pages/dashboard.scss
+++ b/app/assets/stylesheets/pages/dashboard.scss
@@ -29,7 +29,7 @@
line-height: 24px;
.str-truncated {
- max-width: 72%;
+ max-width: 76%;
}
a {
diff --git a/app/assets/stylesheets/pages/issues.scss b/app/assets/stylesheets/pages/issues.scss
index 3572f33e91f..ed938f86b35 100644
--- a/app/assets/stylesheets/pages/issues.scss
+++ b/app/assets/stylesheets/pages/issues.scss
@@ -145,3 +145,9 @@ h2.issue-title {
.issue-form .select2-container {
width: 250px !important;
}
+
+.issues-holder {
+ .issue-info {
+ margin-left: 20px;
+ }
+}
diff --git a/app/assets/stylesheets/pages/merge_requests.scss b/app/assets/stylesheets/pages/merge_requests.scss
index 3165396a94d..61071320973 100644
--- a/app/assets/stylesheets/pages/merge_requests.scss
+++ b/app/assets/stylesheets/pages/merge_requests.scss
@@ -3,7 +3,7 @@
* MR -> show: Automerge widget
*
*/
-.automerge_widget {
+.mr-state-widget {
form {
margin-bottom: 0;
.clearfix {
@@ -123,38 +123,31 @@
.mr-state-widget {
font-size: 13px;
- background: #F9F9F9;
+ background: #FAFAFA;
margin-bottom: 20px;
color: #666;
- border: 1px solid #EEE;
- @include box-shadow(0 1px 1px rgba(0, 0, 0, 0.09));
+ border: 1px solid #e5e5e5;
+ @include box-shadow(0 1px 1px rgba(0, 0, 0, 0.05));
+ @include border-radius(3px);
.ci_widget {
padding: 10px 15px;
font-size: 15px;
- border-bottom: 1px solid #BBB;
- color: #777;
- background-color: $background-color;
+ border-bottom: 1px solid #EEE;
&.ci-success {
color: $gl-success;
- border-color: $gl-success;
- background-color: #F1FAF1;
}
&.ci-pending,
&.ci-running {
color: $gl-warning;
- border-color: $gl-warning;
- background-color: #FAF5F1;
}
&.ci-failed,
&.ci-canceled,
&.ci-error {
color: $gl-danger;
- border-color: $gl-danger;
- background-color: #FAF1F1;
}
}
@@ -162,7 +155,8 @@
padding: 10px 15px;
h4 {
- font-weight: normal;
+ font-weight: bold;
+ margin: 5px 0;
}
p:last-child {
diff --git a/app/assets/stylesheets/pages/note_form.scss b/app/assets/stylesheets/pages/note_form.scss
index a0522030785..203f9374cee 100644
--- a/app/assets/stylesheets/pages/note_form.scss
+++ b/app/assets/stylesheets/pages/note_form.scss
@@ -39,11 +39,8 @@
.new_note, .edit_note {
.buttons {
- float: left;
margin-top: 8px;
- }
- .clearfix {
- margin-bottom: 0;
+ margin-bottom: 3px;
}
.note-preview-holder {
@@ -82,7 +79,6 @@
.note-form-actions {
background: #F9F9F9;
- height: 45px;
.note-form-option {
margin-top: 8px;
diff --git a/app/assets/stylesheets/pages/profile.scss b/app/assets/stylesheets/pages/profile.scss
index 5b528b38d36..5a5fbc468a3 100644
--- a/app/assets/stylesheets/pages/profile.scss
+++ b/app/assets/stylesheets/pages/profile.scss
@@ -84,8 +84,9 @@
}
.btn {
- line-height: 36px;
- height: 56px;
+ line-height: 40px;
+ height: 42px;
+ padding: 0px 12px;
img {
width: 32px;
diff --git a/app/assets/stylesheets/pages/projects.scss b/app/assets/stylesheets/pages/projects.scss
index 83771480cbd..e19b2eafa43 100644
--- a/app/assets/stylesheets/pages/projects.scss
+++ b/app/assets/stylesheets/pages/projects.scss
@@ -15,18 +15,16 @@
}
.project-home-panel {
- margin-bottom: 20px;
+ margin-top: 10px;
+ margin-bottom: 15px;
position: relative;
padding-left: 65px;
- border-bottom: 1px solid #DDD;
- padding-bottom: 10px;
- padding-top: 5px;
min-height: 50px;
.project-identicon-holder {
position: absolute;
left: 0;
- top: -10px;
+ top: -14px;
.avatar {
width: 50px;
@@ -48,14 +46,16 @@
}
.project-home-desc {
+ color: $gray;
+ float: left;
font-size: 16px;
line-height: 1.3;
margin-right: 250px;
- }
- .project-home-desc {
- float: left;
- color: $gray;
+ // Render Markdown-generated HTML inline for this block
+ p {
+ display: inline;
+ }
}
}
@@ -209,13 +209,14 @@ ul.nav.nav-projects-tabs {
line-height: 1.5;
}
- .well {
- padding: 14px;
+ .panel {
+ @include border-radius(3px);
- h4 {
+ .panel-heading, .panel-footer {
font-weight: normal;
- margin: 0;
- color: #555;
+ background-color: transparent;
+ color: #666;
+ border-color: #EEE;
}
.actions {
@@ -224,10 +225,12 @@ ul.nav.nav-projects-tabs {
.nav-pills a {
padding: 10px;
+ font-weight: bold;
+ color: $gl-link-color;
}
.nav {
- margin: 10px 0;
+ margin-bottom: 15px;
}
}
diff --git a/app/assets/stylesheets/themes/gitlab-theme.scss b/app/assets/stylesheets/themes/gitlab-theme.scss
index 9b8e3d8e291..10fcaf18fa9 100644
--- a/app/assets/stylesheets/themes/gitlab-theme.scss
+++ b/app/assets/stylesheets/themes/gitlab-theme.scss
@@ -1,8 +1,9 @@
@mixin gitlab-theme($color-light, $color, $color-darker, $color-dark) {
header {
&.navbar-gitlab {
- .app_logo {
+ .header-logo {
background-color: $color-darker;
+ border-color: $color-darker;
a {
color: $color-light;
@@ -19,8 +20,6 @@
}
.page-with-sidebar {
- background: $color-darker;
-
.collapse-nav a {
color: #FFF;
background: $color;
@@ -31,8 +30,12 @@
border-right: 1px solid $color-darker;
.sidebar-user {
- a {
- color: $color-light;
+ color: $color-light;
+
+ &:hover {
+ background-color: $color-dark;
+ color: #FFF;
+ text-decoration: none;
}
}
}
diff --git a/app/controllers/admin/application_settings_controller.rb b/app/controllers/admin/application_settings_controller.rb
index 4c35622fff1..c7c643db401 100644
--- a/app/controllers/admin/application_settings_controller.rb
+++ b/app/controllers/admin/application_settings_controller.rb
@@ -38,11 +38,14 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController
:twitter_sharing_enabled,
:sign_in_text,
:home_page_url,
+ :after_sign_out_path,
:max_attachment_size,
+ :session_expire_delay,
:default_project_visibility,
:default_snippet_visibility,
:restricted_signup_domains_raw,
:version_check_enabled,
+ :user_oauth_applications,
restricted_visibility_levels: [],
)
end
diff --git a/app/controllers/admin/deploy_keys_controller.rb b/app/controllers/admin/deploy_keys_controller.rb
index c301e61d1c7..285e8495342 100644
--- a/app/controllers/admin/deploy_keys_controller.rb
+++ b/app/controllers/admin/deploy_keys_controller.rb
@@ -1,13 +1,8 @@
class Admin::DeployKeysController < Admin::ApplicationController
before_action :deploy_keys, only: [:index]
- before_action :deploy_key, only: [:show, :destroy]
+ before_action :deploy_key, only: [:destroy]
def index
-
- end
-
- def show
-
end
def new
diff --git a/app/controllers/admin/groups_controller.rb b/app/controllers/admin/groups_controller.rb
index 2dfae13ac5c..4d3e48f7f81 100644
--- a/app/controllers/admin/groups_controller.rb
+++ b/app/controllers/admin/groups_controller.rb
@@ -47,7 +47,7 @@ class Admin::GroupsController < Admin::ApplicationController
end
def destroy
- @group.destroy
+ DestroyGroupService.new(@group, current_user).execute
redirect_to admin_groups_path, notice: 'Group was successfully deleted.'
end
diff --git a/app/controllers/admin/projects_controller.rb b/app/controllers/admin/projects_controller.rb
index ee449badf59..f616ccf5684 100644
--- a/app/controllers/admin/projects_controller.rb
+++ b/app/controllers/admin/projects_controller.rb
@@ -5,7 +5,7 @@ class Admin::ProjectsController < Admin::ApplicationController
def index
@projects = Project.all
- @projects = @projects.where(namespace_id: params[:namespace_id]) if params[:namespace_id].present?
+ @projects = @projects.in_namespace(params[:namespace_id]) if params[:namespace_id].present?
@projects = @projects.where("visibility_level IN (?)", params[:visibility_levels]) if params[:visibility_levels].present?
@projects = @projects.with_push if params[:with_push].present?
@projects = @projects.abandoned if params[:abandoned].present?
diff --git a/app/controllers/admin/users_controller.rb b/app/controllers/admin/users_controller.rb
index d36e359934c..06d6d61e907 100644
--- a/app/controllers/admin/users_controller.rb
+++ b/app/controllers/admin/users_controller.rb
@@ -86,11 +86,7 @@ class Admin::UsersController < Admin::ApplicationController
end
def destroy
- # 1. Remove groups where user is the only owner
- user.solo_owned_groups.map(&:destroy)
-
- # 2. Remove user with all authored content including personal projects
- user.destroy
+ DeleteUserService.new.execute(user)
respond_to do |format|
format.html { redirect_to admin_users_path }
diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb
index e5da94b2327..62d46a5482e 100644
--- a/app/controllers/application_controller.rb
+++ b/app/controllers/application_controller.rb
@@ -89,7 +89,7 @@ class ApplicationController < ActionController::Base
end
def after_sign_out_path_for(resource)
- new_user_session_path
+ current_application_settings.after_sign_out_path || new_user_session_path
end
def abilities
diff --git a/app/controllers/groups/group_members_controller.rb b/app/controllers/groups/group_members_controller.rb
index a11c554a2af..040255f08e6 100644
--- a/app/controllers/groups/group_members_controller.rb
+++ b/app/controllers/groups/group_members_controller.rb
@@ -66,7 +66,11 @@ class Groups::GroupMembersController < Groups::ApplicationController
@group_member.destroy
redirect_to(dashboard_groups_path, notice: "You left #{group.name} group.")
else
- return render_403
+ if @group.last_owner?(current_user)
+ redirect_to(dashboard_groups_path, alert: "You can not leave #{group.name} group because you're the last owner. Transfer or delete the group.")
+ else
+ return render_403
+ end
end
end
diff --git a/app/controllers/groups_controller.rb b/app/controllers/groups_controller.rb
index 34f0b257db3..2e381822e42 100644
--- a/app/controllers/groups_controller.rb
+++ b/app/controllers/groups_controller.rb
@@ -82,7 +82,7 @@ class GroupsController < Groups::ApplicationController
end
def destroy
- @group.destroy
+ DestroyGroupService.new(@group, current_user).execute
redirect_to root_path, notice: 'Group was removed.'
end
diff --git a/app/controllers/oauth/applications_controller.rb b/app/controllers/oauth/applications_controller.rb
index 507b8290a2b..fc31118124b 100644
--- a/app/controllers/oauth/applications_controller.rb
+++ b/app/controllers/oauth/applications_controller.rb
@@ -1,6 +1,8 @@
class Oauth::ApplicationsController < Doorkeeper::ApplicationsController
+ include Gitlab::CurrentSettings
include PageLayoutHelper
+ before_action :verify_user_oauth_applications_enabled
before_action :authenticate_user!
layout 'profile'
@@ -32,6 +34,12 @@ class Oauth::ApplicationsController < Doorkeeper::ApplicationsController
private
+ def verify_user_oauth_applications_enabled
+ return if current_application_settings.user_oauth_applications?
+
+ redirect_to applications_profile_url
+ end
+
def set_application
@application = current_user.oauth_applications.find(params[:id])
end
diff --git a/app/controllers/omniauth_callbacks_controller.rb b/app/controllers/omniauth_callbacks_controller.rb
index dcd949a71de..765adaf2128 100644
--- a/app/controllers/omniauth_callbacks_controller.rb
+++ b/app/controllers/omniauth_callbacks_controller.rb
@@ -1,4 +1,7 @@
class OmniauthCallbacksController < Devise::OmniauthCallbacksController
+
+ protect_from_forgery except: [:kerberos, :saml]
+
Gitlab.config.omniauth.providers.each do |provider|
define_method provider['name'] do
handle_omniauth
@@ -21,7 +24,7 @@ class OmniauthCallbacksController < Devise::OmniauthCallbacksController
@user = Gitlab::LDAP::User.new(oauth)
@user.save if @user.changed? # will also save new users
gl_user = @user.gl_user
- gl_user.remember_me = true if @user.persisted?
+ gl_user.remember_me = params[:remember_me] if @user.persisted?
# Do additional LDAP checks for the user filter and EE features
if @user.allowed?
diff --git a/app/controllers/projects/blob_controller.rb b/app/controllers/projects/blob_controller.rb
index b762518d377..100d3d3b317 100644
--- a/app/controllers/projects/blob_controller.rb
+++ b/app/controllers/projects/blob_controller.rb
@@ -13,27 +13,20 @@ class Projects::BlobController < Projects::ApplicationController
before_action :commit, except: [:new, :create]
before_action :blob, except: [:new, :create]
before_action :from_merge_request, only: [:edit, :update]
- before_action :after_edit_path, only: [:edit, :update]
before_action :require_branch_head, only: [:edit, :update]
+ before_action :editor_variables, except: [:show, :preview, :diff]
+ before_action :after_edit_path, only: [:edit, :update]
def new
commit unless @repository.empty?
end
def create
- file_path = File.join(@path, File.basename(params[:file_name]))
- result = Files::CreateService.new(
- @project,
- current_user,
- params.merge(new_branch: sanitized_new_branch_name),
- @ref,
- file_path
- ).execute
+ result = Files::CreateService.new(@project, current_user, @commit_params).execute
if result[:status] == :success
flash[:notice] = "Your changes have been successfully committed"
- ref = sanitized_new_branch_name.presence || @ref
- redirect_to namespace_project_blob_path(@project.namespace, @project, File.join(ref, file_path))
+ redirect_to namespace_project_blob_path(@project.namespace, @project, File.join(@target_branch, @file_path))
else
flash[:alert] = result[:message]
render :new
@@ -48,22 +41,10 @@ class Projects::BlobController < Projects::ApplicationController
end
def update
- result = Files::UpdateService.
- new(
- @project,
- current_user,
- params.merge(new_branch: sanitized_new_branch_name),
- @ref,
- @path
- ).execute
+ result = Files::UpdateService.new(@project, current_user, @commit_params).execute
if result[:status] == :success
flash[:notice] = "Your changes have been successfully committed"
-
- if from_merge_request
- from_merge_request.reload_code
- end
-
redirect_to after_edit_path
else
flash[:alert] = result[:message]
@@ -80,12 +61,11 @@ class Projects::BlobController < Projects::ApplicationController
end
def destroy
- result = Files::DeleteService.new(@project, current_user, params, @ref, @path).execute
+ result = Files::DeleteService.new(@project, current_user, @commit_params).execute
if result[:status] == :success
flash[:notice] = "Your changes have been successfully committed"
- redirect_to namespace_project_tree_path(@project.namespace, @project,
- @ref)
+ redirect_to namespace_project_tree_path(@project.namespace, @project, @target_branch)
else
flash[:alert] = result[:message]
render :show
@@ -135,7 +115,6 @@ class Projects::BlobController < Projects::ApplicationController
@id = params[:id]
@ref, @path = extract_ref(@id)
-
rescue InvalidPathError
not_found!
end
@@ -145,8 +124,8 @@ class Projects::BlobController < Projects::ApplicationController
if from_merge_request
diffs_namespace_project_merge_request_path(from_merge_request.target_project.namespace, from_merge_request.target_project, from_merge_request) +
"#file-path-#{hexdigest(@path)}"
- elsif sanitized_new_branch_name.present?
- namespace_project_blob_path(@project.namespace, @project, File.join(sanitized_new_branch_name, @path))
+ elsif @target_branch.present?
+ namespace_project_blob_path(@project.namespace, @project, File.join(@target_branch, @path))
else
namespace_project_blob_path(@project.namespace, @project, @id)
end
@@ -160,4 +139,25 @@ class Projects::BlobController < Projects::ApplicationController
def sanitized_new_branch_name
@new_branch ||= sanitize(strip_tags(params[:new_branch]))
end
+
+ def editor_variables
+ @current_branch = @ref
+ @target_branch = (sanitized_new_branch_name || @ref)
+
+ @file_path =
+ if action_name.to_s == 'create'
+ File.join(@path, File.basename(params[:file_name]))
+ else
+ @path
+ end
+
+ @commit_params = {
+ file_path: @file_path,
+ current_branch: @current_branch,
+ target_branch: @target_branch,
+ commit_message: params[:commit_message],
+ file_content: params[:content],
+ file_content_encoding: params[:encoding]
+ }
+ end
end
diff --git a/app/controllers/projects/deploy_keys_controller.rb b/app/controllers/projects/deploy_keys_controller.rb
index 8c1bbf76917..40e2b37912b 100644
--- a/app/controllers/projects/deploy_keys_controller.rb
+++ b/app/controllers/projects/deploy_keys_controller.rb
@@ -18,10 +18,6 @@ class Projects::DeployKeysController < Projects::ApplicationController
@available_public_keys -= @available_project_keys
end
- def show
- @key = @project.deploy_keys.find(params[:id])
- end
-
def new
@key = @project.deploy_keys.new
diff --git a/app/controllers/projects/merge_requests_controller.rb b/app/controllers/projects/merge_requests_controller.rb
index c7467e9b2f5..14069bafe71 100644
--- a/app/controllers/projects/merge_requests_controller.rb
+++ b/app/controllers/projects/merge_requests_controller.rb
@@ -2,10 +2,13 @@ require 'gitlab/satellite/satellite'
class Projects::MergeRequestsController < Projects::ApplicationController
before_action :module_enabled
- before_action :merge_request, only: [:edit, :update, :show, :diffs, :automerge, :automerge_check, :ci_status, :toggle_subscription]
- before_action :closes_issues, only: [:edit, :update, :show, :diffs]
- before_action :validates_merge_request, only: [:show, :diffs]
- before_action :define_show_vars, only: [:show, :diffs]
+ before_action :merge_request, only: [
+ :edit, :update, :show, :diffs, :commits, :automerge, :automerge_check,
+ :ci_status, :toggle_subscription
+ ]
+ before_action :closes_issues, only: [:edit, :update, :show, :diffs, :commits]
+ before_action :validates_merge_request, only: [:show, :diffs, :commits]
+ before_action :define_show_vars, only: [:show, :diffs, :commits]
# Allow read any merge_request
before_action :authorize_read_merge_request!
@@ -27,7 +30,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController
@merge_requests = @merge_requests.full_search(terms)
end
end
-
+
@merge_requests = @merge_requests.page(params[:page]).per(PER_PAGE)
respond_to do |format|
@@ -67,6 +70,10 @@ class Projects::MergeRequestsController < Projects::ApplicationController
end
end
+ def commits
+ render 'show'
+ end
+
def new
params[:merge_request] ||= ActionController::Parameters.new(source_project: @project)
@merge_request = MergeRequests::BuildService.new(project, current_user, merge_request_params).execute
@@ -132,11 +139,13 @@ class Projects::MergeRequestsController < Projects::ApplicationController
@merge_request.check_if_can_be_merged
end
- render json: { merge_status: @merge_request.automerge_status }
+ closes_issues
+
+ render partial: "projects/merge_requests/widget/show.html.haml", layout: false
end
def automerge
- return access_denied! unless allowed_to_merge?
+ return access_denied! unless @merge_request.can_be_merged_by?(current_user)
if @merge_request.automergeable?
AutoMergeWorker.perform_async(@merge_request.id, current_user.id, params)
@@ -245,8 +254,6 @@ class Projects::MergeRequestsController < Projects::ApplicationController
@commits = @merge_request.commits
@merge_request_diff = @merge_request.merge_request_diff
- @allowed_to_merge = allowed_to_merge?
- @show_merge_controls = @merge_request.open? && @commits.any? && @allowed_to_merge
@source_branch = @merge_request.source_project.repository.find_branch(@merge_request.source_branch).try(:name)
if @merge_request.locked_long_ago?
@@ -255,19 +262,11 @@ class Projects::MergeRequestsController < Projects::ApplicationController
end
end
- def allowed_to_merge?
- allowed_to_push_code?(project, @merge_request.target_branch)
- end
-
def invalid_mr
# Render special view for MR with removed source or target branch
render 'invalid'
end
- def allowed_to_push_code?(project, branch)
- ::Gitlab::GitAccess.new(current_user, project).can_push_to_branch?(branch)
- end
-
def merge_request_params
params.require(:merge_request).permit(
:title, :assignee_id, :source_project_id, :source_branch,
diff --git a/app/controllers/projects/project_members_controller.rb b/app/controllers/projects/project_members_controller.rb
index d7fbc979067..b82b6f45d59 100644
--- a/app/controllers/projects/project_members_controller.rb
+++ b/app/controllers/projects/project_members_controller.rb
@@ -2,8 +2,6 @@ class Projects::ProjectMembersController < Projects::ApplicationController
# Authorize
before_action :authorize_admin_project!, except: :leave
- layout "project_settings"
-
def index
@project_members = @project.project_members
@project_members = @project_members.non_invite unless can?(current_user, :admin_project, @project)
@@ -73,10 +71,14 @@ class Projects::ProjectMembersController < Projects::ApplicationController
end
def leave
+ if @project.namespace == current_user.namespace
+ return redirect_to(:back, alert: 'You can not leave your own project. Transfer or delete the project.')
+ end
+
@project.project_members.find_by(user_id: current_user).destroy
respond_to do |format|
- format.html { redirect_to :back }
+ format.html { redirect_to dashboard_path }
format.js { render nothing: true }
end
end
diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb
index dc430351551..be5968cd7b0 100644
--- a/app/controllers/projects_controller.rb
+++ b/app/controllers/projects_controller.rb
@@ -97,18 +97,15 @@ class ProjectsController < ApplicationController
return access_denied! unless can?(current_user, :remove_project, @project)
::Projects::DestroyService.new(@project, current_user, {}).execute
+ flash[:alert] = 'Project deleted.'
- respond_to do |format|
- format.html do
- flash[:alert] = 'Project deleted.'
-
- if request.referer.include?('/admin')
- redirect_to admin_namespaces_projects_path
- else
- redirect_to dashboard_path
- end
- end
+ if request.referer.include?('/admin')
+ redirect_to admin_namespaces_projects_path
+ else
+ redirect_to dashboard_path
end
+ rescue Projects::DestroyService::DestroyError => ex
+ redirect_to edit_project_path(@project), alert: ex.message
end
def autocomplete_sources
@@ -154,7 +151,17 @@ class ProjectsController < ApplicationController
end
def markdown_preview
- render text: view_context.markdown(params[:md_text])
+ text = params[:text]
+
+ ext = Gitlab::ReferenceExtractor.new(@project, current_user)
+ ext.analyze(text)
+
+ render json: {
+ body: view_context.markdown(text),
+ references: {
+ users: ext.users.map(&:username)
+ }
+ }
end
private
diff --git a/app/controllers/registrations_controller.rb b/app/controllers/registrations_controller.rb
index 830751a989f..6ccc7934f2f 100644
--- a/app/controllers/registrations_controller.rb
+++ b/app/controllers/registrations_controller.rb
@@ -6,7 +6,7 @@ class RegistrationsController < Devise::RegistrationsController
end
def destroy
- current_user.destroy
+ DeleteUserService.new.execute(current_user)
respond_to do |format|
format.html { redirect_to new_user_session_path, notice: "Account successfully removed." }
diff --git a/app/controllers/sessions_controller.rb b/app/controllers/sessions_controller.rb
index b89b4c27350..4d976fe6630 100644
--- a/app/controllers/sessions_controller.rb
+++ b/app/controllers/sessions_controller.rb
@@ -2,6 +2,7 @@ class SessionsController < Devise::SessionsController
include AuthenticatesWithTwoFactor
prepend_before_action :authenticate_with_two_factor, only: [:create]
+ before_action :auto_sign_in_with_provider, only: [:new]
def new
redirect_path =
@@ -75,6 +76,21 @@ class SessionsController < Devise::SessionsController
end
end
+ def auto_sign_in_with_provider
+ provider = Gitlab.config.omniauth.auto_sign_in_with_provider
+ return unless provider.present?
+
+ # Auto sign in with an Omniauth provider only if the standard "you need to sign-in" alert is
+ # registered or no alert at all. In case of another alert (such as a blocked user), it is safer
+ # to do nothing to prevent redirection loops with certain Omniauth providers.
+ return unless flash[:alert].blank? || flash[:alert] == I18n.t('devise.failure.unauthenticated')
+
+ # Prevent alert from popping up on the first page shown after authentication.
+ flash[:alert] = nil
+
+ redirect_to omniauth_authorize_path(:user, provider.to_sym)
+ end
+
def valid_otp_attempt?(user)
user.valid_otp?(user_params[:otp_attempt]) ||
user.invalidate_otp_backup_code!(user_params[:otp_attempt])
diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb
index 89dcdf57798..a539ec49f7a 100644
--- a/app/helpers/application_helper.rb
+++ b/app/helpers/application_helper.rb
@@ -279,10 +279,6 @@ module ApplicationHelper
html_options
end
- def escaped_autolink(text)
- auto_link ERB::Util.html_escape(text), link: :urls
- end
-
def promo_host
'about.gitlab.com'
end
diff --git a/app/helpers/application_settings_helper.rb b/app/helpers/application_settings_helper.rb
index 241d6075c9f..63c3ff5674d 100644
--- a/app/helpers/application_settings_helper.rb
+++ b/app/helpers/application_settings_helper.rb
@@ -19,6 +19,10 @@ module ApplicationSettingsHelper
current_application_settings.sign_in_text
end
+ def user_oauth_applications?
+ current_application_settings.user_oauth_applications
+ end
+
# Return a group of checkboxes that use Bootstrap's button plugin for a
# toggle button effect.
def restricted_level_checkboxes(help_block_id)
diff --git a/app/helpers/blob_helper.rb b/app/helpers/blob_helper.rb
index 9fe5f82f02f..50df3801703 100644
--- a/app/helpers/blob_helper.rb
+++ b/app/helpers/blob_helper.rb
@@ -1,6 +1,6 @@
module BlobHelper
- def highlight(blob_name, blob_content, nowrap = false)
- formatter = Rugments::Formatters::HTML.new(
+ def highlight(blob_name, blob_content, nowrap: false, continue: false)
+ @formatter ||= Rugments::Formatters::HTML.new(
nowrap: nowrap,
cssclass: 'code highlight',
lineanchors: true,
@@ -8,11 +8,11 @@ module BlobHelper
)
begin
- lexer = Rugments::Lexer.guess(filename: blob_name, source: blob_content)
- result = formatter.format(lexer.lex(blob_content)).html_safe
+ @lexer ||= Rugments::Lexer.guess(filename: blob_name, source: blob_content).new
+ result = @formatter.format(@lexer.lex(blob_content, continue: continue)).html_safe
rescue
lexer = Rugments::Lexers::PlainText
- result = formatter.format(lexer.lex(blob_content)).html_safe
+ result = @formatter.format(lexer.lex(blob_content)).html_safe
end
result
diff --git a/app/helpers/gitlab_markdown_helper.rb b/app/helpers/gitlab_markdown_helper.rb
index d89f7b4a28d..2777944fc9d 100644
--- a/app/helpers/gitlab_markdown_helper.rb
+++ b/app/helpers/gitlab_markdown_helper.rb
@@ -1,3 +1,5 @@
+require 'nokogiri'
+
module GitlabMarkdownHelper
include Gitlab::Markdown
@@ -21,36 +23,44 @@ module GitlabMarkdownHelper
gfm_body = gfm(escaped_body, {}, html_options)
- gfm_body.gsub!(%r{<a.*?>.*?</a>}m) do |match|
- "</a>#{match}#{link_to("", url, html_options)[0..-5]}" # "</a>".length +1
+ fragment = Nokogiri::XML::DocumentFragment.parse(gfm_body)
+ if fragment.children.size == 1 && fragment.children[0].name == 'a'
+ # Fragment has only one node, and it's a link generated by `gfm`.
+ # Replace it with our requested link.
+ text = fragment.children[0].text
+ fragment.children[0].replace(link_to(text, url, html_options))
+ else
+ # Traverse the fragment's first generation of children looking for pure
+ # text, wrapping anything found in the requested link
+ fragment.children.each do |node|
+ next unless node.text?
+ node.replace(link_to(node.text, url, html_options))
+ end
end
- link_to(gfm_body.html_safe, url, html_options)
+ fragment.to_html.html_safe
end
+ MARKDOWN_OPTIONS = {
+ no_intra_emphasis: true,
+ tables: true,
+ fenced_code_blocks: true,
+ strikethrough: true,
+ lax_spacing: true,
+ space_after_headers: true,
+ superscript: true,
+ footnotes: true
+ }.freeze
+
def markdown(text, options={})
unless @markdown && options == @options
@options = options
- options.merge!(
- # Handled further down the line by Gitlab::Markdown::SanitizationFilter
- escape_html: false
- )
-
# see https://github.com/vmg/redcarpet#darling-i-packed-you-a-couple-renderers-for-lunch
rend = Redcarpet::Render::GitlabHTML.new(self, user_color_scheme_class, options)
# see https://github.com/vmg/redcarpet#and-its-like-really-simple-to-use
- @markdown = Redcarpet::Markdown.new(rend,
- no_intra_emphasis: true,
- tables: true,
- fenced_code_blocks: true,
- strikethrough: true,
- lax_spacing: true,
- space_after_headers: true,
- superscript: true,
- footnotes: true
- )
+ @markdown = Redcarpet::Markdown.new(rend, MARKDOWN_OPTIONS)
end
@markdown.render(text).html_safe
diff --git a/app/helpers/projects_helper.rb b/app/helpers/projects_helper.rb
index f8df39d236a..94ce6646634 100644
--- a/app/helpers/projects_helper.rb
+++ b/app/helpers/projects_helper.rb
@@ -294,4 +294,16 @@ module ProjectsHelper
nil
end
end
+
+ def user_max_access_in_project(user, project)
+ level = project.team.max_member_access(user)
+
+ if level
+ Gitlab::Access.options_with_owner.key(level)
+ end
+ end
+
+ def leave_project_message(project)
+ "Are you sure you want to leave \"#{project.name}\" project?"
+ end
end
diff --git a/app/helpers/tab_helper.rb b/app/helpers/tab_helper.rb
index a1d263d9d3a..77727337f07 100644
--- a/app/helpers/tab_helper.rb
+++ b/app/helpers/tab_helper.rb
@@ -89,7 +89,7 @@ module TabHelper
def project_tab_class
return "active" if current_page?(controller: "/projects", action: :edit, id: @project)
- if ['services', 'hooks', 'deploy_keys', 'project_members', 'protected_branches'].include? controller.controller_name
+ if ['services', 'hooks', 'deploy_keys', 'protected_branches'].include? controller.controller_name
"active"
end
end
diff --git a/app/models/application_setting.rb b/app/models/application_setting.rb
index d5123249c53..fee52694099 100644
--- a/app/models/application_setting.rb
+++ b/app/models/application_setting.rb
@@ -15,9 +15,12 @@
# twitter_sharing_enabled :boolean default(TRUE)
# restricted_visibility_levels :text
# max_attachment_size :integer default(10), not null
+# session_expire_delay :integer default(10080), not null
# default_project_visibility :integer
# default_snippet_visibility :integer
# restricted_signup_domains :text
+# user_oauth_applications :bool default(TRUE)
+# after_sign_out_path :string(255)
#
class ApplicationSetting < ActiveRecord::Base
@@ -25,11 +28,19 @@ class ApplicationSetting < ActiveRecord::Base
serialize :restricted_signup_domains, Array
attr_accessor :restricted_signup_domains_raw
+ validates :session_expire_delay,
+ presence: true,
+ numericality: { only_integer: true, greater_than_or_equal_to: 0 }
+
validates :home_page_url,
allow_blank: true,
format: { with: /\A#{URI.regexp(%w(http https))}\z/, message: "should be a valid url" },
if: :home_page_url_column_exist
+ validates :after_sign_out_path,
+ allow_blank: true,
+ format: { with: /\A#{URI.regexp(%w(http https))}\z/, message: "should be a valid url" }
+
validates_each :restricted_visibility_levels do |record, attr, value|
unless value.nil?
value.each do |level|
@@ -55,6 +66,7 @@ class ApplicationSetting < ActiveRecord::Base
sign_in_text: Settings.extra['sign_in_text'],
restricted_visibility_levels: Settings.gitlab['restricted_visibility_levels'],
max_attachment_size: Settings.gitlab['max_attachment_size'],
+ session_expire_delay: Settings.gitlab['session_expire_delay'],
default_project_visibility: Settings.gitlab.default_projects_features['visibility_level'],
default_snippet_visibility: Settings.gitlab.default_projects_features['visibility_level'],
restricted_signup_domains: Settings.gitlab['restricted_signup_domains']
diff --git a/app/models/commit.rb b/app/models/commit.rb
index f02fe240540..9d721661629 100644
--- a/app/models/commit.rb
+++ b/app/models/commit.rb
@@ -172,10 +172,8 @@ class Commit
@raw.send(m, *args, &block)
end
- def respond_to?(method)
- return true if @raw.respond_to?(method)
-
- super
+ def respond_to_missing?(method, include_private = false)
+ @raw.respond_to?(method, include_private) || super
end
# Truncate sha to 8 characters
diff --git a/app/models/concerns/mentionable.rb b/app/models/concerns/mentionable.rb
index 6f9f54d08cc..10c39cb1ece 100644
--- a/app/models/concerns/mentionable.rb
+++ b/app/models/concerns/mentionable.rb
@@ -67,7 +67,13 @@ module Mentionable
# Create a cross-reference Note for each GFM reference to another Mentionable found in +mentionable_text+.
def create_cross_references!(p = project, a = author, without = [])
- refs = references(p) - without
+ refs = references(p)
+
+ # We're using this method instead of Array diffing because that requires
+ # both of the object's `hash` values to be the same, which may not be the
+ # case for otherwise identical Commit objects.
+ refs.reject! { |ref| without.include?(ref) }
+
refs.each do |ref|
Note.create_cross_reference_note(ref, local_reference, a)
end
diff --git a/app/models/concerns/taskable.rb b/app/models/concerns/taskable.rb
index 33b4814d7ec..660e58b876d 100644
--- a/app/models/concerns/taskable.rb
+++ b/app/models/concerns/taskable.rb
@@ -1,4 +1,5 @@
require 'task_list'
+require 'task_list/filter'
# Contains functionality for objects that can have task lists in their
# descriptions. Task list items can be added with Markdown like "* [x] Fix
diff --git a/app/models/group.rb b/app/models/group.rb
index b4e908c5602..051c672cb33 100644
--- a/app/models/group.rb
+++ b/app/models/group.rb
@@ -101,10 +101,14 @@ class Group < Namespace
end
def post_create_hook
+ Gitlab::AppLogger.info("Group \"#{name}\" was created")
+
system_hook_service.execute_hooks_for(self, :create)
end
def post_destroy_hook
+ Gitlab::AppLogger.info("Group \"#{name}\" was removed")
+
system_hook_service.execute_hooks_for(self, :destroy)
end
diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb
index 5690c375b96..487d62e65b6 100644
--- a/app/models/merge_request.rb
+++ b/app/models/merge_request.rb
@@ -197,7 +197,6 @@ class MergeRequest < ActiveRecord::Base
def update_merge_request_diff
if source_branch_changed? || target_branch_changed?
reload_code
- mark_as_unchecked
end
end
@@ -406,4 +405,16 @@ class MergeRequest < ActiveRecord::Base
locked_at.nil? || locked_at < (Time.now - 1.day)
end
+
+ def has_ci?
+ source_project.ci_service && commits.any?
+ end
+
+ def branch_missing?
+ !source_branch_exists? || !target_branch_exists?
+ end
+
+ def can_be_merged_by?(user)
+ ::Gitlab::GitAccess.new(user, project).can_push_to_branch?(target_branch)
+ end
end
diff --git a/app/models/namespace.rb b/app/models/namespace.rb
index 211dfa76b81..03d2ab165ea 100644
--- a/app/models/namespace.rb
+++ b/app/models/namespace.rb
@@ -72,7 +72,7 @@ class Namespace < ActiveRecord::Base
path.gsub!(/[^a-zA-Z0-9_\-\.]/, "")
# Users with the great usernames of "." or ".." would end up with a blank username.
- # Work around that by setting their username to "blank", followed by a counter.
+ # Work around that by setting their username to "blank", followed by a counter.
path = "blank" if path.blank?
counter = 0
@@ -99,7 +99,18 @@ class Namespace < ActiveRecord::Base
end
def rm_dir
- gitlab_shell.rm_namespace(path)
+ # Move namespace directory into trash.
+ # We will remove it later async
+ new_path = "#{path}+#{id}+deleted"
+
+ if gitlab_shell.mv_namespace(path, new_path)
+ message = "Namespace directory \"#{path}\" moved to \"#{new_path}\""
+ Gitlab::AppLogger.info message
+
+ # Remove namespace directroy async with delay so
+ # GitLab has time to remove all projects first
+ GitlabShellWorker.perform_in(5.minutes, :rm_namespace, new_path)
+ end
end
def move_dir
diff --git a/app/models/project.rb b/app/models/project.rb
index 3c9f0dad28b..b161cbe86b9 100644
--- a/app/models/project.rb
+++ b/app/models/project.rb
@@ -158,7 +158,7 @@ class Project < ActiveRecord::Base
scope :without_user, ->(user) { where('projects.id NOT IN (:ids)', ids: user.authorized_projects.map(&:id) ) }
scope :without_team, ->(team) { team.projects.present? ? where('projects.id NOT IN (:ids)', ids: team.projects.map(&:id)) : scoped }
scope :not_in_group, ->(group) { where('projects.id NOT IN (:ids)', ids: group.project_ids ) }
- scope :in_namespace, ->(namespace) { where(namespace_id: namespace.id) }
+ scope :in_namespace, ->(namespace_ids) { where(namespace_id: namespace_ids) }
scope :in_group_namespace, -> { joins(:group) }
scope :personal, ->(user) { where(namespace_id: user.namespace_id) }
scope :joined, ->(user) { where('namespace_id != ?', user.namespace_id) }
diff --git a/app/models/project_services/gitlab_ci_service.rb b/app/models/project_services/gitlab_ci_service.rb
index 949a4d7111b..a9354754686 100644
--- a/app/models/project_services/gitlab_ci_service.rb
+++ b/app/models/project_services/gitlab_ci_service.rb
@@ -40,6 +40,12 @@ class GitlabCiService < CiService
def execute(data)
return unless supported_events.include?(data[:object_kind])
+ ci_yaml_file = ci_yaml_file(data)
+
+ if ci_yaml_file
+ data.merge!(ci_yaml_file: ci_yaml_file)
+ end
+
service_hook.execute(data)
end
@@ -123,6 +129,14 @@ class GitlabCiService < CiService
private
+ def ci_yaml_file(data)
+ ref = data[:checkout_sha]
+ repo = project.repository
+ commit = repo.commit(ref)
+ blob = Gitlab::Git::Blob.find(repo, commit.id, ".gitlab-ci.yml")
+ blob && blob.data
+ end
+
def fork_registration_path
project_url.sub(/projects\/\d*/, "#{API_PREFIX}/forks")
end
diff --git a/app/models/repository.rb b/app/models/repository.rb
index 1b8c74028d9..2c6347222aa 100644
--- a/app/models/repository.rb
+++ b/app/models/repository.rb
@@ -163,10 +163,8 @@ class Repository
end
end
- def respond_to?(method)
- return true if raw_repository.respond_to?(method)
-
- super
+ def respond_to_missing?(method, include_private = false)
+ raw_repository.respond_to?(method, include_private) || super
end
def blob_at(sha, path)
@@ -370,8 +368,55 @@ class Repository
@root_ref ||= raw_repository.root_ref
end
+ def commit_file(user, path, content, message, ref)
+ path[0] = '' if path[0] == '/'
+
+ committer = user_to_comitter(user)
+ options = {}
+ options[:committer] = committer
+ options[:author] = committer
+ options[:commit] = {
+ message: message,
+ branch: ref
+ }
+
+ options[:file] = {
+ content: content,
+ path: path
+ }
+
+ Gitlab::Git::Blob.commit(raw_repository, options)
+ end
+
+ def remove_file(user, path, message, ref)
+ path[0] = '' if path[0] == '/'
+
+ committer = user_to_comitter(user)
+ options = {}
+ options[:committer] = committer
+ options[:author] = committer
+ options[:commit] = {
+ message: message,
+ branch: ref
+ }
+
+ options[:file] = {
+ path: path
+ }
+
+ Gitlab::Git::Blob.remove(raw_repository, options)
+ end
+
private
+ def user_to_comitter(user)
+ {
+ email: user.email,
+ name: user.name,
+ time: Time.now
+ }
+ end
+
def cache
@cache ||= RepositoryCache.new(path_with_namespace)
end
diff --git a/app/models/user.rb b/app/models/user.rb
index 50ca4bc5acc..8be0b622704 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -352,9 +352,11 @@ class User < ActiveRecord::Base
end
def owned_projects
- @owned_projects ||= begin
- Project.where(namespace_id: owned_groups.pluck(:id).push(namespace.id)).joins(:namespace)
- end
+ @owned_projects ||=
+ begin
+ namespace_ids = owned_groups.pluck(:id).push(namespace.id)
+ Project.in_namespace(namespace_ids).joins(:namespace)
+ end
end
# Team membership in authorized projects
@@ -483,7 +485,7 @@ class User < ActiveRecord::Base
end
def sanitize_attrs
- %w(name username skype linkedin twitter bio).each do |attr|
+ %w(name username skype linkedin twitter).each do |attr|
value = self.send(attr)
self.send("#{attr}=", Sanitize.clean(value)) if value.present?
end
@@ -655,6 +657,12 @@ class User < ActiveRecord::Base
end
end
+ def namespaces
+ namespace_ids = groups.pluck(:id)
+ namespace_ids.push(namespace.id)
+ Namespace.where(id: namespace_ids)
+ end
+
def oauth_authorized_tokens
Doorkeeper::AccessToken.where(resource_owner_id: self.id, revoked_at: nil)
end
@@ -689,4 +697,8 @@ class User < ActiveRecord::Base
true
end
+
+ def can_be_removed?
+ !solo_owned_groups.present?
+ end
end
diff --git a/app/services/delete_user_service.rb b/app/services/delete_user_service.rb
new file mode 100644
index 00000000000..9017a63af3b
--- /dev/null
+++ b/app/services/delete_user_service.rb
@@ -0,0 +1,16 @@
+class DeleteUserService
+ def execute(user)
+ if user.solo_owned_groups.present?
+ user.errors[:base] << 'You must transfer ownership or delete groups before you can remove user'
+ user
+ else
+ user.personal_projects.each do |project|
+ # Skip repository removal because we remove directory with namespace
+ # that contain all this repositories
+ ::Projects::DestroyService.new(project, current_user, skip_repo: true).execute
+ end
+
+ user.destroy
+ end
+ end
+end
diff --git a/app/services/destroy_group_service.rb b/app/services/destroy_group_service.rb
new file mode 100644
index 00000000000..d929a676293
--- /dev/null
+++ b/app/services/destroy_group_service.rb
@@ -0,0 +1,17 @@
+class DestroyGroupService
+ attr_accessor :group, :current_user
+
+ def initialize(group, user)
+ @group, @current_user = group, user
+ end
+
+ def execute
+ @group.projects.each do |project|
+ # Skip repository removal because we remove directory with namespace
+ # that contain all this repositories
+ ::Projects::DestroyService.new(project, current_user, skip_repo: true).execute
+ end
+
+ @group.destroy
+ end
+end
diff --git a/app/services/files/base_service.rb b/app/services/files/base_service.rb
index bd245100955..f587ee266da 100644
--- a/app/services/files/base_service.rb
+++ b/app/services/files/base_service.rb
@@ -1,11 +1,34 @@
module Files
class BaseService < ::BaseService
- attr_reader :ref, :path
+ class ValidationError < StandardError; end
- def initialize(project, user, params, ref, path = nil)
- @project, @current_user, @params = project, user, params.dup
- @ref = ref
- @path = path
+ def execute
+ @current_branch = params[:current_branch]
+ @target_branch = params[:target_branch]
+ @commit_message = params[:commit_message]
+ @file_path = params[:file_path]
+ @file_content = if params[:file_content_encoding] == 'base64'
+ Base64.decode64(params[:file_content])
+ else
+ params[:file_content]
+ end
+
+ # Validate parameters
+ validate
+
+ # Create new branch if it different from current_branch
+ if @target_branch != @current_branch
+ create_target_branch
+ end
+
+ if sha = commit
+ after_commit(sha, @target_branch)
+ success
+ else
+ error("Something went wrong. Your changes were not committed")
+ end
+ rescue ValidationError => ex
+ error(ex.message)
end
private
@@ -13,5 +36,52 @@ module Files
def repository
project.repository
end
+
+ def after_commit(sha, branch)
+ commit = repository.commit(sha)
+ full_ref = 'refs/heads/' + branch
+ old_sha = commit.parent_id || Gitlab::Git::BLANK_SHA
+ GitPushService.new.execute(project, current_user, old_sha, sha, full_ref)
+ end
+
+ def current_branch
+ @current_branch ||= params[:current_branch]
+ end
+
+ def target_branch
+ @target_branch ||= params[:target_branch]
+ end
+
+ def raise_error(message)
+ raise ValidationError.new(message)
+ end
+
+ def validate
+ allowed = ::Gitlab::GitAccess.new(current_user, project).can_push_to_branch?(@target_branch)
+
+ unless allowed
+ raise_error("You are not allowed to push into this branch")
+ end
+
+ unless project.empty_repo?
+ unless repository.branch_names.include?(@current_branch)
+ raise_error("You can only create files if you are on top of a branch")
+ end
+
+ if @current_branch != @target_branch
+ if repository.branch_names.include?(@target_branch)
+ raise_error("Branch with such name already exists. You need to switch to this branch in order to make changes")
+ end
+ end
+ end
+ end
+
+ def create_target_branch
+ result = CreateBranchService.new(project, current_user).execute(@target_branch, @current_branch)
+
+ unless result[:status] == :success
+ raise_error("Something went wrong when we tried to create #{@target_branch} for you")
+ end
+ end
end
end
diff --git a/app/services/files/create_service.rb b/app/services/files/create_service.rb
index 23833aa78ec..91d715b2d63 100644
--- a/app/services/files/create_service.rb
+++ b/app/services/files/create_service.rb
@@ -1,52 +1,30 @@
require_relative "base_service"
module Files
- class CreateService < BaseService
- def execute
- allowed = Gitlab::GitAccess.new(current_user, project).can_push_to_branch?(ref)
+ class CreateService < Files::BaseService
+ def commit
+ repository.commit_file(current_user, @file_path, @file_content, @commit_message, @target_branch)
+ end
- unless allowed
- return error("You are not allowed to create file in this branch")
- end
+ def validate
+ super
- file_name = File.basename(path)
- file_path = path
+ file_name = File.basename(@file_path)
unless file_name =~ Gitlab::Regex.file_name_regex
- return error(
+ raise_error(
'Your changes could not be committed, because the file name ' +
Gitlab::Regex.file_name_regex_message
)
end
- if project.empty_repo?
- # everything is ok because repo does not have a commits yet
- else
- unless repository.branch_names.include?(ref)
- return error("You can only create files if you are on top of a branch")
- end
-
- blob = repository.blob_at_branch(ref, file_path)
+ unless project.empty_repo?
+ blob = repository.blob_at_branch(@current_branch, @file_path)
if blob
- return error("Your changes could not be committed, because file with such name exists")
+ raise_error("Your changes could not be committed, because file with such name exists")
end
end
-
-
- new_file_action = Gitlab::Satellite::NewFileAction.new(current_user, project, ref, file_path)
- created_successfully = new_file_action.commit!(
- params[:content],
- params[:commit_message],
- params[:encoding],
- params[:new_branch]
- )
-
- if created_successfully
- success
- else
- error("Your changes could not be committed, because the file has been changed")
- end
end
end
end
diff --git a/app/services/files/delete_service.rb b/app/services/files/delete_service.rb
index 1497a0f883b..27c881c3430 100644
--- a/app/services/files/delete_service.rb
+++ b/app/services/files/delete_service.rb
@@ -1,36 +1,9 @@
require_relative "base_service"
module Files
- class DeleteService < BaseService
- def execute
- allowed = ::Gitlab::GitAccess.new(current_user, project).can_push_to_branch?(ref)
-
- unless allowed
- return error("You are not allowed to push into this branch")
- end
-
- unless repository.branch_names.include?(ref)
- return error("You can only create files if you are on top of a branch")
- end
-
- blob = repository.blob_at_branch(ref, path)
-
- unless blob
- return error("You can only edit text files")
- end
-
- delete_file_action = Gitlab::Satellite::DeleteFileAction.new(current_user, project, ref, path)
-
- deleted_successfully = delete_file_action.commit!(
- nil,
- params[:commit_message]
- )
-
- if deleted_successfully
- success
- else
- error("Your changes could not be committed, because the file has been changed")
- end
+ class DeleteService < Files::BaseService
+ def commit
+ repository.remove_file(current_user, @file_path, @commit_message, @target_branch)
end
end
end
diff --git a/app/services/files/update_service.rb b/app/services/files/update_service.rb
index 0724d3ae634..a20903c6f02 100644
--- a/app/services/files/update_service.rb
+++ b/app/services/files/update_service.rb
@@ -1,39 +1,9 @@
require_relative "base_service"
module Files
- class UpdateService < BaseService
- def execute
- allowed = ::Gitlab::GitAccess.new(current_user, project).can_push_to_branch?(ref)
-
- unless allowed
- return error("You are not allowed to push into this branch")
- end
-
- unless repository.branch_names.include?(ref)
- return error("You can only create files if you are on top of a branch")
- end
-
- blob = repository.blob_at_branch(ref, path)
-
- unless blob
- return error("You can only edit text files")
- end
-
- edit_file_action = Gitlab::Satellite::EditFileAction.new(current_user, project, ref, path)
- edit_file_action.commit!(
- params[:content],
- params[:commit_message],
- params[:encoding],
- params[:new_branch]
- )
-
- success
- rescue Gitlab::Satellite::CheckoutFailed => ex
- error("Your changes could not be committed because ref '#{ref}' could not be checked out", 400)
- rescue Gitlab::Satellite::CommitFailed => ex
- error("Your changes could not be committed. Maybe there was nothing to commit?", 409)
- rescue Gitlab::Satellite::PushFailed => ex
- error("Your changes could not be committed. Maybe the file was changed by another process?", 409)
+ class UpdateService < Files::BaseService
+ def commit
+ repository.commit_file(current_user, @file_path, @file_content, @commit_message, @target_branch)
end
end
end
diff --git a/app/services/git_push_service.rb b/app/services/git_push_service.rb
index bdf36af02fd..cde65349d5c 100644
--- a/app/services/git_push_service.rb
+++ b/app/services/git_push_service.rb
@@ -127,7 +127,8 @@ class GitPushService
end
def is_default_branch?(ref)
- Gitlab::Git.branch_ref?(ref) && Gitlab::Git.ref_name(ref) == project.default_branch
+ Gitlab::Git.branch_ref?(ref) &&
+ (Gitlab::Git.ref_name(ref) == project.default_branch || project.default_branch.nil?)
end
def commit_user(commit)
diff --git a/app/services/issuable_base_service.rb b/app/services/issuable_base_service.rb
index c5769a5ad27..1d99223cfe6 100644
--- a/app/services/issuable_base_service.rb
+++ b/app/services/issuable_base_service.rb
@@ -20,4 +20,10 @@ class IssuableBaseService < BaseService
SystemNoteService.change_title(
issuable, issuable.project, current_user, old_title)
end
+
+ def create_branch_change_note(issuable, branch_type, old_branch, new_branch)
+ SystemNoteService.change_branch(
+ issuable, issuable.project, current_user, branch_type,
+ old_branch, new_branch)
+ end
end
diff --git a/app/services/merge_requests/update_service.rb b/app/services/merge_requests/update_service.rb
index 34fd59d6927..4f6c6cba9a9 100644
--- a/app/services/merge_requests/update_service.rb
+++ b/app/services/merge_requests/update_service.rb
@@ -5,10 +5,11 @@ require_relative 'close_service'
module MergeRequests
class UpdateService < MergeRequests::BaseService
def execute(merge_request)
- # We dont allow change of source/target projects
+ # We don't allow change of source/target projects and source branch
# after merge request was created
params.except!(:source_project_id)
params.except!(:target_project_id)
+ params.except!(:source_branch)
state = params[:state_event]
@@ -41,6 +42,12 @@ module MergeRequests
)
end
+ if merge_request.previous_changes.include?('target_branch')
+ create_branch_change_note(merge_request, 'target',
+ merge_request.previous_changes['target_branch'].first,
+ merge_request.target_branch)
+ end
+
if merge_request.previous_changes.include?('milestone_id')
create_milestone_note(merge_request)
end
@@ -54,6 +61,11 @@ module MergeRequests
create_title_change_note(merge_request, merge_request.previous_changes['title'].first)
end
+ if merge_request.previous_changes.include?('target_branch') ||
+ merge_request.previous_changes.include?('source_branch')
+ merge_request.mark_as_unchecked
+ end
+
merge_request.notice_added_references(merge_request.project, current_user)
execute_hooks(merge_request, 'update')
end
diff --git a/app/services/projects/destroy_service.rb b/app/services/projects/destroy_service.rb
index 7e1d753b021..403f419ec50 100644
--- a/app/services/projects/destroy_service.rb
+++ b/app/services/projects/destroy_service.rb
@@ -1,28 +1,69 @@
module Projects
class DestroyService < BaseService
+ include Gitlab::ShellAdapter
+
+ class DestroyError < StandardError; end
+
+ DELETED_FLAG = '+deleted'
+
def execute
return false unless can?(current_user, :remove_project, project)
project.team.truncate
project.repository.expire_cache unless project.empty_repo?
- if project.destroy
- GitlabShellWorker.perform_async(
- :remove_repository,
- project.path_with_namespace
- )
+ repo_path = project.path_with_namespace
+ wiki_path = repo_path + '.wiki'
- GitlabShellWorker.perform_async(
- :remove_repository,
- project.path_with_namespace + ".wiki"
- )
+ Project.transaction do
+ project.destroy!
- project.satellite.destroy
+ unless remove_repository(repo_path)
+ raise_error('Failed to remove project repository. Please try again or contact administrator')
+ end
- log_info("Project \"#{project.name}\" was removed")
- system_hook_service.execute_hooks_for(project, :destroy)
- true
+ unless remove_repository(wiki_path)
+ raise_error('Failed to remove wiki repository. Please try again or contact administrator')
+ end
end
+
+ project.satellite.destroy
+ log_info("Project \"#{project.name}\" was removed")
+ system_hook_service.execute_hooks_for(project, :destroy)
+ true
+ end
+
+ private
+
+ def remove_repository(path)
+ # Skip repository removal. We use this flag when remove user or group
+ return true if params[:skip_repo] == true
+
+ # There is a possibility project does not have repository or wiki
+ return true unless gitlab_shell.exists?(path + '.git')
+
+ new_path = removal_path(path)
+
+ if gitlab_shell.mv_repository(path, new_path)
+ log_info("Repository \"#{path}\" moved to \"#{new_path}\"")
+ GitlabShellWorker.perform_in(5.minutes, :remove_repository, new_path)
+ else
+ false
+ end
+ end
+
+ def raise_error(message)
+ raise DestroyError.new(message)
+ end
+
+ # Build a path for removing repositories
+ # We use `+` because its not allowed by GitLab so user can not create
+ # project with name cookies+119+deleted and capture someone stalled repository
+ #
+ # gitlab/cookies.git -> gitlab/cookies+119+deleted.git
+ #
+ def removal_path(path)
+ "#{path}+#{project.id}#{DELETED_FLAG}"
end
end
end
diff --git a/app/services/projects/participants_service.rb b/app/services/projects/participants_service.rb
index b91590a1a90..0004a399f47 100644
--- a/app/services/projects/participants_service.rb
+++ b/app/services/projects/participants_service.rb
@@ -38,13 +38,13 @@ module Projects
def groups
current_user.authorized_groups.sort_by(&:path).map do |group|
count = group.users.count
- { username: group.path, name: "#{group.name} (#{count})" }
+ { username: group.path, name: group.name, count: count }
end
end
def all_members
count = project.team.members.flatten.count
- [{ username: "all", name: "All Project and Group Members (#{count})" }]
+ [{ username: "all", name: "All Project and Group Members", count: count }]
end
end
end
diff --git a/app/services/search/global_service.rb b/app/services/search/global_service.rb
index 0bcc50c81a7..e904cb6c6fc 100644
--- a/app/services/search/global_service.rb
+++ b/app/services/search/global_service.rb
@@ -9,7 +9,7 @@ module Search
def execute
group = Group.find_by(id: params[:group_id]) if params[:group_id].present?
projects = ProjectsFinder.new.execute(current_user)
- projects = projects.where(namespace_id: group.id) if group
+ projects = projects.in_namespace(group.id) if group
project_ids = projects.pluck(:id)
Gitlab::SearchResults.new(project_ids, params[:search])
diff --git a/app/services/system_note_service.rb b/app/services/system_note_service.rb
index 1527ae0486d..b6801a92330 100644
--- a/app/services/system_note_service.rb
+++ b/app/services/system_note_service.rb
@@ -149,6 +149,25 @@ class SystemNoteService
create_note(noteable: noteable, project: project, author: author, note: body)
end
+ # Called when a branch in Noteable is changed
+ #
+ # noteable - Noteable object
+ # project - Project owning noteable
+ # author - User performing the change
+ # branch_type - 'source' or 'target'
+ # old_branch - old branch name
+ # new_branch - new branch nmae
+ #
+ # Example Note text:
+ #
+ # "Target branch changed from `Old` to `New`"
+ #
+ # Returns the created Note object
+ def self.change_branch(noteable, project, author, branch_type, old_branch, new_branch)
+ body = "#{branch_type} branch changed from `#{old_branch}` to `#{new_branch}`".capitalize
+ create_note(noteable: noteable, project: project, author: author, note: body)
+ end
+
# Called when a Mentionable references a Noteable
#
# noteable - Noteable object being referenced
diff --git a/app/views/admin/application_settings/_form.html.haml b/app/views/admin/application_settings/_form.html.haml
index 4ceae814805..d5a49fc41f4 100644
--- a/app/views/admin/application_settings/_form.html.haml
+++ b/app/views/admin/application_settings/_form.html.haml
@@ -30,7 +30,7 @@
.checkbox
= f.label :twitter_sharing_enabled do
= f.check_box :twitter_sharing_enabled, :'aria-describedby' => 'twitter_help_block'
- %strong Twitter enabled
+ Twitter enabled
%span.help-block#twitter_help_block Show users a button to share their newly created public or internal projects on twitter
.form-group
.col-sm-offset-2.col-sm-10
@@ -70,6 +70,11 @@
= f.text_field :home_page_url, class: 'form-control', placeholder: 'http://company.example.com', :'aria-describedby' => 'home_help_block'
%span.help-block#home_help_block We will redirect non-logged in users to this page
.form-group
+ = f.label :after_sign_out_path, class: 'control-label col-sm-2'
+ .col-sm-10
+ = f.text_field :after_sign_out_path, class: 'form-control', placeholder: 'http://company.example.com', :'aria-describedby' => 'after_sign_out_path_help_block'
+ %span.help-block#after_sign_out_path_help_block We will redirect users to this page after they sign out
+ .form-group
= f.label :sign_in_text, class: 'control-label col-sm-2'
.col-sm-10
= f.text_area :sign_in_text, class: 'form-control', rows: 4
@@ -79,10 +84,22 @@
.col-sm-10
= f.number_field :max_attachment_size, class: 'form-control'
.form-group
+ = f.label :session_expire_delay, 'Session duration (minutes)', class: 'control-label col-sm-2'
+ .col-sm-10
+ = f.number_field :session_expire_delay, class: 'form-control'
+ %span.help-block#session_expire_delay_help_block GitLab restart is required to apply changes
+ .form-group
= f.label :restricted_signup_domains, 'Restricted domains for sign-ups', class: 'control-label col-sm-2'
.col-sm-10
= f.text_area :restricted_signup_domains_raw, placeholder: 'domain.com', class: 'form-control'
.help-block Only users with e-mail addresses that match these domain(s) will be able to sign-up. Wildcards allowed. Use separate lines for multiple entries. Ex: domain.com, *.domain.com
+ .form_group
+ = f.label :user_oauth_applications, 'User OAuth applications', class: 'control-label col-sm-2'
+ .col-sm-10
+ .checkbox
+ = f.label :user_oauth_applications do
+ = f.check_box :user_oauth_applications
+ Allow users to register any application to use GitLab as an OAuth provider
.form-actions
= f.submit 'Save', class: 'btn btn-primary'
diff --git a/app/views/admin/broadcast_messages/index.html.haml b/app/views/admin/broadcast_messages/index.html.haml
index 267c9a52921..17dffebd360 100644
--- a/app/views/admin/broadcast_messages/index.html.haml
+++ b/app/views/admin/broadcast_messages/index.html.haml
@@ -22,11 +22,11 @@
.form-group.js-toggle-colors-container.hide
= f.label :color, "Background Color", class: 'control-label'
.col-sm-10
- = f.color_field :color, value: "#AA33EE", class: "form-control"
+ = f.color_field :color, value: "#eb9532", class: "form-control"
.form-group.js-toggle-colors-container.hide
= f.label :font, "Font Color", class: 'control-label'
.col-sm-10
- = f.color_field :font, value: "#224466", class: "form-control"
+ = f.color_field :font, value: "#FFFFFF", class: "form-control"
.form-group
= f.label :starts_at, class: 'control-label'
.col-sm-10.datetime-controls
diff --git a/app/views/admin/deploy_keys/index.html.haml b/app/views/admin/deploy_keys/index.html.haml
index 367d25cd6a1..6405a69fad3 100644
--- a/app/views/admin/deploy_keys/index.html.haml
+++ b/app/views/admin/deploy_keys/index.html.haml
@@ -19,8 +19,7 @@
= link_to admin_deploy_key_path(deploy_key) do
%strong= deploy_key.title
%td
- %span
- (#{deploy_key.fingerprint})
+ %code.key-fingerprint= deploy_key.fingerprint
%td
%span.cgray
added #{time_ago_with_tooltip(deploy_key.created_at)}
diff --git a/app/views/admin/deploy_keys/show.html.haml b/app/views/admin/deploy_keys/show.html.haml
deleted file mode 100644
index ea361ca4bdb..00000000000
--- a/app/views/admin/deploy_keys/show.html.haml
+++ /dev/null
@@ -1,35 +0,0 @@
-- page_title @deploy_key.title, "Deploy Keys"
-.row
- .col-md-4
- .panel.panel-default
- .panel-heading
- Deploy Key
- %ul.well-list
- %li
- %span.light Title:
- %strong= @deploy_key.title
- %li
- %span.light Created on:
- %strong= @deploy_key.created_at.stamp("Aug 21, 2011")
-
- .panel.panel-default
- .panel-heading Projects (#{@deploy_key.deploy_keys_projects.count})
- - if @deploy_key.deploy_keys_projects.any?
- %ul.well-list
- - @deploy_key.projects.each do |project|
- %li
- %span
- %strong
- = link_to project.name_with_namespace, [:admin, project.namespace.becomes(Namespace), project]
- .pull-right
- = link_to disable_namespace_project_deploy_key_path(project.namespace, project, @deploy_key), data: { confirm: "Are you sure?" }, method: :put, class: "btn-xs btn btn-remove", title: 'Remove deploy key from project' do
- %i.fa.fa-times.fa-inverse
-
- .col-md-8
- %p
- %span.light Fingerprint:
- %strong= @deploy_key.fingerprint
- %pre.well-pre
- = @deploy_key.key
- .pull-right
- = link_to 'Remove', admin_deploy_key_path(@deploy_key), data: {confirm: 'Are you sure?'}, method: :delete, class: "btn btn-remove delete-key"
diff --git a/app/views/admin/groups/index.html.haml b/app/views/admin/groups/index.html.haml
index e00b23ad99f..5ce7cdf2f8d 100644
--- a/app/views/admin/groups/index.html.haml
+++ b/app/views/admin/groups/index.html.haml
@@ -11,7 +11,7 @@
= form_tag admin_groups_path, method: :get, class: 'form-inline' do
= hidden_field_tag :sort, @sort
.form-group
- = text_field_tag :name, params[:name], class: "form-control input-mn-300"
+ = text_field_tag :name, params[:name], class: "form-control"
= button_tag "Search", class: "btn submit btn-primary"
.pull-right
diff --git a/app/views/admin/users/index.html.haml b/app/views/admin/users/index.html.haml
index fe648470233..45dee86b017 100644
--- a/app/views/admin/users/index.html.haml
+++ b/app/views/admin/users/index.html.haml
@@ -79,11 +79,12 @@
%i.fa.fa-envelope
= mail_to user.email, user.email, class: 'light'
&nbsp;
- = link_to 'Edit', edit_admin_user_path(user), id: "edit_#{dom_id(user)}", class: "btn btn-sm"
+ = link_to 'Edit', edit_admin_user_path(user), id: "edit_#{dom_id(user)}", class: "btn btn-xs"
- unless user == current_user
- if user.blocked?
- = link_to 'Unblock', unblock_admin_user_path(user), method: :put, class: "btn btn-sm success"
+ = link_to 'Unblock', unblock_admin_user_path(user), method: :put, class: "btn btn-xs btn-success"
- else
- = link_to 'Block', block_admin_user_path(user), data: {confirm: 'USER WILL BE BLOCKED! Are you sure?'}, method: :put, class: "btn btn-sm btn-remove"
- = link_to 'Destroy', [:admin, user], data: { confirm: "USER #{user.name} WILL BE REMOVED! All tickets linked to this user will also be removed! Maybe block the user instead? Are you sure?" }, method: :delete, class: "btn btn-sm btn-remove"
+ = link_to 'Block', block_admin_user_path(user), data: {confirm: 'USER WILL BE BLOCKED! Are you sure?'}, method: :put, class: "btn btn-xs btn-warning"
+ - if user.can_be_removed?
+ = link_to 'Destroy', [:admin, user], data: { confirm: "USER #{user.name} WILL BE REMOVED! All tickets linked to this user will also be removed! Maybe block the user instead? Are you sure?" }, method: :delete, class: "btn btn-xs btn-remove"
= paginate @users, theme: "gitlab"
diff --git a/app/views/admin/users/show.html.haml b/app/views/admin/users/show.html.haml
index 7fc85206109..f7195ac3326 100644
--- a/app/views/admin/users/show.html.haml
+++ b/app/views/admin/users/show.html.haml
@@ -140,18 +140,22 @@
.panel-heading
Remove user
.panel-body
- %p Deleting a user has the following effects:
- %ul
- %li All user content like authored issues, snippets, comments will be removed
- - rp = @user.personal_projects.count
- - unless rp.zero?
- %li #{pluralize rp, 'personal project'} will be removed and cannot be restored
+ - if @user.can_be_removed?
+ %p Deleting a user has the following effects:
+ %ul
+ %li All user content like authored issues, snippets, comments will be removed
+ - rp = @user.personal_projects.count
+ - unless rp.zero?
+ %li #{pluralize rp, 'personal project'} will be removed and cannot be restored
+ %br
+ = link_to 'Remove user', [:admin, @user], data: { confirm: "USER #{@user.name} WILL BE REMOVED! Are you sure?" }, method: :delete, class: "btn btn-remove"
+ - else
- if @user.solo_owned_groups.present?
- %li
- Next groups with all content will be removed:
+ %p
+ This user is currently an owner in these groups:
%strong #{@user.solo_owned_groups.map(&:name).join(', ')}
- %br
- = link_to 'Remove user', [:admin, @user], data: { confirm: "USER #{@user.name} WILL BE REMOVED! Are you sure?" }, method: :delete, class: "btn btn-remove"
+ %p
+ You must transfer ownership or delete these groups before you can delete this user.
#profile.tab-pane
.row
diff --git a/app/views/dashboard/_activities.html.haml b/app/views/dashboard/_activities.html.haml
index ba49013d834..213b5d65b3c 100644
--- a/app/views/dashboard/_activities.html.haml
+++ b/app/views/dashboard/_activities.html.haml
@@ -6,7 +6,6 @@
%li.pull-right
= link_to dashboard_path(:atom, { private_token: current_user.private_token }), class: 'rss-btn' do
%i.fa.fa-rss
- Activity Feed
= render 'shared/event_filter'
%hr
diff --git a/app/views/dashboard/groups/index.html.haml b/app/views/dashboard/groups/index.html.haml
index 5ecd53cff84..0a354373b9b 100644
--- a/app/views/dashboard/groups/index.html.haml
+++ b/app/views/dashboard/groups/index.html.haml
@@ -2,7 +2,7 @@
%h3.page-title
Group Membership
- if current_user.can_create_group?
- %span.pull-right
+ %span.pull-right.hidden-xs
= link_to new_group_path, class: "btn btn-new" do
%i.fa.fa-plus
New Group
@@ -17,18 +17,17 @@
- @group_members.each do |group_member|
- group = group_member.group
%li
- .pull-right
+ .pull-right.hidden-xs
- if can?(current_user, :admin_group, group)
= link_to edit_group_path(group), class: "btn-sm btn btn-grouped" do
%i.fa.fa-cogs
Settings
- - if can?(current_user, :destroy_group_member, group_member)
- = link_to leave_group_group_members_path(group), data: { confirm: leave_group_message(group.name) }, method: :delete, class: "btn-sm btn btn-grouped", title: 'Remove user from group' do
- %i.fa.fa-sign-out
- Leave
+ = link_to leave_group_group_members_path(group), data: { confirm: leave_group_message(group.name) }, method: :delete, class: "btn-sm btn btn-grouped", title: 'Leave this group' do
+ %i.fa.fa-sign-out
+ Leave
- = image_tag group_icon(group), class: "avatar s40 avatar-tile"
+ = image_tag group_icon(group), class: "avatar s40 avatar-tile hidden-xs"
= link_to group, class: 'group-name' do
%strong= group.name
diff --git a/app/views/devise/sessions/_new_ldap.html.haml b/app/views/devise/sessions/_new_ldap.html.haml
index 812e22373a7..6ec741e4882 100644
--- a/app/views/devise/sessions/_new_ldap.html.haml
+++ b/app/views/devise/sessions/_new_ldap.html.haml
@@ -1,4 +1,9 @@
= form_tag(user_omniauth_callback_path(server['provider_name']), id: 'new_ldap_user' ) do
= text_field_tag :username, nil, {class: "form-control top", placeholder: "#{server['label']} Login", autofocus: "autofocus"}
= password_field_tag :password, nil, {class: "form-control bottom", placeholder: "Password"}
+ - if devise_mapping.rememberable?
+ .remember-me.checkbox
+ %label{for: "remember_me"}
+ = check_box_tag :remember_me, '1', false, id: 'remember_me'
+ %span Remember me
= button_tag "#{server['label']} Sign in", class: "btn-save btn"
diff --git a/app/views/events/event/_push.html.haml b/app/views/events/event/_push.html.haml
index 1da702be384..34a7c00dc43 100644
--- a/app/views/events/event/_push.html.haml
+++ b/app/views/events/event/_push.html.haml
@@ -4,8 +4,8 @@
- if event.rm_ref?
%strong= event.ref_name
- else
- = link_to namespace_project_commits_path(event.project.namespace, event.project, event.ref_name) do
- %strong= event.ref_name
+ %strong
+ = link_to event.ref_name, namespace_project_commits_path(event.project.namespace, event.project, event.ref_name)
at
= link_to_project event.project
diff --git a/app/views/explore/groups/index.html.haml b/app/views/explore/groups/index.html.haml
index c05d45e0100..f3f0b778539 100644
--- a/app/views/explore/groups/index.html.haml
+++ b/app/views/explore/groups/index.html.haml
@@ -4,7 +4,7 @@
= form_tag explore_groups_path, method: :get, class: 'form-inline form-tiny' do |f|
= hidden_field_tag :sort, @sort
.form-group
- = search_field_tag :search, params[:search], placeholder: "Filter by name", class: "form-control search-text-input input-mn-300", id: "groups_search"
+ = search_field_tag :search, params[:search], placeholder: "Filter by name", class: "form-control search-text-input", id: "groups_search"
.form-group
= button_tag 'Search', class: "btn btn-primary wide"
diff --git a/app/views/explore/projects/_filter.html.haml b/app/views/explore/projects/_filter.html.haml
index b3963a9d901..82622a58ed2 100644
--- a/app/views/explore/projects/_filter.html.haml
+++ b/app/views/explore/projects/_filter.html.haml
@@ -1,7 +1,7 @@
.pull-left
= form_tag explore_projects_filter_path, method: :get, class: 'form-inline form-tiny' do |f|
.form-group
- = search_field_tag :search, params[:search], placeholder: "Filter by name", class: "form-control search-text-input input-mn-300", id: "projects_search"
+ = search_field_tag :search, params[:search], placeholder: "Filter by name", class: "form-control search-text-input", id: "projects_search"
.form-group
= button_tag 'Search', class: "btn btn-primary wide"
diff --git a/app/views/groups/group_members/_group_member.html.haml b/app/views/groups/group_members/_group_member.html.haml
index 56b1948a474..ec39a755f0f 100644
--- a/app/views/groups/group_members/_group_member.html.haml
+++ b/app/views/groups/group_members/_group_member.html.haml
@@ -40,7 +40,8 @@
&nbsp;
- if current_user == user
= link_to leave_group_group_members_path(@group), data: { confirm: leave_group_message(@group.name)}, method: :delete, class: "btn-xs btn btn-remove", title: 'Remove user from group' do
- %i.fa.fa-minus.fa-inverse
+ = icon("sign-out")
+ Leave
- else
= link_to group_group_member_path(@group, member), data: { confirm: remove_user_from_group_message(@group, member) }, method: :delete, remote: true, class: "btn-xs btn btn-remove", title: 'Remove user from group' do
%i.fa.fa-minus.fa-inverse
diff --git a/app/views/groups/group_members/index.html.haml b/app/views/groups/group_members/index.html.haml
index 903ca877218..a70d1ff0697 100644
--- a/app/views/groups/group_members/index.html.haml
+++ b/app/views/groups/group_members/index.html.haml
@@ -14,7 +14,7 @@
.clearfix.js-toggle-container
= form_tag group_group_members_path(@group), method: :get, class: 'form-inline member-search-form' do
.form-group
- = search_field_tag :search, params[:search], { placeholder: 'Find existing member by name', class: 'form-control search-text-input input-mn-300' }
+ = search_field_tag :search, params[:search], { placeholder: 'Find existing member by name', class: 'form-control search-text-input' }
= button_tag 'Search', class: 'btn'
- if current_user && current_user.can?(:admin_group, @group)
diff --git a/app/views/groups/show.html.haml b/app/views/groups/show.html.haml
index 1678311141e..d31dae7d648 100644
--- a/app/views/groups/show.html.haml
+++ b/app/views/groups/show.html.haml
@@ -11,7 +11,7 @@
@#{@group.path}
- if @group.description.present?
.description
- = escaped_autolink(@group.description)
+ = markdown(@group.description, pipeline: :description)
%hr
= render 'shared/show_aside'
@@ -27,7 +27,6 @@
%li
= link_to group_path(@group, { format: :atom, private_token: current_user.private_token }), title: "Feed", class: 'rss-btn' do
%i.fa.fa-rss
- Activity Feed
= render 'shared/event_filter'
%hr
diff --git a/app/views/layouts/_head.html.haml b/app/views/layouts/_head.html.haml
index b1a57d9824e..dbc68c39bf1 100644
--- a/app/views/layouts/_head.html.haml
+++ b/app/views/layouts/_head.html.haml
@@ -15,7 +15,7 @@
%meta{name: 'viewport', content: 'width=device-width, initial-scale=1, maximum-scale=1'}
%meta{name: 'theme-color', content: '#474D57'}
- = yield(:meta_tags)
+ = yield :meta_tags
= render 'layouts/google_analytics' if extra_config.has_key?('google_analytics_id')
= render 'layouts/piwik' if extra_config.has_key?('piwik_url') && extra_config.has_key?('piwik_site_id')
diff --git a/app/views/layouts/_head_panel.html.haml b/app/views/layouts/_head_panel.html.haml
deleted file mode 100644
index 979755db652..00000000000
--- a/app/views/layouts/_head_panel.html.haml
+++ /dev/null
@@ -1,42 +0,0 @@
-%header.navbar.navbar-fixed-top.navbar-gitlab{ class: nav_header_class }
- .container
- %div.app_logo
- = link_to root_path, class: 'home', title: 'Dashboard', id: 'js-shortcuts-home', data: {toggle: 'tooltip', placement: 'bottom'} do
- = brand_header_logo
- %h3 GitLab
- %h1.title
- = title
-
- %button.navbar-toggle{type: 'button', data: {target: '.navbar-collapse', toggle: 'collapse'}}
- %span.sr-only Toggle navigation
- = icon('bars')
-
- .navbar-collapse.collapse
- %ul.nav.navbar-nav
- %li.hidden-sm.hidden-xs
- = render 'layouts/search'
- %li.visible-sm.visible-xs
- = link_to search_path, title: 'Search', data: {toggle: 'tooltip', placement: 'bottom'} do
- = icon('search')
- %li
- = link_to help_path, title: 'Help', data: {toggle: 'tooltip', placement: 'bottom'} do
- = icon('question-circle')
- %li
- = link_to explore_root_path, title: 'Explore', data: {toggle: 'tooltip', placement: 'bottom'} do
- = icon('globe')
- %li
- = link_to user_snippets_path(current_user), title: 'Your snippets', data: {toggle: 'tooltip', placement: 'bottom'} do
- = icon('clipboard')
- - if current_user.is_admin?
- %li
- = link_to admin_root_path, title: 'Admin area', data: {toggle: 'tooltip', placement: 'bottom'} do
- = icon('wrench')
- - if current_user.can_create_project?
- %li
- = link_to new_project_path, title: 'New project', data: {toggle: 'tooltip', placement: 'bottom'} do
- = icon('plus')
- %li
- = link_to profile_path, title: 'Profile settings', data: {toggle: 'tooltip', placement: 'bottom'} do
- = icon('cog')
-
-= render 'shared/outdated_browser'
diff --git a/app/views/layouts/_page.html.haml b/app/views/layouts/_page.html.haml
index c1283734d25..f17f6fdd91c 100644
--- a/app/views/layouts/_page.html.haml
+++ b/app/views/layouts/_page.html.haml
@@ -8,19 +8,13 @@
.collapse-nav
= render partial: 'layouts/collapse_button'
- if current_user
- .sidebar-user
- = link_to current_user, class: 'profile-pic', id: 'profile-pic', data: {toggle: 'tooltip', placement: 'top'} do
- = image_tag avatar_icon(current_user.email, 60), alt: 'User activity', class: 'avatar avatar s32'
+ = link_to current_user, class: 'sidebar-user' do
+ = image_tag avatar_icon(current_user.email, 60), alt: 'User activity', class: 'avatar avatar s32'
.username
= current_user.username
- .logout-holder
- = link_to destroy_user_session_path, class: 'logout', method: :delete, title: 'Sign out', data: {toggle: 'tooltip', placement: 'top'} do
- = icon('sign-out')
.content-wrapper
.container-fluid
.content
= render "layouts/flash"
.clearfix
= yield
-
-= yield :embedded_scripts
diff --git a/app/views/layouts/_public_head_panel.html.haml b/app/views/layouts/_public_head_panel.html.haml
deleted file mode 100644
index 8a297566d6c..00000000000
--- a/app/views/layouts/_public_head_panel.html.haml
+++ /dev/null
@@ -1,22 +0,0 @@
-%header.navbar.navbar-fixed-top.navbar-gitlab{ class: nav_header_class }
- .container
- %div.app_logo
- = link_to explore_root_path, class: "home" do
- = brand_header_logo
- %h3 GitLab
- %h1.title= title
-
- %button.navbar-toggle{"data-target" => ".navbar-collapse", "data-toggle" => "collapse", type: "button"}
- %span.sr-only Toggle navigation
- %i.fa.fa-bars
-
- - unless current_controller?('sessions')
- .pull-right.hidden-xs
- = link_to "Sign in", new_session_path(:user, redirect_to_referer: 'yes'), class: 'btn btn-sign-in btn-new append-right-10'
-
- .navbar-collapse.collapse
- %ul.nav.navbar-nav
- %li.visible-xs
- = link_to "Sign in", new_session_path(:user, redirect_to_referer: 'yes')
-
-= render 'shared/outdated_browser'
diff --git a/app/views/layouts/application.html.haml b/app/views/layouts/application.html.haml
index a97feeb1ecd..173033f7eab 100644
--- a/app/views/layouts/application.html.haml
+++ b/app/views/layouts/application.html.haml
@@ -2,9 +2,14 @@
%html{ lang: "en"}
= render "layouts/head"
%body{class: "#{app_theme}", :'data-page' => body_data_page}
+ / Ideally this would be inside the head, but turbolinks only evaluates page-specific JS in the body.
+ = yield :scripts_body_top
+
- if current_user
- = render "layouts/head_panel", title: header_title
+ = render "layouts/header/default", title: header_title
- else
- = render "layouts/public_head_panel", title: header_title
+ = render "layouts/header/public", title: header_title
= render 'layouts/page', sidebar: sidebar
+
+ = yield :scripts_body
diff --git a/app/views/layouts/devise.html.haml b/app/views/layouts/devise.html.haml
index 5a59c9fd59a..d406f5764a7 100644
--- a/app/views/layouts/devise.html.haml
+++ b/app/views/layouts/devise.html.haml
@@ -2,7 +2,7 @@
%html{ lang: "en"}
= render "layouts/head"
%body.ui_mars.login-page.application
- = render "layouts/empty_head_panel"
+ = render "layouts/header/empty"
= render "layouts/broadcast"
.container.navless-container
.content
diff --git a/app/views/layouts/errors.html.haml b/app/views/layouts/errors.html.haml
index aa0f3f0a819..2e3a2b16eb7 100644
--- a/app/views/layouts/errors.html.haml
+++ b/app/views/layouts/errors.html.haml
@@ -2,7 +2,7 @@
%html{ lang: "en"}
= render "layouts/head"
%body{class: "#{app_theme} application"}
- = render "layouts/head_panel", title: "" if current_user
+ = render "layouts/header/empty"
.container.navless-container
= render "layouts/flash"
.error-page
diff --git a/app/views/layouts/header/_default.html.haml b/app/views/layouts/header/_default.html.haml
new file mode 100644
index 00000000000..8b4510d6516
--- /dev/null
+++ b/app/views/layouts/header/_default.html.haml
@@ -0,0 +1,46 @@
+%header.navbar.navbar-fixed-top.navbar-gitlab{ class: nav_header_class }
+ .container
+ .header-logo
+ = link_to root_path, class: 'home', title: 'Dashboard', id: 'js-shortcuts-home', data: {toggle: 'tooltip', placement: 'bottom'} do
+ = brand_header_logo
+ %h3 GitLab
+ .header-content
+ %h1.title
+ = title
+
+ %button.navbar-toggle
+ %span.sr-only Toggle navigation
+ = icon('bars')
+
+ .navbar-collapse.collapse
+ %ul.nav.navbar-nav.pull-right
+ %li.hidden-sm.hidden-xs
+ = render 'layouts/search'
+ %li.visible-sm.visible-xs
+ = link_to search_path, title: 'Search', data: {toggle: 'tooltip', placement: 'bottom'} do
+ = icon('search')
+ %li.hidden-xs
+ = link_to help_path, title: 'Help', data: {toggle: 'tooltip', placement: 'bottom'} do
+ = icon('question-circle fw')
+ %li
+ = link_to explore_root_path, title: 'Explore', data: {toggle: 'tooltip', placement: 'bottom'} do
+ = icon('globe fw')
+ %li
+ = link_to user_snippets_path(current_user), title: 'Your snippets', data: {toggle: 'tooltip', placement: 'bottom'} do
+ = icon('clipboard fw')
+ - if current_user.is_admin?
+ %li
+ = link_to admin_root_path, title: 'Admin area', data: {toggle: 'tooltip', placement: 'bottom'} do
+ = icon('wrench fw')
+ - if current_user.can_create_project?
+ %li.hidden-xs
+ = link_to new_project_path, title: 'New project', data: {toggle: 'tooltip', placement: 'bottom'} do
+ = icon('plus fw')
+ %li
+ = link_to profile_path, title: 'Profile settings', data: {toggle: 'tooltip', placement: 'bottom'} do
+ = icon('cog fw')
+ %li
+ = link_to destroy_user_session_path, class: 'logout', method: :delete, title: 'Sign out', data: {toggle: 'tooltip', placement: 'bottom'} do
+ = icon('sign-out')
+
+= render 'shared/outdated_browser'
diff --git a/app/views/layouts/_empty_head_panel.html.haml b/app/views/layouts/header/_empty.html.haml
index 358caa3868b..a52a3c8f0ef 100644
--- a/app/views/layouts/_empty_head_panel.html.haml
+++ b/app/views/layouts/header/_empty.html.haml
@@ -1,4 +1,4 @@
-%header.navbar.navbar-fixed-top.navbar-gitlab
+%header.navbar.navbar-fixed-top.navbar-empty
.container
- %h4.center
+ .center-logo
= image_tag 'logo-white.png', width: 32, height: 32
diff --git a/app/views/layouts/header/_public.html.haml b/app/views/layouts/header/_public.html.haml
new file mode 100644
index 00000000000..6a031722aaa
--- /dev/null
+++ b/app/views/layouts/header/_public.html.haml
@@ -0,0 +1,14 @@
+%header.navbar.navbar-fixed-top.navbar-gitlab{ class: nav_header_class }
+ .container
+ .header-logo
+ = link_to explore_root_path, class: "home" do
+ = brand_header_logo
+ %h3 GitLab
+ .header-content
+ %h1.title= title
+
+ - unless current_controller?('sessions')
+ .pull-right
+ = link_to "Sign in", new_session_path(:user, redirect_to_referer: 'yes'), class: 'btn btn-sign-in btn-success btn-sm'
+
+= render 'shared/outdated_browser'
diff --git a/app/views/layouts/nav/_project.html.haml b/app/views/layouts/nav/_project.html.haml
index 172f5197b24..cbcf560d0af 100644
--- a/app/views/layouts/nav/_project.html.haml
+++ b/app/views/layouts/nav/_project.html.haml
@@ -56,6 +56,13 @@
Merge Requests
%span.count.merge_counter= @project.merge_requests.opened.count
+ - if project_nav_tab? :settings
+ = nav_link(controller: [:project_members, :teams]) do
+ = link_to namespace_project_project_members_path(@project.namespace, @project), title: 'Members', class: 'team-tab tab', data: {placement: 'right'} do
+ = icon('users fw')
+ %span
+ Members
+
- if project_nav_tab? :labels
= nav_link(controller: :labels) do
= link_to namespace_project_labels_path(@project.namespace, @project), title: 'Labels', data: {placement: 'right'} do
diff --git a/app/views/layouts/nav/_project_settings.html.haml b/app/views/layouts/nav/_project_settings.html.haml
index 7dd14449def..633c6ae6bfb 100644
--- a/app/views/layouts/nav/_project_settings.html.haml
+++ b/app/views/layouts/nav/_project_settings.html.haml
@@ -10,32 +10,27 @@
%ul.project-settings-nav.sidebar-subnav
= nav_link(path: 'projects#edit') do
= link_to edit_project_path(@project), title: 'Project', class: 'stat-tab tab', data: {placement: 'right'} do
- = icon('pencil-square-o')
+ = icon('pencil-square-o fw')
%span
Project Settings
- = nav_link(controller: [:project_members, :teams]) do
- = link_to namespace_project_project_members_path(@project.namespace, @project), title: 'Members', class: 'team-tab tab', data: {placement: 'right'} do
- = icon('users')
- %span
- Members
= nav_link(controller: :deploy_keys) do
= link_to namespace_project_deploy_keys_path(@project.namespace, @project), title: 'Deploy Keys', data: {placement: 'right'} do
- = icon('key')
+ = icon('key fw')
%span
Deploy Keys
= nav_link(controller: :hooks) do
= link_to namespace_project_hooks_path(@project.namespace, @project), title: 'Web Hooks', data: {placement: 'right'} do
- = icon('link')
+ = icon('link fw')
%span
Web Hooks
= nav_link(controller: :services) do
= link_to namespace_project_services_path(@project.namespace, @project), title: 'Services', data: {placement: 'right'} do
- = icon('cogs')
+ = icon('cogs fw')
%span
Services
= nav_link(controller: :protected_branches) do
= link_to namespace_project_protected_branches_path(@project.namespace, @project), title: 'Protected Branches', data: {placement: 'right'} do
- = icon('lock')
+ = icon('lock fw')
%span
Protected branches
diff --git a/app/views/layouts/notify.html.haml b/app/views/layouts/notify.html.haml
index 00c7cedce40..ee1b57278b6 100644
--- a/app/views/layouts/notify.html.haml
+++ b/app/views/layouts/notify.html.haml
@@ -27,7 +27,7 @@
}
.file-stats .deleted-file {
color: #B00;
- }}
+ }
%body
%div.content
= yield
diff --git a/app/views/layouts/profile.html.haml b/app/views/layouts/profile.html.haml
index 9799b4cc4d7..3193206fe12 100644
--- a/app/views/layouts/profile.html.haml
+++ b/app/views/layouts/profile.html.haml
@@ -1,5 +1,5 @@
-- page_title "Profile"
-- header_title "Profile", profile_path
+- page_title "Settings"
+- header_title "Settings", profile_path
- sidebar "profile"
= render template: "layouts/application"
diff --git a/app/views/layouts/project.html.haml b/app/views/layouts/project.html.haml
index 4aeb9d397d2..44afa33dfe5 100644
--- a/app/views/layouts/project.html.haml
+++ b/app/views/layouts/project.html.haml
@@ -2,7 +2,13 @@
- header_title project_title(@project)
- sidebar "project" unless sidebar
-- content_for :embedded_scripts do
+- content_for :scripts_body_top do
+ - if current_user
+ :javascript
+ window.project_uploads_path = "#{namespace_project_uploads_path @project.namespace, @project}";
+ window.markdown_preview_path = "#{markdown_preview_namespace_project_path(@project.namespace, @project)}";
+
+- content_for :scripts_body do
= render "layouts/init_auto_complete" if current_user
= render template: "layouts/application"
diff --git a/app/views/notify/repository_push_email.html.haml b/app/views/notify/repository_push_email.html.haml
index a374a662333..12f83aae04b 100644
--- a/app/views/notify/repository_push_email.html.haml
+++ b/app/views/notify/repository_push_email.html.haml
@@ -35,7 +35,7 @@
= diff.new_path
- elsif diff.new_file
%span.new-file
- &plus;
+ &#43;
= diff.new_path
- else
= diff.new_path
diff --git a/app/views/profiles/accounts/show.html.haml b/app/views/profiles/accounts/show.html.haml
index 06bad7dd84a..ed009c86568 100644
--- a/app/views/profiles/accounts/show.html.haml
+++ b/app/views/profiles/accounts/show.html.haml
@@ -1,13 +1,18 @@
- page_title "Account"
+%h3.page-title
+ = page_title
+%p.light
+ Change your username and basic account settings.
+%hr
- if current_user.ldap_user?
.alert.alert-info
Some options are unavailable for LDAP accounts
.account-page
- %fieldset.update-token
- %legend
+ .panel.panel-default.update-token
+ .panel-heading
Reset Private token
- %div
+ .panel-body
= form_for @user, url: reset_private_token_profile_path, method: :put do |f|
.data
%p
@@ -21,21 +26,23 @@
- if current_user.private_token
= text_field_tag "token", current_user.private_token, class: "form-control"
%div
- = f.submit 'Reset private token', data: { confirm: "Are you sure?" }, class: "btn btn-primary btn-build-token"
+ = f.submit 'Reset private token', data: { confirm: "Are you sure?" }, class: "btn btn-default btn-build-token"
- else
%span You don`t have one yet. Click generate to fix it.
- = f.submit 'Generate', class: "btn success btn-build-token"
+ = f.submit 'Generate', class: "btn btn-default btn-build-token"
- unless current_user.ldap_user?
- %fieldset
- - if current_user.otp_required_for_login
- %legend.text-success
- = icon('check')
- Two-factor Authentication enabled
- %div
+ .panel.panel-default
+ .panel-heading
+ Two-factor Authentication
+ .panel-body
+ - if current_user.otp_required_for_login
.pull-right
= link_to 'Disable Two-factor Authentication', profile_two_factor_auth_path, method: :delete, class: 'btn btn-close btn-sm',
data: { confirm: 'Are you sure?' }
+ %p.text-success
+ %strong
+ Two-factor Authentication is enabled
%p
If you lose your recovery codes you can
%strong
@@ -43,9 +50,7 @@
= link_to 'generate new ones', codes_profile_two_factor_auth_path, method: :post, data: { confirm: 'Are you sure?' }
invalidating all previous codes.
- - else
- %legend Two-factor Authentication
- %div
+ - else
%p
Increase your account's security by enabling two-factor authentication (2FA).
%p
@@ -55,51 +60,57 @@
= link_to 'Enable Two-factor Authentication', new_profile_two_factor_auth_path, class: 'btn btn-success'
- if show_profile_social_tab?
- %fieldset
- %legend Connected Accounts
- .oauth-buttons.append-bottom-10
- %p Click on icon to activate signin with one of the following services
- - enabled_social_providers.each do |provider|
- .btn-group
- = link_to oauth_image_tag(provider), omniauth_authorize_path(User, provider),
- method: :post, class: "btn btn-lg #{'active' if oauth_active?(provider)}"
- - if oauth_active?(provider)
- = link_to unlink_profile_account_path(provider: provider), method: :delete, class: 'btn btn-lg' do
- = icon('close')
+ .panel.panel-default
+ .panel-heading
+ Connected Accounts
+ .panel-body
+ .oauth-buttons.append-bottom-10
+ %p Click on icon to activate signin with one of the following services
+ - enabled_social_providers.each do |provider|
+ .btn-group
+ = link_to oauth_image_tag(provider), omniauth_authorize_path(User, provider),
+ method: :post, class: "btn btn-lg #{'active' if oauth_active?(provider)}"
+ - if oauth_active?(provider)
+ = link_to unlink_profile_account_path(provider: provider), method: :delete, class: 'btn btn-lg' do
+ = icon('close')
- if show_profile_username_tab?
- %fieldset.update-username
- %legend
+ .panel.panel-warning.update-username
+ .panel-heading
Change Username
- = form_for @user, url: update_username_profile_path, method: :put, remote: true do |f|
- %p
- Changing your username will change path to all personal projects!
- %div
- = f.text_field :username, required: true, class: 'form-control'
- &nbsp;
- .loading-gif.hide
+ .panel-body
+ = form_for @user, url: update_username_profile_path, method: :put, remote: true do |f|
%p
- = icon('spinner spin')
- Saving new username
- %p.light
- = user_url(@user)
- %div
- = f.submit 'Save username', class: "btn btn-warning"
+ Changing your username will change path to all personal projects!
+ %div
+ = f.text_field :username, required: true, class: 'form-control'
+ &nbsp;
+ .loading-gif.hide
+ %p
+ = icon('spinner spin')
+ Saving new username
+ %p.light
+ = user_url(@user)
+ %div
+ = f.submit 'Save username', class: "btn btn-warning"
- if show_profile_remove_tab?
- %fieldset.remove-account
- %legend
+ .panel.panel-danger.remove-account
+ .panel-heading
Remove account
- %div
- %p Deleting an account has the following effects:
- %ul
- %li All user content like authored issues, snippets, comments will be removed
- - rp = current_user.personal_projects.count
- - unless rp.zero?
- %li #{pluralize rp, 'personal project'} will be removed and cannot be restored
- - if current_user.solo_owned_groups.present?
- %li
- The following groups will be abandoned. You should transfer or remove them:
- %strong #{current_user.solo_owned_groups.map(&:name).join(', ')}
- = link_to 'Delete account', user_registration_path, data: { confirm: "REMOVE #{current_user.name}? Are you sure?" }, method: :delete, class: "btn btn-remove"
-
+ .panel-body
+ - if @user.can_be_removed?
+ %p Deleting an account has the following effects:
+ %ul
+ %li All user content like authored issues, snippets, comments will be removed
+ - rp = current_user.personal_projects.count
+ - unless rp.zero?
+ %li #{pluralize rp, 'personal project'} will be removed and cannot be restored
+ = link_to 'Delete account', user_registration_path, data: { confirm: "REMOVE #{current_user.name}? Are you sure?" }, method: :delete, class: "btn btn-remove"
+ - else
+ - if @user.solo_owned_groups.present?
+ %p
+ Your account is currently an owner in these groups:
+ %strong #{@user.solo_owned_groups.map(&:name).join(', ')}
+ %p
+ You must transfer ownership or delete these groups before you can delete your account.
diff --git a/app/views/profiles/applications.html.haml b/app/views/profiles/applications.html.haml
index c4f6f59624b..2c4f0804f0b 100644
--- a/app/views/profiles/applications.html.haml
+++ b/app/views/profiles/applications.html.haml
@@ -1,34 +1,44 @@
- page_title "Applications"
%h3.page-title
- Application Settings
+ = page_title
%p.light
- OAuth2 protocol settings below.
+ - if user_oauth_applications?
+ Manage applications that can use GitLab as an OAuth provider,
+ and applications that you've authorized to use your account.
+ - else
+ Manage applications that you've authorized to use your account.
+%hr
-%fieldset.oauth-applications
- %legend Your applications
- %p= link_to 'New Application', new_oauth_application_path, class: 'btn btn-success'
- - if @applications.any?
- %table.table.table-striped
- %thead
- %tr
- %th Name
- %th Callback URL
- %th Clients
- %th
- %th
- %tbody
- - @applications.each do |application|
- %tr{:id => "application_#{application.id}"}
- %td= link_to application.name, oauth_application_path(application)
- %td
- - application.redirect_uri.split.each do |uri|
- %div= uri
- %td= application.access_tokens.count
- %td= link_to 'Edit', edit_oauth_application_path(application), class: 'btn btn-link btn-sm'
- %td= render 'doorkeeper/applications/delete_form', application: application
+- if user_oauth_applications?
+ .oauth-applications
+ %h3
+ Your applications
+ .pull-right
+ = link_to 'New Application', new_oauth_application_path, class: 'btn btn-success'
+ - if @applications.any?
+ %table.table.table-striped
+ %thead
+ %tr
+ %th Name
+ %th Callback URL
+ %th Clients
+ %th
+ %th
+ %tbody
+ - @applications.each do |application|
+ %tr{:id => "application_#{application.id}"}
+ %td= link_to application.name, oauth_application_path(application)
+ %td
+ - application.redirect_uri.split.each do |uri|
+ %div= uri
+ %td= application.access_tokens.count
+ %td= link_to 'Edit', edit_oauth_application_path(application), class: 'btn btn-link btn-sm'
+ %td= render 'doorkeeper/applications/delete_form', application: application
-%fieldset.oauth-authorized-applications.prepend-top-20
- %legend Authorized applications
+.oauth-authorized-applications.prepend-top-20
+ - if user_oauth_applications?
+ %h3
+ Authorized applications
- if @authorized_tokens.any?
%table.table.table-striped
diff --git a/app/views/profiles/design.html.haml b/app/views/profiles/design.html.haml
index af284f60409..f450ec1c018 100644
--- a/app/views/profiles/design.html.haml
+++ b/app/views/profiles/design.html.haml
@@ -1,54 +1,56 @@
- page_title "Design"
%h3.page-title
- Design Settings
+ = page_title
%p.light
Appearance settings will be saved to your profile and made available across all devices.
%hr
= form_for @user, url: profile_path, remote: true, method: :put do |f|
- %fieldset.application-theme
- %legend
+ .panel.panel-default.application-theme
+ .panel-heading
Application theme
- .themes_opts
- = label_tag do
- .prev.default
- = f.radio_button :theme_id, 1
- Graphite
+ .panel-body
+ .themes_opts
+ = label_tag do
+ .prev.default
+ = f.radio_button :theme_id, 1
+ Graphite
- = label_tag do
- .prev.classic
- = f.radio_button :theme_id, 2
- Charcoal
+ = label_tag do
+ .prev.classic
+ = f.radio_button :theme_id, 2
+ Charcoal
- = label_tag do
- .prev.modern
- = f.radio_button :theme_id, 3
- Green
+ = label_tag do
+ .prev.modern
+ = f.radio_button :theme_id, 3
+ Green
- = label_tag do
- .prev.gray
- = f.radio_button :theme_id, 4
- Gray
+ = label_tag do
+ .prev.gray
+ = f.radio_button :theme_id, 4
+ Gray
- = label_tag do
- .prev.violet
- = f.radio_button :theme_id, 5
- Violet
+ = label_tag do
+ .prev.violet
+ = f.radio_button :theme_id, 5
+ Violet
- = label_tag do
- .prev.blue
- = f.radio_button :theme_id, 6
- Blue
- %br
- .clearfix
+ = label_tag do
+ .prev.blue
+ = f.radio_button :theme_id, 6
+ Blue
+ %br
+ .clearfix
- %fieldset.code-preview-theme
- %legend
+ .panel.panel-default.code-preview-theme
+ .panel-heading
Code preview theme
- .code_highlight_opts
- - color_schemes.each do |color_scheme_id, color_scheme|
- = label_tag do
- .prev
- = image_tag "#{color_scheme}-scheme-preview.png"
- = f.radio_button :color_scheme_id, color_scheme_id
- = color_scheme.gsub(/[-_]+/, ' ').humanize
+ .panel-body
+ .code_highlight_opts
+ - color_schemes.each do |color_scheme_id, color_scheme|
+ = label_tag do
+ .prev
+ = image_tag "#{color_scheme}-scheme-preview.png"
+ = f.radio_button :color_scheme_id, color_scheme_id
+ = color_scheme.gsub(/[-_]+/, ' ').humanize
diff --git a/app/views/profiles/emails/index.html.haml b/app/views/profiles/emails/index.html.haml
index 2c0d0e10a4c..66812872c41 100644
--- a/app/views/profiles/emails/index.html.haml
+++ b/app/views/profiles/emails/index.html.haml
@@ -1,23 +1,27 @@
- page_title "Emails"
%h3.page-title
- Email Settings
+ = page_title
%p.light
- Your
- %b Primary Email
- will be used for avatar detection and web based operations, such as edits and merges.
-%p.light
- Your
- %b Notification Email
- will be used for account notifications.
-%p.light
- Your
- %b Public Email
- will be displayed on your public profile.
-%p.light
- All email addresses will be used to identify your commits.
-
+ Control emails linked to your account
%hr
+
+%ul
+ %li
+ Your
+ %b Primary Email
+ will be used for avatar detection and web based operations, such as edits and merges.
+ %li
+ Your
+ %b Notification Email
+ will be used for account notifications.
+ %li
+ Your
+ %b Public Email
+ will be displayed on your public profile.
+ %li
+ All email addresses will be used to identify your commits.
+
.panel.panel-default
.panel-heading
Emails (#{@emails.count + 1})
diff --git a/app/views/profiles/keys/_key.html.haml b/app/views/profiles/keys/_key.html.haml
index fe5770f45c3..9bbccbc45ea 100644
--- a/app/views/profiles/keys/_key.html.haml
+++ b/app/views/profiles/keys/_key.html.haml
@@ -3,8 +3,7 @@
= link_to path_to_key(key, is_admin) do
%strong= key.title
%td
- %span
- (#{key.fingerprint})
+ %code.key-fingerprint= key.fingerprint
%td
%span.cgray
added #{time_ago_with_tooltip(key.created_at)}
diff --git a/app/views/profiles/keys/_key_details.html.haml b/app/views/profiles/keys/_key_details.html.haml
index 8bac22a2e1a..e0ae4d9720f 100644
--- a/app/views/profiles/keys/_key_details.html.haml
+++ b/app/views/profiles/keys/_key_details.html.haml
@@ -15,7 +15,7 @@
.col-md-8
%p
%span.light Fingerprint:
- %strong= @key.fingerprint
+ %code.key-fingerprint= @key.fingerprint
%pre.well-pre
= @key.key
.pull-right
diff --git a/app/views/profiles/keys/index.html.haml b/app/views/profiles/keys/index.html.haml
index e3af0d4e189..06655f7ba3a 100644
--- a/app/views/profiles/keys/index.html.haml
+++ b/app/views/profiles/keys/index.html.haml
@@ -1,6 +1,6 @@
- page_title "SSH Keys"
%h3.page-title
- SSH Keys Settings
+ = page_title
.pull-right
= link_to "Add SSH Key", new_profile_key_path, class: "btn btn-new"
%p.light
diff --git a/app/views/profiles/notifications/show.html.haml b/app/views/profiles/notifications/show.html.haml
index a74d97dac3b..9480a19f5b2 100644
--- a/app/views/profiles/notifications/show.html.haml
+++ b/app/views/profiles/notifications/show.html.haml
@@ -1,6 +1,6 @@
- page_title "Notifications"
%h3.page-title
- Notifications Settings
+ = page_title
%p.light
These are your global notification settings.
%hr
diff --git a/app/views/profiles/passwords/edit.html.haml b/app/views/profiles/passwords/edit.html.haml
index 21dabbdfe2c..399ae98adf9 100644
--- a/app/views/profiles/passwords/edit.html.haml
+++ b/app/views/profiles/passwords/edit.html.haml
@@ -1,5 +1,6 @@
- page_title "Password"
-%h3.page-title Password Settings
+%h3.page-title
+ = page_title
%p.light
- if @user.password_automatically_set?
Set your password.
diff --git a/app/views/profiles/show.html.haml b/app/views/profiles/show.html.haml
index 29c30905117..6534afb0e89 100644
--- a/app/views/profiles/show.html.haml
+++ b/app/views/profiles/show.html.haml
@@ -1,6 +1,6 @@
-- page_title "Settings"
+- page_title "Profile"
%h3.page-title
- Profile Settings
+ = page_title
%p.light
This information will appear on your profile.
- if current_user.ldap_user?
@@ -37,8 +37,11 @@
= f.text_field :email, class: "form-control", required: true
- if @user.unconfirmed_email.present?
%span.help-block
- Please click the link in the confirmation email before continuing, it was sent to
- %strong #{@user.unconfirmed_email}
+ Please click the link in the confirmation email before continuing. It was sent to
+ = succeed "." do
+ %strong #{@user.unconfirmed_email}
+ %p
+ = link_to "Resend confirmation e-mail", user_confirmation_path(user: { email: @user.unconfirmed_email }), method: :post
- else
%span.help-block We also use email for avatar detection if no avatar is uploaded.
diff --git a/app/views/profiles/two_factor_auths/new.html.haml b/app/views/profiles/two_factor_auths/new.html.haml
index b9f3e2380fe..74268c9bde2 100644
--- a/app/views/profiles/two_factor_auths/new.html.haml
+++ b/app/views/profiles/two_factor_auths/new.html.haml
@@ -2,8 +2,10 @@
%h2.page-title Two-Factor Authentication (2FA)
%p
- Download the Google Authenticator application from App Store for iOS or
- Google Play for Android and scan this code.
+ Download the Google Authenticator application from App Store for iOS or Google
+ Play for Android and scan this code.
+
+ More information is available in the #{link_to('documentation', help_page_path('workflow', 'two_factor_authentication'))}.
%hr
@@ -12,10 +14,9 @@
.alert.alert-danger
= @error
.form-group
- .col-sm-2
- .col-sm-2
+ .col-lg-2.col-lg-offset-2
= raw @qr_code
- .col-sm-8.manual-instructions
+ .col-lg-7.col-lg-offset-1.manual-instructions
%h3 Can't scan the code?
%p
@@ -33,7 +34,7 @@
%dd Yes
.form-group
= label_tag :pin_code, nil, class: "control-label"
- .col-sm-10
+ .col-lg-10
= text_field_tag :pin_code, nil, class: "form-control", required: true, autofocus: true
.form-actions
= submit_tag 'Submit', class: 'btn btn-success'
diff --git a/app/views/projects/_aside.html.haml b/app/views/projects/_aside.html.haml
index e90c7b26dd2..c9c17110d2b 100644
--- a/app/views/projects/_aside.html.haml
+++ b/app/views/projects/_aside.html.haml
@@ -1,96 +1,108 @@
.clearfix
- unless @project.empty_repo?
- .well
- %h4.visibility-level-label
+ .panel.panel-default
+ .panel-heading
= visibility_level_icon(@project.visibility_level)
= "#{visibility_level_label(@project.visibility_level).capitalize} project"
- - if @repository.changelog || @repository.license || @repository.contribution_guide
- %ul.nav.nav-pills
- - if @repository.changelog
- %li.hidden-xs
- = link_to changelog_url(@project) do
- Changelog
- - if @repository.license
- %li
- = link_to license_url(@project) do
- License
- - if @repository.contribution_guide
- %li
- = link_to contribution_guide_url(@project) do
- Contribution guide
+ .panel-body
+ - if @repository.changelog || @repository.license || @repository.contribution_guide
+ %ul.nav.nav-pills
+ - if @repository.changelog
+ %li.hidden-xs
+ = link_to changelog_url(@project) do
+ Changelog
+ - if @repository.license
+ %li
+ = link_to license_url(@project) do
+ License
+ - if @repository.contribution_guide
+ %li
+ = link_to contribution_guide_url(@project) do
+ Contribution guide
- .actions
- - if can? current_user, :write_issue, @project
- = link_to url_for_new_issue(@project, only_path: true), title: "New Issue", class: 'btn btn-sm append-right-10' do
- = icon("exclamation-circle fw")
- New Issue
+ .actions
+ - if can? current_user, :write_issue, @project
+ = link_to url_for_new_issue(@project, only_path: true), title: "New Issue", class: 'btn btn-sm append-right-10' do
+ = icon("exclamation-circle fw")
+ New Issue
- - if can? current_user, :write_merge_request, @project
- = link_to new_namespace_project_merge_request_path(@project.namespace, @project), class: "btn btn-sm", title: "New Merge Request" do
- = icon("plus fw")
- New Merge Request
+ - if can? current_user, :write_merge_request, @project
+ = link_to new_namespace_project_merge_request_path(@project.namespace, @project), class: "btn btn-sm", title: "New Merge Request" do
+ = icon("plus fw")
+ New Merge Request
- - if forked_from_project = @project.forked_from_project
- .well
- %h4
- = icon("code-fork fw")
- Forked from
- .pull-right
- = link_to forked_from_project.namespace.try(:name), project_path(forked_from_project)
+ - if forked_from_project = @project.forked_from_project
+ .panel-footer
+ = icon("code-fork fw")
+ Forked from
+ .pull-right
+ = link_to forked_from_project.namespace.try(:name), project_path(forked_from_project)
- - if version = @repository.version
- .well
- %h4
- = icon("clock-o fw")
- Version
- .pull-right
- = link_to version_url(@project) do
- = @repository.blob_by_oid(version.id).data
- - @project.ci_services.each do |ci_service|
- - if ci_service.active? && ci_service.respond_to?(:builds_path)
- .well
- %h4
- = icon("check fw")
- = ci_service.title
- .pull-right
- - if ci_service.respond_to?(:status_img_path)
- = link_to ci_service.builds_path, :'data-no-turbolink' => 'data-no-turbolink' do
- = image_tag ci_service.status_img_path, alt: "build status", class: 'ci-status-image'
- - else
- = link_to 'view builds', ci_service.builds_path, :'data-no-turbolink' => 'data-no-turbolink'
+ - @project.ci_services.each do |ci_service|
+ - if ci_service.active? && ci_service.respond_to?(:builds_path)
+ .panel-footer
+ = icon("check fw")
+ = ci_service.title
+ .pull-right
+ - if ci_service.respond_to?(:status_img_path)
+ = link_to ci_service.builds_path, :'data-no-turbolink' => 'data-no-turbolink' do
+ = image_tag ci_service.status_img_path, alt: "build status", class: 'ci-status-image'
+ - else
+ = link_to 'view builds', ci_service.builds_path, :'data-no-turbolink' => 'data-no-turbolink'
+
- unless @project.empty_repo?
- .well
- %h4
- = icon("archive fw")
+ .panel.panel-default
+ .panel-heading
+ = icon("folder-o fw")
Repository
+ .panel-body
+ %ul.nav.nav-pills
+ %li
+ = link_to namespace_project_commits_path(@project.namespace, @project, @ref || @repository.root_ref) do
+ = pluralize(number_with_delimiter(@repository.commit_count), 'commit')
+ %li
+ = link_to namespace_project_branches_path(@project.namespace, @project) do
+ = pluralize(number_with_delimiter(@repository.branch_names.count), 'branch')
+ %li
+ = link_to namespace_project_tags_path(@project.namespace, @project) do
+ = pluralize(number_with_delimiter(@repository.tag_names.count), 'tag')
- %ul.nav.nav-pills
- %li
- = link_to namespace_project_commits_path(@project.namespace, @project, @ref || @repository.root_ref) do
- = pluralize(number_with_delimiter(@repository.commit_count), 'commit')
- %li
- = link_to namespace_project_branches_path(@project.namespace, @project) do
- = pluralize(number_with_delimiter(@repository.branch_names.count), 'branch')
- %li
- = link_to namespace_project_tags_path(@project.namespace, @project) do
- = pluralize(number_with_delimiter(@repository.tag_names.count), 'tag')
-
- .actions
- = link_to namespace_project_compare_index_path(@project.namespace, @project, from: @repository.root_ref, to: @ref || @repository.root_ref), class: 'btn btn-sm append-right-10' do
- %i.fa.fa-exchange
- Compare code
+ .actions
+ = link_to namespace_project_compare_index_path(@project.namespace, @project, from: @repository.root_ref, to: @ref || @repository.root_ref), class: 'btn btn-sm append-right-10' do
+ %i.fa.fa-exchange
+ Compare code
- - if can?(current_user, :download_code, @project)
- = render 'projects/repositories/download_archive', split_button: true, btn_class: 'btn-group-sm'
+ - if can?(current_user, :download_code, @project)
+ = render 'projects/repositories/download_archive', split_button: true, btn_class: 'btn-group-sm'
+ - if version = @repository.version
+ .panel-footer
+ = icon("clock-o fw")
+ Version
+ .pull-right
+ = link_to version_url(@project) do
+ = @repository.blob_by_oid(version.id).data
= render "shared/clone_panel"
- if @project.archived?
+ %br
.alert.alert-warning
%h4
= icon("exclamation-triangle fw")
Archived project!
%p Repository is read-only
+
+ - if current_user
+ - access = user_max_access_in_project(current_user, @project)
+ - if access
+ .light-well.light.prepend-top-20
+ %small
+ You have #{access} access to this project.
+ - if @project.project_member_by_id(current_user)
+ %br
+ = link_to leave_namespace_project_project_members_path(@project.namespace, @project),
+ data: { confirm: leave_project_message(@project) }, method: :delete, title: 'Leave project' do
+ Leave this project
diff --git a/app/views/projects/_home_panel.html.haml b/app/views/projects/_home_panel.html.haml
index f9cdda4a3ba..076afb11a9d 100644
--- a/app/views/projects/_home_panel.html.haml
+++ b/app/views/projects/_home_panel.html.haml
@@ -5,7 +5,7 @@
.project-home-row.project-home-row-top
.project-home-desc
- if @project.description.present?
- = escaped_autolink(@project.description)
+ = markdown(@project.description, pipeline: :description)
- if can?(current_user, :admin_project, @project)
&ndash;
= link_to 'Edit', edit_namespace_project_path
diff --git a/app/views/projects/_issuable_form.html.haml b/app/views/projects/_issuable_form.html.haml
index 2292aaaa214..491e2107da4 100644
--- a/app/views/projects/_issuable_form.html.haml
+++ b/app/views/projects/_issuable_form.html.haml
@@ -15,16 +15,16 @@
- if issuable.is_a?(MergeRequest)
%p.help-block
- if issuable.work_in_progress?
- This merge request is marked a <strong>Work In Progress</strong>.
- When it's ready, remove the <code>WIP</code> prefix from the title to allow it to be accepted.
+ Remove the <code>WIP</code> prefix from the title to allow this
+ <strong>Work In Progress</strong> merge request to be accepted when it's ready.
- else
- To prevent this merge request from being accepted before it's ready,
- mark it a <strong>Work In Progress</strong> by starting the title with <code>[WIP]</code> or <code>WIP:</code>.
+ Start the title with <code>[WIP]</code> or <code>WIP:</code> to prevent a
+ <strong>Work In Progress</strong> merge request from being accepted before it's ready.
.form-group.issuable-description
= f.label :description, 'Description', class: 'control-label'
.col-sm-10
- = render layout: 'projects/md_preview', locals: { preview_class: "wiki" } do
+ = render layout: 'projects/md_preview', locals: { preview_class: "wiki", referenced_users: true } do
= render 'projects/zen', f: f, attr: :description,
classes: 'description form-control'
.col-sm-12.hint
@@ -79,6 +79,24 @@
- if can? current_user, :admin_label, issuable.project
= link_to 'Create new label', new_namespace_project_label_path(issuable.project.namespace, issuable.project), target: :blank
+- if issuable.is_a?(MergeRequest)
+ %hr
+ - unless @merge_request.persisted?
+ .form-group
+ = f.label :source_branch, class: 'control-label' do
+ %i.fa.fa-code-fork
+ Source Branch
+ .col-sm-10
+ = f.select(:source_branch, [@merge_request.source_branch], { }, { class: 'source_branch select2 span2', disabled: true })
+ %p.help-block
+ = link_to 'Change source branch', mr_change_branches_path(@merge_request)
+ .form-group
+ = f.label :target_branch, class: 'control-label' do
+ %i.fa.fa-code-fork
+ Target Branch
+ .col-sm-10
+ = f.select(:target_branch, @merge_request.target_branches, { include_blank: "Select branch" }, { class: 'target_branch select2 span2' })
+
.form-actions
- if !issuable.project.empty_repo? && (guide_url = contribution_guide_url(issuable.project)) && !issuable.persisted?
%p
diff --git a/app/views/projects/_md_preview.html.haml b/app/views/projects/_md_preview.html.haml
index b869fd6e12a..b7bca6dae09 100644
--- a/app/views/projects/_md_preview.html.haml
+++ b/app/views/projects/_md_preview.html.haml
@@ -1,13 +1,24 @@
-%ul.nav.nav-tabs
- %li.active
- = link_to '#md-write-holder', class: 'js-md-write-button' do
- Write
- %li
- = link_to '#md-preview-holder', class: 'js-md-preview-button',
- data: { url: markdown_preview_namespace_project_path(@project.namespace, @project) } do
- Preview
-%div
- .md-write-holder
- = yield
- .md.md-preview-holder.hide
- .js-md-preview{class: (preview_class if defined?(preview_class))}
+.md-area
+ .md-header.clearfix
+ %ul.nav.nav-tabs
+ %li.active
+ = link_to '#md-write-holder', class: 'js-md-write-button', tabindex: '-1' do
+ Write
+ %li
+ = link_to '#md-preview-holder', class: 'js-md-preview-button', tabindex: '-1' do
+ Preview
+
+ - if defined?(referenced_users) && referenced_users
+ %span.referenced-users.pull-left.hide
+ = icon('exclamation-triangle')
+ You are about to add
+ %strong
+ %span.js-referenced-users-count 0
+ people
+ to the discussion. Proceed with caution.
+
+ %div
+ .md-write-holder
+ = yield
+ .md.md-preview-holder.hide
+ .js-md-preview{class: (preview_class if defined?(preview_class))}
diff --git a/app/views/projects/_section.html.haml b/app/views/projects/_section.html.haml
index f4f876f3809..d7b06197f67 100644
--- a/app/views/projects/_section.html.haml
+++ b/app/views/projects/_section.html.haml
@@ -18,7 +18,6 @@
%li
= link_to namespace_project_path(@project.namespace, @project, format: :atom, private_token: current_user.private_token), title: "Feed", class: 'rss-btn' do
%i.fa.fa-rss
- Activity Feed
= render 'shared/event_filter'
%hr
diff --git a/app/views/projects/blame/show.html.haml b/app/views/projects/blame/show.html.haml
index 462f5b7afb0..8019c7f4569 100644
--- a/app/views/projects/blame/show.html.haml
+++ b/app/views/projects/blame/show.html.haml
@@ -32,5 +32,5 @@
%code
:erb
<% lines.each do |line| %>
- <%= highlight(@blob.name, line, true).html_safe %>
+ <%= highlight(@blob.name, line, nowrap: true, continue: true).html_safe %>
<% end %>
diff --git a/app/views/projects/blob/_editor.html.haml b/app/views/projects/blob/_editor.html.haml
index 96f188e4aa7..9c3e1703c89 100644
--- a/app/views/projects/blob/_editor.html.haml
+++ b/app/views/projects/blob/_editor.html.haml
@@ -12,8 +12,8 @@
\/
= text_field_tag 'file_name', params[:file_name], placeholder: "File name",
required: true, class: 'form-control new-file-name'
- .pull-right
- = select_tag :encoding, options_for_select([ "base64", "text" ], "text"), class: 'form-control'
+ .pull-right
+ = select_tag :encoding, options_for_select([ "base64", "text" ], "text"), class: 'form-control'
.file-content.code
%pre.js-edit-mode-pane#editor
diff --git a/app/views/projects/blob/new.html.haml b/app/views/projects/blob/new.html.haml
index 9b1d03b820e..f7ddf74b4fc 100644
--- a/app/views/projects/blob/new.html.haml
+++ b/app/views/projects/blob/new.html.haml
@@ -6,11 +6,12 @@
= render 'shared/commit_message_container', params: params,
placeholder: 'Add new file'
- .form-group.branch
- = label_tag 'branch', class: 'control-label' do
- Branch
- .col-sm-10
- = text_field_tag 'new_branch', @ref, class: "form-control"
+ - unless @project.empty_repo?
+ .form-group.branch
+ = label_tag 'branch', class: 'control-label' do
+ Branch
+ .col-sm-10
+ = text_field_tag 'new_branch', @ref, class: "form-control"
= hidden_field_tag 'content', '', id: 'file-content'
= render 'projects/commit_button', ref: @ref,
diff --git a/app/views/projects/deploy_keys/_deploy_key.html.haml b/app/views/projects/deploy_keys/_deploy_key.html.haml
index c577dfa8d55..8d66bae8cdf 100644
--- a/app/views/projects/deploy_keys/_deploy_key.html.haml
+++ b/app/views/projects/deploy_keys/_deploy_key.html.haml
@@ -2,24 +2,20 @@
.pull-right
- if @available_keys.include?(deploy_key)
= link_to enable_namespace_project_deploy_key_path(@project.namespace, @project, deploy_key), class: 'btn btn-sm', method: :put do
- %i.fa.fa-plus
+ = icon('plus')
Enable
- else
- if deploy_key.destroyed_when_orphaned? && deploy_key.almost_orphaned?
= link_to 'Remove', disable_namespace_project_deploy_key_path(@project.namespace, @project, deploy_key), data: { confirm: 'You are going to remove deploy key. Are you sure?'}, method: :put, class: "btn btn-remove delete-key btn-sm pull-right"
- else
= link_to disable_namespace_project_deploy_key_path(@project.namespace, @project, deploy_key), class: 'btn btn-sm', method: :put do
- %i.fa.fa-power-off
+ = icon('power-off')
Disable
- - if project = project_for_deploy_key(deploy_key)
- = link_to namespace_project_deploy_key_path(project.namespace, project, deploy_key) do
- %i.fa.fa-key
- %strong= deploy_key.title
- - else
- %i.fa.fa-key
- %strong= deploy_key.title
-
+ = icon('key')
+ %strong= deploy_key.title
+ %br
+ %code.key-fingerprint= deploy_key.fingerprint
%p.light.prepend-top-10
- if deploy_key.public?
diff --git a/app/views/projects/deploy_keys/show.html.haml b/app/views/projects/deploy_keys/show.html.haml
deleted file mode 100644
index 7d44652af72..00000000000
--- a/app/views/projects/deploy_keys/show.html.haml
+++ /dev/null
@@ -1,14 +0,0 @@
-- page_title @key.title, "Deploy Keys"
-%h3.page-title
- Deploy key:
- = @key.title
- %small
- created on
- = @key.created_at.stamp("Aug 21, 2011")
-.back-link
- = link_to namespace_project_deploy_keys_path(@project.namespace, @project) do
- &larr; To keys list
-%hr
-%pre= @key.key
-.pull-right
- = link_to 'Remove', namespace_project_deploy_key_path(@project.namespace, @project, @key), data: { confirm: 'Are you sure?'}, method: :delete, class: "btn-remove btn delete-key"
diff --git a/app/views/projects/diffs/_file.html.haml b/app/views/projects/diffs/_file.html.haml
index d4b019780f5..99ee23a1ddc 100644
--- a/app/views/projects/diffs/_file.html.haml
+++ b/app/views/projects/diffs/_file.html.haml
@@ -10,8 +10,9 @@
- if @commit.parent_ids.present?
= view_file_btn(@commit.parent_id, diff_file, project)
- elsif diff_file.diff.submodule?
- - submodule_item = project.repository.blob_at(@commit.id, diff_file.file_path)
- = submodule_link(submodule_item, @commit.id, project.repository)
+ %span
+ - submodule_item = project.repository.blob_at(@commit.id, diff_file.file_path)
+ = submodule_link(submodule_item, @commit.id, project.repository)
- else
%span
- if diff_file.renamed_file
diff --git a/app/views/projects/edit.html.haml b/app/views/projects/edit.html.haml
index c09d794ef7f..2765f63c6bc 100644
--- a/app/views/projects/edit.html.haml
+++ b/app/views/projects/edit.html.haml
@@ -6,7 +6,7 @@
Project settings
%hr
.panel-body
- = form_for [@project.namespace.becomes(Namespace), @project], remote: true, html: { multipart: true, class: "edit_project form-horizontal" }, authenticity_token: true do |f|
+ = form_for [@project.namespace.becomes(Namespace), @project], remote: true, html: { multipart: true, class: "edit_project form-horizontal fieldset-form" }, authenticity_token: true do |f|
%fieldset
.form-group.project_name_holder
@@ -41,32 +41,40 @@
%legend
Features:
.form-group
- = f.label :issues_enabled, "Issues", class: 'control-label'
- .col-sm-10
+ .col-sm-offset-2.col-sm-10
.checkbox
- = f.check_box :issues_enabled
- %span.descr Lightweight issue tracking system for this project
+ = f.label :issues_enabled do
+ = f.check_box :issues_enabled
+ %strong Issues
+ %br
+ %span.descr Lightweight issue tracking system for this project
.form-group
- = f.label :merge_requests_enabled, "Merge Requests", class: 'control-label'
- .col-sm-10
+ .col-sm-offset-2.col-sm-10
.checkbox
- = f.check_box :merge_requests_enabled
- %span.descr Submit changes to be merged upstream.
+ = f.label :merge_requests_enabled do
+ = f.check_box :merge_requests_enabled
+ %strong Merge Requests
+ %br
+ %span.descr Submit changes to be merged upstream.
.form-group
- = f.label :wiki_enabled, "Wiki", class: 'control-label'
- .col-sm-10
+ .col-sm-offset-2.col-sm-10
.checkbox
- = f.check_box :wiki_enabled
- %span.descr Pages for project documentation
+ = f.label :wiki_enabled do
+ = f.check_box :wiki_enabled
+ %strong Wiki
+ %br
+ %span.descr Pages for project documentation
.form-group
- = f.label :snippets_enabled, "Snippets", class: 'control-label'
- .col-sm-10
+ .col-sm-offset-2.col-sm-10
.checkbox
- = f.check_box :snippets_enabled
- %span.descr Share code pastes with others out of git repository
+ = f.label :snippets_enabled do
+ = f.check_box :snippets_enabled
+ %strong Snippets
+ %br
+ %span.descr Share code pastes with others out of git repository
%fieldset.features
%legend
diff --git a/app/views/projects/issues/_form.html.haml b/app/views/projects/issues/_form.html.haml
index 7d7217eb2a8..8d2564be55e 100644
--- a/app/views/projects/issues/_form.html.haml
+++ b/app/views/projects/issues/_form.html.haml
@@ -10,5 +10,3 @@
$('#issue_assignee_id').val("#{current_user.id}").trigger("change");
e.preventDefault();
});
-
- window.project_uploads_path = "#{namespace_project_uploads_path @project.namespace, @project}";
diff --git a/app/views/projects/issues/_issue.html.haml b/app/views/projects/issues/_issue.html.haml
index a4e25e5ce88..64d62b45657 100644
--- a/app/views/projects/issues/_issue.html.haml
+++ b/app/views/projects/issues/_issue.html.haml
@@ -4,7 +4,7 @@
= check_box_tag dom_id(issue,"selected"), nil, false, 'data-id' => issue.id, class: "selected_issue", disabled: !can?(current_user, :modify_issue, issue)
.issue-title
- %span.str-truncated
+ %span.issue-title-text
= link_to_gfm issue.title, issue_path(issue), class: "row_title"
.issue-labels
- issue.labels.each do |label|
diff --git a/app/views/projects/labels/index.html.haml b/app/views/projects/labels/index.html.haml
index 7d19415a7f4..d44fe486212 100644
--- a/app/views/projects/labels/index.html.haml
+++ b/app/views/projects/labels/index.html.haml
@@ -13,4 +13,7 @@
= paginate @labels, theme: 'gitlab'
- else
.light-well
- .nothing-here-block Create first label or #{link_to 'generate', generate_namespace_project_labels_path(@project.namespace, @project), method: :post} default set of labels
+ - if can? current_user, :admin_label, @project
+ .nothing-here-block Create first label or #{link_to 'generate', generate_namespace_project_labels_path(@project.namespace, @project), method: :post} default set of labels
+ - else
+ .nothing-here-block No labels created
diff --git a/app/views/projects/merge_requests/_form.html.haml b/app/views/projects/merge_requests/_form.html.haml
index 1c7160bce5f..be73f087449 100644
--- a/app/views/projects/merge_requests/_form.html.haml
+++ b/app/views/projects/merge_requests/_form.html.haml
@@ -8,5 +8,3 @@
$('#merge_request_assignee_id').val("#{current_user.id}").trigger("change");
e.preventDefault();
});
-
- window.project_uploads_path = "#{namespace_project_uploads_path @project.namespace, @project}";
diff --git a/app/views/projects/merge_requests/_merge_request.html.haml b/app/views/projects/merge_requests/_merge_request.html.haml
index 65f5c3d6a19..c16df27ee8f 100644
--- a/app/views/projects/merge_requests/_merge_request.html.haml
+++ b/app/views/projects/merge_requests/_merge_request.html.haml
@@ -1,6 +1,6 @@
%li{ class: mr_css_classes(merge_request) }
.merge-request-title
- %span.str-truncated
+ %span.merge-request-title-text
= link_to_gfm merge_request.title, merge_request_path(merge_request), class: "row_title"
.merge-request-labels
- merge_request.labels.each do |label|
diff --git a/app/views/projects/merge_requests/_new_submit.html.haml b/app/views/projects/merge_requests/_new_submit.html.haml
index e83b7649928..6792104569b 100644
--- a/app/views/projects/merge_requests/_new_submit.html.haml
+++ b/app/views/projects/merge_requests/_new_submit.html.haml
@@ -20,12 +20,12 @@
.mr-compare.merge-request
%ul.nav.nav-tabs.merge-request-tabs
%li.commits-tab
- = link_to '#commits', data: {action: 'commits', toggle: 'tab'} do
+ = link_to url_for(params), data: {target: '#commits', action: 'commits', toggle: 'tab'} do
= icon('history')
Commits
%span.badge= @commits.size
%li.diffs-tab
- = link_to '#diffs', data: {action: 'diffs', toggle: 'tab'} do
+ = link_to url_for(params), data: {target: '#diffs', action: 'diffs', toggle: 'tab'} do
= icon('list-alt')
Changes
%span.badge= @diffs.size
@@ -51,12 +51,10 @@
e.preventDefault();
});
- window.project_uploads_path = "#{namespace_project_uploads_path @project.namespace, @project}";
-
:javascript
var merge_request
merge_request = new MergeRequest({
- action: 'diffs',
+ action: 'new',
diffs_loaded: true,
commits_loaded: true
});
diff --git a/app/views/projects/merge_requests/_show.html.haml b/app/views/projects/merge_requests/_show.html.haml
index 0d894e360ea..5d7e73f2b28 100644
--- a/app/views/projects/merge_requests/_show.html.haml
+++ b/app/views/projects/merge_requests/_show.html.haml
@@ -22,33 +22,32 @@
%span into
%strong.label-branch #{@merge_request.target_branch}
- if @merge_request.open?
- %span.pull-right
- .btn-group
- %a.btn.dropdown-toggle{ data: {toggle: :dropdown} }
- = icon('download')
- Download as
- %span.caret
- %ul.dropdown-menu
- %li= link_to "Email Patches", merge_request_path(@merge_request, format: :patch)
- %li= link_to "Plain Diff", merge_request_path(@merge_request, format: :diff)
+ .btn-group.btn-group-sm.pull-right
+ %a.btn.btn-sm.dropdown-toggle{ data: {toggle: :dropdown} }
+ = icon('download')
+ Download as
+ %span.caret
+ %ul.dropdown-menu
+ %li= link_to "Email Patches", merge_request_path(@merge_request, format: :patch)
+ %li= link_to "Plain Diff", merge_request_path(@merge_request, format: :diff)
= render "projects/merge_requests/show/how_to_merge"
- = render "projects/merge_requests/show/state_widget"
+ = render "projects/merge_requests/widget/show.html.haml"
- if @commits.present?
%ul.nav.nav-tabs.merge-request-tabs
%li.notes-tab
- = link_to '#notes', data: {action: 'notes', toggle: 'tab'} do
+ = link_to namespace_project_merge_request_path(@project.namespace, @project, @merge_request), data: {target: '#notes', action: 'notes', toggle: 'tab'} do
= icon('comments')
Discussion
%span.badge= @merge_request.mr_and_commit_notes.user.count
%li.commits-tab
- = link_to '#commits', data: {action: 'commits', toggle: 'tab'} do
+ = link_to commits_namespace_project_merge_request_path(@project.namespace, @project, @merge_request), data: {target: '#commits', action: 'commits', toggle: 'tab'} do
= icon('history')
Commits
%span.badge= @commits.size
%li.diffs-tab
- = link_to '#diffs', data: {source: diffs_namespace_project_merge_request_path(@project.namespace, @project, @merge_request), action: 'diffs', toggle: 'tab'} do
+ = link_to diffs_namespace_project_merge_request_path(@project.namespace, @project, @merge_request), data: {target: '#diffs', action: 'diffs', toggle: 'tab'} do
= icon('list-alt')
Changes
%span.badge= @merge_request.diffs.size
@@ -70,10 +69,5 @@
var merge_request;
merge_request = new MergeRequest({
- url_to_automerge_check: "#{automerge_check_namespace_project_merge_request_path(@project.namespace, @project, @merge_request)}",
- check_enable: #{@merge_request.unchecked? ? "true" : "false"},
- url_to_ci_check: "#{ci_status_namespace_project_merge_request_path(@project.namespace, @project, @merge_request)}",
- ci_enable: #{@project.ci_service ? "true" : "false"},
- current_status: "#{@merge_request.automerge_status}",
action: "#{controller.action_name}"
});
diff --git a/app/views/projects/merge_requests/automerge.js.haml b/app/views/projects/merge_requests/automerge.js.haml
index a53cbb150a4..33321651e32 100644
--- a/app/views/projects/merge_requests/automerge.js.haml
+++ b/app/views/projects/merge_requests/automerge.js.haml
@@ -1,6 +1,6 @@
--if @status
+- if @status
:plain
- merge_request.mergeInProgress();
--else
+ merge_request_widget.mergeInProgress();
+- else
:plain
- merge_request.alreadyOrCannotBeMerged()
+ $('.mr-widget-body').html("#{escape_javascript(render('projects/merge_requests/widget/open/reload'))}");
diff --git a/app/views/projects/merge_requests/index.html.haml b/app/views/projects/merge_requests/index.html.haml
index 841d1e1cfe9..fa591b0537e 100644
--- a/app/views/projects/merge_requests/index.html.haml
+++ b/app/views/projects/merge_requests/index.html.haml
@@ -4,9 +4,10 @@
= render 'shared/issuable_search_form', path: namespace_project_merge_requests_path(@project.namespace, @project)
- if can? current_user, :write_merge_request, @project
- = link_to new_namespace_project_merge_request_path(@project.namespace, @project), class: "btn btn-new pull-left", title: "New Merge Request" do
- %i.fa.fa-plus
- New Merge Request
+ .pull-left.hidden-xs
+ = link_to new_namespace_project_merge_request_path(@project.namespace, @project), class: "btn btn-new", title: "New Merge Request" do
+ %i.fa.fa-plus
+ New Merge Request
= render 'shared/issuable_filter', type: :merge_requests
.merge-requests-holder
= render 'merge_requests'
diff --git a/app/views/projects/merge_requests/show/_mr_accept.html.haml b/app/views/projects/merge_requests/show/_mr_accept.html.haml
deleted file mode 100644
index 906cc11dc67..00000000000
--- a/app/views/projects/merge_requests/show/_mr_accept.html.haml
+++ /dev/null
@@ -1,89 +0,0 @@
-- unless @allowed_to_merge
- - if @project.archived?
- %p
- %strong Archived projects do not provide commit access.
- - else
- .automerge_widget.cannot_be_merged.hide
- %strong This merge request contains merge conflicts that must be resolved.
- Only those with write access to this repository can merge merge requests.
- .automerge_widget.work_in_progress.hide
- %strong This merge request is marked as Work In Progress.
- Only those with write access to this repository can merge merge requests.
- .automerge_widget.can_be_merged.hide
- %strong This request can be merged automatically.
- Only those with write access to this repository can merge merge requests.
-
-
-- if @show_merge_controls
- .automerge_widget.can_be_merged.hide
- .clearfix
- = form_for [:automerge, @project.namespace.becomes(Namespace), @project, @merge_request], remote: true, method: :post do |f|
- .accept-merge-holder.clearfix.js-toggle-container
- .accept-action
- = f.submit "Accept Merge Request", class: "btn btn-create accept_merge_request"
- - if can_remove_branch?(@merge_request.source_project, @merge_request.source_branch) && !@merge_request.for_fork?
- .accept-control.checkbox
- = label_tag :should_remove_source_branch, class: "remove_source_checkbox" do
- = check_box_tag :should_remove_source_branch
- Remove source-branch
- .accept-control
- = link_to "#", class: "modify-merge-commit-link js-toggle-button", title: "Modify merge commit message" do
- %i.fa.fa-edit
- Modify commit message
- .js-toggle-content.hide.prepend-top-20
- = render 'shared/commit_message_container', params: params,
- text: @merge_request.merge_commit_message,
- rows: 14, hint: true
-
- %br
- .light
- If you want to merge this request manually, you can use the
- %strong
- = link_to "command line", "#modal_merge_info", class: "how_to_merge_link vlink", title: "How To Merge", "data-toggle" => "modal"
-
-
- .automerge_widget.no_satellite.hide
- %p
- %span
- %strong This repository does not have a satellite. Please ask an administrator to fix this issue!
-
- .automerge_widget.cannot_be_merged.hide
- %h4
- This pull request contains merge conflicts that must be resolved.
- You can try it manually on the
- %strong
- = link_to "command line", "#modal_merge_info", class: "how_to_merge_link vlink", title: "How To Merge", "data-toggle" => "modal"
-
- %p
- %button.btn.disabled{:type => 'button'}
- %i.fa.fa-warning
- Accept Merge Request
- &nbsp;
- This happens when Git is not able to automatically resolve conflicts between branches.
-
- .automerge_widget.work_in_progress.hide
- %h4
- This request cannot be merged because it is marked as <strong>Work In Progress</strong>.
-
- %p
- %button.btn.disabled{:type => 'button'}
- %i.fa.fa-warning
- Accept Merge Request
- &nbsp;
- When the merge request is ready, remove the "WIP" prefix from the title to allow merging.
-
- .automerge_widget.unchecked
- %p
- %strong
- %i.fa.fa-spinner.fa-spin
- Checking automatic merge…
-
- .automerge_widget.already_cannot_be_merged.hide
- %p
- %strong This merge request cannot be merged. Try to reload the page.
-
- .merge-in-progress.hide
- %p
- %i.fa.fa-spinner.fa-spin
- &nbsp;
- Merge is in progress. Please wait… Page will be reloaded automatically. &nbsp;
diff --git a/app/views/projects/merge_requests/show/_mr_ci.html.haml b/app/views/projects/merge_requests/show/_mr_ci.html.haml
deleted file mode 100644
index 3b1cd53df37..00000000000
--- a/app/views/projects/merge_requests/show/_mr_ci.html.haml
+++ /dev/null
@@ -1,34 +0,0 @@
-- if @commits.any?
- .ci_widget.ci-success{style: "display:none"}
- = icon("check")
- %span CI build passed
- for #{@merge_request.last_commit_short_sha}.
- = link_to "View build page", ci_build_details_path(@merge_request), :"data-no-turbolink" => "data-no-turbolink"
-
-
- .ci_widget.ci-failed{style: "display:none"}
- = icon("times")
- %span CI build failed
- for #{@merge_request.last_commit_short_sha}.
- = link_to "View build page", ci_build_details_path(@merge_request), :"data-no-turbolink" => "data-no-turbolink"
-
- - [:running, :pending].each do |status|
- .ci_widget{class: "ci-#{status}", style: "display:none"}
- = icon("clock-o")
- %span CI build #{status}
- for #{@merge_request.last_commit_short_sha}.
- = link_to "View build page", ci_build_details_path(@merge_request), :"data-no-turbolink" => "data-no-turbolink"
-
- .ci_widget
- = icon("spinner spin")
- Checking for CI status for #{@merge_request.last_commit_short_sha}
-
- .ci_widget.ci-canceled{style: "display:none"}
- = icon("times")
- %span CI build canceled
- for #{@merge_request.last_commit_short_sha}.
- = link_to "View build page", ci_build_details_path(@merge_request), :"data-no-turbolink" => "data-no-turbolink"
-
- .ci_widget.ci-error{style: "display:none"}
- = icon("times")
- %span Cannot connect to the CI server. Please check your settings and try again.
diff --git a/app/views/projects/merge_requests/show/_remove_source_branch.html.haml b/app/views/projects/merge_requests/show/_remove_source_branch.html.haml
deleted file mode 100644
index 59cb85edfce..00000000000
--- a/app/views/projects/merge_requests/show/_remove_source_branch.html.haml
+++ /dev/null
@@ -1,17 +0,0 @@
-- if @source_branch.blank?
- Source branch has been removed
-
-- elsif can_remove_branch?(@merge_request.source_project, @merge_request.source_branch) && @merge_request.merged?
- .remove_source_branch_widget
- %p Changes merged into #{@merge_request.target_branch}. You can remove source branch now
- = link_to namespace_project_branch_path(@merge_request.source_project.namespace, @merge_request.source_project, @source_branch), remote: true, method: :delete, class: "btn btn-primary btn-sm remove_source_branch" do
- %i.fa.fa-times
- Remove Source Branch
-
- .remove_source_branch_widget.failed.hide
- Failed to remove source branch '#{@merge_request.source_branch}'
-
- .remove_source_branch_in_progress.hide
- %i.fa.fa-spinner.fa-spin
- &nbsp;
- Removing source branch '#{@merge_request.source_branch}'. Please wait. Page will be automatically reloaded. &nbsp;
diff --git a/app/views/projects/merge_requests/show/_state_widget.html.haml b/app/views/projects/merge_requests/show/_state_widget.html.haml
deleted file mode 100644
index e4c71bfc1be..00000000000
--- a/app/views/projects/merge_requests/show/_state_widget.html.haml
+++ /dev/null
@@ -1,50 +0,0 @@
-.mr-state-widget
- - if @merge_request.source_project.ci_service && @commits.any?
- .mr-widget-heading
- = render "projects/merge_requests/show/mr_ci"
- .mr-widget-body
- - if @merge_request.open?
- - if @merge_request.source_branch_exists? && @merge_request.target_branch_exists?
- = render "projects/merge_requests/show/mr_accept"
- - else
- = render "projects/merge_requests/show/no_accept"
-
- - if @merge_request.closed?
- %h4
- Rejected
- - if @merge_request.closed_event
- by #{link_to_member(@project, @merge_request.closed_event.author, avatar: false)}
- #{time_ago_with_tooltip(@merge_request.closed_event.created_at)}
- %p Changes were not merged into target branch
-
- - if @merge_request.merged?
- %h4
- Accepted
- - if @merge_request.merge_event
- by #{link_to_member(@project, @merge_request.merge_event.author, avatar: false)}
- #{time_ago_with_tooltip(@merge_request.merge_event.created_at)}
- = render "projects/merge_requests/show/remove_source_branch"
-
- - if @merge_request.locked?
- %h4
- Merge in progress...
- %p
- Merging is in progress. While merging this request is locked and cannot be closed.
-
- - unless @commits.any?
- %h4 Nothing to merge
- %p
- Nothing to merge from
- %span.label-branch #{@merge_request.source_branch}
- to
- %span.label-branch #{@merge_request.target_branch}
- %br
- Try to use different branches or push new code.
-
- - if @closes_issues.present? && @merge_request.open?
- .mr-widget-footer
- %span
- %i.fa.fa-check
- Accepting this merge request will close #{@closes_issues.size == 1 ? 'issue' : 'issues'}
- = succeed '.' do
- != gfm(issues_sentence(@closes_issues))
diff --git a/app/views/projects/merge_requests/widget/_closed.html.haml b/app/views/projects/merge_requests/widget/_closed.html.haml
new file mode 100644
index 00000000000..18164ba771f
--- /dev/null
+++ b/app/views/projects/merge_requests/widget/_closed.html.haml
@@ -0,0 +1,9 @@
+.mr-state-widget
+ = render 'projects/merge_requests/widget/heading'
+ .mr-widget-body
+ %h4
+ Rejected
+ - if @merge_request.closed_event
+ by #{link_to_member(@project, @merge_request.closed_event.author, avatar: true)}
+ #{time_ago_with_tooltip(@merge_request.closed_event.created_at)}
+ %p Changes were not merged into target branch
diff --git a/app/views/projects/merge_requests/widget/_heading.html.haml b/app/views/projects/merge_requests/widget/_heading.html.haml
new file mode 100644
index 00000000000..107c61477e3
--- /dev/null
+++ b/app/views/projects/merge_requests/widget/_heading.html.haml
@@ -0,0 +1,38 @@
+- if @merge_request.has_ci?
+ .mr-widget-heading
+ .ci_widget.ci-success{style: "display:none"}
+ = icon("check")
+ %span CI build passed
+ for #{@merge_request.last_commit_short_sha}.
+ = link_to "View build page", ci_build_details_path(@merge_request), :"data-no-turbolink" => "data-no-turbolink"
+
+ .ci_widget.ci-failed{style: "display:none"}
+ = icon("times")
+ %span CI build failed
+ for #{@merge_request.last_commit_short_sha}.
+ = link_to "View build page", ci_build_details_path(@merge_request), :"data-no-turbolink" => "data-no-turbolink"
+
+ - [:running, :pending].each do |status|
+ .ci_widget{class: "ci-#{status}", style: "display:none"}
+ = icon("clock-o")
+ %span CI build #{status}
+ for #{@merge_request.last_commit_short_sha}.
+ = link_to "View build page", ci_build_details_path(@merge_request), :"data-no-turbolink" => "data-no-turbolink"
+
+ .ci_widget
+ = icon("spinner spin")
+ Checking for CI status for #{@merge_request.last_commit_short_sha}
+
+ .ci_widget.ci-canceled{style: "display:none"}
+ = icon("times")
+ %span CI build canceled
+ for #{@merge_request.last_commit_short_sha}.
+ = link_to "View build page", ci_build_details_path(@merge_request), :"data-no-turbolink" => "data-no-turbolink"
+
+ .ci_widget.ci-error{style: "display:none"}
+ = icon("times")
+ %span Cannot connect to the CI server. Please check your settings and try again.
+
+ :coffeescript
+ $ ->
+ merge_request_widget.getCiStatus()
diff --git a/app/views/projects/merge_requests/widget/_locked.html.haml b/app/views/projects/merge_requests/widget/_locked.html.haml
new file mode 100644
index 00000000000..13ec278847b
--- /dev/null
+++ b/app/views/projects/merge_requests/widget/_locked.html.haml
@@ -0,0 +1,8 @@
+.mr-state-widget
+ = render 'projects/merge_requests/widget/heading'
+ .mr-widget-body
+ %h4
+ Merge in progress...
+ %p
+ Merging is in progress. While merging this request is locked and cannot be closed.
+
diff --git a/app/views/projects/merge_requests/widget/_merged.html.haml b/app/views/projects/merge_requests/widget/_merged.html.haml
new file mode 100644
index 00000000000..17c3fdacda8
--- /dev/null
+++ b/app/views/projects/merge_requests/widget/_merged.html.haml
@@ -0,0 +1,41 @@
+.mr-state-widget
+ = render 'projects/merge_requests/widget/heading'
+ .mr-widget-body
+ %h4
+ Accepted
+ - if @merge_request.merge_event
+ by #{link_to_member(@project, @merge_request.merge_event.author, avatar: true)}
+ #{time_ago_with_tooltip(@merge_request.merge_event.created_at)}
+ %div
+ - if @source_branch.blank?
+ Source branch has been removed
+
+ - elsif can_remove_branch?(@merge_request.source_project, @merge_request.source_branch) && @merge_request.merged?
+ .remove_source_branch_widget
+ %p Changes merged into #{@merge_request.target_branch}. You can remove source branch now
+ = link_to namespace_project_branch_path(@merge_request.source_project.namespace, @merge_request.source_project, @source_branch), remote: true, method: :delete, class: "btn btn-primary btn-sm remove_source_branch" do
+ %i.fa.fa-times
+ Remove Source Branch
+
+ .remove_source_branch_widget.failed.hide
+ Failed to remove source branch '#{@merge_request.source_branch}'
+
+ .remove_source_branch_in_progress.hide
+ %i.fa.fa-spinner.fa-spin
+ &nbsp;
+ Removing source branch '#{@merge_request.source_branch}'. Please wait. Page will be automatically reloaded. &nbsp;
+
+ :coffeescript
+ $('.remove_source_branch').on 'click', ->
+ $('.remove_source_branch_widget').hide()
+ $('.remove_source_branch_in_progress').show()
+
+ $(".remove_source_branch").on "ajax:success", (e, data, status, xhr) ->
+ location.reload()
+
+ $(".remove_source_branch").on "ajax:error", (e, data, status, xhr) ->
+ $('.remove_source_branch_widget').hide()
+ $('.remove_source_branch_in_progress').hide()
+ $('.remove_source_branch_widget.failed').show()
+
+
diff --git a/app/views/projects/merge_requests/widget/_open.html.haml b/app/views/projects/merge_requests/widget/_open.html.haml
new file mode 100644
index 00000000000..bb794912f8f
--- /dev/null
+++ b/app/views/projects/merge_requests/widget/_open.html.haml
@@ -0,0 +1,29 @@
+.mr-state-widget
+ = render 'projects/merge_requests/widget/heading'
+ .mr-widget-body
+ - if @project.archived?
+ = render 'projects/merge_requests/widget/open/archived'
+ - elsif !@project.satellite.exists?
+ = render 'projects/merge_requests/widget/open/no_satellite'
+ - elsif @merge_request.commits.blank?
+ = render 'projects/merge_requests/widget/open/nothing'
+ - elsif @merge_request.branch_missing?
+ = render 'projects/merge_requests/widget/open/missing_branch'
+ - elsif @merge_request.unchecked?
+ = render 'projects/merge_requests/widget/open/check'
+ - elsif @merge_request.cannot_be_merged?
+ = render 'projects/merge_requests/widget/open/conflicts'
+ - elsif @merge_request.work_in_progress?
+ = render 'projects/merge_requests/widget/open/wip'
+ - elsif !@merge_request.can_be_merged_by?(current_user)
+ = render 'projects/merge_requests/widget/open/not_allowed'
+ - elsif @merge_request.can_be_merged?
+ = render 'projects/merge_requests/widget/open/accept'
+
+ - if @closes_issues.present?
+ .mr-widget-footer
+ %span
+ %i.fa.fa-check
+ Accepting this merge request will close #{@closes_issues.size == 1 ? 'issue' : 'issues'}
+ = succeed '.' do
+ != gfm(issues_sentence(@closes_issues))
diff --git a/app/views/projects/merge_requests/widget/_show.html.haml b/app/views/projects/merge_requests/widget/_show.html.haml
new file mode 100644
index 00000000000..263cab7a9e8
--- /dev/null
+++ b/app/views/projects/merge_requests/widget/_show.html.haml
@@ -0,0 +1,20 @@
+- if @merge_request.open?
+ = render 'projects/merge_requests/widget/open'
+- elsif @merge_request.merged?
+ = render 'projects/merge_requests/widget/merged'
+- elsif @merge_request.closed?
+ = render 'projects/merge_requests/widget/closed'
+- elsif @merge_request.locked?
+ = render 'projects/merge_requests/widget/locked'
+
+:javascript
+ var merge_request_widget;
+
+ merge_request_widget = new MergeRequestWidget({
+ url_to_automerge_check: "#{automerge_check_namespace_project_merge_request_path(@project.namespace, @project, @merge_request)}",
+ check_enable: #{@merge_request.unchecked? ? "true" : "false"},
+ url_to_ci_check: "#{ci_status_namespace_project_merge_request_path(@project.namespace, @project, @merge_request)}",
+ ci_enable: #{@project.ci_service ? "true" : "false"},
+ current_status: "#{@merge_request.automerge_status}",
+ });
+
diff --git a/app/views/projects/merge_requests/widget/open/_accept.html.haml b/app/views/projects/merge_requests/widget/open/_accept.html.haml
new file mode 100644
index 00000000000..41aa66dd76b
--- /dev/null
+++ b/app/views/projects/merge_requests/widget/open/_accept.html.haml
@@ -0,0 +1,34 @@
+= form_for [:automerge, @project.namespace.becomes(Namespace), @project, @merge_request], remote: true, method: :post, html: { class: 'accept-mr-form' } do |f|
+ = hidden_field_tag :authenticity_token, form_authenticity_token
+ .accept-merge-holder.clearfix.js-toggle-container
+ .accept-action
+ = f.button class: "btn btn-create accept_merge_request" do
+ Accept Merge Request
+ - if can_remove_branch?(@merge_request.source_project, @merge_request.source_branch) && !@merge_request.for_fork?
+ .accept-control.checkbox
+ = label_tag :should_remove_source_branch, class: "remove_source_checkbox" do
+ = check_box_tag :should_remove_source_branch
+ Remove source-branch
+ .accept-control
+ = link_to "#", class: "modify-merge-commit-link js-toggle-button", title: "Modify merge commit message" do
+ %i.fa.fa-edit
+ Modify commit message
+ .js-toggle-content.hide.prepend-top-20
+ = render 'shared/commit_message_container', params: params,
+ text: @merge_request.merge_commit_message,
+ rows: 14, hint: true
+
+ %br
+ .light
+ If you want to merge this request manually, you can use the
+ %strong
+ = link_to "command line", "#modal_merge_info", class: "how_to_merge_link vlink", title: "How To Merge", "data-toggle" => "modal"
+
+ :coffeescript
+ disableButtonIfEmptyField '#commit_message', '.accept_merge_request'
+
+ $('.accept-mr-form').on 'ajax:before', ->
+ btn = $('.accept_merge_request')
+ btn.disable()
+ btn.html("<i class='fa fa-spinner fa-spin'></i> Merge in progress")
+
diff --git a/app/views/projects/merge_requests/widget/open/_archived.html.haml b/app/views/projects/merge_requests/widget/open/_archived.html.haml
new file mode 100644
index 00000000000..eaf113ee568
--- /dev/null
+++ b/app/views/projects/merge_requests/widget/open/_archived.html.haml
@@ -0,0 +1,2 @@
+%p
+ %strong Archived projects do not provide commit access.
diff --git a/app/views/projects/merge_requests/widget/open/_check.html.haml b/app/views/projects/merge_requests/widget/open/_check.html.haml
new file mode 100644
index 00000000000..e775447cb75
--- /dev/null
+++ b/app/views/projects/merge_requests/widget/open/_check.html.haml
@@ -0,0 +1,7 @@
+%strong
+ %i.fa.fa-spinner.fa-spin
+ Checking automatic merge…
+
+:coffeescript
+ $ ->
+ merge_request_widget.getMergeStatus()
diff --git a/app/views/projects/merge_requests/widget/open/_conflicts.html.haml b/app/views/projects/merge_requests/widget/open/_conflicts.html.haml
new file mode 100644
index 00000000000..d1db5fec43a
--- /dev/null
+++ b/app/views/projects/merge_requests/widget/open/_conflicts.html.haml
@@ -0,0 +1,9 @@
+- if @merge_request.can_be_merged_by?(current_user)
+ %h4
+ This merge request contains merge conflicts that must be resolved.
+ You can try it manually on the
+ %strong
+ = link_to "command line", "#modal_merge_info", class: "how_to_merge_link vlink", title: "How To Merge", "data-toggle" => "modal"
+- else
+ %strong This merge request contains merge conflicts that must be resolved.
+ Only those with write access to this repository can merge merge requests.
diff --git a/app/views/projects/merge_requests/show/_no_accept.html.haml b/app/views/projects/merge_requests/widget/open/_missing_branch.html.haml
index 423fcd48e25..423fcd48e25 100644
--- a/app/views/projects/merge_requests/show/_no_accept.html.haml
+++ b/app/views/projects/merge_requests/widget/open/_missing_branch.html.haml
diff --git a/app/views/projects/merge_requests/widget/open/_no_satellite.html.haml b/app/views/projects/merge_requests/widget/open/_no_satellite.html.haml
new file mode 100644
index 00000000000..3718cfd8333
--- /dev/null
+++ b/app/views/projects/merge_requests/widget/open/_no_satellite.html.haml
@@ -0,0 +1,3 @@
+%p
+ %span
+ %strong This repository does not have a satellite. Please ask an administrator to fix this issue!
diff --git a/app/views/projects/merge_requests/widget/open/_not_allowed.html.haml b/app/views/projects/merge_requests/widget/open/_not_allowed.html.haml
new file mode 100644
index 00000000000..82f6ffd8fcb
--- /dev/null
+++ b/app/views/projects/merge_requests/widget/open/_not_allowed.html.haml
@@ -0,0 +1,2 @@
+%strong This request can be merged automatically.
+Only those with write access to this repository can merge merge requests.
diff --git a/app/views/projects/merge_requests/widget/open/_nothing.html.haml b/app/views/projects/merge_requests/widget/open/_nothing.html.haml
new file mode 100644
index 00000000000..4d526576bc2
--- /dev/null
+++ b/app/views/projects/merge_requests/widget/open/_nothing.html.haml
@@ -0,0 +1,8 @@
+%h4 Nothing to merge
+%p
+ Nothing to merge from
+ %span.label-branch #{@merge_request.source_branch}
+ to
+ %span.label-branch #{@merge_request.target_branch}
+ %br
+ Try to use different branches or push new code.
diff --git a/app/views/projects/merge_requests/widget/open/_reload.html.haml b/app/views/projects/merge_requests/widget/open/_reload.html.haml
new file mode 100644
index 00000000000..5787f6efea4
--- /dev/null
+++ b/app/views/projects/merge_requests/widget/open/_reload.html.haml
@@ -0,0 +1 @@
+This merge request cannot be merged. Try to reload the page.
diff --git a/app/views/projects/merge_requests/widget/open/_wip.html.haml b/app/views/projects/merge_requests/widget/open/_wip.html.haml
new file mode 100644
index 00000000000..4ce3ab31278
--- /dev/null
+++ b/app/views/projects/merge_requests/widget/open/_wip.html.haml
@@ -0,0 +1,13 @@
+- if @merge_request.can_be_merged_by?(current_user)
+ %h4
+ This merge request cannot be accepted because it is marked as Work In Progress.
+
+ %p
+ %button.btn.disabled{:type => 'button'}
+ %i.fa.fa-warning
+ Accept Merge Request
+ &nbsp;
+ When the merge request is ready, remove the "WIP" prefix from the title to allow it to be accepted.
+- else
+ %strong This merge request is marked as Work In Progress.
+ Only those with write access to this repository can merge merge requests.
diff --git a/app/views/projects/milestones/_form.html.haml b/app/views/projects/milestones/_form.html.haml
index 95b7070ce5c..5650607f31f 100644
--- a/app/views/projects/milestones/_form.html.haml
+++ b/app/views/projects/milestones/_form.html.haml
@@ -50,5 +50,3 @@
dateFormat: "yy-mm-dd",
onSelect: function(dateText, inst) { $("#milestone_due_date").val(dateText) }
}).datepicker("setDate", $.datepicker.parseDate('yy-mm-dd', $('#milestone_due_date').val()));
-
- window.project_uploads_path = "#{namespace_project_uploads_path @project.namespace, @project}";
diff --git a/app/views/projects/notes/_form.html.haml b/app/views/projects/notes/_form.html.haml
index 2ada6cb6700..f28b3e9b508 100644
--- a/app/views/projects/notes/_form.html.haml
+++ b/app/views/projects/notes/_form.html.haml
@@ -5,7 +5,7 @@
= f.hidden_field :noteable_id
= f.hidden_field :noteable_type
- = render layout: 'projects/md_preview', locals: { preview_class: "note-text" } do
+ = render layout: 'projects/md_preview', locals: { preview_class: "note-text", referenced_users: true } do
= render 'projects/zen', f: f, attr: :note,
classes: 'note_text js-note-text'
@@ -15,10 +15,7 @@
.error-alert
.note-form-actions
- .buttons
+ .buttons.clearfix
= f.submit 'Add Comment', class: "btn comment-btn btn-grouped js-comment-button"
= yield(:note_actions)
%a.btn.grouped.js-close-discussion-note-form Cancel
-
-:javascript
- window.project_uploads_path = "#{namespace_project_uploads_path @project.namespace, @project}";
diff --git a/app/views/projects/project_members/_project_member.html.haml b/app/views/projects/project_members/_project_member.html.haml
index 635e4d70941..860a997cff8 100644
--- a/app/views/projects/project_members/_project_member.html.haml
+++ b/app/views/projects/project_members/_project_member.html.haml
@@ -38,8 +38,9 @@
&nbsp;
- if current_user == user
- = link_to leave_namespace_project_project_members_path(@project.namespace, @project), data: { confirm: "Leave project?"}, method: :delete, class: "btn-xs btn btn-remove", title: 'Leave project' do
- %i.fa.fa-minus.fa-inverse
+ = link_to leave_namespace_project_project_members_path(@project.namespace, @project), data: { confirm: leave_project_message(@project) }, method: :delete, class: "btn-xs btn btn-remove", title: 'Leave project' do
+ = icon("sign-out")
+ Leave
- else
= link_to namespace_project_project_member_path(@project.namespace, @project, member), data: { confirm: remove_from_project_team_message(@project, member) }, method: :delete, remote: true, class: "btn-xs btn btn-remove", title: 'Remove user from team' do
%i.fa.fa-minus.fa-inverse
diff --git a/app/views/projects/project_members/index.html.haml b/app/views/projects/project_members/index.html.haml
index 6edb92acd4d..162583e4b1d 100644
--- a/app/views/projects/project_members/index.html.haml
+++ b/app/views/projects/project_members/index.html.haml
@@ -11,7 +11,7 @@
.clearfix.js-toggle-container
= form_tag namespace_project_project_members_path(@project.namespace, @project), method: :get, class: 'form-inline member-search-form' do
.form-group
- = search_field_tag :search, params[:search], { placeholder: 'Find existing member by name', class: 'form-control search-text-input input-mn-300' }
+ = search_field_tag :search, params[:search], { placeholder: 'Find existing member by name', class: 'form-control search-text-input' }
= button_tag 'Search', class: 'btn'
- if can?(current_user, :admin_project_member, @project)
diff --git a/app/views/projects/update.js.haml b/app/views/projects/update.js.haml
index 4f3f4cab8d5..7d9bd08385a 100644
--- a/app/views/projects/update.js.haml
+++ b/app/views/projects/update.js.haml
@@ -6,4 +6,4 @@
$(".project-edit-errors").html("#{escape_javascript(render('errors'))}");
$('.save-project-loader').hide();
$('.project-edit-container').show();
- $('.project-edit-content .btn-save').enableButton();
+ $('.project-edit-content .btn-save').enable();
diff --git a/app/views/projects/wikis/_form.html.haml b/app/views/projects/wikis/_form.html.haml
index 9fbfa0b1aeb..2a8ceaa2844 100644
--- a/app/views/projects/wikis/_form.html.haml
+++ b/app/views/projects/wikis/_form.html.haml
@@ -41,6 +41,3 @@
- else
= f.submit 'Create page', class: "btn-create btn"
= link_to "Cancel", namespace_project_wiki_path(@project.namespace, @project, :home), class: "btn btn-cancel"
-
-:javascript
- window.project_uploads_path = "#{namespace_project_uploads_path @project.namespace, @project}";
diff --git a/app/views/search/_form.html.haml b/app/views/search/_form.html.haml
index 47016daf1f0..5ee70be1ad6 100644
--- a/app/views/search/_form.html.haml
+++ b/app/views/search/_form.html.haml
@@ -5,7 +5,7 @@
= hidden_field_tag :scope, params[:scope]
.search-holder.clearfix
.form-group
- = search_field_tag :search, params[:search], placeholder: "Search for projects, issues etc", class: "form-control search-text-input input-mn-300", id: "dashboard_search", autofocus: true
+ = search_field_tag :search, params[:search], placeholder: "Search for projects, issues etc", class: "form-control search-text-input", id: "dashboard_search", autofocus: true
= button_tag 'Search', class: "btn btn-primary"
- unless params[:snippets].eql? 'true'
.pull-right
diff --git a/app/views/shared/_file_highlight.html.haml b/app/views/shared/_file_highlight.html.haml
index fba69dd0f3f..86921f0a777 100644
--- a/app/views/shared/_file_highlight.html.haml
+++ b/app/views/shared/_file_highlight.html.haml
@@ -4,7 +4,8 @@
- blob.data.lines.to_a.size.times do |index|
- offset = defined?(first_line_number) ? first_line_number : 1
- i = index + offset
- = link_to "#L#{i}", id: "L#{i}", rel: "#L#{i}" do
+ / We're not using `link_to` because it is too slow once we get to thousands of lines.
+ %a{href: "#L#{i}", id: "L#{i}", rel: "#L#{i}"}
%i.fa.fa-link
= i
:preserve
diff --git a/app/views/shared/_issuable_search_form.html.haml b/app/views/shared/_issuable_search_form.html.haml
index 639d203dcd6..58c3de64b77 100644
--- a/app/views/shared/_issuable_search_form.html.haml
+++ b/app/views/shared/_issuable_search_form.html.haml
@@ -1,6 +1,6 @@
= form_tag(path, method: :get, id: "issue_search_form", class: 'pull-left issue-search-form') do
.append-right-10.hidden-xs.hidden-sm
- = search_field_tag :issue_search, params[:issue_search], { placeholder: 'Filter by title or description', class: 'form-control issue_search search-text-input input-mn-300' }
+ = search_field_tag :issue_search, params[:issue_search], { placeholder: 'Filter by title or description', class: 'form-control issue_search search-text-input' }
= hidden_field_tag :state, params['state']
= hidden_field_tag :scope, params['scope']
= hidden_field_tag :assignee_id, params['assignee_id']
diff --git a/app/views/shared/_project.html.haml b/app/views/shared/_project.html.haml
index 722a7f7ce0f..4537f8eec86 100644
--- a/app/views/shared/_project.html.haml
+++ b/app/views/shared/_project.html.haml
@@ -16,6 +16,3 @@
%span.pull-right.light
%i.fa.fa-star
= project.star_count
- - else
- %span.arrow
- %i.fa.fa-angle-right
diff --git a/app/views/users/show.html.haml b/app/views/users/show.html.haml
index 6ed45fedfa2..1694818aef6 100644
--- a/app/views/users/show.html.haml
+++ b/app/views/users/show.html.haml
@@ -13,7 +13,7 @@
%h3
= @user.name
- if @user == current_user
- .pull-right
+ .pull-right.hidden-xs
= link_to profile_path, class: 'btn btn-sm' do
%i.fa.fa-pencil-square-o
Edit Profile settings
diff --git a/bin/guard b/bin/guard
deleted file mode 100755
index 0c1a532bd01..00000000000
--- a/bin/guard
+++ /dev/null
@@ -1,16 +0,0 @@
-#!/usr/bin/env ruby
-#
-# This file was generated by Bundler.
-#
-# The application 'guard' is installed as part of a gem, and
-# this file is here to facilitate running it.
-#
-
-require 'pathname'
-ENV['BUNDLE_GEMFILE'] ||= File.expand_path("../../Gemfile",
- Pathname.new(__FILE__).realpath)
-
-require 'rubygems'
-require 'bundler/setup'
-
-load Gem.bin_path('guard', 'guard')
diff --git a/config/aws.yml.example b/config/aws.yml.example
index 29d029b078d..bb10c3cec7b 100644
--- a/config/aws.yml.example
+++ b/config/aws.yml.example
@@ -1,5 +1,8 @@
# See https://github.com/jnicklas/carrierwave#using-amazon-s3
# for more options
+# If you change this file in a Merge Request, please also create
+# a Merge Request on https://gitlab.com/gitlab-org/omnibus-gitlab/merge_requests
+#
production:
access_key_id: AKIA1111111111111UA
secret_access_key: secret
diff --git a/config/gitlab.yml.example b/config/gitlab.yml.example
index fbc7f515f34..96b6ed01f42 100644
--- a/config/gitlab.yml.example
+++ b/config/gitlab.yml.example
@@ -4,9 +4,13 @@
#
########################### NOTE #####################################
# This file should not receive new settings. All configuration options #
-# are being moved to ApplicationSetting model! #
+# that do not require application restart are being moved to #
+# ApplicationSetting model! #
+# If you change this file in a Merge Request, please also create #
+# a MR on https://gitlab.com/gitlab-org/omnibus-gitlab/merge_requests #
########################################################################
#
+#
# How to use:
# 1. Copy file as gitlab.yml
# 2. Update gitlab -> host with your fully qualified domain name
@@ -149,7 +153,7 @@ production: &base
allow_username_or_email_login: false
# To maintain tight control over the number of active users on your GitLab installation,
- # enable this setting to keep new users blocked until they have been cleared by the admin
+ # enable this setting to keep new users blocked until they have been cleared by the admin
# (default: false).
block_auto_created_users: false
@@ -182,12 +186,19 @@ production: &base
# Allow login via Twitter, Google, etc. using OmniAuth providers
enabled: false
+ # Uncomment this to automatically sign in with a specific omniauth provider's without
+ # showing GitLab's sign-in page (default: show the GitLab sign-in page)
+ # auto_sign_in_with_provider: saml
+
# CAUTION!
# This allows users to login without having a user account first (default: false).
# User accounts will be created automatically when authentication was successful.
allow_single_sign_on: false
# Locks down those users until they have been cleared by the admin (default: true).
block_auto_created_users: true
+ # Look up new users in LDAP servers. If a match is found (same uid), automatically
+ # link the omniauth identity with the LDAP account. (default: false)
+ auto_link_ldap_user: false
## Auth providers
# Uncomment the following lines and fill in the data of the auth provider you want to use
@@ -210,6 +221,15 @@ production: &base
# args: { scope: 'api' } }
# - { name: 'bitbucket', app_id: 'YOUR_APP_ID',
# app_secret: 'YOUR_APP_SECRET'}
+ # - { name: 'saml',
+ # args: {
+ # assertion_consumer_service_url: 'https://gitlab.example.com/users/auth/saml/callback',
+ # idp_cert_fingerprint: '43:51:43:a1:b5:fc:8b:b7:0a:3a:a9:b1:0f:66:73:a8',
+ # idp_sso_target_url: 'https://login.example.com/idp',
+ # issuer: 'https://gitlab.example.com',
+ # name_identifier_format: 'urn:oasis:names:tc:SAML:2.0:nameid-format:transient'
+ # } }
+
@@ -236,6 +256,9 @@ production: &base
# aws_secret_access_key: 'secret123'
# # The remote 'directory' to store your backups. For S3, this would be the bucket name.
# remote_directory: 'my.s3.bucket'
+ # # Use multipart uploads when file size reaches 100MB, see
+ # # http://docs.aws.amazon.com/AmazonS3/latest/dev/uploadobjusingmpu.html
+ # multipart_chunk_size: 104857600
## GitLab Shell settings
gitlab_shell:
diff --git a/config/initializers/1_settings.rb b/config/initializers/1_settings.rb
index 2351ef7b0ce..9c622b73016 100644
--- a/config/initializers/1_settings.rb
+++ b/config/initializers/1_settings.rb
@@ -87,6 +87,11 @@ end
Settings['omniauth'] ||= Settingslogic.new({})
Settings.omniauth['enabled'] = false if Settings.omniauth['enabled'].nil?
+Settings.omniauth['auto_sign_in_with_provider'] = false if Settings.omniauth['auto_sign_in_with_provider'].nil?
+Settings.omniauth['allow_single_sign_on'] = false if Settings.omniauth['allow_single_sign_on'].nil?
+Settings.omniauth['block_auto_created_users'] = true if Settings.omniauth['block_auto_created_users'].nil?
+Settings.omniauth['auto_link_ldap_user'] = false if Settings.omniauth['auto_link_ldap_user'].nil?
+
Settings.omniauth['providers'] ||= []
Settings['issues_tracker'] ||= {}
@@ -126,6 +131,7 @@ Settings.gitlab['issue_closing_pattern'] = '((?:[Cc]los(?:e[sd]?|ing)|[Ff]ix(?:e
Settings.gitlab['default_projects_features'] ||= {}
Settings.gitlab['webhook_timeout'] ||= 10
Settings.gitlab['max_attachment_size'] ||= 10
+Settings.gitlab['session_expire_delay'] ||= 10080
Settings.gitlab.default_projects_features['issues'] = true if Settings.gitlab.default_projects_features['issues'].nil?
Settings.gitlab.default_projects_features['merge_requests'] = true if Settings.gitlab.default_projects_features['merge_requests'].nil?
Settings.gitlab.default_projects_features['wiki'] = true if Settings.gitlab.default_projects_features['wiki'].nil?
@@ -169,6 +175,7 @@ Settings.backup['upload'] ||= Settingslogic.new({ 'remote_directory' => nil, 'co
if Settings.backup['upload']['connection']
Settings.backup['upload']['connection'] = Hash[Settings.backup['upload']['connection'].map { |k, v| [k.to_sym, v] }]
end
+Settings.backup['upload']['multipart_chunk_size'] ||= 104857600
#
# Git
diff --git a/config/initializers/6_rack_profiler.rb b/config/initializers/6_rack_profiler.rb
index 38a5fa98dc2..5312fd8e89a 100644
--- a/config/initializers/6_rack_profiler.rb
+++ b/config/initializers/6_rack_profiler.rb
@@ -3,7 +3,8 @@ if Rails.env.development?
# initialization is skipped so trigger it
Rack::MiniProfilerRails.initialize!(Rails.application)
+
Rack::MiniProfiler.config.position = 'right'
Rack::MiniProfiler.config.start_hidden = true
- Rack::MiniProfiler.config.skip_paths << '/specs'
+ Rack::MiniProfiler.config.skip_paths << '/teaspoon'
end
diff --git a/config/initializers/7_omniauth.rb b/config/initializers/7_omniauth.rb
index 103aa06ca32..6f1f267bf97 100644
--- a/config/initializers/7_omniauth.rb
+++ b/config/initializers/7_omniauth.rb
@@ -12,6 +12,8 @@ if Gitlab::LDAP::Config.enabled?
end
OmniAuth.config.allowed_request_methods = [:post]
+#In case of auto sign-in, the GET method is used (users don't get to click on a button)
+OmniAuth.config.allowed_request_methods << :get if Gitlab.config.omniauth.auto_sign_in_with_provider.present?
OmniAuth.config.before_request_phase do |env|
OmniAuth::RequestForgeryProtection.new(env).call
end
diff --git a/config/initializers/rack_attack.rb.example b/config/initializers/rack_attack.rb.example
index 332865d2881..b1bbcca1d61 100644
--- a/config/initializers/rack_attack.rb.example
+++ b/config/initializers/rack_attack.rb.example
@@ -1,6 +1,7 @@
# 1. Rename this file to rack_attack.rb
# 2. Review the paths_to_be_protected and add any other path you need protecting
#
+# If you change this file in a Merge Request, please also create a Merge Request on https://gitlab.com/gitlab-org/omnibus-gitlab/merge_requests
paths_to_be_protected = [
"#{Rails.application.config.relative_url_root}/users/password",
diff --git a/config/initializers/session_store.rb b/config/initializers/session_store.rb
index b2d59f1c4b7..6d274cd95a1 100644
--- a/config/initializers/session_store.rb
+++ b/config/initializers/session_store.rb
@@ -1,11 +1,15 @@
# Be sure to restart your server when you modify this file.
+require 'gitlab/current_settings'
+include Gitlab::CurrentSettings
+Settings.gitlab['session_expire_delay'] = current_application_settings.session_expire_delay
+
Gitlab::Application.config.session_store(
:redis_store, # Using the cookie_store would enable session replay attacks.
servers: Gitlab::Application.config.cache_store[1].merge(namespace: 'session:gitlab'), # re-use the Redis config from the Rails cache store
key: '_gitlab_session',
secure: Gitlab.config.gitlab.https,
httponly: true,
- expire_after: 1.week,
+ expire_after: Settings.gitlab['session_expire_delay'] * 60,
path: (Rails.application.config.relative_url_root.nil?) ? '/' : Rails.application.config.relative_url_root
)
diff --git a/config/initializers/smtp_settings.rb.sample b/config/initializers/smtp_settings.rb.sample
index f0fe2fdfa43..25ec247a095 100644
--- a/config/initializers/smtp_settings.rb.sample
+++ b/config/initializers/smtp_settings.rb.sample
@@ -5,6 +5,7 @@
#
# For full list of options and their values see http://api.rubyonrails.org/classes/ActionMailer/Base.html
#
+# If you change this file in a Merge Request, please also create a Merge Request on https://gitlab.com/gitlab-org/omnibus-gitlab/merge_requests
if Rails.env.production?
Gitlab::Application.config.action_mailer.delivery_method = :smtp
diff --git a/config/resque.yml.example b/config/resque.yml.example
index 347f3599b20..d98f43f71b2 100644
--- a/config/resque.yml.example
+++ b/config/resque.yml.example
@@ -1,3 +1,6 @@
+# If you change this file in a Merge Request, please also create
+# a Merge Request on https://gitlab.com/gitlab-org/omnibus-gitlab/merge_requests
+#
development: redis://localhost:6379
test: redis://localhost:6379
production: unix:/var/run/redis/redis.sock
diff --git a/config/routes.rb b/config/routes.rb
index bf2cb6421c5..f4a104664f3 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -2,7 +2,6 @@ require 'sidekiq/web'
require 'api/api'
Gitlab::Application.routes.draw do
- mount JasmineRails::Engine => '/specs' if defined?(JasmineRails)
use_doorkeeper do
controllers applications: 'oauth/applications',
authorized_applications: 'oauth/authorized_applications',
@@ -166,7 +165,7 @@ Gitlab::Application.routes.draw do
end
end
- resources :deploy_keys, only: [:index, :show, :new, :create, :destroy]
+ resources :deploy_keys, only: [:index, :new, :create, :destroy]
resources :hooks, only: [:index, :create, :destroy] do
get :test
@@ -422,7 +421,7 @@ Gitlab::Application.routes.draw do
end
end
- resources :deploy_keys, constraints: { id: /\d+/ }, only: [:index, :show, :new, :create] do
+ resources :deploy_keys, constraints: { id: /\d+/ }, only: [:index, :new, :create] do
member do
put :enable
put :disable
@@ -450,6 +449,7 @@ Gitlab::Application.routes.draw do
resources :merge_requests, constraints: { id: /\d+/ }, except: [:destroy] do
member do
get :diffs
+ get :commits
post :automerge
get :automerge_check
get :ci_status
diff --git a/config/unicorn.rb.example b/config/unicorn.rb.example
index 86a5512e761..b937b092789 100644
--- a/config/unicorn.rb.example
+++ b/config/unicorn.rb.example
@@ -8,6 +8,9 @@
# See http://unicorn.bogomips.org/Unicorn/Configurator.html for complete
# documentation.
+# Note: If you change this file in a Merge Request, please also create a
+# Merge Request on https://gitlab.com/gitlab-org/omnibus-gitlab/merge_requests
+#
# WARNING: See config/application.rb under "Relative url support" for the list of
# other files that need to be changed for relative url support
#
diff --git a/db/migrate/20150529111607_add_user_oauth_applications_to_application_settings.rb b/db/migrate/20150529111607_add_user_oauth_applications_to_application_settings.rb
new file mode 100644
index 00000000000..6a78294f0b2
--- /dev/null
+++ b/db/migrate/20150529111607_add_user_oauth_applications_to_application_settings.rb
@@ -0,0 +1,5 @@
+class AddUserOauthApplicationsToApplicationSettings < ActiveRecord::Migration
+ def change
+ add_column :application_settings, :user_oauth_applications, :bool, default: true
+ end
+end
diff --git a/db/migrate/20150529150354_add_after_sign_out_path_for_application_settings.rb b/db/migrate/20150529150354_add_after_sign_out_path_for_application_settings.rb
new file mode 100644
index 00000000000..83e08101407
--- /dev/null
+++ b/db/migrate/20150529150354_add_after_sign_out_path_for_application_settings.rb
@@ -0,0 +1,5 @@
+class AddAfterSignOutPathForApplicationSettings < ActiveRecord::Migration
+ def change
+ add_column :application_settings, :after_sign_out_path, :string
+ end
+end \ No newline at end of file
diff --git a/db/migrate/20150609141121_add_session_expire_delay_for_application_settings.rb b/db/migrate/20150609141121_add_session_expire_delay_for_application_settings.rb
new file mode 100644
index 00000000000..ffa22e6d5ef
--- /dev/null
+++ b/db/migrate/20150609141121_add_session_expire_delay_for_application_settings.rb
@@ -0,0 +1,5 @@
+class AddSessionExpireDelayForApplicationSettings < ActiveRecord::Migration
+ def change
+ add_column :application_settings, :session_expire_delay, :integer, default: 10080, null: false
+ end
+end \ No newline at end of file
diff --git a/db/schema.rb b/db/schema.rb
index 1ab91256406..9a9d4a85e4b 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -11,7 +11,7 @@
#
# It's strongly recommended that you check this file into your version control system.
-ActiveRecord::Schema.define(version: 20150516060434) do
+ActiveRecord::Schema.define(version: 20150609141121) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
@@ -29,10 +29,13 @@ ActiveRecord::Schema.define(version: 20150516060434) do
t.boolean "twitter_sharing_enabled", default: true
t.text "restricted_visibility_levels"
t.boolean "version_check_enabled", default: true
- t.integer "max_attachment_size", default: 10, null: false
+ t.integer "max_attachment_size", default: 10, null: false
t.integer "default_project_visibility"
t.integer "default_snippet_visibility"
t.text "restricted_signup_domains"
+ t.boolean "user_oauth_applications", default: true
+ t.string "after_sign_out_path"
+ t.integer "session_expire_delay", default: 10080, null: false
end
create_table "broadcast_messages", force: true do |t|
@@ -493,12 +496,12 @@ ActiveRecord::Schema.define(version: 20150516060434) do
t.string "bitbucket_access_token"
t.string "bitbucket_access_token_secret"
t.string "location"
- t.string "public_email", default: "", null: false
t.string "encrypted_otp_secret"
t.string "encrypted_otp_secret_iv"
t.string "encrypted_otp_secret_salt"
t.boolean "otp_required_for_login"
t.text "otp_backup_codes"
+ t.string "public_email", default: "", null: false
end
add_index "users", ["admin"], name: "index_users_on_admin", using: :btree
diff --git a/doc/README.md b/doc/README.md
index 4e00dceac2b..7a2181edded 100644
--- a/doc/README.md
+++ b/doc/README.md
@@ -1,34 +1,35 @@
-# Documentation
-
-## User documentation
-
-- [API](api/README.md) Automate GitLab via a simple and powerful API.
-- [Markdown](markdown/markdown.md) GitLab's advanced formatting system.
-- [Permissions](permissions/permissions.md) Learn what each role in a project (guest/reporter/developer/master/owner) can do.
-- [Project Services](project_services/project_services.md) Integrate a project with external services, such as CI and chat.
-- [Public access](public_access/public_access.md) Learn how you can allow public and internal access to projects.
-- [SSH](ssh/README.md) Setup your ssh keys and deploy keys for secure access to your projects.
-- [Web hooks](web_hooks/web_hooks.md) Let GitLab notify you when new code has been pushed to your project.
-- [Workflow](workflow/README.md) Using GitLab functionality and importing projects from GitHub and SVN.
-- [GitLab as OAuth2 authentication service provider](integration/oauth_provider.md). It allows you to login to other applications from GitLab.
-
-## Administrator documentation
-
-- [Install](install/README.md) Requirements, directory structures and installation from source.
-- [Integration](integration/README.md) How to integrate with systems such as JIRA, Redmine, LDAP and Twitter.
-- [Raketasks](raketasks/README.md) Backups, maintenance, automatic web hook setup and the importing of projects.
-- [Custom git hooks](hooks/custom_hooks.md) Custom git hooks (on the filesystem) for when web hooks aren't enough.
-- [System hooks](system_hooks/system_hooks.md) Notifications when users, projects and keys are changed.
-- [Security](security/README.md) Learn what you can do to further secure your GitLab instance.
-- [Update](update/README.md) Update guides to upgrade your installation.
-- [Welcome message](customization/welcome_message.md) Add a custom welcome message to the sign-in page.
-- [Issue closing](customization/issue_closing.md) Customize how to close an issue from commit messages.
-- [Libravatar](customization/libravatar.md) Use Libravatar for user avatars.
-- [Operations](operations/README.md) Keeping GitLab up and running
-- [Log system](logs/logs.md) Log system
-
-## Contributor documentation
-
-- [Development](development/README.md) Explains the architecture and the guidelines for shell commands.
-- [Legal](legal/README.md) Contributor license agreements.
-- [Release](release/README.md) How to make the monthly and security releases.
+# Documentation
+
+## User documentation
+
+- [API](api/README.md) Automate GitLab via a simple and powerful API.
+- [GitLab as OAuth2 authentication service provider](integration/oauth_provider.md). It allows you to login to other applications from GitLab.
+- [Importing to GitLab](workflow/importing/README.md).
+- [Markdown](markdown/markdown.md) GitLab's advanced formatting system.
+- [Permissions](permissions/permissions.md) Learn what each role in a project (guest/reporter/developer/master/owner) can do.
+- [Project Services](project_services/project_services.md) Integrate a project with external services, such as CI and chat.
+- [Public access](public_access/public_access.md) Learn how you can allow public and internal access to projects.
+- [SSH](ssh/README.md) Setup your ssh keys and deploy keys for secure access to your projects.
+- [Web hooks](web_hooks/web_hooks.md) Let GitLab notify you when new code has been pushed to your project.
+- [Workflow](workflow/README.md) Using GitLab functionality and importing projects from GitHub and SVN.
+
+## Administrator documentation
+
+- [Custom git hooks](hooks/custom_hooks.md) Custom git hooks (on the filesystem) for when web hooks aren't enough.
+- [Install](install/README.md) Requirements, directory structures and installation from source.
+- [Integration](integration/README.md) How to integrate with systems such as JIRA, Redmine, LDAP and Twitter.
+- [Issue closing](customization/issue_closing.md) Customize how to close an issue from commit messages.
+- [Libravatar](customization/libravatar.md) Use Libravatar for user avatars.
+- [Log system](logs/logs.md) Log system.
+- [Operations](operations/README.md) Keeping GitLab up and running
+- [Raketasks](raketasks/README.md) Backups, maintenance, automatic web hook setup and the importing of projects.
+- [Security](security/README.md) Learn what you can do to further secure your GitLab instance.
+- [System hooks](system_hooks/system_hooks.md) Notifications when users, projects and keys are changed.
+- [Update](update/README.md) Update guides to upgrade your installation.
+- [Welcome message](customization/welcome_message.md) Add a custom welcome message to the sign-in page.
+
+## Contributor documentation
+
+- [Development](development/README.md) Explains the architecture and the guidelines for shell commands.
+- [Legal](legal/README.md) Contributor license agreements.
+- [Release](release/README.md) How to make the monthly and security releases. \ No newline at end of file
diff --git a/doc/api/README.md b/doc/api/README.md
index f6757b0a6aa..ca58c184543 100644
--- a/doc/api/README.md
+++ b/doc/api/README.md
@@ -19,6 +19,7 @@
- [Deploy Keys](deploy_keys.md)
- [System Hooks](system_hooks.md)
- [Groups](groups.md)
+- [Namespaces](namespaces.md)
## Clients
diff --git a/doc/api/groups.md b/doc/api/groups.md
index c903a850fdd..0b9f6406d8d 100644
--- a/doc/api/groups.md
+++ b/doc/api/groups.md
@@ -1,178 +1,192 @@
-# Groups
-
-## List project groups
-
-Get a list of groups. (As user: my groups, as admin: all groups)
-
-```
-GET /groups
-```
-
-```json
-[
- {
- "id": 1,
- "name": "Foobar Group",
- "path": "foo-bar",
- "description": "An interesting group"
- }
-]
-```
-
-You can search for groups by name or path, see below.
-
-## Details of a group
-
-Get all details of a group.
-
-```
-GET /groups/:id
-```
-
-Parameters:
-
-- `id` (required) - The ID or path of a group
-
-## New group
-
-Creates a new project group. Available only for users who can create groups.
-
-```
-POST /groups
-```
-
-Parameters:
-
-- `name` (required) - The name of the group
-- `path` (required) - The path of the group
-- `description` (optional) - The group's description
-
-## Transfer project to group
-
-Transfer a project to the Group namespace. Available only for admin
-
-```
-POST /groups/:id/projects/:project_id
-```
-
-Parameters:
-
-- `id` (required) - The ID or path of a group
-- `project_id` (required) - The ID of a project
-
-## Remove group
-
-Removes group with all projects inside.
-
-```
-DELETE /groups/:id
-```
-
-Parameters:
-
-- `id` (required) - The ID or path of a user group
-
-## Search for group
-
-Get all groups that match your string in their name or path.
-
-```
-GET /groups?search=foobar
-```
-
-```json
-[
- {
- "id": 1,
- "name": "Foobar Group",
- "path": "foo-bar",
- "description": "An interesting group"
- }
-]
-```
-
-## Group members
-
-**Group access levels**
-
-The group access levels are defined in the `Gitlab::Access` module. Currently, these levels are recognized:
-
-```
-GUEST = 10
-REPORTER = 20
-DEVELOPER = 30
-MASTER = 40
-OWNER = 50
-```
-
-### List group members
-
-Get a list of group members viewable by the authenticated user.
-
-```
-GET /groups/:id/members
-```
-
-```json
-[
- {
- "id": 1,
- "username": "raymond_smith",
- "email": "ray@smith.org",
- "name": "Raymond Smith",
- "state": "active",
- "created_at": "2012-10-22T14:13:35Z",
- "access_level": 30
- },
- {
- "id": 2,
- "username": "john_doe",
- "email": "joh@doe.org",
- "name": "John Doe",
- "state": "active",
- "created_at": "2012-10-22T14:13:35Z",
- "access_level": 30
- }
-]
-```
-
-### Add group member
-
-Adds a user to the list of group members.
-
-```
-POST /groups/:id/members
-```
-
-Parameters:
-
-- `id` (required) - The ID or path of a group
-- `user_id` (required) - The ID of a user to add
-- `access_level` (required) - Project access level
-
-### Edit group team member
-
-Updates a group team member to a specified access level.
-
-```
-PUT /groups/:id/members/:user_id
-```
-
-Parameters:
-
-- `id` (required) - The ID of a group
-- `user_id` (required) - The ID of a group member
-- `access_level` (required) - Project access level
-
-### Remove user team member
-
-Removes user from user team.
-
-```
-DELETE /groups/:id/members/:user_id
-```
-
-Parameters:
-
-- `id` (required) - The ID or path of a user group
-- `user_id` (required) - The ID of a group member
+# Groups
+
+## List project groups
+
+Get a list of groups. (As user: my groups, as admin: all groups)
+
+```
+GET /groups
+```
+
+```json
+[
+ {
+ "id": 1,
+ "name": "Foobar Group",
+ "path": "foo-bar",
+ "description": "An interesting group"
+ }
+]
+```
+
+You can search for groups by name or path, see below.
+
+## Details of a group
+
+Get all details of a group.
+
+```
+GET /groups/:id
+```
+
+Parameters:
+
+- `id` (required) - The ID or path of a group
+
+## New group
+
+Creates a new project group. Available only for users who can create groups.
+
+```
+POST /groups
+```
+
+Parameters:
+
+- `name` (required) - The name of the group
+- `path` (required) - The path of the group
+- `description` (optional) - The group's description
+
+## Transfer project to group
+
+Transfer a project to the Group namespace. Available only for admin
+
+```
+POST /groups/:id/projects/:project_id
+```
+
+Parameters:
+
+- `id` (required) - The ID or path of a group
+- `project_id` (required) - The ID of a project
+
+## Remove group
+
+Removes group with all projects inside.
+
+```
+DELETE /groups/:id
+```
+
+Parameters:
+
+- `id` (required) - The ID or path of a user group
+
+## Search for group
+
+Get all groups that match your string in their name or path.
+
+```
+GET /groups?search=foobar
+```
+
+```json
+[
+ {
+ "id": 1,
+ "name": "Foobar Group",
+ "path": "foo-bar",
+ "description": "An interesting group"
+ }
+]
+```
+
+## Group members
+
+**Group access levels**
+
+The group access levels are defined in the `Gitlab::Access` module. Currently, these levels are recognized:
+
+```
+GUEST = 10
+REPORTER = 20
+DEVELOPER = 30
+MASTER = 40
+OWNER = 50
+```
+
+### List group members
+
+Get a list of group members viewable by the authenticated user.
+
+```
+GET /groups/:id/members
+```
+
+```json
+[
+ {
+ "id": 1,
+ "username": "raymond_smith",
+ "email": "ray@smith.org",
+ "name": "Raymond Smith",
+ "state": "active",
+ "created_at": "2012-10-22T14:13:35Z",
+ "access_level": 30
+ },
+ {
+ "id": 2,
+ "username": "john_doe",
+ "email": "joh@doe.org",
+ "name": "John Doe",
+ "state": "active",
+ "created_at": "2012-10-22T14:13:35Z",
+ "access_level": 30
+ }
+]
+```
+
+### Add group member
+
+Adds a user to the list of group members.
+
+```
+POST /groups/:id/members
+```
+
+Parameters:
+
+- `id` (required) - The ID or path of a group
+- `user_id` (required) - The ID of a user to add
+- `access_level` (required) - Project access level
+
+### Edit group team member
+
+Updates a group team member to a specified access level.
+
+```
+PUT /groups/:id/members/:user_id
+```
+
+Parameters:
+
+- `id` (required) - The ID of a group
+- `user_id` (required) - The ID of a group member
+- `access_level` (required) - Project access level
+
+### Remove user team member
+
+Removes user from user team.
+
+```
+DELETE /groups/:id/members/:user_id
+```
+
+Parameters:
+
+- `id` (required) - The ID or path of a user group
+- `user_id` (required) - The ID of a group member
+
+## Namespaces in groups
+
+By default, groups only get 20 namespaces at a time because the API results are paginated.
+
+To get more (up to 100), pass the following as an argument to the API call:
+```
+/groups?per_page=100
+```
+
+And to switch pages add:
+```
+/groups?per_page=100&page=2
+``` \ No newline at end of file
diff --git a/doc/api/merge_requests.md b/doc/api/merge_requests.md
index c1d82ad9576..7b0873a9111 100644
--- a/doc/api/merge_requests.md
+++ b/doc/api/merge_requests.md
@@ -221,7 +221,7 @@ If an error occurs, an error number and a message explaining the reason is retur
## Update MR
-Updates an existing merge request. You can change branches, title, or even close the MR.
+Updates an existing merge request. You can change the target branch, title, or even close the MR.
```
PUT /projects/:id/merge_request/:merge_request_id
@@ -231,7 +231,6 @@ Parameters:
- `id` (required) - The ID of a project
- `merge_request_id` (required) - ID of MR
-- `source_branch` - The source branch
- `target_branch` - The target branch
- `assignee_id` - Assignee user ID
- `title` - Title of MR
@@ -242,7 +241,6 @@ Parameters:
{
"id": 1,
"target_branch": "master",
- "source_branch": "test1",
"project_id": 3,
"title": "test1",
"description": "description1",
diff --git a/doc/api/namespaces.md b/doc/api/namespaces.md
new file mode 100644
index 00000000000..7b3238441f6
--- /dev/null
+++ b/doc/api/namespaces.md
@@ -0,0 +1,44 @@
+# Namespaces
+
+## List namespaces
+
+Get a list of namespaces. (As user: my namespaces, as admin: all namespaces)
+
+```
+GET /namespaces
+```
+
+```json
+[
+ {
+ "id": 1,
+ "path": "user1",
+ "kind": "user"
+ },
+ {
+ "id": 2,
+ "path": "group1",
+ "kind": "group"
+ }
+]
+```
+
+You can search for namespaces by name or path, see below.
+
+## Search for namespace
+
+Get all namespaces that match your string in their name or path.
+
+```
+GET /namespaces?search=foobar
+```
+
+```json
+[
+ {
+ "id": 1,
+ "path": "user1",
+ "kind": "user"
+ }
+]
+```
diff --git a/doc/development/shell_commands.md b/doc/development/shell_commands.md
index 821027f43fa..2d1d0fb4154 100644
--- a/doc/development/shell_commands.md
+++ b/doc/development/shell_commands.md
@@ -177,3 +177,33 @@ File.open(full_path) do # Etc.
```
A check like this could have avoided CVE-2013-4583.
+
+## Properly anchor regular expressions to the start and end of strings
+
+When using regular expressions to validate user input that is passed as an argument to a shell command, make sure to use the `\A` and `\z` anchors that designate the start and end of the string, rather than `^` and `$`, or no anchors at all.
+
+If you don't, an attacker could use this to execute commands with potentially harmful effect.
+
+For example, when a project's `import_url` is validated like below, the user could trick GitLab into cloning from a Git repository on the local filesystem.
+
+```ruby
+validates :import_url, format: { with: URI.regexp(%w(ssh git http https)) }
+# URI.regexp(%w(ssh git http https)) roughly evaluates to /(ssh|git|http|https):(something_that_looks_like_a_url)/
+```
+
+Suppose the user submits the following as their import URL:
+
+```
+file://git:/tmp/lol
+```
+
+Since there are no anchors in the used regular expression, the `git:/tmp/lol` in the value would match, and the validation would pass.
+
+When importing, GitLab would execute the following command, passing the `import_url` as an argument:
+
+
+```sh
+git clone file://git:/tmp/lol
+```
+
+Git would simply ignore the `git:` part, interpret the path as `file:///tmp/lol` and import the repository into the new project, in turn potentially giving the attacker access to any repository in the system, whether private or not.
diff --git a/doc/install/installation.md b/doc/install/installation.md
index 1db2b438292..ff0361c5e52 100644
--- a/doc/install/installation.md
+++ b/doc/install/installation.md
@@ -88,8 +88,8 @@ Is the system packaged Git too old? Remove it and compile from source.
# Download and compile from source
cd /tmp
- curl -L --progress https://www.kernel.org/pub/software/scm/git/git-2.1.2.tar.gz | tar xz
- cd git-2.1.2/
+ curl -L --progress https://www.kernel.org/pub/software/scm/git/git-2.4.3.tar.gz | tar xz
+ cd git-2.4.3/
./configure
make prefix=/usr/local all
@@ -195,9 +195,9 @@ We recommend using a PostgreSQL database. For MySQL check [MySQL setup guide](da
### Clone the Source
# Clone GitLab repository
- sudo -u git -H git clone https://gitlab.com/gitlab-org/gitlab-ce.git -b 7-11-stable gitlab
+ sudo -u git -H git clone https://gitlab.com/gitlab-org/gitlab-ce.git -b 7-12-stable gitlab
-**Note:** You can change `7-11-stable` to `master` if you want the *bleeding edge* version, but never install master on a production server!
+**Note:** You can change `7-12-stable` to `master` if you want the *bleeding edge* version, but never install master on a production server!
### Configure It
@@ -241,10 +241,7 @@ We recommend using a PostgreSQL database. For MySQL check [MySQL setup guide](da
# Copy the example Rack attack config
sudo -u git -H cp config/initializers/rack_attack.rb.example config/initializers/rack_attack.rb
- # Configure Git global settings for git user, useful when editing via web
- # Edit user.email according to what is set in gitlab.yml
- sudo -u git -H git config --global user.name "GitLab"
- sudo -u git -H git config --global user.email "example@example.com"
+ # Configure Git global settings for git user, used when editing via web editor
sudo -u git -H git config --global core.autocrlf input
# Configure Redis connection settings
diff --git a/doc/integration/bitbucket.md b/doc/integration/bitbucket.md
index d82e1f8b41b..6a0fa4ce015 100644
--- a/doc/integration/bitbucket.md
+++ b/doc/integration/bitbucket.md
@@ -68,6 +68,8 @@ Bitbucket will generate an application ID and secret key for you to use.
1. Save the configuration file.
+1. If you're using the omnibus package, reconfigure GitLab (```gitlab-ctl reconfigure```).
+
1. Restart GitLab for the changes to take effect.
On the sign in page there should now be a Bitbucket icon below the regular sign in form.
@@ -80,43 +82,59 @@ To allow projects to be imported directly into GitLab, Bitbucket requires two ex
Bitbucket doesn't allow OAuth applications to clone repositories over HTTPS, and instead requires GitLab to use SSH and identify itself using your GitLab server's SSH key.
-### Step 1: Known hosts
+### Step 1: Public key
-To allow GitLab to connect to Bitbucket over SSH, you need to add 'bitbucket.org' to your GitLab server's known SSH hosts. Take the following steps to do so:
+To be able to access repositories on Bitbucket, GitLab will automatically register your public key with Bitbucket as a deploy key for the repositories to be imported. Your public key needs to be at `~/.ssh/bitbucket_rsa.pub`, which will expand to `/home/git/.ssh/bitbucket_rsa.pub` in most configurations.
-1. Manually connect to 'bitbucket.org' over SSH, while logged in as the `git` account that GitLab will use:
+If you have that file in place, you're all set and should see the "Import projects from Bitbucket" option enabled. If you don't, do the following:
+
+1. Create a new SSH key:
```sh
- ssh git@bitbucket.org
+ sudo -u git -H ssh-keygen
```
-1. Verify the RSA key fingerprint you'll see in the response matches the one in the [Bitbucket documentation](https://confluence.atlassian.com/display/BITBUCKET/Use+the+SSH+protocol+with+Bitbucket#UsetheSSHprotocolwithBitbucket-KnownhostorBitbucket'spublickeyfingerprints) (the specific IP address doesn't matter):
+ When asked `Enter file in which to save the key` specify the correct path, eg. `/home/git/.ssh/bitbucket_rsa`.
+ Make sure to use an **empty passphrase**.
+
+1. Configure SSH client to use your new key:
+
+ Open the SSH configuration file of the git user.
```sh
- The authenticity of host 'bitbucket.org (207.223.240.182)' can't be established.
- RSA key fingerprint is 97:8c:1b:f2:6f:14:6b:5c:3b:ec:aa:46:46:74:7c:40.
- Are you sure you want to continue connecting (yes/no)?
+ sudo editor /home/git/.ssh/config
```
-1. If the fingerprint matches, type `yes` to continue connecting and have 'bitbucket.org' be added to your known hosts.
+ Add a host configuration for `bitbucket.org`.
+
+ ```sh
+ Host bitbucket.org
+ IdentityFile ~/.ssh/bitbucket_rsa
+ User git
+ ```
-1. Your GitLab server is now able to connect to Bitbucket over SSH. Continue to step 2:
+### Step 2: Known hosts
-### Step 2: Public key
+To allow GitLab to connect to Bitbucket over SSH, you need to add 'bitbucket.org' to your GitLab server's known SSH hosts. Take the following steps to do so:
-To be able to access repositories on Bitbucket, GitLab will automatically register your public key with Bitbucket as a deploy key for the repositories to be imported. Your public key needs to be at `~/.ssh/bitbucket_rsa.pub`, which will expand to `/home/git/.ssh/bitbucket_rsa.pub` in most configurations.
+1. Manually connect to 'bitbucket.org' over SSH, while logged in as the `git` account that GitLab will use:
-If you have that file in place, you're all set and should see the "Import projects from Bitbucket" option enabled. If you don't, do the following:
+ ```sh
+ sudo -u git -H ssh bitbucket.org
+ ```
-1. Create a new SSH key:
+1. Verify the RSA key fingerprint you'll see in the response matches the one in the [Bitbucket documentation](https://confluence.atlassian.com/display/BITBUCKET/Use+the+SSH+protocol+with+Bitbucket#UsetheSSHprotocolwithBitbucket-KnownhostorBitbucket'spublickeyfingerprints) (the specific IP address doesn't matter):
```sh
- sudo -u git -H ssh-keygen
+ The authenticity of host 'bitbucket.org (207.223.240.182)' can't be established.
+ RSA key fingerprint is 97:8c:1b:f2:6f:14:6b:5c:3b:ec:aa:46:46:74:7c:40.
+ Are you sure you want to continue connecting (yes/no)?
```
- When asked `Enter file in which to save the key` specify the correct path, eg. `/home/git/.ssh/bitbucket_rsa`.
- Make sure to use an **empty passphrase**.
+1. If the fingerprint matches, type `yes` to continue connecting and have 'bitbucket.org' be added to your known hosts.
+
+1. Your GitLab server is now able to connect to Bitbucket over SSH.
-2. Restart GitLab to allow it to find the new public key.
+1. Restart GitLab to allow it to find the new public key.
You should now see the "Import projects from Bitbucket" option on the New Project page enabled.
diff --git a/doc/integration/ldap.md b/doc/integration/ldap.md
index b67f793c591..904d5d7fee2 100644
--- a/doc/integration/ldap.md
+++ b/doc/integration/ldap.md
@@ -6,6 +6,13 @@ The first time a user signs in with LDAP credentials, GitLab will create a new G
GitLab user attributes such as nickname and email will be copied from the LDAP user entry.
+## Security
+
+GitLab assumes that LDAP users are not able to change their LDAP 'mail', 'email' or 'userPrincipalName' attribute.
+An LDAP user who is allowed to change their email on the LDAP server can [take over any account](#enabling-ldap-sign-in-for-existing-gitlab-users) on your GitLab server.
+
+We recommend against using GitLab LDAP integration if your LDAP users are allowed to change their 'mail', 'email' or 'userPrincipalName' attribute on the LDAP server.
+
## Configuring GitLab for LDAP integration
To enable GitLab LDAP integration you need to add your LDAP server settings in `/etc/gitlab/gitlab.rb` or `/home/git/gitlab/config/gitlab.yml`.
diff --git a/doc/integration/omniauth.md b/doc/integration/omniauth.md
index 24f7b4bb4b4..8e2a602ec35 100644
--- a/doc/integration/omniauth.md
+++ b/doc/integration/omniauth.md
@@ -75,6 +75,7 @@ Now we can choose one or more of the Supported Providers below to continue confi
- [Google](google.md)
- [Shibboleth](shibboleth.md)
- [Twitter](twitter.md)
+- [SAML](saml.md)
## Enable OmniAuth for an Existing User
diff --git a/doc/integration/saml.md b/doc/integration/saml.md
new file mode 100644
index 00000000000..a8cc5c8f74a
--- /dev/null
+++ b/doc/integration/saml.md
@@ -0,0 +1,77 @@
+# SAML OmniAuth Provider
+
+GitLab can be configured to act as a SAML 2.0 Service Provider (SP). This allows GitLab to consume assertions from a SAML 2.0 Identity Provider (IdP) such as Microsoft ADFS to authenticate users.
+
+First configure SAML 2.0 support in GitLab, then register the GitLab application in your SAML IdP:
+
+1. Make sure GitLab is configured with HTTPS. See [Using HTTPS](../install/installation.md#using-https) for instructions.
+
+1. On your GitLab server, open the configuration file.
+
+ For omnibus package:
+
+ ```sh
+ sudo editor /etc/gitlab/gitlab.rb
+ ```
+
+ For instalations from source:
+
+ ```sh
+ cd /home/git/gitlab
+
+ sudo -u git -H editor config/gitlab.yml
+ ```
+
+1. See [Initial OmniAuth Configuration](omniauth.md#initial-omniauth-configuration) for initial settings.
+
+1. Add the provider configuration:
+
+ For omnibus package:
+
+ ```ruby
+ gitlab_rails['omniauth_providers'] = [
+ {
+ "name" => "saml",
+ args: {
+ assertion_consumer_service_url: 'https://gitlab.example.com/users/auth/saml/callback',
+ idp_cert_fingerprint: '43:51:43:a1:b5:fc:8b:b7:0a:3a:a9:b1:0f:66:73:a8',
+ idp_sso_target_url: 'https://login.example.com/idp',
+ issuer: 'https://gitlab.example.com',
+ name_identifier_format: 'urn:oasis:names:tc:SAML:2.0:nameid-format:transient'
+ }
+ }
+ ]
+ ```
+
+ For installations from source:
+
+ ```yaml
+ - { name: 'saml',
+ args: {
+ assertion_consumer_service_url: 'https://gitlab.example.com/users/auth/saml/callback',
+ idp_cert_fingerprint: '43:51:43:a1:b5:fc:8b:b7:0a:3a:a9:b1:0f:66:73:a8',
+ idp_sso_target_url: 'https://login.example.com/idp',
+ issuer: 'https://gitlab.example.com',
+ name_identifier_format: 'urn:oasis:names:tc:SAML:2.0:nameid-format:transient'
+ } }
+ ```
+
+1. Change the value for 'assertion_consumer_service_url' to match the HTTPS endpoint of GitLab (append 'users/auth/saml/callback' to the HTTPS URL of your GitLab installation to generate the correct value).
+
+1. Change the values of 'idp_cert_fingerprint', 'idp_sso_target_url', 'name_identifier_format' to match your IdP. Check [the omniauth-saml documentation](https://github.com/PracticallyGreen/omniauth-saml) for details on these options.
+
+1. Change the value of 'issuer' to a unique name, which will identify the application to the IdP.
+
+1. Restart GitLab for the changes to take effect.
+
+1. Register the GitLab SP in your SAML 2.0 IdP, using the application name specified in 'issuer'.
+
+To ease configuration, most IdP accept a metadata URL for the application to provide configuration information to the IdP. To build the metadata URL for GitLab, append 'users/auth/saml/metadata' to the HTTPS URL of your GitLab installation, for instance:
+ ```
+ https://gitlab.example.com/users/auth/saml/metadata
+ ```
+
+At a minimum the IdP *must* provide a claim containing the user's email address, using claim name 'email' or 'mail'. The email will be used to automatically generate the GitLab username. GitLab will also use claims with name 'name', 'first_name', 'last_name' (see [the omniauth-saml gem](https://github.com/PracticallyGreen/omniauth-saml/blob/master/lib/omniauth/strategies/saml.rb) for supported claims).
+
+On the sign in page there should now be a SAML button below the regular sign in form. Click the icon to begin the authentication process. If everything goes well the user will be returned to GitLab and will be signed in.
+
diff --git a/doc/operations/README.md b/doc/operations/README.md
index f1456c6c8e2..6a35dab7b6c 100644
--- a/doc/operations/README.md
+++ b/doc/operations/README.md
@@ -2,3 +2,4 @@
- [Sidekiq MemoryKiller](sidekiq_memory_killer.md)
- [Cleaning up Redis sessions](cleaning_up_redis_sessions.md)
+- [Understanding Unicorn and unicorn-worker-killer](unicorn.md)
diff --git a/doc/operations/unicorn.md b/doc/operations/unicorn.md
new file mode 100644
index 00000000000..31b432cd411
--- /dev/null
+++ b/doc/operations/unicorn.md
@@ -0,0 +1,86 @@
+# Understanding Unicorn and unicorn-worker-killer
+
+## Unicorn
+
+GitLab uses [Unicorn](http://unicorn.bogomips.org/), a pre-forking Ruby web
+server, to handle web requests (web browsers and Git HTTP clients). Unicorn is
+a daemon written in Ruby and C that can load and run a Ruby on Rails
+application; in our case the Rails application is GitLab Community Edition or
+GitLab Enterprise Edition.
+
+Unicorn has a multi-process architecture to make better use of available CPU
+cores (processes can run on different cores) and to have stronger fault
+tolerance (most failures stay isolated in only one process and cannot take down
+GitLab entirely). On startup, the Unicorn 'master' process loads a clean Ruby
+environment with the GitLab application code, and then spawns 'workers' which
+inherit this clean initial environment. The 'master' never handles any
+requests, that is left to the workers. The operating system network stack
+queues incoming requests and distributes them among the workers.
+
+In a perfect world, the master would spawn its pool of workers once, and then
+the workers handle incoming web requests one after another until the end of
+time. In reality, worker processes can crash or time out: if the master notices
+that a worker takes too long to handle a request it will terminate the worker
+process with SIGKILL ('kill -9'). No matter how the worker process ended, the
+master process will replace it with a new 'clean' process again. Unicorn is
+designed to be able to replace 'crashed' workers without dropping user
+requests.
+
+This is what a Unicorn worker timeout looks like in `unicorn_stderr.log`. The
+master process has PID 56227 below.
+
+```
+[2015-06-05T10:58:08.660325 #56227] ERROR -- : worker=10 PID:53009 timeout (61s > 60s), killing
+[2015-06-05T10:58:08.699360 #56227] ERROR -- : reaped #<Process::Status: pid 53009 SIGKILL (signal 9)> worker=10
+[2015-06-05T10:58:08.708141 #62538] INFO -- : worker=10 spawned pid=62538
+[2015-06-05T10:58:08.708824 #62538] INFO -- : worker=10 ready
+```
+
+### Tunables
+
+The main tunables for Unicorn are the number of worker processes and the
+request timeout after which the Unicorn master terminates a worker process.
+See the [omnibus-gitlab Unicorn settings
+documentation](https://gitlab.com/gitlab-org/omnibus-gitlab/blob/master/doc/settings/unicorn.md)
+if you want to adjust these settings.
+
+## unicorn-worker-killer
+
+GitLab has memory leaks. These memory leaks manifest themselves in long-running
+processes, such as Unicorn workers. (The Unicorn master process is not known to
+leak memory, probably because it does not handle user requests.)
+
+To make these memory leaks manageable, GitLab comes with the
+[unicorn-worker-killer gem](https://github.com/kzk/unicorn-worker-killer). This
+gem [monkey-patches](http://en.wikipedia.org/wiki/Monkey_patch) the Unicorn
+workers to do a memory self-check after every 16 requests. If the memory of the
+Unicorn worker exceeds a pre-set limit then the worker process exits. The
+Unicorn master then automatically replaces the worker process.
+
+This is a robust way to handle memory leaks: Unicorn is designed to handle
+workers that 'crash' so no user requests will be dropped. The
+unicorn-worker-killer gem is designed to only terminate a worker process _in
+between requests_, so no user requests are affected.
+
+This is what a Unicorn worker memory restart looks like in unicorn_stderr.log.
+You see that worker 4 (PID 125918) is inspecting itself and decides to exit.
+The threshold memory value was 254802235 bytes, about 250MB. With GitLab this
+threshold is a random value between 200 and 250 MB. The master process (PID
+117565) then reaps the worker process and spawns a new 'worker 4' with PID
+127549.
+
+```
+[2015-06-05T12:07:41.828374 #125918] WARN -- : #<Unicorn::HttpServer:0x00000002734770>: worker (pid: 125918) exceeds memory limit (256413696 bytes > 254802235 bytes)
+[2015-06-05T12:07:41.828472 #125918] WARN -- : Unicorn::WorkerKiller send SIGQUIT (pid: 125918) alive: 23 sec (trial 1)
+[2015-06-05T12:07:42.025916 #117565] INFO -- : reaped #<Process::Status: pid 125918 exit 0> worker=4
+[2015-06-05T12:07:42.034527 #127549] INFO -- : worker=4 spawned pid=127549
+[2015-06-05T12:07:42.035217 #127549] INFO -- : worker=4 ready
+```
+
+One other thing that stands out in the log snippet above, taken from
+Gitlab.com, is that 'worker 4' was serving requests for only 23 seconds. This
+is a normal value for our current GitLab.com setup and traffic.
+
+The high frequency of Unicorn memory restarts on some GitLab sites can be a
+source of confusion for administrators. Usually they are a [red
+herring](http://en.wikipedia.org/wiki/Red_herring).
diff --git a/doc/raketasks/maintenance.md b/doc/raketasks/maintenance.md
index 41a994f3f68..2aca91d5371 100644
--- a/doc/raketasks/maintenance.md
+++ b/doc/raketasks/maintenance.md
@@ -47,7 +47,6 @@ Git: /usr/bin/git
Runs the following rake tasks:
-- `gitlab:env:check`
- `gitlab:gitlab_shell:check`
- `gitlab:sidekiq:check`
- `gitlab:app:check`
@@ -147,7 +146,7 @@ Do you want to continue (yes/no)? yes
## Clear redis cache
-If for some reason the dashboard shows wrong information you might want to
+If for some reason the dashboard shows wrong information you might want to
clear Redis' cache.
For Omnibus-packages:
diff --git a/doc/release/monthly.md b/doc/release/monthly.md
index eb97f3cd7f6..97418db7478 100644
--- a/doc/release/monthly.md
+++ b/doc/release/monthly.md
@@ -39,7 +39,7 @@ Xth: (6 working days before the 22nd)
- [ ] Merge CE master in to EE master via merge request (#LINK)
- [ ] Determine QA person and notify this person
-- [ ] Check the tasks in [how to rc1 guide](howto_rc1.md) and delegate tasks if necessary
+- [ ] Check the tasks in [how to rc1 guide](https://dev.gitlab.org/gitlab/gitlabhq/blob/master/doc/release/howto_rc1.md) and delegate tasks if necessary
- [ ] Create CE, EE, CI RC1 versions (#LINK)
Xth: (5 working days before the 22nd)
@@ -71,9 +71,14 @@ Xth: (1 working day before the 22nd)
- [ ] Update GitLab.com with the stable version (#LINK)
- [ ] Update ci.gitLab.com with the stable version (#LINK)
-22nd:
+22nd before 12AM CET:
+
+Release before 12AM CET / 3AM PST, to make sure the majority of our users
+get the new version on the 22nd and there is sufficient time in the European
+workday to quickly fix any issues.
- [ ] Release CE, EE and CI (#LINK)
+- [ ] Schedule a second tweet of the release announcement at 6PM CET / 9AM PST
```
diff --git a/doc/update/6.x-or-7.x-to-7.11.md b/doc/update/6.x-or-7.x-to-7.12.md
index b1daa648f1f..5705fb360db 100644
--- a/doc/update/6.x-or-7.x-to-7.11.md
+++ b/doc/update/6.x-or-7.x-to-7.12.md
@@ -1,7 +1,7 @@
-# From 6.x or 7.x to 7.11
-*Make sure you view this [upgrade guide from the `master` branch](../../../master/doc/update/6.x-or-7.x-to-7.11.md) for the most up to date instructions.*
+# From 6.x or 7.x to 7.12
+*Make sure you view this [upgrade guide from the `master` branch](../../../master/doc/update/6.x-or-7.x-to-7.12.md) for the most up to date instructions.*
-This allows you to upgrade any version of GitLab from 6.0 and up (including 7.0 and up) to 7.11.
+This allows you to upgrade any version of GitLab from 6.0 and up (including 7.0 and up) to 7.12.
## Global issue numbers
@@ -71,7 +71,7 @@ sudo -u git -H git checkout -- db/schema.rb # local changes will be restored aut
For GitLab Community Edition:
```bash
-sudo -u git -H git checkout 7-11-stable
+sudo -u git -H git checkout 7-12-stable
```
OR
@@ -79,7 +79,7 @@ OR
For GitLab Enterprise Edition:
```bash
-sudo -u git -H git checkout 7-11-stable-ee
+sudo -u git -H git checkout 7-12-stable-ee
```
## 4. Install additional packages
@@ -162,11 +162,11 @@ sudo cp lib/support/init.d/gitlab /etc/init.d/gitlab
TIP: to see what changed in `gitlab.yml.example` in this release use next command:
```
-git diff 6-0-stable:config/gitlab.yml.example 7-11-stable:config/gitlab.yml.example
+git diff 6-0-stable:config/gitlab.yml.example 7-12-stable:config/gitlab.yml.example
```
-* Make `/home/git/gitlab/config/gitlab.yml` the same as https://gitlab.com/gitlab-org/gitlab-ce/blob/7-11-stable/config/gitlab.yml.example but with your settings.
-* Make `/home/git/gitlab/config/unicorn.rb` the same as https://gitlab.com/gitlab-org/gitlab-ce/blob/7-11-stable/config/unicorn.rb.example but with your settings.
+* Make `/home/git/gitlab/config/gitlab.yml` the same as https://gitlab.com/gitlab-org/gitlab-ce/blob/7-12-stable/config/gitlab.yml.example but with your settings.
+* Make `/home/git/gitlab/config/unicorn.rb` the same as https://gitlab.com/gitlab-org/gitlab-ce/blob/7-12-stable/config/unicorn.rb.example but with your settings.
* Make `/home/git/gitlab-shell/config.yml` the same as https://gitlab.com/gitlab-org/gitlab-shell/blob/v2.6.0/config.yml.example but with your settings.
* Copy rack attack middleware config
@@ -182,10 +182,15 @@ sudo cp lib/support/logrotate/gitlab /etc/logrotate.d/gitlab
### Change Nginx settings
-* HTTP setups: Make `/etc/nginx/sites-available/gitlab` the same as https://gitlab.com/gitlab-org/gitlab-ce/blob/7-11-stable/lib/support/nginx/gitlab but with your settings.
-* HTTPS setups: Make `/etc/nginx/sites-available/gitlab-ssl` the same as https://gitlab.com/gitlab-org/gitlab-ce/blob/7-11-stable/lib/support/nginx/gitlab-ssl but with your settings.
+* HTTP setups: Make `/etc/nginx/sites-available/gitlab` the same as https://gitlab.com/gitlab-org/gitlab-ce/blob/7-12-stable/lib/support/nginx/gitlab but with your settings.
+* HTTPS setups: Make `/etc/nginx/sites-available/gitlab-ssl` the same as https://gitlab.com/gitlab-org/gitlab-ce/blob/7-12-stable/lib/support/nginx/gitlab-ssl but with your settings.
* A new `location /uploads/` section has been added that needs to have the same content as the existing `location @gitlab` section.
+### Check the version of /usr/local/bin/git
+
+If you installed Git from source into /usr/local/bin/git then please [check
+your version](7.11-to-7.12.md).
+
## 9. Start application
sudo service gitlab start
diff --git a/doc/update/7.11-to-7.12.md b/doc/update/7.11-to-7.12.md
new file mode 100644
index 00000000000..cc14a135926
--- /dev/null
+++ b/doc/update/7.11-to-7.12.md
@@ -0,0 +1,129 @@
+# From 7.11 to 7.12
+
+### 0. Double-check your Git version
+
+**This notice applies only to /usr/local/bin/git**
+
+If you compiled Git from source on your GitLab server then please double-check
+that you are using a version that protects against CVE-2014-9390. For six
+months after this vulnerability became known the GitLab installation guide
+still contained instructions that would install the outdated, 'vulnerable' Git
+version 2.1.2.
+
+Run the following command to get your current Git version.
+
+```
+/usr/local/bin/git --version
+```
+
+If you see 'No such file or directory' then you did not install Git according
+to the outdated instructions from the GitLab installation guide and you can go
+to the next step 'Stop server' below.
+
+If you see a version string then it should be v1.8.5.6, v1.9.5, v2.0.5, v2.1.4,
+v2.2.1 or newer. You can use the [instructions in the GitLab source
+installation
+guide](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/install/installation.md#1-packages-dependencies)
+to install a newer version of Git.
+
+### 1. Stop server
+
+ 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. Get latest code
+
+```bash
+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
+sudo -u git -H git checkout 7-12-stable
+```
+
+OR
+
+For GitLab Enterprise Edition:
+
+```bash
+sudo -u git -H git checkout 7-12-stable-ee
+```
+
+### 4. Update gitlab-shell
+
+```bash
+cd /home/git/gitlab-shell
+sudo -u git -H git fetch
+sudo -u git -H git checkout v2.6.3
+```
+
+### 5. 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 development test postgres --deployment
+
+# PostgreSQL installations (note: the line below states '--without ... mysql')
+sudo -u git -H bundle install --without development test mysql --deployment
+
+# Run database migrations
+sudo -u git -H bundle exec rake db:migrate RAILS_ENV=production
+
+# Clean up assets and cache
+sudo -u git -H bundle exec rake assets:clean assets:precompile cache:clear RAILS_ENV=production
+
+# Update init.d script
+sudo cp lib/support/init.d/gitlab /etc/init.d/gitlab
+```
+
+### 6. Update config files
+
+#### New configuration options for `gitlab.yml`
+
+There are new configuration options available for [`gitlab.yml`](config/gitlab.yml.example). View them with the command below and apply them to your current `gitlab.yml`.
+
+```
+git diff origin/7-11-stable:config/gitlab.yml.example origin/7-12-stable:config/gitlab.yml.example
+``````
+
+### 7. Start application
+
+ sudo service gitlab start
+ sudo service nginx restart
+
+### 8. Check application status
+
+Check if GitLab and its environment are configured correctly:
+
+ 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 with:
+
+ 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 (7.11)
+
+### 1. Revert the code to the previous version
+Follow the [upgrade guide from 7.10 to 7.11](7.10-to-7.11.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.
diff --git a/doc/workflow/2fa.png b/doc/workflow/2fa.png
new file mode 100644
index 00000000000..bbf415210d5
--- /dev/null
+++ b/doc/workflow/2fa.png
Binary files differ
diff --git a/doc/workflow/2fa_auth.png b/doc/workflow/2fa_auth.png
new file mode 100644
index 00000000000..4a4fbe68984
--- /dev/null
+++ b/doc/workflow/2fa_auth.png
Binary files differ
diff --git a/doc/workflow/README.md b/doc/workflow/README.md
index 0fca68f364e..70a8179c8eb 100644
--- a/doc/workflow/README.md
+++ b/doc/workflow/README.md
@@ -1,17 +1,16 @@
-# Workflow
-
-- [Feature branch workflow](workflow.md)
-- [Project forking workflow](forking_workflow.md)
-- [Project Features](project_features.md)
-- [Authorization for merge requests](authorization_for_merge_requests.md)
-- [Groups](groups.md)
-- [Labels](labels.md)
-- [GitLab Flow](gitlab_flow.md)
-- [Notifications](notifications.md)
-- [Migrating from SVN to GitLab](migrating_from_svn.md)
-- [Project importing from GitHub to GitLab](import_projects_from_github.md)
-- [Project importing from GitLab.com to your private GitLab instance](import_projects_from_gitlab_com.md)
-- [Protected branches](protected_branches.md)
-- [Change your time zone](timezone.md)
-- [Keyboard shortcuts](shortcuts.md)
-- [Web Editor](web_editor.md) \ No newline at end of file
+# Workflow
+
+- [Authorization for merge requests](authorization_for_merge_requests.md)
+- [Change your time zone](timezone.md)
+- [Feature branch workflow](workflow.md)
+- [GitLab Flow](gitlab_flow.md)
+- [Groups](groups.md)
+- [Keyboard shortcuts](shortcuts.md)
+- [Labels](labels.md)
+- [Notifications](notifications.md)
+- [Project Features](project_features.md)
+- [Project forking workflow](forking_workflow.md)
+- [Protected branches](protected_branches.md)
+- [Two-factor Authentication (2FA)](two_factor_authentication.md)
+- [Web Editor](web_editor.md)
+- ["Work In Progress" Merge Requests](wip_merge_requests.md) \ No newline at end of file
diff --git a/doc/workflow/import_projects_from_github.md b/doc/workflow/import_projects_from_github.md
deleted file mode 100644
index 8644b4ffc73..00000000000
--- a/doc/workflow/import_projects_from_github.md
+++ /dev/null
@@ -1,13 +0,0 @@
-# Project importing from GitHub to GitLab
-
-You can import your existing GitHub projects to GitLab. But keep in mind that it is possible only if
-GitHub support is enabled on your GitLab instance. You can read more about GitHub support [here](http://doc.gitlab.com/ce/integration/github.html)
-To get to the importer page you need to go to "New project" page.
-
-![New project page](github_importer/new_project_page.png)
-
-Click on the "Import project from GitHub" link and you will be redirected to GitHub for permission to access your projects. After accepting, you'll be automatically redirected to the importer.
-
-![Importer page](github_importer/importer.png)
-
-To import a project, you can simple click "Add". The importer will import your repository and issues. Once the importer is done, a new GitLab project will be created with your imported data. \ No newline at end of file
diff --git a/doc/workflow/importing/README.md b/doc/workflow/importing/README.md
new file mode 100644
index 00000000000..2b2e9037425
--- /dev/null
+++ b/doc/workflow/importing/README.md
@@ -0,0 +1,6 @@
+# Migrating projects to a GitLab instance
+
+1. [Bitbucket](import_projects_from_bitbucket.md)
+2. [GitHub](import_projects_from_github.md)
+3. [GitLab.com](import_projects_from_gitlab_com.md)
+4. [SVN](migrating_from_svn.md)
diff --git a/doc/workflow/importing/bitbucket_importer/bitbucket_import_grant_access.png b/doc/workflow/importing/bitbucket_importer/bitbucket_import_grant_access.png
new file mode 100644
index 00000000000..df55a081803
--- /dev/null
+++ b/doc/workflow/importing/bitbucket_importer/bitbucket_import_grant_access.png
Binary files differ
diff --git a/doc/workflow/importing/bitbucket_importer/bitbucket_import_new_project.png b/doc/workflow/importing/bitbucket_importer/bitbucket_import_new_project.png
new file mode 100644
index 00000000000..5253889d251
--- /dev/null
+++ b/doc/workflow/importing/bitbucket_importer/bitbucket_import_new_project.png
Binary files differ
diff --git a/doc/workflow/importing/bitbucket_importer/bitbucket_import_select_bitbucket.png b/doc/workflow/importing/bitbucket_importer/bitbucket_import_select_bitbucket.png
new file mode 100644
index 00000000000..ffa87ce5b2e
--- /dev/null
+++ b/doc/workflow/importing/bitbucket_importer/bitbucket_import_select_bitbucket.png
Binary files differ
diff --git a/doc/workflow/importing/bitbucket_importer/bitbucket_import_select_project.png b/doc/workflow/importing/bitbucket_importer/bitbucket_import_select_project.png
new file mode 100644
index 00000000000..0e08703f421
--- /dev/null
+++ b/doc/workflow/importing/bitbucket_importer/bitbucket_import_select_project.png
Binary files differ
diff --git a/doc/workflow/github_importer/importer.png b/doc/workflow/importing/github_importer/importer.png
index 57636717571..57636717571 100644
--- a/doc/workflow/github_importer/importer.png
+++ b/doc/workflow/importing/github_importer/importer.png
Binary files differ
diff --git a/doc/workflow/github_importer/new_project_page.png b/doc/workflow/importing/github_importer/new_project_page.png
index 002f22d81d7..002f22d81d7 100644
--- a/doc/workflow/github_importer/new_project_page.png
+++ b/doc/workflow/importing/github_importer/new_project_page.png
Binary files differ
diff --git a/doc/workflow/gitlab_importer/importer.png b/doc/workflow/importing/gitlab_importer/importer.png
index d2a286d8cac..d2a286d8cac 100644
--- a/doc/workflow/gitlab_importer/importer.png
+++ b/doc/workflow/importing/gitlab_importer/importer.png
Binary files differ
diff --git a/doc/workflow/gitlab_importer/new_project_page.png b/doc/workflow/importing/gitlab_importer/new_project_page.png
index 5e239208e1e..5e239208e1e 100644
--- a/doc/workflow/gitlab_importer/new_project_page.png
+++ b/doc/workflow/importing/gitlab_importer/new_project_page.png
Binary files differ
diff --git a/doc/workflow/importing/import_projects_from_bitbucket.md b/doc/workflow/importing/import_projects_from_bitbucket.md
new file mode 100644
index 00000000000..1e9825e2e10
--- /dev/null
+++ b/doc/workflow/importing/import_projects_from_bitbucket.md
@@ -0,0 +1,26 @@
+# Import your project from Bitbucket to GitLab
+
+It takes just a few steps to import your existing Bitbucket projects to GitLab. But keep in mind that it is possible only if Bitbucket support is enabled on your GitLab instance. You can read more about Bitbucket support [here](doc/integration/bitbucket.md).
+
+* Sign in to GitLab.com and go to your dashboard
+
+* Click on "New project"
+
+![New project in GitLab](bitbucket_importer/bitbucket_import_new_project.png)
+
+* Click on the "Bitbucket" button
+
+![Bitbucket](bitbucket_importer/bitbucket_import_select_bitbucket.png)
+
+* Grant GitLab access to your Bitbucket account
+
+![Grant access](bitbucket_importer/bitbucket_import_grant_access.png)
+
+* Click on the projects that you'd like to import or "Import all projects"
+
+![Import projects](bitbucket_importer/bitbucket_import_select_project.png)
+
+A new GitLab project will be created with your imported data.
+
+### Note
+Milestones and wiki pages are not imported from Bitbucket.
diff --git a/doc/workflow/importing/import_projects_from_github.md b/doc/workflow/importing/import_projects_from_github.md
new file mode 100644
index 00000000000..aad2c63817d
--- /dev/null
+++ b/doc/workflow/importing/import_projects_from_github.md
@@ -0,0 +1,18 @@
+# Import your project from GitHub to GitLab
+
+It takes just a couple of steps to import your existing GitHub projects to GitLab. Keep in mind that it is possible only if
+GitHub support is enabled on your GitLab instance. You can read more about GitHub support [here](http://doc.gitlab.com/ce/integration/github.html)
+
+* Sign in to GitLab.com and go to your dashboard.
+* To get to the importer page, you need to go to the "New project" page.
+
+![New project page](github_importer/new_project_page.png)
+
+* Click on the "Import project from GitHub" link and you will be redirected to GitHub for permission to access your projects. After accepting, you'll be automatically redirected to the importer.
+
+![Importer page](github_importer/importer.png)
+
+* To import a project, you can simple click "Add". The importer will import your repository and issues. Once the importer is done, a new GitLab project will be created with your imported data.
+
+### Note
+When you import your projects from GitHub, it is not possible to keep your labels and milestones and issue numbers won't match. We are working on improving this in the near future. \ No newline at end of file
diff --git a/doc/workflow/import_projects_from_gitlab_com.md b/doc/workflow/importing/import_projects_from_gitlab_com.md
index f4c4e955d46..f4c4e955d46 100644
--- a/doc/workflow/import_projects_from_gitlab_com.md
+++ b/doc/workflow/importing/import_projects_from_gitlab_com.md
diff --git a/doc/workflow/migrating_from_svn.md b/doc/workflow/importing/migrating_from_svn.md
index 485db4834e9..485db4834e9 100644
--- a/doc/workflow/migrating_from_svn.md
+++ b/doc/workflow/importing/migrating_from_svn.md
diff --git a/doc/workflow/two_factor_authentication.md b/doc/workflow/two_factor_authentication.md
new file mode 100644
index 00000000000..fb215c8b269
--- /dev/null
+++ b/doc/workflow/two_factor_authentication.md
@@ -0,0 +1,67 @@
+# Two-factor Authentication (2FA)
+
+Two-factor Authentication (2FA) provides an additional level of security to your
+GitLab account. Once enabled, in addition to supplying your username and
+password to login, you'll be prompted for a code generated by an application on
+your phone.
+
+By enabling 2FA, the only way someone other than you can log into your account
+is to know your username and password *and* have access to your phone.
+
+## Enabling 2FA
+
+**In GitLab:**
+
+1. Log in to your GitLab account.
+1. Go to your **Profile Settings**.
+1. Go to **Account**.
+1. Click **Enable Two-factor Authentication**.
+
+![Two-factor setup](2fa.png)
+
+**On your phone:**
+
+1. Install a compatible application. We recommend [Google Authenticator]
+\(proprietary\) or [FreeOTP] \(open source\).
+1. In the application, add a new entry in one of two ways:
+ * Scan the code with your phone's camera to add the entry automatically.
+ * Enter the details provided to add the entry manually.
+
+**In GitLab:**
+
+1. Enter the six-digit pin number from the entry on your phone into the **Pin
+ code** field.
+1. Click **Submit**.
+
+If the pin you entered was correct, you'll see a message indicating that
+Two-factor Authentication has been enabled, and you'll be presented with a list
+of recovery codes.
+
+## Recovery Codes
+
+Should you ever lose access to your phone, you can use one of the ten provided
+backup codes to login to your account. We suggest copying or printing them for
+storage in a safe place. **Each code can be used only once** to log in to your
+account.
+
+If you lose the recovery codes or just want to generate new ones, you can do so
+from the **Profile Settings** > **Account** page where you first enabled 2FA.
+
+## Logging in with 2FA Enabled
+
+Logging in with 2FA enabled is only slightly different than a normal login.
+Enter your username and password credentials as you normally would, and you'll
+be presented with a second prompt for an authentication code. Enter the pin from
+your phone's application or a recovery code to log in.
+
+![Two-factor authentication on sign in](2fa_auth.png)
+
+## Disabling 2FA
+
+1. Log in to your GitLab account.
+1. Go to your **Profile Settings**.
+1. Go to **Account**.
+1. Click **Disable Two-factor Authentication**.
+
+[Google Authenticator]: https://support.google.com/accounts/answer/1066447?hl=en
+[FreeOTP]: https://fedorahosted.org/freeotp/
diff --git a/doc/workflow/wip_merge_requests.md b/doc/workflow/wip_merge_requests.md
new file mode 100644
index 00000000000..46035a5e6b6
--- /dev/null
+++ b/doc/workflow/wip_merge_requests.md
@@ -0,0 +1,13 @@
+# "Work In Progress" Merge Requests
+
+To prevent merge requests from accidentally being accepted before they're completely ready, GitLab blocks the "Accept" button for merge requests that have been marked a **Work In Progress**.
+
+![Blocked Accept Button](wip_merge_requests/blocked_accept_button.png)
+
+To mark a merge request a Work In Progress, simply start its title with `[WIP]` or `WIP:`.
+
+![Mark as WIP](wip_merge_requests/mark_as_wip.png)
+
+To allow a Work In Progress merge request to be accepted again when it's ready, simply remove the `WIP` prefix.
+
+![Unark as WIP](wip_merge_requests/unmark_as_wip.png)
diff --git a/doc/workflow/wip_merge_requests/blocked_accept_button.png b/doc/workflow/wip_merge_requests/blocked_accept_button.png
new file mode 100644
index 00000000000..4791e5de972
--- /dev/null
+++ b/doc/workflow/wip_merge_requests/blocked_accept_button.png
Binary files differ
diff --git a/doc/workflow/wip_merge_requests/mark_as_wip.png b/doc/workflow/wip_merge_requests/mark_as_wip.png
new file mode 100644
index 00000000000..8fa83a201ac
--- /dev/null
+++ b/doc/workflow/wip_merge_requests/mark_as_wip.png
Binary files differ
diff --git a/doc/workflow/wip_merge_requests/unmark_as_wip.png b/doc/workflow/wip_merge_requests/unmark_as_wip.png
new file mode 100644
index 00000000000..d45e68f31c5
--- /dev/null
+++ b/doc/workflow/wip_merge_requests/unmark_as_wip.png
Binary files differ
diff --git a/doc_styleguide.md b/doc_styleguide.md
index 670af765f3a..db30a737f14 100644
--- a/doc_styleguide.md
+++ b/doc_styleguide.md
@@ -12,6 +12,7 @@ This styleguide recommends best practices to improve documentation and to keep i
* Be brief and clear.
+* Whenever it applies, add documents in alphabetical order.
## When adding images to a document
diff --git a/docker/README.md b/docker/README.md
index 2e533ae9dd5..fb3bde5016d 100644
--- a/docker/README.md
+++ b/docker/README.md
@@ -80,7 +80,7 @@ sudo docker pull sytse/gitlab-app:7.10.1
```bash
sudo docker run --name gitlab-data sytse/gitlab-data /bin/true
-sudo docker run --detach --name gitlab_app --publish 8080:80 --publish 2222:22 --volumes-from gitlab_data sytse/gitlab-app:7.10.1
+sudo docker run --detach --name gitlab-app --publish 8080:80 --publish 2222:22 --volumes-from gitlab-data sytse/gitlab-app:7.10.1
```
After this you can login to the web interface as explained above in 'After starting a container'.
@@ -94,7 +94,12 @@ sudo docker build --tag gitlab-data docker/data/
sudo docker build --tag gitlab-app:7.10.1 docker/app/
```
-After this run the images as described in the previous section.
+After this run the images:
+
+```bash
+sudo docker run --name gitlab-data gitlab-data /bin/true
+sudo docker run --detach --name gitlab-app --publish 8080:80 --publish 2222:22 --volumes-from gitlab-data gitlab-app:7.10.1
+```
We assume using a data volume container, this will simplify migrations and backups.
This empty container will exist to persist as volumes the 3 directories used by GitLab, so remember not to delete it.
@@ -107,7 +112,7 @@ The directories on data container are:
### Configure GitLab
-These container uses the official Omnibus GitLab distribution, so all configuration is done in the unique configuration file `/etc/gitlab/gitlab.rb`.
+This container uses the official Omnibus GitLab distribution, so all configuration is done in the unique configuration file `/etc/gitlab/gitlab.rb`.
To access GitLab configuration, you can start an interactive command line in a new container using the shared data volume container, you will be able to browse the 3 directories and use your favorite text editor:
@@ -122,7 +127,7 @@ You can find all available options in [Omnibus GitLab documentation](https://git
### Upgrade GitLab with app and data images
-To updgrade GitLab to new versions, stop running container, create new docker image and container from that image.
+To upgrade GitLab to new versions, stop running container, create new docker image and container from that image.
It Assumes that you're upgrading from 7.8.1 to 7.10.1 and you're in the updated GitLab repo root directory:
@@ -130,7 +135,7 @@ It Assumes that you're upgrading from 7.8.1 to 7.10.1 and you're in the updated
sudo docker stop gitlab-app
sudo docker rm gitlab-app
sudo docker build --tag gitlab-app:7.10.1 docker/app/
-sudo docker run --detach --name gitlab-app --publish 8080:80 --publish 2222:22 --volumes-from gitlab_data gitlab-app:7.10.1
+sudo docker run --detach --name gitlab-app --publish 8080:80 --publish 2222:22 --volumes-from gitlab-data gitlab-app:7.10.1
```
On the first run GitLab will reconfigure and update itself. If everything runs OK don't forget to cleanup the app image:
@@ -141,13 +146,15 @@ sudo docker rmi gitlab-app:7.8.1
### Publish images to Dockerhub
-Login to Dockerhub with `sudo docker login` and run the following (replace '7.9.2' with the version you're using and 'Sytse Sijbrandij' with your name):
+- Ensure the containers are running
+- Login to Dockerhub with `sudo docker login`
+- Run the following (replace '7.10.1' with the version you're using and 'Sytse Sijbrandij' with your name):
```bash
-sudo docker commit -m "Initial commit" -a "Sytse Sijbrandij" gitlab-app:7.10.1 sytse/gitlab-app:7.10.1
+sudo docker commit -m "Initial commit" -a "Sytse Sijbrandij" gitlab-app sytse/gitlab-app:7.10.1
sudo docker push sytse/gitlab-app:7.10.1
-sudo docker commit -m "Initial commit" -a "Sytse Sijbrandij" gitlab_data sytse/gitlab_data
-sudo docker push sytse/gitlab_data
+sudo docker commit -m "Initial commit" -a "Sytse Sijbrandij" gitlab-data sytse/gitlab-data
+sudo docker push sytse/gitlab-data
```
## Troubleshooting
diff --git a/features/admin/deploy_keys.feature b/features/admin/deploy_keys.feature
index 9df47eb51fd..33439cd1e85 100644
--- a/features/admin/deploy_keys.feature
+++ b/features/admin/deploy_keys.feature
@@ -8,11 +8,6 @@ Feature: Admin Deploy Keys
When I visit admin deploy keys page
Then I should see all public deploy keys
- Scenario: Deploy Keys show
- When I visit admin deploy keys page
- And I click on first deploy key
- Then I should see deploy key details
-
Scenario: Deploy Keys new
When I visit admin deploy keys page
And I click 'New Deploy Key'
diff --git a/features/dashboard/group.feature b/features/dashboard/group.feature
index cf4b8d7283b..e3c01db2ebb 100644
--- a/features/dashboard/group.feature
+++ b/features/dashboard/group.feature
@@ -24,7 +24,8 @@ Feature: Dashboard Group
When I visit dashboard groups page
Then I should see group "Owned" in group list
Then I should see group "Guest" in group list
- Then I should not see the "Leave" button for group "Owned"
+ When I click on the "Leave" button for group "Owned"
+ Then I should see the "Can not leave message"
@javascript
Scenario: Guest should be able to leave from group
diff --git a/features/project/active_tab.feature b/features/project/active_tab.feature
index 05faad4e645..8661ea98c20 100644
--- a/features/project/active_tab.feature
+++ b/features/project/active_tab.feature
@@ -35,6 +35,11 @@ Feature: Project Active Tab
Then the active main tab should be Merge Requests
And no other main tabs should be active
+ Scenario: On Project Members
+ Given I visit my project's members page
+ Then the active main tab should be Members
+ And no other main tabs should be active
+
Scenario: On Project Wiki
Given I visit my project's wiki page
Then the active main tab should be Wiki
@@ -49,13 +54,6 @@ Feature: Project Active Tab
# Sub Tabs: Settings
- Scenario: On Project Settings/Team
- Given I visit my project's settings page
- And I click the "Team" tab
- Then the active sub nav should be Team
- And no other sub navs should be active
- And the active main tab should be Settings
-
Scenario: On Project Settings/Edit
Given I visit my project's settings page
And I click the "Edit" tab
diff --git a/features/project/merge_requests.feature b/features/project/merge_requests.feature
index 7a831901607..eb091c291e9 100644
--- a/features/project/merge_requests.feature
+++ b/features/project/merge_requests.feature
@@ -207,3 +207,11 @@ Feature: Project Merge Requests
Then I should see that I am subscribed
When I click button "Unsubscribe"
Then I should see that I am unsubscribed
+
+ @javascript
+ Scenario: I can change the target branch
+ Given I visit merge request page "Bug NS-04"
+ And I click link "Edit" for the merge request
+ When I click the "Target branch" dropdown
+ And I select a new target branch
+ Then I should see new target branch changes
diff --git a/features/steps/admin/applications.rb b/features/steps/admin/applications.rb
index d59088fa3c3..7c12cb96921 100644
--- a/features/steps/admin/applications.rb
+++ b/features/steps/admin/applications.rb
@@ -8,7 +8,7 @@ class Spinach::Features::AdminApplications < Spinach::FeatureSteps
end
step 'I should see application form' do
- page.should have_content "New application"
+ expect(page).to have_content "New application"
end
step 'I fill application form out and submit' do
@@ -18,9 +18,9 @@ class Spinach::Features::AdminApplications < Spinach::FeatureSteps
end
step 'I see application' do
- page.should have_content "Application: test"
- page.should have_content "Application Id"
- page.should have_content "Secret"
+ expect(page).to have_content "Application: test"
+ expect(page).to have_content "Application Id"
+ expect(page).to have_content "Secret"
end
step 'I click edit' do
@@ -28,28 +28,28 @@ class Spinach::Features::AdminApplications < Spinach::FeatureSteps
end
step 'I see edit application form' do
- page.should have_content "Edit application"
+ expect(page).to have_content "Edit application"
end
step 'I change name of application and submit' do
- page.should have_content "Edit application"
+ expect(page).to have_content "Edit application"
fill_in :doorkeeper_application_name, with: 'test_changed'
click_on "Submit"
end
step 'I see that application was changed' do
- page.should have_content "test_changed"
- page.should have_content "Application Id"
- page.should have_content "Secret"
+ expect(page).to have_content "test_changed"
+ expect(page).to have_content "Application Id"
+ expect(page).to have_content "Secret"
end
step 'I click to remove application' do
- within '.oauth-applications' do
+ page.within '.oauth-applications' do
click_on "Destroy"
end
end
step "I see that application is removed" do
- page.find(".oauth-applications").should_not have_content "test_changed"
+ expect(page.find(".oauth-applications")).not_to have_content "test_changed"
end
end
diff --git a/features/steps/admin/broadcast_messages.rb b/features/steps/admin/broadcast_messages.rb
index a35fa34a3a2..2ecb6f0191a 100644
--- a/features/steps/admin/broadcast_messages.rb
+++ b/features/steps/admin/broadcast_messages.rb
@@ -8,7 +8,7 @@ class Spinach::Features::AdminBroadcastMessages < Spinach::FeatureSteps
end
step 'I should be all broadcast messages' do
- page.should have_content "Migration to new server"
+ expect(page).to have_content "Migration to new server"
end
step 'submit form with new broadcast message' do
@@ -18,11 +18,11 @@ class Spinach::Features::AdminBroadcastMessages < Spinach::FeatureSteps
end
step 'I should be redirected to admin messages page' do
- current_path.should == admin_broadcast_messages_path
+ expect(current_path).to eq admin_broadcast_messages_path
end
step 'I should see newly created broadcast message' do
- page.should have_content 'Application update from 4:00 CST to 5:00 CST'
+ expect(page).to have_content 'Application update from 4:00 CST to 5:00 CST'
end
step 'submit form with new customized broadcast message' do
@@ -35,7 +35,7 @@ class Spinach::Features::AdminBroadcastMessages < Spinach::FeatureSteps
end
step 'I should see a customized broadcast message' do
- page.should have_content 'Application update from 4:00 CST to 5:00 CST'
- page.should have_selector %(div[style="background-color:#f2dede;color:#b94a48"])
+ expect(page).to have_content 'Application update from 4:00 CST to 5:00 CST'
+ expect(page).to have_selector %(div[style="background-color:#f2dede;color:#b94a48"])
end
end
diff --git a/features/steps/admin/deploy_keys.rb b/features/steps/admin/deploy_keys.rb
index fb0b611762e..56787eeb6b3 100644
--- a/features/steps/admin/deploy_keys.rb
+++ b/features/steps/admin/deploy_keys.rb
@@ -10,21 +10,10 @@ class Spinach::Features::AdminDeployKeys < Spinach::FeatureSteps
step 'I should see all public deploy keys' do
DeployKey.are_public.each do |p|
- page.should have_content p.title
+ expect(page).to have_content p.title
end
end
- step 'I click on first deploy key' do
- click_link DeployKey.are_public.first.title
- end
-
- step 'I should see deploy key details' do
- deploy_key = DeployKey.are_public.first
- current_path.should == admin_deploy_key_path(deploy_key)
- page.should have_content(deploy_key.title)
- page.should have_content(deploy_key.key)
- end
-
step 'I visit admin deploy key page' do
visit admin_deploy_key_path(deploy_key)
end
@@ -44,11 +33,11 @@ class Spinach::Features::AdminDeployKeys < Spinach::FeatureSteps
end
step 'I should be on admin deploy keys page' do
- current_path.should == admin_deploy_keys_path
+ expect(current_path).to eq admin_deploy_keys_path
end
step 'I should see newly created deploy key' do
- page.should have_content(deploy_key.title)
+ expect(page).to have_content(deploy_key.title)
end
def deploy_key
diff --git a/features/steps/admin/groups.rb b/features/steps/admin/groups.rb
index 721460b9371..9cc74a97c3a 100644
--- a/features/steps/admin/groups.rb
+++ b/features/steps/admin/groups.rb
@@ -28,32 +28,32 @@ class Spinach::Features::AdminGroups < Spinach::FeatureSteps
end
step 'I should see newly created group' do
- page.should have_content "Group: gitlab"
- page.should have_content "Group description"
+ expect(page).to have_content "Group: gitlab"
+ expect(page).to have_content "Group description"
end
step 'I should be redirected to group page' do
- current_path.should == admin_group_path(Group.find_by(path: 'gitlab'))
+ expect(current_path).to eq admin_group_path(Group.find_by(path: 'gitlab'))
end
When 'I select user "John Doe" from user list as "Reporter"' do
select2(user_john.id, from: "#user_ids", multiple: true)
- within "#new_project_member" do
+ page.within "#new_project_member" do
select "Reporter", from: "access_level"
end
click_button "Add users to group"
end
step 'I should see "John Doe" in team list in every project as "Reporter"' do
- within ".group-users-list" do
- page.should have_content "John Doe"
- page.should have_content "Reporter"
+ page.within ".group-users-list" do
+ expect(page).to have_content "John Doe"
+ expect(page).to have_content "Reporter"
end
end
step 'I should be all groups' do
Group.all.each do |group|
- page.should have_content group.name
+ expect(page).to have_content group.name
end
end
@@ -62,14 +62,14 @@ class Spinach::Features::AdminGroups < Spinach::FeatureSteps
end
step 'I remove user "John Doe" from group' do
- within "#user_#{user_john.id}" do
+ page.within "#user_#{user_john.id}" do
click_link 'Remove user from group'
end
end
step 'I should not see "John Doe" in team list' do
- within ".group-users-list" do
- page.should_not have_content "John Doe"
+ page.within ".group-users-list" do
+ expect(page).not_to have_content "John Doe"
end
end
diff --git a/features/steps/admin/logs.rb b/features/steps/admin/logs.rb
index 904e5468655..f9e49588c75 100644
--- a/features/steps/admin/logs.rb
+++ b/features/steps/admin/logs.rb
@@ -4,8 +4,8 @@ class Spinach::Features::AdminLogs < Spinach::FeatureSteps
include SharedAdmin
step 'I should see tabs with available logs' do
- page.should have_content 'production.log'
- page.should have_content 'githost.log'
- page.should have_content 'application.log'
+ expect(page).to have_content 'production.log'
+ expect(page).to have_content 'githost.log'
+ expect(page).to have_content 'application.log'
end
end
diff --git a/features/steps/admin/projects.rb b/features/steps/admin/projects.rb
index 9be4d39d2d5..655f1895279 100644
--- a/features/steps/admin/projects.rb
+++ b/features/steps/admin/projects.rb
@@ -5,7 +5,7 @@ class Spinach::Features::AdminProjects < Spinach::FeatureSteps
step 'I should see all projects' do
Project.all.each do |p|
- page.should have_content p.name_with_namespace
+ expect(page).to have_content p.name_with_namespace
end
end
@@ -15,9 +15,9 @@ class Spinach::Features::AdminProjects < Spinach::FeatureSteps
step 'I should see project details' do
project = Project.first
- current_path.should == admin_namespace_project_path(project.namespace, project)
- page.should have_content(project.name_with_namespace)
- page.should have_content(project.creator.name)
+ expect(current_path).to eq admin_namespace_project_path(project.namespace, project)
+ expect(page).to have_content(project.name_with_namespace)
+ expect(page).to have_content(project.creator.name)
end
step 'I visit admin project page' do
@@ -34,8 +34,8 @@ class Spinach::Features::AdminProjects < Spinach::FeatureSteps
end
step 'I should see project transfered' do
- page.should have_content 'Web / ' + project.name
- page.should have_content 'Namespace: Web'
+ expect(page).to have_content 'Web / ' + project.name
+ expect(page).to have_content 'Namespace: Web'
end
def project
diff --git a/features/steps/admin/settings.rb b/features/steps/admin/settings.rb
index 15ca0d80f1a..1c0b7a4b712 100644
--- a/features/steps/admin/settings.rb
+++ b/features/steps/admin/settings.rb
@@ -11,9 +11,9 @@ class Spinach::Features::AdminSettings < Spinach::FeatureSteps
end
step 'I should see application settings saved' do
- current_application_settings.gravatar_enabled.should be_false
- current_application_settings.home_page_url.should == 'https://about.gitlab.com/'
- page.should have_content 'Application settings saved successfully'
+ expect(current_application_settings.gravatar_enabled).to be_false
+ expect(current_application_settings.home_page_url).to eq 'https://about.gitlab.com/'
+ expect(page).to have_content 'Application settings saved successfully'
end
step 'I click on "Service Templates"' do
@@ -41,18 +41,18 @@ class Spinach::Features::AdminSettings < Spinach::FeatureSteps
end
step 'I should see service template settings saved' do
- page.should have_content 'Application settings saved successfully'
+ expect(page).to have_content 'Application settings saved successfully'
end
step 'I should see all checkboxes checked' do
- all('input[type=checkbox]').each do |checkbox|
- checkbox.should be_checked
+ page.all('input[type=checkbox]').each do |checkbox|
+ expect(checkbox).to be_checked
end
end
step 'I should see Slack settings saved' do
- find_field('Webhook').value.should eq 'http://localhost'
- find_field('Username').value.should eq 'test_user'
- find_field('Channel').value.should eq '#test_channel'
+ expect(find_field('Webhook').value).to eq 'http://localhost'
+ expect(find_field('Username').value).to eq 'test_user'
+ expect(find_field('Channel').value).to eq '#test_channel'
end
end
diff --git a/features/steps/admin/users.rb b/features/steps/admin/users.rb
index e1383097248..34a3ed9f615 100644
--- a/features/steps/admin/users.rb
+++ b/features/steps/admin/users.rb
@@ -5,7 +5,7 @@ class Spinach::Features::AdminUsers < Spinach::FeatureSteps
step 'I should see all users' do
User.all.each do |user|
- page.should have_content user.name
+ expect(page).to have_content user.name
end
end
@@ -23,13 +23,13 @@ class Spinach::Features::AdminUsers < Spinach::FeatureSteps
end
step 'See username error message' do
- within "#error_explanation" do
- page.should have_content "Username"
+ page.within "#error_explanation" do
+ expect(page).to have_content "Username"
end
end
step 'Not changed form action url' do
- page.should have_selector %(form[action="/admin/users/#{@user.username}"])
+ expect(page).to have_selector %(form[action="/admin/users/#{@user.username}"])
end
step 'I submit modified user' do
@@ -38,7 +38,7 @@ class Spinach::Features::AdminUsers < Spinach::FeatureSteps
end
step 'I see user attributes changed' do
- page.should have_content 'Can create groups: Yes'
+ expect(page).to have_content 'Can create groups: Yes'
end
step 'click edit on my user' do
@@ -53,7 +53,7 @@ class Spinach::Features::AdminUsers < Spinach::FeatureSteps
end
step 'I see the secondary email' do
- page.should have_content "Secondary email: #{@user_with_secondary_email.emails.last.email}"
+ expect(page).to have_content "Secondary email: #{@user_with_secondary_email.emails.last.email}"
end
step 'I click remove secondary email' do
@@ -61,7 +61,7 @@ class Spinach::Features::AdminUsers < Spinach::FeatureSteps
end
step 'I should not see secondary email anymore' do
- page.should_not have_content "Secondary email:"
+ expect(page).not_to have_content "Secondary email:"
end
step 'user "Mike" with groups and projects' do
@@ -79,8 +79,8 @@ class Spinach::Features::AdminUsers < Spinach::FeatureSteps
end
step 'I should see user "Mike" details' do
- page.should have_content 'Account'
- page.should have_content 'Personal projects limit'
+ expect(page).to have_content 'Account'
+ expect(page).to have_content 'Personal projects limit'
end
step 'user "Pete" with ssh keys' do
@@ -94,8 +94,8 @@ class Spinach::Features::AdminUsers < Spinach::FeatureSteps
end
step 'I should see key list' do
- page.should have_content 'ssh-rsa Key2'
- page.should have_content 'ssh-rsa Key1'
+ expect(page).to have_content 'ssh-rsa Key2'
+ expect(page).to have_content 'ssh-rsa Key1'
end
step 'I click on the key title' do
@@ -103,8 +103,8 @@ class Spinach::Features::AdminUsers < Spinach::FeatureSteps
end
step 'I should see key details' do
- page.should have_content 'ssh-rsa Key2'
- page.should have_content 'ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDQSTWXhJAX/He+nG78MiRRRn7m0Pb0XbcgTxE0etArgoFoh9WtvDf36HG6tOSg/0UUNcp0dICsNAmhBKdncp6cIyPaXJTURPRAGvhI0/VDk4bi27bRnccGbJ/hDaUxZMLhhrzY0r22mjVf8PF6dvv5QUIQVm1/LeaWYsHHvLgiIjwrXirUZPnFrZw6VLREoBKG8uWvfSXw1L5eapmstqfsME8099oi+vWLR8MgEysZQmD28M73fgW4zek6LDQzKQyJx9nB+hJkKUDvcuziZjGmRFlNgSA2mguERwL1OXonD8WYUrBDGKroIvBT39zS5d9tQDnidEJZ9Y8gv5ViYP7x Key2'
+ expect(page).to have_content 'ssh-rsa Key2'
+ expect(page).to have_content 'ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDQSTWXhJAX/He+nG78MiRRRn7m0Pb0XbcgTxE0etArgoFoh9WtvDf36HG6tOSg/0UUNcp0dICsNAmhBKdncp6cIyPaXJTURPRAGvhI0/VDk4bi27bRnccGbJ/hDaUxZMLhhrzY0r22mjVf8PF6dvv5QUIQVm1/LeaWYsHHvLgiIjwrXirUZPnFrZw6VLREoBKG8uWvfSXw1L5eapmstqfsME8099oi+vWLR8MgEysZQmD28M73fgW4zek6LDQzKQyJx9nB+hJkKUDvcuziZjGmRFlNgSA2mguERwL1OXonD8WYUrBDGKroIvBT39zS5d9tQDnidEJZ9Y8gv5ViYP7x Key2'
end
step 'I click on remove key' do
@@ -112,6 +112,6 @@ class Spinach::Features::AdminUsers < Spinach::FeatureSteps
end
step 'I should see the key removed' do
- page.should_not have_content 'ssh-rsa Key2'
+ expect(page).not_to have_content 'ssh-rsa Key2'
end
end
diff --git a/features/steps/dashboard/archived_projects.rb b/features/steps/dashboard/archived_projects.rb
index 969baf92287..36e092f50c6 100644
--- a/features/steps/dashboard/archived_projects.rb
+++ b/features/steps/dashboard/archived_projects.rb
@@ -9,14 +9,14 @@ class Spinach::Features::DashboardArchivedProjects < Spinach::FeatureSteps
end
step 'I should see "Shop" project link' do
- page.should have_link "Shop"
+ expect(page).to have_link "Shop"
end
step 'I should not see "Forum" project link' do
- page.should_not have_link "Forum"
+ expect(page).not_to have_link "Forum"
end
step 'I should see "Forum" project link' do
- page.should have_link "Forum"
+ expect(page).to have_link "Forum"
end
end
diff --git a/features/steps/dashboard/dashboard.rb b/features/steps/dashboard/dashboard.rb
index 8508b2a8096..945bf35ff27 100644
--- a/features/steps/dashboard/dashboard.rb
+++ b/features/steps/dashboard/dashboard.rb
@@ -4,16 +4,16 @@ class Spinach::Features::Dashboard < Spinach::FeatureSteps
include SharedProject
step 'I should see "New Project" link' do
- page.should have_link "New project"
+ expect(page).to have_link "New project"
end
step 'I should see "Shop" project link' do
- page.should have_link "Shop"
+ expect(page).to have_link "Shop"
end
step 'I should see last push widget' do
- page.should have_content "You pushed to fix"
- page.should have_link "Create Merge Request"
+ expect(page).to have_content "You pushed to fix"
+ expect(page).to have_link "Create Merge Request"
end
step 'I click "Create Merge Request" link' do
@@ -21,10 +21,10 @@ class Spinach::Features::Dashboard < Spinach::FeatureSteps
end
step 'I see prefilled new Merge Request page' do
- current_path.should == new_namespace_project_merge_request_path(@project.namespace, @project)
- find("#merge_request_target_project_id").value.should == @project.id.to_s
- find("#merge_request_source_branch").value.should == "fix"
- find("#merge_request_target_branch").value.should == "master"
+ expect(current_path).to eq new_namespace_project_merge_request_path(@project.namespace, @project)
+ expect(find("#merge_request_target_project_id").value).to eq @project.id.to_s
+ expect(find("input#merge_request_source_branch").value).to eq "fix"
+ expect(find("input#merge_request_target_branch").value).to eq "master"
end
step 'user with name "John Doe" joined project "Shop"' do
@@ -38,7 +38,7 @@ class Spinach::Features::Dashboard < Spinach::FeatureSteps
end
step 'I should see "John Doe joined project Shop" event' do
- page.should have_content "John Doe joined project #{project.name_with_namespace}"
+ expect(page).to have_content "John Doe joined project #{project.name_with_namespace}"
end
step 'user with name "John Doe" left project "Shop"' do
@@ -51,7 +51,7 @@ class Spinach::Features::Dashboard < Spinach::FeatureSteps
end
step 'I should see "John Doe left project Shop" event' do
- page.should have_content "John Doe left project #{project.name_with_namespace}"
+ expect(page).to have_content "John Doe left project #{project.name_with_namespace}"
end
step 'I have group with projects' do
@@ -64,13 +64,13 @@ class Spinach::Features::Dashboard < Spinach::FeatureSteps
step 'I should see projects list' do
@user.authorized_projects.all.each do |project|
- page.should have_link project.name_with_namespace
+ expect(page).to have_link project.name_with_namespace
end
end
step 'I should see groups list' do
Group.all.each do |group|
- page.should have_link group.name
+ expect(page).to have_link group.name
end
end
@@ -80,6 +80,6 @@ class Spinach::Features::Dashboard < Spinach::FeatureSteps
end
step 'I should see 1 project at group list' do
- find('span.last_activity/span').should have_content('1')
+ expect(find('span.last_activity/span')).to have_content('1')
end
end
diff --git a/features/steps/dashboard/event_filters.rb b/features/steps/dashboard/event_filters.rb
index 3da3d62d0c0..834afa439a0 100644
--- a/features/steps/dashboard/event_filters.rb
+++ b/features/steps/dashboard/event_filters.rb
@@ -4,27 +4,27 @@ class Spinach::Features::EventFilters < Spinach::FeatureSteps
include SharedProject
step 'I should see push event' do
- page.should have_selector('span.pushed')
+ expect(page).to have_selector('span.pushed')
end
step 'I should not see push event' do
- page.should_not have_selector('span.pushed')
+ expect(page).not_to have_selector('span.pushed')
end
step 'I should see new member event' do
- page.should have_selector('span.joined')
+ expect(page).to have_selector('span.joined')
end
step 'I should not see new member event' do
- page.should_not have_selector('span.joined')
+ expect(page).not_to have_selector('span.joined')
end
step 'I should see merge request event' do
- page.should have_selector('span.accepted')
+ expect(page).to have_selector('span.accepted')
end
step 'I should not see merge request event' do
- page.should_not have_selector('span.accepted')
+ expect(page).not_to have_selector('span.accepted')
end
step 'this project has push event' do
diff --git a/features/steps/dashboard/group.rb b/features/steps/dashboard/group.rb
index 8384df2fb59..0c6a0ae3725 100644
--- a/features/steps/dashboard/group.rb
+++ b/features/steps/dashboard/group.rb
@@ -17,29 +17,29 @@ class Spinach::Features::DashboardGroup < Spinach::FeatureSteps
end
step 'I should not see the "Leave" button for group "Owned"' do
- find(:css, 'li', text: "Owner").should_not have_selector(:css, 'i.fa.fa-sign-out')
+ expect(find(:css, 'li', text: "Owner")).not_to have_selector(:css, 'i.fa.fa-sign-out')
# poltergeist always confirms popups.
end
step 'I should not see the "Leave" button for groupr "Guest"' do
- find(:css, 'li', text: "Guest").should_not have_selector(:css, 'i.fa.fa-sign-out')
+ expect(find(:css, 'li', text: "Guest")).not_to have_selector(:css, 'i.fa.fa-sign-out')
# poltergeist always confirms popups.
end
step 'I should see group "Owned" in group list' do
- page.should have_content("Owned")
+ expect(page).to have_content("Owned")
end
step 'I should not see group "Owned" in group list' do
- page.should_not have_content("Owned")
+ expect(page).not_to have_content("Owned")
end
step 'I should see group "Guest" in group list' do
- page.should have_content("Guest")
+ expect(page).to have_content("Guest")
end
step 'I should not see group "Guest" in group list' do
- page.should_not have_content("Guest")
+ expect(page).not_to have_content("Guest")
end
step 'I click new group link' do
@@ -53,11 +53,15 @@ class Spinach::Features::DashboardGroup < Spinach::FeatureSteps
end
step 'I should be redirected to group "Samurai" page' do
- current_path.should == group_path(Group.find_by(name: 'Samurai'))
+ expect(current_path).to eq group_path(Group.find_by(name: 'Samurai'))
end
step 'I should see newly created group "Samurai"' do
- page.should have_content "Samurai"
- page.should have_content "Tokugawa Shogunate"
+ expect(page).to have_content "Samurai"
+ expect(page).to have_content "Tokugawa Shogunate"
+ end
+
+ step 'I should see the "Can not leave message"' do
+ expect(page).to have_content "You can not leave Owned group because you're the last owner"
end
end
diff --git a/features/steps/dashboard/help.rb b/features/steps/dashboard/help.rb
index ef433c57c6e..86ab31a58ab 100644
--- a/features/steps/dashboard/help.rb
+++ b/features/steps/dashboard/help.rb
@@ -12,7 +12,7 @@ class Spinach::Features::DashboardHelp < Spinach::FeatureSteps
end
step 'I should see "Rake Tasks" page markdown rendered' do
- page.should have_content "Gather information about GitLab and the system it runs on"
+ expect(page).to have_content "Gather information about GitLab and the system it runs on"
end
step 'Header "Rebuild project satellites" should have correct ids and links' do
diff --git a/features/steps/dashboard/issues.rb b/features/steps/dashboard/issues.rb
index 60da36e86de..cbe54e2dc79 100644
--- a/features/steps/dashboard/issues.rb
+++ b/features/steps/dashboard/issues.rb
@@ -46,11 +46,11 @@ class Spinach::Features::DashboardIssues < Spinach::FeatureSteps
end
def should_see(issue)
- page.should have_content(issue.title[0..10])
+ expect(page).to have_content(issue.title[0..10])
end
def should_not_see(issue)
- page.should_not have_content(issue.title[0..10])
+ expect(page).not_to have_content(issue.title[0..10])
end
def assigned_issue
diff --git a/features/steps/dashboard/merge_requests.rb b/features/steps/dashboard/merge_requests.rb
index 9d92082bb83..cec8d06adee 100644
--- a/features/steps/dashboard/merge_requests.rb
+++ b/features/steps/dashboard/merge_requests.rb
@@ -50,11 +50,11 @@ class Spinach::Features::DashboardMergeRequests < Spinach::FeatureSteps
end
def should_see(merge_request)
- page.should have_content(merge_request.title[0..10])
+ expect(page).to have_content(merge_request.title[0..10])
end
def should_not_see(merge_request)
- page.should_not have_content(merge_request.title[0..10])
+ expect(page).not_to have_content(merge_request.title[0..10])
end
def assigned_merge_request
diff --git a/features/steps/dashboard/new_project.rb b/features/steps/dashboard/new_project.rb
index 93456a81ecf..b4ade65ee53 100644
--- a/features/steps/dashboard/new_project.rb
+++ b/features/steps/dashboard/new_project.rb
@@ -4,13 +4,13 @@ class Spinach::Features::NewProject < Spinach::FeatureSteps
include SharedProject
step 'I click "New project" link' do
- within('.content') do
+ page.within('.content') do
click_link "New project"
end
end
step 'I see "New project" page' do
- page.should have_content("Project path")
+ expect(page).to have_content("Project path")
end
step 'I click on "Import project from GitHub"' do
@@ -19,11 +19,11 @@ class Spinach::Features::NewProject < Spinach::FeatureSteps
step 'I see instructions on how to import from GitHub' do
github_modal = first('.modal-body')
- github_modal.should be_visible
- github_modal.should have_content "To enable importing projects from GitHub"
+ expect(github_modal).to be_visible
+ expect(github_modal).to have_content "To enable importing projects from GitHub"
- all('.modal-body').each do |element|
- element.should_not be_visible unless element == github_modal
+ page.all('.modal-body').each do |element|
+ expect(element).not_to be_visible unless element == github_modal
end
end
end
diff --git a/features/steps/dashboard/starred_projects.rb b/features/steps/dashboard/starred_projects.rb
index b9ad2f13e29..59c73fe63f2 100644
--- a/features/steps/dashboard/starred_projects.rb
+++ b/features/steps/dashboard/starred_projects.rb
@@ -8,8 +8,8 @@ class Spinach::Features::DashboardStarredProjects < Spinach::FeatureSteps
end
step 'I should not see project "Shop"' do
- within 'aside' do
- page.should_not have_content('Shop')
+ page.within 'aside' do
+ expect(page).not_to have_content('Shop')
end
end
end
diff --git a/features/steps/explore/groups.rb b/features/steps/explore/groups.rb
index 0c2127d4c4b..89b82293ef2 100644
--- a/features/steps/explore/groups.rb
+++ b/features/steps/explore/groups.rb
@@ -39,19 +39,19 @@ class Spinach::Features::ExploreGroups < Spinach::FeatureSteps
end
step 'I should not see project "Enterprise" items' do
- page.should_not have_content "Enterprise"
+ expect(page).not_to have_content "Enterprise"
end
step 'I should see project "Internal" items' do
- page.should have_content "Internal"
+ expect(page).to have_content "Internal"
end
step 'I should not see project "Internal" items' do
- page.should_not have_content "Internal"
+ expect(page).not_to have_content "Internal"
end
step 'I should see project "Community" items' do
- page.should have_content "Community"
+ expect(page).to have_content "Community"
end
step 'I change filter to Everyone\'s' do
@@ -59,11 +59,11 @@ class Spinach::Features::ExploreGroups < Spinach::FeatureSteps
end
step 'I should see group member "John Doe"' do
- page.should have_content "John Doe"
+ expect(page).to have_content "John Doe"
end
step 'I should not see member roles' do
- body.should_not match(%r{owner|developer|reporter|guest}i)
+ expect(body).not_to match(%r{owner|developer|reporter|guest}i)
end
protected
diff --git a/features/steps/explore/projects.rb b/features/steps/explore/projects.rb
index 26b71406bd8..49c2f6a1253 100644
--- a/features/steps/explore/projects.rb
+++ b/features/steps/explore/projects.rb
@@ -4,56 +4,56 @@ class Spinach::Features::ExploreProjects < Spinach::FeatureSteps
include SharedProject
step 'I should see project "Empty Public Project"' do
- page.should have_content "Empty Public Project"
+ expect(page).to have_content "Empty Public Project"
end
step 'I should see public project details' do
- page.should have_content '32 branches'
- page.should have_content '16 tags'
+ expect(page).to have_content '32 branches'
+ expect(page).to have_content '16 tags'
end
step 'I should see project readme' do
- page.should have_content 'README.md'
+ expect(page).to have_content 'README.md'
end
step 'I should see empty public project details' do
- page.should have_content 'Git global setup'
+ expect(page).to have_content 'Git global setup'
end
step 'I should see empty public project details with http clone info' do
project = Project.find_by(name: 'Empty Public Project')
- all(:css, '.git-empty .clone').each do |element|
- element.text.should include(project.http_url_to_repo)
+ page.all(:css, '.git-empty .clone').each do |element|
+ expect(element.text).to include(project.http_url_to_repo)
end
end
step 'I should see empty public project details with ssh clone info' do
project = Project.find_by(name: 'Empty Public Project')
- all(:css, '.git-empty .clone').each do |element|
- element.text.should include(project.url_to_repo)
+ page.all(:css, '.git-empty .clone').each do |element|
+ expect(element.text).to include(project.url_to_repo)
end
end
step 'I should see project "Community" home page' do
- within '.navbar-gitlab .title' do
- page.should have_content 'Community'
+ page.within '.navbar-gitlab .title' do
+ expect(page).to have_content 'Community'
end
end
step 'I should see project "Internal" home page' do
- within '.navbar-gitlab .title' do
- page.should have_content 'Internal'
+ page.within '.navbar-gitlab .title' do
+ expect(page).to have_content 'Internal'
end
end
step 'I should see an http link to the repository' do
project = Project.find_by(name: 'Community')
- page.should have_field('project_clone', with: project.http_url_to_repo)
+ expect(page).to have_field('project_clone', with: project.http_url_to_repo)
end
step 'I should see an ssh link to the repository' do
project = Project.find_by(name: 'Community')
- page.should have_field('project_clone', with: project.url_to_repo)
+ expect(page).to have_field('project_clone', with: project.url_to_repo)
end
step 'I visit "Community" issues page' do
@@ -70,9 +70,9 @@ class Spinach::Features::ExploreProjects < Spinach::FeatureSteps
step 'I should see list of issues for "Community" project' do
- page.should have_content "Bug"
- page.should have_content public_project.name
- page.should have_content "New feature"
+ expect(page).to have_content "Bug"
+ expect(page).to have_content public_project.name
+ expect(page).to have_content "New feature"
end
step 'I visit "Internal" issues page' do
@@ -89,9 +89,9 @@ class Spinach::Features::ExploreProjects < Spinach::FeatureSteps
step 'I should see list of issues for "Internal" project' do
- page.should have_content "Internal Bug"
- page.should have_content internal_project.name
- page.should have_content "New internal feature"
+ expect(page).to have_content "Internal Bug"
+ expect(page).to have_content internal_project.name
+ expect(page).to have_content "New internal feature"
end
step 'I visit "Community" merge requests page' do
@@ -107,8 +107,8 @@ class Spinach::Features::ExploreProjects < Spinach::FeatureSteps
end
step 'I should see list of merge requests for "Community" project' do
- page.should have_content public_project.name
- page.should have_content public_merge_request.source_project.name
+ expect(page).to have_content public_project.name
+ expect(page).to have_content public_merge_request.source_project.name
end
step 'I visit "Internal" merge requests page' do
@@ -124,8 +124,8 @@ class Spinach::Features::ExploreProjects < Spinach::FeatureSteps
end
step 'I should see list of merge requests for "Internal" project' do
- page.should have_content internal_project.name
- page.should have_content internal_merge_request.source_project.name
+ expect(page).to have_content internal_project.name
+ expect(page).to have_content internal_merge_request.source_project.name
end
def internal_project
diff --git a/features/steps/groups.rb b/features/steps/groups.rb
index 84348d1709a..c6c855a7c22 100644
--- a/features/steps/groups.rb
+++ b/features/steps/groups.rb
@@ -16,7 +16,7 @@ class Spinach::Features::Groups < Spinach::FeatureSteps
step 'I select "Mike" as "Reporter"' do
user = User.find_by(name: "Mike")
- within ".users-group-form" do
+ page.within ".users-group-form" do
select2(user.id, from: "#user_ids", multiple: true)
select "Reporter", from: "access_level"
end
@@ -25,14 +25,14 @@ class Spinach::Features::Groups < Spinach::FeatureSteps
end
step 'I should see "Mike" in team list as "Reporter"' do
- within '.well-list' do
- page.should have_content('Mike')
- page.should have_content('Reporter')
+ page.within '.well-list' do
+ expect(page).to have_content('Mike')
+ expect(page).to have_content('Reporter')
end
end
step 'I select "sjobs@apple.com" as "Reporter"' do
- within ".users-group-form" do
+ page.within ".users-group-form" do
select2("sjobs@apple.com", from: "#user_ids", multiple: true)
select "Reporter", from: "access_level"
end
@@ -41,39 +41,39 @@ class Spinach::Features::Groups < Spinach::FeatureSteps
end
step 'I should see "sjobs@apple.com" in team list as invited "Reporter"' do
- within '.well-list' do
- page.should have_content('sjobs@apple.com')
- page.should have_content('invited')
- page.should have_content('Reporter')
+ page.within '.well-list' do
+ expect(page).to have_content('sjobs@apple.com')
+ expect(page).to have_content('invited')
+ expect(page).to have_content('Reporter')
end
end
step 'I should see group "Owned" projects list' do
Group.find_by(name: "Owned").projects.each do |project|
- page.should have_link project.name
+ expect(page).to have_link project.name
end
end
step 'I should see projects activity feed' do
- page.should have_content 'closed issue'
+ expect(page).to have_content 'closed issue'
end
step 'I should see issues from group "Owned" assigned to me' do
assigned_to_me(:issues).each do |issue|
- page.should have_content issue.title
+ expect(page).to have_content issue.title
end
end
step 'I should see merge requests from group "Owned" assigned to me' do
assigned_to_me(:merge_requests).each do |issue|
- page.should have_content issue.title[0..80]
+ expect(page).to have_content issue.title[0..80]
end
end
step 'I select user "Mary Jane" from list with role "Reporter"' do
user = User.find_by(name: "Mary Jane") || create(:user, name: "Mary Jane")
click_button 'Add members'
- within ".users-group-form" do
+ page.within ".users-group-form" do
select2(user.id, from: "#user_ids", multiple: true)
select "Reporter", from: "access_level"
end
@@ -82,22 +82,22 @@ class Spinach::Features::Groups < Spinach::FeatureSteps
step 'I should see user "John Doe" in team list' do
projects_with_access = find(".panel .well-list")
- projects_with_access.should have_content("John Doe")
+ expect(projects_with_access).to have_content("John Doe")
end
step 'I should not see user "John Doe" in team list' do
projects_with_access = find(".panel .well-list")
- projects_with_access.should_not have_content("John Doe")
+ expect(projects_with_access).not_to have_content("John Doe")
end
step 'I should see user "Mary Jane" in team list' do
projects_with_access = find(".panel .well-list")
- projects_with_access.should have_content("Mary Jane")
+ expect(projects_with_access).to have_content("Mary Jane")
end
step 'I should not see user "Mary Jane" in team list' do
projects_with_access = find(".panel .well-list")
- projects_with_access.should_not have_content("Mary Jane")
+ expect(projects_with_access).not_to have_content("Mary Jane")
end
step 'project from group "Owned" has issues assigned to me' do
@@ -122,8 +122,8 @@ class Spinach::Features::Groups < Spinach::FeatureSteps
end
step 'I should see new group "Owned" name' do
- within ".navbar-gitlab" do
- page.should have_content "new-name"
+ page.within ".navbar-gitlab" do
+ expect(page).to have_content "new-name"
end
end
@@ -134,12 +134,12 @@ class Spinach::Features::Groups < Spinach::FeatureSteps
end
step 'I should see new group "Owned" avatar' do
- Group.find_by(name: "Owned").avatar.should be_instance_of AvatarUploader
- Group.find_by(name: "Owned").avatar.url.should == "/uploads/group/avatar/#{ Group.find_by(name:"Owned").id }/gitlab_logo.png"
+ expect(Group.find_by(name: "Owned").avatar).to be_instance_of AvatarUploader
+ expect(Group.find_by(name: "Owned").avatar.url).to eq "/uploads/group/avatar/#{ Group.find_by(name:"Owned").id }/gitlab_logo.png"
end
step 'I should see the "Remove avatar" button' do
- page.should have_link("Remove avatar")
+ expect(page).to have_link("Remove avatar")
end
step 'I have group "Owned" avatar' do
@@ -154,11 +154,11 @@ class Spinach::Features::Groups < Spinach::FeatureSteps
end
step 'I should not see group "Owned" avatar' do
- Group.find_by(name: "Owned").avatar?.should be_false
+ expect(Group.find_by(name: "Owned").avatar?).to be_false
end
step 'I should not see the "Remove avatar" button' do
- page.should_not have_link("Remove avatar")
+ expect(page).not_to have_link("Remove avatar")
end
step 'I click on the "Remove User From Group" button for "John Doe"' do
@@ -172,17 +172,17 @@ class Spinach::Features::Groups < Spinach::FeatureSteps
end
step 'I should not see the "Remove User From Group" button for "John Doe"' do
- find(:css, 'li', text: "John Doe").should_not have_selector(:css, 'a.btn-remove')
+ expect(find(:css, 'li', text: "John Doe")).not_to have_selector(:css, 'a.btn-remove')
# poltergeist always confirms popups.
end
step 'I should not see the "Remove User From Group" button for "Mary Jane"' do
- find(:css, 'li', text: "Mary Jane").should_not have_selector(:css, 'a.btn-remove')
+ expect(find(:css, 'li', text: "Mary Jane")).not_to have_selector(:css, 'a.btn-remove')
# poltergeist always confirms popups.
end
step 'I search for \'Mary\' member' do
- within '.member-search-form' do
+ page.within '.member-search-form' do
fill_in 'search', with: 'Mary'
click_button 'Search'
end
@@ -193,7 +193,7 @@ class Spinach::Features::Groups < Spinach::FeatureSteps
end
step 'I should see group milestones index page has no milestones' do
- page.should have_content('No milestones to show')
+ expect(page).to have_content('No milestones to show')
end
step 'Group has projects with milestones' do
@@ -201,10 +201,10 @@ class Spinach::Features::Groups < Spinach::FeatureSteps
end
step 'I should see group milestones index page with milestones' do
- page.should have_content('Version 7.2')
- page.should have_content('GL-113')
- page.should have_link('2 Issues', href: issues_group_path("owned", milestone_title: "Version 7.2"))
- page.should have_link('3 Merge Requests', href: merge_requests_group_path("owned", milestone_title: "GL-113"))
+ expect(page).to have_content('Version 7.2')
+ expect(page).to have_content('GL-113')
+ expect(page).to have_link('2 Issues', href: issues_group_path("owned", milestone_title: "Version 7.2"))
+ expect(page).to have_link('3 Merge Requests', href: merge_requests_group_path("owned", milestone_title: "GL-113"))
end
step 'I click on one group milestone' do
@@ -212,14 +212,14 @@ class Spinach::Features::Groups < Spinach::FeatureSteps
end
step 'I should see group milestone with descriptions and expiry date' do
- page.should have_content('expires at Aug 20, 2114')
+ expect(page).to have_content('expires at Aug 20, 2114')
end
step 'I should see group milestone with all issues and MRs assigned to that milestone' do
- page.should have_content('Milestone GL-113')
- page.should have_content('Progress: 0 closed – 4 open')
- page.should have_link(@issue1.title, href: namespace_project_issue_path(@project1.namespace, @project1, @issue1))
- page.should have_link(@mr3.title, href: namespace_project_merge_request_path(@project3.namespace, @project3, @mr3))
+ expect(page).to have_content('Milestone GL-113')
+ expect(page).to have_content('Progress: 0 closed – 4 open')
+ expect(page).to have_link(@issue1.title, href: namespace_project_issue_path(@project1.namespace, @project1, @issue1))
+ expect(page).to have_link(@mr3.title, href: namespace_project_merge_request_path(@project3.namespace, @project3, @mr3))
end
protected
diff --git a/features/steps/profile/emails.rb b/features/steps/profile/emails.rb
index 2b6ac37d866..10ebe705365 100644
--- a/features/steps/profile/emails.rb
+++ b/features/steps/profile/emails.rb
@@ -6,9 +6,9 @@ class Spinach::Features::ProfileEmails < Spinach::FeatureSteps
end
step 'I should see my emails' do
- page.should have_content(@user.email)
+ expect(page).to have_content(@user.email)
@user.emails.each do |email|
- page.should have_content(email.email)
+ expect(page).to have_content(email.email)
end
end
@@ -19,14 +19,14 @@ class Spinach::Features::ProfileEmails < Spinach::FeatureSteps
step 'I should see new email "my@email.com"' do
email = @user.emails.find_by(email: "my@email.com")
- email.should_not be_nil
- page.should have_content("my@email.com")
+ expect(email).not_to be_nil
+ expect(page).to have_content("my@email.com")
end
step 'I should not see email "my@email.com"' do
email = @user.emails.find_by(email: "my@email.com")
- email.should be_nil
- page.should_not have_content("my@email.com")
+ expect(email).to be_nil
+ expect(page).not_to have_content("my@email.com")
end
step 'I click link "Remove" for "my@email.com"' do
@@ -43,6 +43,6 @@ class Spinach::Features::ProfileEmails < Spinach::FeatureSteps
step 'I should not have @user.email added' do
email = @user.emails.find_by(email: @user.email)
- email.should be_nil
+ expect(email).to be_nil
end
end
diff --git a/features/steps/profile/notifications.rb b/features/steps/profile/notifications.rb
index 13e93618eb7..447ea6d9d10 100644
--- a/features/steps/profile/notifications.rb
+++ b/features/steps/profile/notifications.rb
@@ -7,6 +7,6 @@ class Spinach::Features::ProfileNotifications < Spinach::FeatureSteps
end
step 'I should see global notifications settings' do
- page.should have_content "Notifications Settings"
+ expect(page).to have_content "Notifications"
end
end
diff --git a/features/steps/profile/profile.rb b/features/steps/profile/profile.rb
index 791982d16c3..32e6859eff7 100644
--- a/features/steps/profile/profile.rb
+++ b/features/steps/profile/profile.rb
@@ -3,25 +3,27 @@ class Spinach::Features::Profile < Spinach::FeatureSteps
include SharedPaths
step 'I should see my profile info' do
- page.should have_content "Profile Settings"
+ expect(page).to have_content "This information will appear on your profile"
end
step 'I change my profile info' do
- fill_in "user_skype", with: "testskype"
- fill_in "user_linkedin", with: "testlinkedin"
- fill_in "user_twitter", with: "testtwitter"
- fill_in "user_website_url", with: "testurl"
- fill_in "user_location", with: "Ukraine"
- click_button "Save changes"
+ fill_in 'user_skype', with: 'testskype'
+ fill_in 'user_linkedin', with: 'testlinkedin'
+ fill_in 'user_twitter', with: 'testtwitter'
+ fill_in 'user_website_url', with: 'testurl'
+ fill_in 'user_location', with: 'Ukraine'
+ fill_in 'user_bio', with: 'I <3 GitLab'
+ click_button 'Save changes'
@user.reload
end
step 'I should see new profile info' do
- @user.skype.should == 'testskype'
- @user.linkedin.should == 'testlinkedin'
- @user.twitter.should == 'testtwitter'
- @user.website_url.should == 'testurl'
- find("#user_location").value.should == "Ukraine"
+ expect(@user.skype).to eq 'testskype'
+ expect(@user.linkedin).to eq 'testlinkedin'
+ expect(@user.twitter).to eq 'testtwitter'
+ expect(@user.website_url).to eq 'testurl'
+ expect(@user.bio).to eq 'I <3 GitLab'
+ expect(find('#user_location').value).to eq 'Ukraine'
end
step 'I change my avatar' do
@@ -31,12 +33,12 @@ class Spinach::Features::Profile < Spinach::FeatureSteps
end
step 'I should see new avatar' do
- @user.avatar.should be_instance_of AvatarUploader
- @user.avatar.url.should == "/uploads/user/avatar/#{ @user.id }/gitlab_logo.png"
+ expect(@user.avatar).to be_instance_of AvatarUploader
+ expect(@user.avatar.url).to eq "/uploads/user/avatar/#{ @user.id }/gitlab_logo.png"
end
step 'I should see the "Remove avatar" button' do
- page.should have_link("Remove avatar")
+ expect(page).to have_link("Remove avatar")
end
step 'I have an avatar' do
@@ -51,15 +53,15 @@ class Spinach::Features::Profile < Spinach::FeatureSteps
end
step 'I should see my gravatar' do
- @user.avatar?.should be_false
+ expect(@user.avatar?).to be_false
end
step 'I should not see the "Remove avatar" button' do
- page.should_not have_link("Remove avatar")
+ expect(page).not_to have_link("Remove avatar")
end
step 'I try change my password w/o old one' do
- within '.update-password' do
+ page.within '.update-password' do
fill_in "user_password", with: "22233344"
fill_in "user_password_confirmation", with: "22233344"
click_button "Save"
@@ -67,7 +69,7 @@ class Spinach::Features::Profile < Spinach::FeatureSteps
end
step 'I change my password' do
- within '.update-password' do
+ page.within '.update-password' do
fill_in "user_current_password", with: "12345678"
fill_in "user_password", with: "22233344"
fill_in "user_password_confirmation", with: "22233344"
@@ -76,7 +78,7 @@ class Spinach::Features::Profile < Spinach::FeatureSteps
end
step 'I unsuccessfully change my password' do
- within '.update-password' do
+ page.within '.update-password' do
fill_in "user_current_password", with: "12345678"
fill_in "user_password", with: "password"
fill_in "user_password_confirmation", with: "confirmation"
@@ -85,23 +87,23 @@ class Spinach::Features::Profile < Spinach::FeatureSteps
end
step "I should see a missing password error message" do
- page.should have_content "You must provide a valid current password"
+ expect(page).to have_content "You must provide a valid current password"
end
step "I should see a password error message" do
- page.should have_content "Password confirmation doesn't match"
+ expect(page).to have_content "Password confirmation doesn't match"
end
step 'I reset my token' do
- within '.update-token' do
+ page.within '.update-token' do
@old_token = @user.private_token
click_button "Reset"
end
end
step 'I should see new token' do
- find("#token").value.should_not == @old_token
- find("#token").value.should == @user.reload.private_token
+ expect(find("#token").value).not_to eq @old_token
+ expect(find("#token").value).to eq @user.reload.private_token
end
step 'I have activity' do
@@ -109,28 +111,28 @@ class Spinach::Features::Profile < Spinach::FeatureSteps
end
step 'I should see my activity' do
- page.should have_content "#{current_user.name} closed issue"
+ expect(page).to have_content "#{current_user.name} closed issue"
end
step "I change my application theme" do
- within '.application-theme' do
+ page.within '.application-theme' do
choose "Violet"
end
end
step "I change my code preview theme" do
- within '.code-preview-theme' do
+ page.within '.code-preview-theme' do
choose "Solarized dark"
end
end
step "I should see the theme change immediately" do
- page.should have_selector('body.ui_color')
- page.should_not have_selector('body.ui_basic')
+ expect(page).to have_selector('body.ui_color')
+ expect(page).not_to have_selector('body.ui_basic')
end
step "I should receive feedback that the changes were saved" do
- page.should have_content("saved")
+ expect(page).to have_content("saved")
end
step 'my password is expired' do
@@ -139,11 +141,11 @@ class Spinach::Features::Profile < Spinach::FeatureSteps
step "I am not an ldap user" do
current_user.identities.delete
- current_user.ldap_user?.should be_false
+ expect(current_user.ldap_user?).to be_false
end
step 'I redirected to expired password page' do
- current_path.should == new_profile_password_path
+ expect(current_path).to eq new_profile_password_path
end
step 'I submit new password' do
@@ -154,26 +156,26 @@ class Spinach::Features::Profile < Spinach::FeatureSteps
end
step 'I redirected to sign in page' do
- current_path.should == new_user_session_path
+ expect(current_path).to eq new_user_session_path
end
step 'I should be redirected to password page' do
- current_path.should == edit_profile_password_path
+ expect(current_path).to eq edit_profile_password_path
end
step 'I should be redirected to account page' do
- current_path.should == profile_account_path
+ expect(current_path).to eq profile_account_path
end
step 'I click on my profile picture' do
- click_link 'profile-pic'
+ find(:css, '.sidebar-user').click
end
step 'I should see my user page' do
- page.should have_content "User Activity"
+ expect(page).to have_content "User Activity"
- within '.navbar-gitlab' do
- page.should have_content current_user.name
+ page.within '.navbar-gitlab' do
+ expect(page).to have_content current_user.name
end
end
@@ -187,7 +189,7 @@ class Spinach::Features::Profile < Spinach::FeatureSteps
end
step 'I should see groups I belong to' do
- page.should have_css('.profile-groups-avatars', visible: true)
+ expect(page).to have_css('.profile-groups-avatars', visible: true)
end
step 'I click on new application button' do
@@ -195,7 +197,7 @@ class Spinach::Features::Profile < Spinach::FeatureSteps
end
step 'I should see application form' do
- page.should have_content "New application"
+ expect(page).to have_content "New application"
end
step 'I fill application form out and submit' do
@@ -205,9 +207,9 @@ class Spinach::Features::Profile < Spinach::FeatureSteps
end
step 'I see application' do
- page.should have_content "Application: test"
- page.should have_content "Application Id"
- page.should have_content "Secret"
+ expect(page).to have_content "Application: test"
+ expect(page).to have_content "Application Id"
+ expect(page).to have_content "Secret"
end
step 'I click edit' do
@@ -215,28 +217,28 @@ class Spinach::Features::Profile < Spinach::FeatureSteps
end
step 'I see edit application form' do
- page.should have_content "Edit application"
+ expect(page).to have_content "Edit application"
end
step 'I change name of application and submit' do
- page.should have_content "Edit application"
+ expect(page).to have_content "Edit application"
fill_in :doorkeeper_application_name, with: 'test_changed'
click_on "Submit"
end
step 'I see that application was changed' do
- page.should have_content "test_changed"
- page.should have_content "Application Id"
- page.should have_content "Secret"
+ expect(page).to have_content "test_changed"
+ expect(page).to have_content "Application Id"
+ expect(page).to have_content "Secret"
end
step 'I click to remove application' do
- within '.oauth-applications' do
+ page.within '.oauth-applications' do
click_on "Destroy"
end
end
step "I see that application is removed" do
- page.find(".oauth-applications").should_not have_content "test_changed"
+ expect(page.find(".oauth-applications")).not_to have_content "test_changed"
end
end
diff --git a/features/steps/profile/ssh_keys.rb b/features/steps/profile/ssh_keys.rb
index ea912e5b4da..c7f879d247d 100644
--- a/features/steps/profile/ssh_keys.rb
+++ b/features/steps/profile/ssh_keys.rb
@@ -3,7 +3,7 @@ class Spinach::Features::ProfileSshKeys < Spinach::FeatureSteps
step 'I should see my ssh keys' do
@user.keys.each do |key|
- page.should have_content(key.title)
+ expect(page).to have_content(key.title)
end
end
@@ -19,9 +19,9 @@ class Spinach::Features::ProfileSshKeys < Spinach::FeatureSteps
step 'I should see new ssh key "Laptop"' do
key = Key.find_by(title: "Laptop")
- page.should have_content(key.title)
- page.should have_content(key.key)
- current_path.should == profile_key_path(key)
+ expect(page).to have_content(key.title)
+ expect(page).to have_content(key.key)
+ expect(current_path).to eq profile_key_path(key)
end
step 'I click link "Work"' do
@@ -37,7 +37,7 @@ class Spinach::Features::ProfileSshKeys < Spinach::FeatureSteps
end
step 'I should not see "Work" ssh key' do
- page.should_not have_content "Work"
+ expect(page).not_to have_content "Work"
end
step 'I have ssh key "ssh-rsa Work"' do
diff --git a/features/steps/project/active_tab.rb b/features/steps/project/active_tab.rb
index dd3215adb1a..fabbc1d3d81 100644
--- a/features/steps/project/active_tab.rb
+++ b/features/steps/project/active_tab.rb
@@ -20,7 +20,7 @@ class Spinach::Features::ProjectActiveTab < Spinach::FeatureSteps
end
step 'I click the "Edit" tab' do
- within '.project-settings-nav' do
+ page.within '.project-settings-nav' do
click_link('Project')
end
end
diff --git a/features/steps/project/archived.rb b/features/steps/project/archived.rb
index 37ad0c77655..db1387763d5 100644
--- a/features/steps/project/archived.rb
+++ b/features/steps/project/archived.rb
@@ -19,11 +19,11 @@ class Spinach::Features::ProjectArchived < Spinach::FeatureSteps
end
step 'I should not see "Archived"' do
- page.should_not have_content "Archived"
+ expect(page).not_to have_content "Archived"
end
step 'I should see "Archived"' do
- page.should have_content "Archived"
+ expect(page).to have_content "Archived"
end
When 'I set project archived' do
diff --git a/features/steps/project/commits/branches.rb b/features/steps/project/commits/branches.rb
index 07f7e5796a3..338f5e8d3ee 100644
--- a/features/steps/project/commits/branches.rb
+++ b/features/steps/project/commits/branches.rb
@@ -8,8 +8,8 @@ class Spinach::Features::ProjectCommitsBranches < Spinach::FeatureSteps
end
step 'I should see "Shop" all branches list' do
- page.should have_content "Branches"
- page.should have_content "master"
+ expect(page).to have_content "Branches"
+ expect(page).to have_content "master"
end
step 'I click link "Protected"' do
@@ -17,9 +17,9 @@ class Spinach::Features::ProjectCommitsBranches < Spinach::FeatureSteps
end
step 'I should see "Shop" protected branches list' do
- within ".protected-branches-list" do
- page.should have_content "stable"
- page.should_not have_content "master"
+ page.within ".protected-branches-list" do
+ expect(page).to have_content "stable"
+ expect(page).not_to have_content "master"
end
end
@@ -57,29 +57,29 @@ class Spinach::Features::ProjectCommitsBranches < Spinach::FeatureSteps
end
step 'I should see new branch created' do
- page.should have_content 'deploy_keys'
+ expect(page).to have_content 'deploy_keys'
end
step 'I should see new an error that branch is invalid' do
- page.should have_content 'Branch name invalid'
+ expect(page).to have_content 'Branch name invalid'
end
step 'I should see new an error that ref is invalid' do
- page.should have_content 'Invalid reference name'
+ expect(page).to have_content 'Invalid reference name'
end
step 'I should see new an error that branch already exists' do
- page.should have_content 'Branch already exists'
+ expect(page).to have_content 'Branch already exists'
end
step "I click branch 'improve/awesome' delete link" do
- within '.js-branch-improve\/awesome' do
+ page.within '.js-branch-improve\/awesome' do
find('.btn-remove').click
sleep 0.05
end
end
step "I should not see branch 'improve/awesome'" do
- all(visible: true).should_not have_content 'improve/awesome'
+ expect(page.all(visible: true)).not_to have_content 'improve/awesome'
end
end
diff --git a/features/steps/project/commits/commits.rb b/features/steps/project/commits/commits.rb
index c888e82e207..4b19e3beed4 100644
--- a/features/steps/project/commits/commits.rb
+++ b/features/steps/project/commits/commits.rb
@@ -6,9 +6,9 @@ class Spinach::Features::ProjectCommits < Spinach::FeatureSteps
step 'I see project commits' do
commit = @project.repository.commit
- page.should have_content(@project.name)
- page.should have_content(commit.message[0..20])
- page.should have_content(commit.short_id)
+ expect(page).to have_content(@project.name)
+ expect(page).to have_content(commit.message[0..20])
+ expect(page).to have_content(commit.short_id)
end
step 'I click atom feed link' do
@@ -17,10 +17,10 @@ class Spinach::Features::ProjectCommits < Spinach::FeatureSteps
step 'I see commits atom feed' do
commit = @project.repository.commit
- response_headers['Content-Type'].should have_content("application/atom+xml")
- body.should have_selector("title", text: "#{@project.name}:master commits")
- body.should have_selector("author email", text: commit.author_email)
- body.should have_selector("entry summary", text: commit.description[0..10])
+ expect(response_headers['Content-Type']).to have_content("application/atom+xml")
+ expect(body).to have_selector("title", text: "#{@project.name}:master commits")
+ expect(body).to have_selector("author email", text: commit.author_email)
+ expect(body).to have_selector("entry summary", text: commit.description[0..10])
end
step 'I click on commit link' do
@@ -28,8 +28,8 @@ class Spinach::Features::ProjectCommits < Spinach::FeatureSteps
end
step 'I see commit info' do
- page.should have_content sample_commit.message
- page.should have_content "Showing #{sample_commit.files_changed_count} changed files"
+ expect(page).to have_content sample_commit.message
+ expect(page).to have_content "Showing #{sample_commit.files_changed_count} changed files"
end
step 'I fill compare fields with refs' do
@@ -45,38 +45,37 @@ class Spinach::Features::ProjectCommits < Spinach::FeatureSteps
end
step 'I should see additional file lines' do
- within @diff.parent do
- first('.new_line').text.should_not have_content "..."
+ page.within @diff.parent do
+ expect(first('.new_line').text).not_to have_content "..."
end
end
step 'I see compared refs' do
- page.should have_content "Compare View"
- page.should have_content "Commits (1)"
- page.should have_content "Showing 2 changed files"
+ expect(page).to have_content "Compare View"
+ expect(page).to have_content "Commits (1)"
+ expect(page).to have_content "Showing 2 changed files"
end
step 'I see breadcrumb links' do
- page.should have_selector('ul.breadcrumb')
- page.should have_selector('ul.breadcrumb a', count: 4)
+ expect(page).to have_selector('ul.breadcrumb')
+ expect(page).to have_selector('ul.breadcrumb a', count: 4)
end
step 'I see commits stats' do
- page.should have_content 'Top 50 Committers'
- page.should have_content 'Committers'
- page.should have_content 'Total commits'
- page.should have_content 'Authors'
+ expect(page).to have_content 'Top 50 Committers'
+ expect(page).to have_content 'Committers'
+ expect(page).to have_content 'Total commits'
+ expect(page).to have_content 'Authors'
end
step 'I visit big commit page' do
- Commit::DIFF_SAFE_FILES = 20
+ stub_const('Commit::DIFF_SAFE_FILES', 20)
visit namespace_project_commit_path(@project.namespace, @project, sample_big_commit.id)
end
step 'I see big commit warning' do
- page.should have_content sample_big_commit.message
- page.should have_content "Too many changes"
- Commit::DIFF_SAFE_FILES = 100
+ expect(page).to have_content sample_big_commit.message
+ expect(page).to have_content "Too many changes"
end
step 'I visit a commit with an image that changed' do
@@ -84,9 +83,9 @@ class Spinach::Features::ProjectCommits < Spinach::FeatureSteps
end
step 'The diff links to both the previous and current image' do
- links = all('.two-up span div a')
- links[0]['href'].should =~ %r{blob/#{sample_image_commit.old_blob_id}}
- links[1]['href'].should =~ %r{blob/#{sample_image_commit.new_blob_id}}
+ links = page.all('.two-up span div a')
+ expect(links[0]['href']).to match %r{blob/#{sample_image_commit.old_blob_id}}
+ expect(links[1]['href']).to match %r{blob/#{sample_image_commit.new_blob_id}}
end
step 'I click side-by-side diff button' do
@@ -94,10 +93,10 @@ class Spinach::Features::ProjectCommits < Spinach::FeatureSteps
end
step 'I see side-by-side diff button' do
- page.should have_content "Side-by-side"
+ expect(page).to have_content "Side-by-side"
end
step 'I see inline diff button' do
- page.should have_content "Inline"
+ expect(page).to have_content "Inline"
end
end
diff --git a/features/steps/project/commits/tags.rb b/features/steps/project/commits/tags.rb
index 3465fcbfd07..e6f8faf50fd 100644
--- a/features/steps/project/commits/tags.rb
+++ b/features/steps/project/commits/tags.rb
@@ -4,8 +4,8 @@ class Spinach::Features::ProjectCommitsTags < Spinach::FeatureSteps
include SharedPaths
step 'I should see "Shop" all tags list' do
- page.should have_content "Tags"
- page.should have_content "v1.0.0"
+ expect(page).to have_content "Tags"
+ expect(page).to have_content "v1.0.0"
end
step 'I click new tag link' do
@@ -37,37 +37,37 @@ class Spinach::Features::ProjectCommitsTags < Spinach::FeatureSteps
end
step 'I should see new tag created' do
- page.should have_content 'v7.0'
+ expect(page).to have_content 'v7.0'
end
step 'I should see new an error that tag is invalid' do
- page.should have_content 'Tag name invalid'
+ expect(page).to have_content 'Tag name invalid'
end
step 'I should see new an error that tag ref is invalid' do
- page.should have_content 'Invalid reference name'
+ expect(page).to have_content 'Invalid reference name'
end
step 'I should see new an error that tag already exists' do
- page.should have_content 'Tag already exists'
+ expect(page).to have_content 'Tag already exists'
end
step "I delete tag 'v1.1.0'" do
- within '.tags' do
+ page.within '.tags' do
first('.btn-remove').click
sleep 0.05
end
end
step "I should not see tag 'v1.1.0'" do
- within '.tags' do
- all(visible: true).should_not have_content 'v1.1.0'
+ page.within '.tags' do
+ expect(page.all(visible: true)).not_to have_content 'v1.1.0'
end
end
step 'I delete all tags' do
- within '.tags' do
- all('.btn-remove').each do |remove|
+ page.within '.tags' do
+ page.all('.btn-remove').each do |remove|
remove.click
sleep 0.05
end
@@ -75,8 +75,8 @@ class Spinach::Features::ProjectCommitsTags < Spinach::FeatureSteps
end
step 'I should see tags info message' do
- within '.tags' do
- page.should have_content 'Repository has no tags yet.'
+ page.within '.tags' do
+ expect(page).to have_content 'Repository has no tags yet.'
end
end
end
diff --git a/features/steps/project/commits/user_lookup.rb b/features/steps/project/commits/user_lookup.rb
index 63ff84c82ef..40cada6da45 100644
--- a/features/steps/project/commits/user_lookup.rb
+++ b/features/steps/project/commits/user_lookup.rb
@@ -29,9 +29,9 @@ class Spinach::Features::ProjectCommitsUserLookup < Spinach::FeatureSteps
def check_author_link(email, user)
author_link = find('.commit-author-link')
- author_link['href'].should == user_path(user)
- author_link['data-original-title'].should == email
- find('.commit-author-name').text.should == user.name
+ expect(author_link['href']).to eq user_path(user)
+ expect(author_link['data-original-title']).to eq email
+ expect(find('.commit-author-name').text).to eq user.name
end
def user_primary
diff --git a/features/steps/project/create.rb b/features/steps/project/create.rb
index 6b85cf74f5f..0d39e1997b5 100644
--- a/features/steps/project/create.rb
+++ b/features/steps/project/create.rb
@@ -8,20 +8,20 @@ class Spinach::Features::ProjectCreate < Spinach::FeatureSteps
end
step 'I should see project page' do
- page.should have_content "Empty"
- current_path.should == namespace_project_path(Project.last.namespace, Project.last)
+ expect(page).to have_content "Empty"
+ expect(current_path).to eq namespace_project_path(Project.last.namespace, Project.last)
end
step 'I should see empty project instuctions' do
- page.should have_content "git init"
- page.should have_content "git remote"
- page.should have_content Project.last.url_to_repo
+ expect(page).to have_content "git init"
+ expect(page).to have_content "git remote"
+ expect(page).to have_content Project.last.url_to_repo
end
step 'I see empty project instuctions' do
- page.should have_content "git init"
- page.should have_content "git remote"
- page.should have_content Project.last.url_to_repo
+ expect(page).to have_content "git init"
+ expect(page).to have_content "git remote"
+ expect(page).to have_content Project.last.url_to_repo
end
step 'I click on HTTP' do
@@ -29,7 +29,7 @@ class Spinach::Features::ProjectCreate < Spinach::FeatureSteps
end
step 'Remote url should update to http link' do
- page.should have_content "git remote add origin #{Project.last.http_url_to_repo}"
+ expect(page).to have_content "git remote add origin #{Project.last.http_url_to_repo}"
end
step 'If I click on SSH' do
@@ -37,6 +37,6 @@ class Spinach::Features::ProjectCreate < Spinach::FeatureSteps
end
step 'Remote url should update to ssh link' do
- page.should have_content "git remote add origin #{Project.last.url_to_repo}"
+ expect(page).to have_content "git remote add origin #{Project.last.url_to_repo}"
end
end
diff --git a/features/steps/project/deploy_keys.rb b/features/steps/project/deploy_keys.rb
index 81d1182cd1b..a4d6c9a1b8e 100644
--- a/features/steps/project/deploy_keys.rb
+++ b/features/steps/project/deploy_keys.rb
@@ -8,20 +8,20 @@ class Spinach::Features::ProjectDeployKeys < Spinach::FeatureSteps
end
step 'I should see project deploy key' do
- within '.enabled-keys' do
- page.should have_content deploy_key.title
+ page.within '.enabled-keys' do
+ expect(page).to have_content deploy_key.title
end
end
step 'I should see other project deploy key' do
- within '.available-keys' do
- page.should have_content other_deploy_key.title
+ page.within '.available-keys' do
+ expect(page).to have_content other_deploy_key.title
end
end
step 'I should see public deploy key' do
- within '.available-keys' do
- page.should have_content public_deploy_key.title
+ page.within '.available-keys' do
+ expect(page).to have_content public_deploy_key.title
end
end
@@ -36,12 +36,12 @@ class Spinach::Features::ProjectDeployKeys < Spinach::FeatureSteps
end
step 'I should be on deploy keys page' do
- current_path.should == namespace_project_deploy_keys_path(@project.namespace, @project)
+ expect(current_path).to eq namespace_project_deploy_keys_path(@project.namespace, @project)
end
step 'I should see newly created deploy key' do
- within '.enabled-keys' do
- page.should have_content(deploy_key.title)
+ page.within '.enabled-keys' do
+ expect(page).to have_content(deploy_key.title)
end
end
@@ -56,8 +56,8 @@ class Spinach::Features::ProjectDeployKeys < Spinach::FeatureSteps
end
step 'I should only see the same deploy key once' do
- within '.available-keys' do
- page.should have_selector('ul li', count: 1)
+ page.within '.available-keys' do
+ expect(page).to have_selector('ul li', count: 1)
end
end
@@ -66,7 +66,7 @@ class Spinach::Features::ProjectDeployKeys < Spinach::FeatureSteps
end
step 'I click attach deploy key' do
- within '.available-keys' do
+ page.within '.available-keys' do
click_link 'Enable'
end
end
diff --git a/features/steps/project/fork.rb b/features/steps/project/fork.rb
index 8e58597db20..0e433781d7a 100644
--- a/features/steps/project/fork.rb
+++ b/features/steps/project/fork.rb
@@ -4,8 +4,8 @@ class Spinach::Features::ProjectFork < Spinach::FeatureSteps
include SharedProject
step 'I click link "Fork"' do
- page.should have_content "Shop"
- page.should have_content "Fork"
+ expect(page).to have_content "Shop"
+ expect(page).to have_content "Fork"
click_link "Fork"
end
@@ -15,7 +15,7 @@ class Spinach::Features::ProjectFork < Spinach::FeatureSteps
end
step 'I should see the forked project page' do
- page.should have_content "Project was successfully forked."
+ expect(page).to have_content "Project was successfully forked."
end
step 'I already have a project named "Shop" in my namespace' do
@@ -23,11 +23,11 @@ class Spinach::Features::ProjectFork < Spinach::FeatureSteps
end
step 'I should see a "Name has already been taken" warning' do
- page.should have_content "Name has already been taken"
+ expect(page).to have_content "Name has already been taken"
end
step 'I fork to my namespace' do
- within '.fork-namespaces' do
+ page.within '.fork-namespaces' do
click_link current_user.name
end
end
diff --git a/features/steps/project/forked_merge_requests.rb b/features/steps/project/forked_merge_requests.rb
index ebfa102cee5..78812c52026 100644
--- a/features/steps/project/forked_merge_requests.rb
+++ b/features/steps/project/forked_merge_requests.rb
@@ -21,17 +21,17 @@ class Spinach::Features::ProjectForkedMergeRequests < Spinach::FeatureSteps
end
step 'I should see merge request "Merge Request On Forked Project"' do
- @project.merge_requests.size.should >= 1
+ expect(@project.merge_requests.size).to be >= 1
@merge_request = @project.merge_requests.last
- current_path.should == namespace_project_merge_request_path(@project.namespace, @project, @merge_request)
- @merge_request.title.should == "Merge Request On Forked Project"
- @merge_request.source_project.should == @forked_project
- @merge_request.source_branch.should == "fix"
- @merge_request.target_branch.should == "master"
- page.should have_content @forked_project.path_with_namespace
- page.should have_content @project.path_with_namespace
- page.should have_content @merge_request.source_branch
- page.should have_content @merge_request.target_branch
+ expect(current_path).to eq namespace_project_merge_request_path(@project.namespace, @project, @merge_request)
+ expect(@merge_request.title).to eq "Merge Request On Forked Project"
+ expect(@merge_request.source_project).to eq @forked_project
+ expect(@merge_request.source_branch).to eq "fix"
+ expect(@merge_request.target_branch).to eq "master"
+ expect(page).to have_content @forked_project.path_with_namespace
+ expect(page).to have_content @project.path_with_namespace
+ expect(page).to have_content @merge_request.source_branch
+ expect(page).to have_content @merge_request.target_branch
end
step 'I fill out a "Merge Request On Forked Project" merge request' do
@@ -56,7 +56,7 @@ class Spinach::Features::ProjectForkedMergeRequests < Spinach::FeatureSteps
step 'I should see the commit under the forked from project' do
commit = @project.repository.commit
- page.should have_content(commit.message)
+ expect(page).to have_content(commit.message)
end
step 'I click "Create Merge Request on fork" link' do
@@ -64,12 +64,12 @@ class Spinach::Features::ProjectForkedMergeRequests < Spinach::FeatureSteps
end
step 'I see prefilled new Merge Request page for the forked project' do
- current_path.should == new_namespace_project_merge_request_path(@forked_project.namespace, @forked_project)
- find("#merge_request_source_project_id").value.should == @forked_project.id.to_s
- find("#merge_request_target_project_id").value.should == @project.id.to_s
- find("#merge_request_source_branch").value.should have_content "new_design"
- find("#merge_request_target_branch").value.should have_content "master"
- find("#merge_request_title").value.should == "New Design"
+ expect(current_path).to eq new_namespace_project_merge_request_path(@forked_project.namespace, @forked_project)
+ expect(find("#merge_request_source_project_id").value).to eq @forked_project.id.to_s
+ expect(find("#merge_request_target_project_id").value).to eq @project.id.to_s
+ expect(find("#merge_request_source_branch").value).to have_content "new_design"
+ expect(find("#merge_request_target_branch").value).to have_content "master"
+ expect(find("#merge_request_title").value).to eq "New Design"
verify_commit_link(".mr_target_commit", @project)
verify_commit_link(".mr_source_commit", @forked_project)
end
@@ -83,22 +83,22 @@ class Spinach::Features::ProjectForkedMergeRequests < Spinach::FeatureSteps
end
step 'I should see the edited merge request' do
- page.should have_content "An Edited Forked Merge Request"
- @project.merge_requests.size.should >= 1
+ expect(page).to have_content "An Edited Forked Merge Request"
+ expect(@project.merge_requests.size).to be >= 1
@merge_request = @project.merge_requests.last
- current_path.should == namespace_project_merge_request_path(@project.namespace, @project, @merge_request)
- @merge_request.source_project.should == @forked_project
- @merge_request.source_branch.should == "fix"
- @merge_request.target_branch.should == "master"
- page.should have_content @forked_project.path_with_namespace
- page.should have_content @project.path_with_namespace
- page.should have_content @merge_request.source_branch
- page.should have_content @merge_request.target_branch
+ expect(current_path).to eq namespace_project_merge_request_path(@project.namespace, @project, @merge_request)
+ expect(@merge_request.source_project).to eq @forked_project
+ expect(@merge_request.source_branch).to eq "fix"
+ expect(@merge_request.target_branch).to eq "master"
+ expect(page).to have_content @forked_project.path_with_namespace
+ expect(page).to have_content @project.path_with_namespace
+ expect(page).to have_content @merge_request.source_branch
+ expect(page).to have_content @merge_request.target_branch
end
step 'I should see last push widget' do
- page.should have_content "You pushed to new_design"
- page.should have_link "Create Merge Request"
+ expect(page).to have_content "You pushed to new_design"
+ expect(page).to have_link "Create Merge Request"
end
step 'I click link edit "Merge Request On Forked Project"' do
@@ -106,26 +106,26 @@ class Spinach::Features::ProjectForkedMergeRequests < Spinach::FeatureSteps
end
step 'I see the edit page prefilled for "Merge Request On Forked Project"' do
- current_path.should == edit_namespace_project_merge_request_path(@project.namespace, @project, @merge_request)
- page.should have_content "Edit merge request ##{@merge_request.id}"
- find("#merge_request_title").value.should == "Merge Request On Forked Project"
+ expect(current_path).to eq edit_namespace_project_merge_request_path(@project.namespace, @project, @merge_request)
+ expect(page).to have_content "Edit merge request ##{@merge_request.id}"
+ expect(find("#merge_request_title").value).to eq "Merge Request On Forked Project"
end
step 'I fill out an invalid "Merge Request On Forked Project" merge request' do
select "Select branch", from: "merge_request_target_branch"
- find(:select, "merge_request_source_project_id", {}).value.should == @forked_project.id.to_s
- find(:select, "merge_request_target_project_id", {}).value.should == @project.id.to_s
- find(:select, "merge_request_source_branch", {}).value.should == ""
- find(:select, "merge_request_target_branch", {}).value.should == ""
+ expect(find(:select, "merge_request_source_project_id", {}).value).to eq @forked_project.id.to_s
+ expect(find(:select, "merge_request_target_project_id", {}).value).to eq @project.id.to_s
+ expect(find(:select, "merge_request_source_branch", {}).value).to eq ""
+ expect(find(:select, "merge_request_target_branch", {}).value).to eq ""
click_button "Compare branches"
end
step 'I should see validation errors' do
- page.should have_content "You must select source and target branch"
+ expect(page).to have_content "You must select source and target branch"
end
step 'the target repository should be the original repository' do
- page.should have_select("merge_request_target_project_id", selected: @project.path_with_namespace)
+ expect(page).to have_select("merge_request_target_project_id", selected: @project.path_with_namespace)
end
step 'I click "Assign to" dropdown"' do
@@ -139,13 +139,13 @@ class Spinach::Features::ProjectForkedMergeRequests < Spinach::FeatureSteps
step 'I should see the users from the target project ID' do
expect(page).to have_selector('.user-result', visible: true, count: 2)
users = page.all('.user-name')
- users[0].text.should == 'Unassigned'
- users[1].text.should == @project.users.first.name
+ expect(users[0].text).to eq 'Unassigned'
+ expect(users[1].text).to eq @project.users.first.name
end
# Verify a link is generated against the correct project
def verify_commit_link(container_div, container_project)
# This should force a wait for the javascript to execute
- find(:div,container_div).find(".commit_short_id")['href'].should have_content "#{container_project.path_with_namespace}/commit"
+ expect(find(:div,container_div).find(".commit_short_id")['href']).to have_content "#{container_project.path_with_namespace}/commit"
end
end
diff --git a/features/steps/project/graph.rb b/features/steps/project/graph.rb
index a2807c340f6..5e7e573a6ab 100644
--- a/features/steps/project/graph.rb
+++ b/features/steps/project/graph.rb
@@ -3,7 +3,7 @@ class Spinach::Features::ProjectGraph < Spinach::FeatureSteps
include SharedProject
step 'page should have graphs' do
- page.should have_selector ".stat-graph"
+ expect(page).to have_selector ".stat-graph"
end
When 'I visit project "Shop" graph page' do
@@ -17,7 +17,7 @@ class Spinach::Features::ProjectGraph < Spinach::FeatureSteps
end
step 'page should have commits graphs' do
- page.should have_content "Commit statistics for master"
- page.should have_content "Commits per day of month"
+ expect(page).to have_content "Commit statistics for master"
+ expect(page).to have_content "Commits per day of month"
end
end
diff --git a/features/steps/project/hooks.rb b/features/steps/project/hooks.rb
index d06905285fe..04e3bf78ede 100644
--- a/features/steps/project/hooks.rb
+++ b/features/steps/project/hooks.rb
@@ -19,7 +19,7 @@ class Spinach::Features::ProjectHooks < Spinach::FeatureSteps
end
step 'I should see project hook' do
- page.should have_content @hook.url
+ expect(page).to have_content @hook.url
end
step 'I submit new hook' do
@@ -29,8 +29,8 @@ class Spinach::Features::ProjectHooks < Spinach::FeatureSteps
end
step 'I should see newly created hook' do
- current_path.should == namespace_project_hooks_path(current_project.namespace, current_project)
- page.should have_content(@url)
+ expect(current_path).to eq namespace_project_hooks_path(current_project.namespace, current_project)
+ expect(page).to have_content(@url)
end
step 'I click test hook button' do
@@ -44,19 +44,19 @@ class Spinach::Features::ProjectHooks < Spinach::FeatureSteps
end
step 'hook should be triggered' do
- current_path.should == namespace_project_hooks_path(current_project.namespace, current_project)
- page.should have_selector '.flash-notice',
+ expect(current_path).to eq namespace_project_hooks_path(current_project.namespace, current_project)
+ expect(page).to have_selector '.flash-notice',
text: 'Hook successfully executed.'
end
step 'I should see hook error message' do
- page.should have_selector '.flash-alert',
+ expect(page).to have_selector '.flash-alert',
text: 'Hook execution failed. '\
'Ensure the project has commits.'
end
step 'I should see hook service down error message' do
- page.should have_selector '.flash-alert',
+ expect(page).to have_selector '.flash-alert',
text: 'Hook execution failed. '\
'Ensure hook URL is correct and '\
'service is up.'
diff --git a/features/steps/project/issues/filter_labels.rb b/features/steps/project/issues/filter_labels.rb
index 5740bd12837..50bb32429b9 100644
--- a/features/steps/project/issues/filter_labels.rb
+++ b/features/steps/project/issues/filter_labels.rb
@@ -5,26 +5,26 @@ class Spinach::Features::ProjectIssuesFilterLabels < Spinach::FeatureSteps
include Select2Helper
step 'I should see "Bugfix1" in issues list' do
- within ".issues-list" do
- page.should have_content "Bugfix1"
+ page.within ".issues-list" do
+ expect(page).to have_content "Bugfix1"
end
end
step 'I should see "Bugfix2" in issues list' do
- within ".issues-list" do
- page.should have_content "Bugfix2"
+ page.within ".issues-list" do
+ expect(page).to have_content "Bugfix2"
end
end
step 'I should not see "Bugfix2" in issues list' do
- within ".issues-list" do
- page.should_not have_content "Bugfix2"
+ page.within ".issues-list" do
+ expect(page).not_to have_content "Bugfix2"
end
end
step 'I should not see "Feature1" in issues list' do
- within ".issues-list" do
- page.should_not have_content "Feature1"
+ page.within ".issues-list" do
+ expect(page).not_to have_content "Feature1"
end
end
@@ -33,7 +33,7 @@ class Spinach::Features::ProjectIssuesFilterLabels < Spinach::FeatureSteps
end
step 'I click link "feature"' do
- within ".labels-filter" do
+ page.within ".labels-filter" do
click_link "feature"
end
end
diff --git a/features/steps/project/issues/issues.rb b/features/steps/project/issues/issues.rb
index 504f0cff724..6873c043e19 100644
--- a/features/steps/project/issues/issues.rb
+++ b/features/steps/project/issues/issues.rb
@@ -7,24 +7,24 @@ class Spinach::Features::ProjectIssues < Spinach::FeatureSteps
include SharedMarkdown
step 'I should see "Release 0.4" in issues' do
- page.should have_content "Release 0.4"
+ expect(page).to have_content "Release 0.4"
end
step 'I should not see "Release 0.3" in issues' do
- page.should_not have_content "Release 0.3"
+ expect(page).not_to have_content "Release 0.3"
end
step 'I should not see "Tweet control" in issues' do
- page.should_not have_content "Tweet control"
+ expect(page).not_to have_content "Tweet control"
end
step 'I should see that I am subscribed' do
- find(".subscribe-button span").text.should == "Unsubscribe"
+ expect(find(".subscribe-button span").text).to eq "Unsubscribe"
end
step 'I should see that I am unsubscribed' do
sleep 0.2
- find(".subscribe-button span").text.should == "Subscribe"
+ expect(find(".subscribe-button span").text).to eq "Subscribe"
end
step 'I click link "Closed"' do
@@ -36,11 +36,11 @@ class Spinach::Features::ProjectIssues < Spinach::FeatureSteps
end
step 'I should see "Release 0.3" in issues' do
- page.should have_content "Release 0.3"
+ expect(page).to have_content "Release 0.3"
end
step 'I should not see "Release 0.4" in issues' do
- page.should_not have_content "Release 0.4"
+ expect(page).not_to have_content "Release 0.4"
end
step 'I click link "All"' do
@@ -52,7 +52,7 @@ class Spinach::Features::ProjectIssues < Spinach::FeatureSteps
end
step 'I should see issue "Release 0.4"' do
- page.should have_content "Release 0.4"
+ expect(page).to have_content "Release 0.4"
end
step 'I click link "New Issue"' do
@@ -66,9 +66,9 @@ class Spinach::Features::ProjectIssues < Spinach::FeatureSteps
step 'I see current user as the first user' do
expect(page).to have_selector('.user-result', visible: true, count: 4)
users = page.all('.user-name')
- users[0].text.should == 'Any'
- users[1].text.should == 'Unassigned'
- users[2].text.should == current_user.name
+ expect(users[0].text).to eq 'Any'
+ expect(users[1].text).to eq 'Unassigned'
+ expect(users[2].text).to eq current_user.name
end
step 'I submit new issue "500 error on profile"' do
@@ -87,16 +87,16 @@ class Spinach::Features::ProjectIssues < Spinach::FeatureSteps
end
step 'I should see label \'bug\' with issue' do
- within '.issue-show-labels' do
- page.should have_content 'bug'
+ page.within '.issue-show-labels' do
+ expect(page).to have_content 'bug'
end
end
step 'I should see issue "500 error on profile"' do
issue = Issue.find_by(title: "500 error on profile")
- page.should have_content issue.title
- page.should have_content issue.author_name
- page.should have_content issue.project.name
+ expect(page).to have_content issue.title
+ expect(page).to have_content issue.author_name
+ expect(page).to have_content issue.project.name
end
step 'I fill in issue search with "Re"' do
@@ -139,7 +139,7 @@ class Spinach::Features::ProjectIssues < Spinach::FeatureSteps
step 'I should see selected milestone with title "v3.0"' do
issues_milestone_selector = "#issue_milestone_id_chzn > a"
- find(issues_milestone_selector).should have_content("v3.0")
+ expect(find(issues_milestone_selector)).to have_content("v3.0")
end
When 'I select first assignee from "Shop" project' do
@@ -152,7 +152,7 @@ class Spinach::Features::ProjectIssues < Spinach::FeatureSteps
issues_assignee_selector = "#issue_assignee_id_chzn > a"
assignee_name = project.users.first.name
- find(issues_assignee_selector).should have_content(assignee_name)
+ expect(find(issues_assignee_selector)).to have_content(assignee_name)
end
step 'project "Shop" have "Release 0.4" open issue' do
@@ -190,8 +190,8 @@ class Spinach::Features::ProjectIssues < Spinach::FeatureSteps
step 'I see empty project details with ssh clone info' do
project = Project.find_by(name: 'Empty Project')
- all(:css, '.git-empty .clone').each do |element|
- element.text.should include(project.url_to_repo)
+ page.all(:css, '.git-empty .clone').each do |element|
+ expect(element.text).to include(project.url_to_repo)
end
end
@@ -201,7 +201,7 @@ class Spinach::Features::ProjectIssues < Spinach::FeatureSteps
end
step 'I leave a comment with code block' do
- within(".js-main-target-form") do
+ page.within(".js-main-target-form") do
fill_in "note[note]", with: "```\nCommand [1]: /usr/local/bin/git , see [text](doc/text)\n```"
click_button "Add Comment"
sleep 0.05
@@ -209,13 +209,13 @@ class Spinach::Features::ProjectIssues < Spinach::FeatureSteps
end
step 'I should see an error alert section within the comment form' do
- within(".js-main-target-form") do
+ page.within(".js-main-target-form") do
find(".error-alert")
end
end
step 'The code block should be unchanged' do
- page.should have_content("```\nCommand [1]: /usr/local/bin/git , see [text](doc/text)\n```")
+ expect(page).to have_content("```\nCommand [1]: /usr/local/bin/git , see [text](doc/text)\n```")
end
step 'project \'Shop\' has issue \'Bugfix1\' with description: \'Description for issue1\'' do
@@ -239,15 +239,15 @@ class Spinach::Features::ProjectIssues < Spinach::FeatureSteps
end
step 'I should see \'Bugfix1\' in issues' do
- page.should have_content 'Bugfix1'
+ expect(page).to have_content 'Bugfix1'
end
step 'I should see \'Feature1\' in issues' do
- page.should have_content 'Feature1'
+ expect(page).to have_content 'Feature1'
end
step 'I should not see \'Bugfix1\' in issues' do
- page.should_not have_content 'Bugfix1'
+ expect(page).not_to have_content 'Bugfix1'
end
step 'issue \'Release 0.4\' has label \'bug\'' do
@@ -257,7 +257,7 @@ class Spinach::Features::ProjectIssues < Spinach::FeatureSteps
end
step 'I click label \'bug\'' do
- within ".issues-list" do
+ page.within ".issues-list" do
click_link 'bug'
end
end
diff --git a/features/steps/project/issues/labels.rb b/features/steps/project/issues/labels.rb
index 6ce34c500c6..d656acf4220 100644
--- a/features/steps/project/issues/labels.rb
+++ b/features/steps/project/issues/labels.rb
@@ -8,14 +8,14 @@ class Spinach::Features::ProjectIssuesLabels < Spinach::FeatureSteps
end
step 'I remove label \'bug\'' do
- within "#label_#{bug_label.id}" do
+ page.within "#label_#{bug_label.id}" do
click_link 'Remove'
end
end
step 'I delete all labels' do
- within '.labels' do
- all('.btn-remove').each do |remove|
+ page.within '.labels' do
+ page.all('.btn-remove').each do |remove|
remove.click
sleep 0.05
end
@@ -23,8 +23,8 @@ class Spinach::Features::ProjectIssuesLabels < Spinach::FeatureSteps
end
step 'I should see labels help message' do
- within '.labels' do
- page.should have_content 'Create first label or generate default set of '\
+ page.within '.labels' do
+ expect(page).to have_content 'Create first label or generate default set of '\
'labels'
end
end
@@ -48,38 +48,38 @@ class Spinach::Features::ProjectIssuesLabels < Spinach::FeatureSteps
end
step 'I should see label label exist error message' do
- within '.label-form' do
- page.should have_content 'Title has already been taken'
+ page.within '.label-form' do
+ expect(page).to have_content 'Title has already been taken'
end
end
step 'I should see label color error message' do
- within '.label-form' do
- page.should have_content 'Color is invalid'
+ page.within '.label-form' do
+ expect(page).to have_content 'Color is invalid'
end
end
step 'I should see label \'feature\'' do
- within '.manage-labels-list' do
- page.should have_content 'feature'
+ page.within '.manage-labels-list' do
+ expect(page).to have_content 'feature'
end
end
step 'I should see label \'bug\'' do
- within '.manage-labels-list' do
- page.should have_content 'bug'
+ page.within '.manage-labels-list' do
+ expect(page).to have_content 'bug'
end
end
step 'I should not see label \'bug\'' do
- within '.manage-labels-list' do
- page.should_not have_content 'bug'
+ page.within '.manage-labels-list' do
+ expect(page).not_to have_content 'bug'
end
end
step 'I should see label \'support\'' do
- within '.manage-labels-list' do
- page.should have_content 'support'
+ page.within '.manage-labels-list' do
+ expect(page).to have_content 'support'
end
end
@@ -90,8 +90,8 @@ class Spinach::Features::ProjectIssuesLabels < Spinach::FeatureSteps
end
step 'I should see label \'fix\'' do
- within '.manage-labels-list' do
- page.should have_content 'fix'
+ page.within '.manage-labels-list' do
+ expect(page).to have_content 'fix'
end
end
diff --git a/features/steps/project/issues/milestones.rb b/features/steps/project/issues/milestones.rb
index cce87a6d981..708c5243947 100644
--- a/features/steps/project/issues/milestones.rb
+++ b/features/steps/project/issues/milestones.rb
@@ -6,9 +6,9 @@ class Spinach::Features::ProjectIssuesMilestones < Spinach::FeatureSteps
step 'I should see milestone "v2.2"' do
milestone = @project.milestones.find_by(title: "v2.2")
- page.should have_content(milestone.title[0..10])
- page.should have_content(milestone.expires_at)
- page.should have_content("Issues")
+ expect(page).to have_content(milestone.title[0..10])
+ expect(page).to have_content(milestone.expires_at)
+ expect(page).to have_content("Issues")
end
step 'I click link "v2.2"' do
@@ -26,9 +26,9 @@ class Spinach::Features::ProjectIssuesMilestones < Spinach::FeatureSteps
step 'I should see milestone "v2.3"' do
milestone = @project.milestones.find_by(title: "v2.3")
- page.should have_content(milestone.title[0..10])
- page.should have_content(milestone.expires_at)
- page.should have_content("Issues")
+ expect(page).to have_content(milestone.title[0..10])
+ expect(page).to have_content(milestone.expires_at)
+ expect(page).to have_content("Issues")
end
step 'project "Shop" has milestone "v2.2"' do
@@ -54,6 +54,6 @@ class Spinach::Features::ProjectIssuesMilestones < Spinach::FeatureSteps
end
step 'I should see 3 issues' do
- page.should have_selector('#tab-issues li.issue-row', count: 4)
+ expect(page).to have_selector('#tab-issues li.issue-row', count: 4)
end
end
diff --git a/features/steps/project/merge_requests.rb b/features/steps/project/merge_requests.rb
index 48bb316e203..62c64e60f6d 100644
--- a/features/steps/project/merge_requests.rb
+++ b/features/steps/project/merge_requests.rb
@@ -24,44 +24,44 @@ class Spinach::Features::ProjectMergeRequests < Spinach::FeatureSteps
end
step 'I should see merge request "Wiki Feature"' do
- within '.merge-request' do
- page.should have_content "Wiki Feature"
+ page.within '.merge-request' do
+ expect(page).to have_content "Wiki Feature"
end
end
step 'I should see closed merge request "Bug NS-04"' do
merge_request = MergeRequest.find_by!(title: "Bug NS-04")
- merge_request.closed?.should be_true
- page.should have_content "Rejected by"
+ expect(merge_request.closed?).to be_true
+ expect(page).to have_content "Rejected by"
end
step 'I should see merge request "Bug NS-04"' do
- page.should have_content "Bug NS-04"
+ expect(page).to have_content "Bug NS-04"
end
step 'I should see "Bug NS-04" in merge requests' do
- page.should have_content "Bug NS-04"
+ expect(page).to have_content "Bug NS-04"
end
step 'I should see "Feature NS-03" in merge requests' do
- page.should have_content "Feature NS-03"
+ expect(page).to have_content "Feature NS-03"
end
step 'I should not see "Feature NS-03" in merge requests' do
- page.should_not have_content "Feature NS-03"
+ expect(page).not_to have_content "Feature NS-03"
end
step 'I should not see "Bug NS-04" in merge requests' do
- page.should_not have_content "Bug NS-04"
+ expect(page).not_to have_content "Bug NS-04"
end
step 'I should see that I am subscribed' do
- find(".subscribe-button span").text.should == "Unsubscribe"
+ expect(find(".subscribe-button span").text).to eq "Unsubscribe"
end
step 'I should see that I am unsubscribed' do
- find(".subscribe-button span").should have_content("Subscribe")
+ expect(find(".subscribe-button span")).to have_content("Subscribe")
end
step 'I click button "Unsubscribe"' do
@@ -113,7 +113,7 @@ class Spinach::Features::ProjectMergeRequests < Spinach::FeatureSteps
end
step 'I click on the Changes tab via Javascript' do
- within '.merge-request-tabs' do
+ page.within '.merge-request-tabs' do
click_link 'Changes'
end
@@ -121,11 +121,11 @@ class Spinach::Features::ProjectMergeRequests < Spinach::FeatureSteps
end
step 'I should see the proper Inline and Side-by-side links' do
- buttons = all('#commit-diff-viewtype')
+ buttons = page.all('#commit-diff-viewtype')
expect(buttons.count).to eq(2)
buttons.each do |b|
- expect(b['href']).should_not have_content('json')
+ expect(expect(b['href'])).not_to have_content('json')
end
end
@@ -134,11 +134,11 @@ class Spinach::Features::ProjectMergeRequests < Spinach::FeatureSteps
end
step 'I click on the commit in the merge request' do
- within '.merge-request-tabs' do
+ page.within '.merge-request-tabs' do
click_link 'Commits'
end
- within '.commits' do
+ page.within '.commits' do
click_link Commit.truncate_sha(sample_commit.id)
end
end
@@ -164,24 +164,24 @@ class Spinach::Features::ProjectMergeRequests < Spinach::FeatureSteps
end
step 'I should see a discussion has started on diff' do
- page.should have_content "#{current_user.name} started a discussion"
- page.should have_content sample_commit.line_code_path
- page.should have_content "Line is wrong"
+ expect(page).to have_content "#{current_user.name} started a discussion"
+ expect(page).to have_content sample_commit.line_code_path
+ expect(page).to have_content "Line is wrong"
end
step 'I should see a discussion has started on commit diff' do
- page.should have_content "#{current_user.name} started a discussion on commit"
- page.should have_content sample_commit.line_code_path
- page.should have_content "Line is wrong"
+ expect(page).to have_content "#{current_user.name} started a discussion on commit"
+ expect(page).to have_content sample_commit.line_code_path
+ expect(page).to have_content "Line is wrong"
end
step 'I should see a discussion has started on commit' do
- page.should have_content "#{current_user.name} started a discussion on commit"
- page.should have_content "One comment to rule them all"
+ expect(page).to have_content "#{current_user.name} started a discussion on commit"
+ expect(page).to have_content "One comment to rule them all"
end
step 'merge request is mergeable' do
- page.should have_button 'Accept Merge Request'
+ expect(page).to have_button 'Accept Merge Request'
end
step 'I modify merge commit message' do
@@ -190,6 +190,7 @@ class Spinach::Features::ProjectMergeRequests < Spinach::FeatureSteps
end
step 'merge request "Bug NS-05" is mergeable' do
+ merge_request.project.satellite.create
merge_request.mark_as_mergeable
end
@@ -198,14 +199,14 @@ class Spinach::Features::ProjectMergeRequests < Spinach::FeatureSteps
merge!: true,
)
- within '.can_be_merged' do
+ page.within '.mr-state-widget' do
click_button "Accept Merge Request"
end
end
step 'I should see merged request' do
- within '.issue-box' do
- page.should have_content "Accepted"
+ page.within '.issue-box' do
+ expect(page).to have_content "Accepted"
end
end
@@ -214,72 +215,72 @@ class Spinach::Features::ProjectMergeRequests < Spinach::FeatureSteps
end
step 'I should see reopened merge request "Bug NS-04"' do
- within '.issue-box' do
- page.should have_content "Open"
+ page.within '.issue-box' do
+ expect(page).to have_content "Open"
end
end
step 'I click link "Hide inline discussion" of the second file' do
- within '.files [id^=diff]:nth-child(2)' do
+ page.within '.files [id^=diff]:nth-child(2)' do
find('.js-toggle-diff-comments').click
end
end
step 'I click link "Show inline discussion" of the second file' do
- within '.files [id^=diff]:nth-child(2)' do
+ page.within '.files [id^=diff]:nth-child(2)' do
find('.js-toggle-diff-comments').click
end
end
step 'I should not see a comment like "Line is wrong" in the second file' do
- within '.files [id^=diff]:nth-child(2)' do
- page.should_not have_visible_content "Line is wrong"
+ page.within '.files [id^=diff]:nth-child(2)' do
+ expect(page).not_to have_visible_content "Line is wrong"
end
end
step 'I should see a comment like "Line is wrong" in the second file' do
- within '.files [id^=diff]:nth-child(2) .note-body > .note-text' do
- page.should have_visible_content "Line is wrong"
+ page.within '.files [id^=diff]:nth-child(2) .note-body > .note-text' do
+ expect(page).to have_visible_content "Line is wrong"
end
end
step 'I should not see a comment like "Line is wrong here" in the second file' do
- within '.files [id^=diff]:nth-child(2)' do
- page.should_not have_visible_content "Line is wrong here"
+ page.within '.files [id^=diff]:nth-child(2)' do
+ expect(page).not_to have_visible_content "Line is wrong here"
end
end
step 'I should see a comment like "Line is wrong here" in the second file' do
- within '.files [id^=diff]:nth-child(2) .note-body > .note-text' do
- page.should have_visible_content "Line is wrong here"
+ page.within '.files [id^=diff]:nth-child(2) .note-body > .note-text' do
+ expect(page).to have_visible_content "Line is wrong here"
end
end
step 'I leave a comment like "Line is correct" on line 12 of the first file' do
init_diff_note_first_file
- within(".js-discussion-note-form") do
+ page.within(".js-discussion-note-form") do
fill_in "note_note", with: "Line is correct"
click_button "Add Comment"
end
- within ".files [id^=diff]:nth-child(1) .note-body > .note-text" do
- page.should have_content "Line is correct"
+ page.within ".files [id^=diff]:nth-child(1) .note-body > .note-text" do
+ expect(page).to have_content "Line is correct"
end
end
step 'I leave a comment like "Line is wrong" on line 39 of the second file' do
init_diff_note_second_file
- within(".js-discussion-note-form") do
+ page.within(".js-discussion-note-form") do
fill_in "note_note", with: "Line is wrong on here"
click_button "Add Comment"
end
end
step 'I should still see a comment like "Line is correct" in the first file' do
- within '.files [id^=diff]:nth-child(1) .note-body > .note-text' do
- page.should have_visible_content "Line is correct"
+ page.within '.files [id^=diff]:nth-child(1) .note-body > .note-text' do
+ expect(page).to have_visible_content "Line is correct"
end
end
@@ -296,8 +297,8 @@ class Spinach::Features::ProjectMergeRequests < Spinach::FeatureSteps
end
step 'I should see comments on the side-by-side diff page' do
- within '.files [id^=diff]:nth-child(1) .parallel .note-body > .note-text' do
- page.should have_visible_content "Line is correct"
+ page.within '.files [id^=diff]:nth-child(1) .parallel .note-body > .note-text' do
+ expect(page).to have_visible_content "Line is correct"
end
end
@@ -305,6 +306,20 @@ class Spinach::Features::ProjectMergeRequests < Spinach::FeatureSteps
fill_in 'issue_search', with: "Fe"
end
+ step 'I click the "Target branch" dropdown' do
+ first('.target_branch').click
+ end
+
+ step 'I select a new target branch' do
+ select "feature", from: "merge_request_target_branch"
+ click_button 'Save'
+ end
+
+ step 'I should see new target branch changes' do
+ expect(page).to have_content 'From fix into feature'
+ expect(page).to have_content 'Target branch changed from master to feature'
+ end
+
def merge_request
@merge_request ||= MergeRequest.find_by!(title: "Bug NS-05")
end
@@ -314,12 +329,12 @@ class Spinach::Features::ProjectMergeRequests < Spinach::FeatureSteps
end
def leave_comment(message)
- within(".js-discussion-note-form") do
+ page.within(".js-discussion-note-form") do
fill_in "note_note", with: message
click_button "Add Comment"
end
- page.should have_content message
+ expect(page).to have_content message
end
def init_diff_note_first_file
diff --git a/features/steps/project/network_graph.rb b/features/steps/project/network_graph.rb
index a15688ace6a..992cf2734fd 100644
--- a/features/steps/project/network_graph.rb
+++ b/features/steps/project/network_graph.rb
@@ -4,7 +4,7 @@ class Spinach::Features::ProjectNetworkGraph < Spinach::FeatureSteps
include SharedProject
step 'page should have network graph' do
- page.should have_selector ".network-graph"
+ expect(page).to have_selector ".network-graph"
end
When 'I visit project "Shop" network page' do
@@ -16,16 +16,16 @@ class Spinach::Features::ProjectNetworkGraph < Spinach::FeatureSteps
end
step 'page should select "master" in select box' do
- page.should have_selector '.select2-chosen', text: "master"
+ expect(page).to have_selector '.select2-chosen', text: "master"
end
step 'page should select "v1.0.0" in select box' do
- page.should have_selector '.select2-chosen', text: "v1.0.0"
+ expect(page).to have_selector '.select2-chosen', text: "v1.0.0"
end
step 'page should have "master" on graph' do
- within '.network-graph' do
- page.should have_content 'master'
+ page.within '.network-graph' do
+ expect(page).to have_content 'master'
end
end
@@ -45,33 +45,33 @@ class Spinach::Features::ProjectNetworkGraph < Spinach::FeatureSteps
end
step 'page should have content not containing "v1.0.0"' do
- within '.network-graph' do
- page.should have_content 'Change some files'
+ page.within '.network-graph' do
+ expect(page).to have_content 'Change some files'
end
end
step 'page should not have content not containing "v1.0.0"' do
- within '.network-graph' do
- page.should_not have_content 'Change some files'
+ page.within '.network-graph' do
+ expect(page).not_to have_content 'Change some files'
end
end
step 'page should select "feature" in select box' do
- page.should have_selector '.select2-chosen', text: "feature"
+ expect(page).to have_selector '.select2-chosen', text: "feature"
end
step 'page should select "v1.0.0" in select box' do
- page.should have_selector '.select2-chosen', text: "v1.0.0"
+ expect(page).to have_selector '.select2-chosen', text: "v1.0.0"
end
step 'page should have "feature" on graph' do
- within '.network-graph' do
- page.should have_content 'feature'
+ page.within '.network-graph' do
+ expect(page).to have_content 'feature'
end
end
When 'I looking for a commit by SHA of "v1.0.0"' do
- within ".network-form" do
+ page.within ".network-form" do
fill_in 'extended_sha1', with: '6f6d7e7ed97bb5f0054f2b1df789b39ca89b6ff9'
find('button').click
end
@@ -79,13 +79,13 @@ class Spinach::Features::ProjectNetworkGraph < Spinach::FeatureSteps
end
step 'page should have "v1.0.0" on graph' do
- within '.network-graph' do
- page.should have_content 'v1.0.0'
+ page.within '.network-graph' do
+ expect(page).to have_content 'v1.0.0'
end
end
When 'I look for a commit by ";"' do
- within ".network-form" do
+ page.within ".network-form" do
fill_in 'extended_sha1', with: ';'
find('button').click
end
diff --git a/features/steps/project/project.rb b/features/steps/project/project.rb
index fcc15aacc21..ee4c7cd0f06 100644
--- a/features/steps/project/project.rb
+++ b/features/steps/project/project.rb
@@ -13,7 +13,7 @@ class Spinach::Features::Project < Spinach::FeatureSteps
end
step 'I should see project with new settings' do
- find_field('project_name').value.should == 'NewName'
+ expect(find_field('project_name').value).to eq 'NewName'
end
step 'change project path settings' do
@@ -22,7 +22,7 @@ class Spinach::Features::Project < Spinach::FeatureSteps
end
step 'I should see project with new path settings' do
- project.path.should == 'new-path'
+ expect(project.path).to eq 'new-path'
end
step 'I change the project avatar' do
@@ -35,13 +35,13 @@ class Spinach::Features::Project < Spinach::FeatureSteps
end
step 'I should see new project avatar' do
- @project.avatar.should be_instance_of AvatarUploader
+ expect(@project.avatar).to be_instance_of AvatarUploader
url = @project.avatar.url
- url.should == "/uploads/project/avatar/#{ @project.id }/gitlab_logo.png"
+ expect(url).to eq "/uploads/project/avatar/#{ @project.id }/gitlab_logo.png"
end
step 'I should see the "Remove avatar" button' do
- page.should have_link('Remove avatar')
+ expect(page).to have_link('Remove avatar')
end
step 'I have an project avatar' do
@@ -59,16 +59,16 @@ class Spinach::Features::Project < Spinach::FeatureSteps
end
step 'I should see the default project avatar' do
- @project.avatar?.should be_false
+ expect(@project.avatar?).to be_false
end
step 'I should not see the "Remove avatar" button' do
- page.should_not have_link('Remove avatar')
+ expect(page).not_to have_link('Remove avatar')
end
step 'I should see project "Shop" version' do
- within '.project-side' do
- page.should have_content '6.7.0.pre'
+ page.within '.project-side' do
+ expect(page).to have_content '6.7.0.pre'
end
end
@@ -78,7 +78,7 @@ class Spinach::Features::Project < Spinach::FeatureSteps
end
step 'I should see project default branch changed' do
- find(:css, 'select#project_default_branch').value.should == 'fix'
+ expect(find(:css, 'select#project_default_branch').value).to eq 'fix'
end
step 'I select project "Forum" README tab' do
@@ -86,13 +86,13 @@ class Spinach::Features::Project < Spinach::FeatureSteps
end
step 'I should see project "Forum" README' do
- page.should have_link 'README.md'
- page.should have_content 'Sample repo for testing gitlab features'
+ expect(page).to have_link 'README.md'
+ expect(page).to have_content 'Sample repo for testing gitlab features'
end
step 'I should see project "Shop" README' do
- page.should have_link 'README.md'
- page.should have_content 'testme'
+ expect(page).to have_link 'README.md'
+ expect(page).to have_content 'testme'
end
step 'I add project tags' do
@@ -104,14 +104,14 @@ class Spinach::Features::Project < Spinach::FeatureSteps
end
step 'I should not see "New Issue" button' do
- page.should_not have_link 'New Issue'
+ expect(page).not_to have_link 'New Issue'
end
step 'I should not see "New Merge Request" button' do
- page.should_not have_link 'New Merge Request'
+ expect(page).not_to have_link 'New Merge Request'
end
step 'I should not see "Snippets" button' do
- page.should_not have_link 'Snippets'
+ expect(page).not_to have_link 'Snippets'
end
end
diff --git a/features/steps/project/redirects.rb b/features/steps/project/redirects.rb
index 57c6e39c801..0e724138a8a 100644
--- a/features/steps/project/redirects.rb
+++ b/features/steps/project/redirects.rb
@@ -18,8 +18,8 @@ class Spinach::Features::ProjectRedirects < Spinach::FeatureSteps
step 'I should see project "Community" home page' do
Gitlab.config.gitlab.should_receive(:host).and_return("www.example.com")
- within '.navbar-gitlab .title' do
- page.should have_content 'Community'
+ page.within '.navbar-gitlab .title' do
+ expect(page).to have_content 'Community'
end
end
@@ -48,8 +48,8 @@ class Spinach::Features::ProjectRedirects < Spinach::FeatureSteps
step 'I should be redirected to "Community" page' do
project = Project.find_by(name: 'Community')
- current_path.should == "/#{project.path_with_namespace}"
- status_code.should == 200
+ expect(current_path).to eq "/#{project.path_with_namespace}"
+ expect(status_code).to eq 200
end
step 'I get redirected to signin page where I sign in' do
@@ -63,7 +63,7 @@ class Spinach::Features::ProjectRedirects < Spinach::FeatureSteps
step 'I should be redirected to "Enterprise" page' do
project = Project.find_by(name: 'Enterprise')
- current_path.should == "/#{project.path_with_namespace}"
- status_code.should == 200
+ expect(current_path).to eq "/#{project.path_with_namespace}"
+ expect(status_code).to eq 200
end
end
diff --git a/features/steps/project/services.rb b/features/steps/project/services.rb
index 4b3d79324ab..0327fd61981 100644
--- a/features/steps/project/services.rb
+++ b/features/steps/project/services.rb
@@ -8,16 +8,16 @@ class Spinach::Features::ProjectServices < Spinach::FeatureSteps
end
step 'I should see list of available services' do
- page.should have_content 'Project services'
- page.should have_content 'Campfire'
- page.should have_content 'HipChat'
- page.should have_content 'GitLab CI'
- page.should have_content 'Assembla'
- page.should have_content 'Pushover'
- page.should have_content 'Atlassian Bamboo'
- page.should have_content 'JetBrains TeamCity'
- page.should have_content 'Asana'
- page.should have_content 'Irker (IRC gateway)'
+ expect(page).to have_content 'Project services'
+ expect(page).to have_content 'Campfire'
+ expect(page).to have_content 'HipChat'
+ expect(page).to have_content 'GitLab CI'
+ expect(page).to have_content 'Assembla'
+ expect(page).to have_content 'Pushover'
+ expect(page).to have_content 'Atlassian Bamboo'
+ expect(page).to have_content 'JetBrains TeamCity'
+ expect(page).to have_content 'Asana'
+ expect(page).to have_content 'Irker (IRC gateway)'
end
step 'I click gitlab-ci service link' do
@@ -32,7 +32,7 @@ class Spinach::Features::ProjectServices < Spinach::FeatureSteps
end
step 'I should see service settings saved' do
- find_field('Project url').value.should == 'http://ci.gitlab.org/projects/3'
+ expect(find_field('Project url').value).to eq 'http://ci.gitlab.org/projects/3'
end
step 'I click hipchat service link' do
@@ -47,7 +47,7 @@ class Spinach::Features::ProjectServices < Spinach::FeatureSteps
end
step 'I should see hipchat service settings saved' do
- find_field('Room').value.should == 'gitlab'
+ expect(find_field('Room').value).to eq 'gitlab'
end
step 'I fill hipchat settings with custom server' do
@@ -59,7 +59,7 @@ class Spinach::Features::ProjectServices < Spinach::FeatureSteps
end
step 'I should see hipchat service settings with custom server saved' do
- find_field('Server').value.should == 'https://chat.example.com'
+ expect(find_field('Server').value).to eq 'https://chat.example.com'
end
step 'I click pivotaltracker service link' do
@@ -73,7 +73,7 @@ class Spinach::Features::ProjectServices < Spinach::FeatureSteps
end
step 'I should see pivotaltracker service settings saved' do
- find_field('Token').value.should == 'verySecret'
+ expect(find_field('Token').value).to eq 'verySecret'
end
step 'I click Flowdock service link' do
@@ -87,7 +87,7 @@ class Spinach::Features::ProjectServices < Spinach::FeatureSteps
end
step 'I should see Flowdock service settings saved' do
- find_field('Token').value.should == 'verySecret'
+ expect(find_field('Token').value).to eq 'verySecret'
end
step 'I click Assembla service link' do
@@ -101,7 +101,7 @@ class Spinach::Features::ProjectServices < Spinach::FeatureSteps
end
step 'I should see Assembla service settings saved' do
- find_field('Token').value.should == 'verySecret'
+ expect(find_field('Token').value).to eq 'verySecret'
end
step 'I click Asana service link' do
@@ -116,8 +116,8 @@ class Spinach::Features::ProjectServices < Spinach::FeatureSteps
end
step 'I should see Asana service settings saved' do
- find_field('Api key').value.should == 'verySecret'
- find_field('Restrict to branch').value.should == 'master'
+ expect(find_field('Api key').value).to eq 'verySecret'
+ expect(find_field('Restrict to branch').value).to eq 'master'
end
step 'I click email on push service link' do
@@ -130,7 +130,7 @@ class Spinach::Features::ProjectServices < Spinach::FeatureSteps
end
step 'I should see email on push service settings saved' do
- find_field('Recipients').value.should == 'qa@company.name'
+ expect(find_field('Recipients').value).to eq 'qa@company.name'
end
step 'I click Irker service link' do
@@ -145,8 +145,8 @@ class Spinach::Features::ProjectServices < Spinach::FeatureSteps
end
step 'I should see Irker service settings saved' do
- find_field('Recipients').value.should == 'irc://chat.freenode.net/#commits'
- find_field('Colorize messages').value.should == '1'
+ expect(find_field('Recipients').value).to eq 'irc://chat.freenode.net/#commits'
+ expect(find_field('Colorize messages').value).to eq '1'
end
step 'I click Slack service link' do
@@ -160,7 +160,7 @@ class Spinach::Features::ProjectServices < Spinach::FeatureSteps
end
step 'I should see Slack service settings saved' do
- find_field('Webhook').value.should == 'https://hooks.slack.com/services/SVRWFV0VVAR97N/B02R25XN3/ZBqu7xMupaEEICInN685'
+ expect(find_field('Webhook').value).to eq 'https://hooks.slack.com/services/SVRWFV0VVAR97N/B02R25XN3/ZBqu7xMupaEEICInN685'
end
step 'I click Pushover service link' do
@@ -178,11 +178,11 @@ class Spinach::Features::ProjectServices < Spinach::FeatureSteps
end
step 'I should see Pushover service settings saved' do
- find_field('Api key').value.should == 'verySecret'
- find_field('User key').value.should == 'verySecret'
- find_field('Device').value.should == 'myDevice'
- find_field('Priority').find('option[selected]').value.should == '1'
- find_field('Sound').find('option[selected]').value.should == 'bike'
+ expect(find_field('Api key').value).to eq 'verySecret'
+ expect(find_field('User key').value).to eq 'verySecret'
+ expect(find_field('Device').value).to eq 'myDevice'
+ expect(find_field('Priority').find('option[selected]').value).to eq '1'
+ expect(find_field('Sound').find('option[selected]').value).to eq 'bike'
end
step 'I click Atlassian Bamboo CI service link' do
@@ -199,9 +199,9 @@ class Spinach::Features::ProjectServices < Spinach::FeatureSteps
end
step 'I should see Atlassian Bamboo CI service settings saved' do
- find_field('Bamboo url').value.should == 'http://bamboo.example.com'
- find_field('Build key').value.should == 'KEY'
- find_field('Username').value.should == 'user'
+ expect(find_field('Bamboo url').value).to eq 'http://bamboo.example.com'
+ expect(find_field('Build key').value).to eq 'KEY'
+ expect(find_field('Username').value).to eq 'user'
end
step 'I click JetBrains TeamCity CI service link' do
@@ -218,8 +218,8 @@ class Spinach::Features::ProjectServices < Spinach::FeatureSteps
end
step 'I should see JetBrains TeamCity CI service settings saved' do
- find_field('Teamcity url').value.should == 'http://teamcity.example.com'
- find_field('Build type').value.should == 'GitlabTest_Build'
- find_field('Username').value.should == 'user'
+ expect(find_field('Teamcity url').value).to eq 'http://teamcity.example.com'
+ expect(find_field('Build type').value).to eq 'GitlabTest_Build'
+ expect(find_field('Username').value).to eq 'user'
end
end
diff --git a/features/steps/project/snippets.rb b/features/steps/project/snippets.rb
index 343aeb53b11..eedb4e1b74a 100644
--- a/features/steps/project/snippets.rb
+++ b/features/steps/project/snippets.rb
@@ -30,19 +30,19 @@ class Spinach::Features::ProjectSnippets < Spinach::FeatureSteps
end
step 'I should see "Snippet one" in snippets' do
- page.should have_content "Snippet one"
+ expect(page).to have_content "Snippet one"
end
step 'I should not see "Snippet two" in snippets' do
- page.should_not have_content "Snippet two"
+ expect(page).not_to have_content "Snippet two"
end
step 'I should not see "Snippet one" in snippets' do
- page.should_not have_content "Snippet one"
+ expect(page).not_to have_content "Snippet one"
end
step 'I click link "Edit"' do
- within ".file-title" do
+ page.within ".file-title" do
click_link "Edit"
end
end
@@ -54,15 +54,15 @@ class Spinach::Features::ProjectSnippets < Spinach::FeatureSteps
step 'I submit new snippet "Snippet three"' do
fill_in "project_snippet_title", :with => "Snippet three"
fill_in "project_snippet_file_name", :with => "my_snippet.rb"
- within('.file-editor') do
+ page.within('.file-editor') do
find(:xpath, "//input[@id='project_snippet_content']").set 'Content of snippet three'
end
click_button "Create snippet"
end
step 'I should see snippet "Snippet three"' do
- page.should have_content "Snippet three"
- page.should have_content "Content of snippet three"
+ expect(page).to have_content "Snippet three"
+ expect(page).to have_content "Content of snippet three"
end
step 'I submit new title "Snippet new title"' do
@@ -71,18 +71,18 @@ class Spinach::Features::ProjectSnippets < Spinach::FeatureSteps
end
step 'I should see "Snippet new title"' do
- page.should have_content "Snippet new title"
+ expect(page).to have_content "Snippet new title"
end
step 'I leave a comment like "Good snippet!"' do
- within('.js-main-target-form') do
+ page.within('.js-main-target-form') do
fill_in "note_note", with: "Good snippet!"
click_button "Add Comment"
end
end
step 'I should see comment "Good snippet!"' do
- page.should have_content "Good snippet!"
+ expect(page).to have_content "Good snippet!"
end
step 'I visit snippet page "Snippet one"' do
diff --git a/features/steps/project/source/browse_files.rb b/features/steps/project/source/browse_files.rb
index caf6c73ee06..398c9bf5756 100644
--- a/features/steps/project/source/browse_files.rb
+++ b/features/steps/project/source/browse_files.rb
@@ -5,23 +5,23 @@ class Spinach::Features::ProjectSourceBrowseFiles < Spinach::FeatureSteps
include RepoHelpers
step 'I should see files from repository' do
- page.should have_content "VERSION"
- page.should have_content ".gitignore"
- page.should have_content "LICENSE"
+ expect(page).to have_content "VERSION"
+ expect(page).to have_content ".gitignore"
+ expect(page).to have_content "LICENSE"
end
step 'I should see files from repository for "6d39438"' do
- current_path.should == namespace_project_tree_path(@project.namespace, @project, "6d39438")
- page.should have_content ".gitignore"
- page.should have_content "LICENSE"
+ expect(current_path).to eq namespace_project_tree_path(@project.namespace, @project, "6d39438")
+ expect(page).to have_content ".gitignore"
+ expect(page).to have_content "LICENSE"
end
step 'I see the ".gitignore"' do
- page.should have_content '.gitignore'
+ expect(page).to have_content '.gitignore'
end
step 'I don\'t see the ".gitignore"' do
- page.should_not have_content '.gitignore'
+ expect(page).not_to have_content '.gitignore'
end
step 'I click on ".gitignore" file in repo' do
@@ -29,11 +29,11 @@ class Spinach::Features::ProjectSourceBrowseFiles < Spinach::FeatureSteps
end
step 'I should see its content' do
- page.should have_content old_gitignore_content
+ expect(page).to have_content old_gitignore_content
end
step 'I should see its new content' do
- page.should have_content new_gitignore_content
+ expect(page).to have_content new_gitignore_content
end
step 'I click link "Raw"' do
@@ -41,7 +41,7 @@ class Spinach::Features::ProjectSourceBrowseFiles < Spinach::FeatureSteps
end
step 'I should see raw file content' do
- source.should == sample_blob.data
+ expect(source).to eq sample_blob.data
end
step 'I click button "Edit"' do
@@ -49,16 +49,16 @@ class Spinach::Features::ProjectSourceBrowseFiles < Spinach::FeatureSteps
end
step 'I cannot see the edit button' do
- page.should_not have_link 'edit'
+ expect(page).not_to have_link 'edit'
end
step 'The edit button is disabled' do
- page.should have_css '.disabled', text: 'Edit'
+ expect(page).to have_css '.disabled', text: 'Edit'
end
step 'I can edit code' do
set_new_content
- evaluate_script('blob.editor.getValue()').should == new_gitignore_content
+ expect(evaluate_script('blob.editor.getValue()')).to eq new_gitignore_content
end
step 'I edit code' do
@@ -98,7 +98,7 @@ class Spinach::Features::ProjectSourceBrowseFiles < Spinach::FeatureSteps
end
step 'I see diff' do
- page.should have_css '.line_holder.new'
+ expect(page).to have_css '.line_holder.new'
end
step 'I click on "new file" link in repo' do
@@ -106,8 +106,8 @@ class Spinach::Features::ProjectSourceBrowseFiles < Spinach::FeatureSteps
end
step 'I can see new file page' do
- page.should have_content "New file"
- page.should have_content "Commit message"
+ expect(page).to have_content "New file"
+ expect(page).to have_content "Commit message"
end
step 'I click on files directory' do
@@ -119,25 +119,25 @@ class Spinach::Features::ProjectSourceBrowseFiles < Spinach::FeatureSteps
end
step 'I see Browse dir link' do
- page.should have_link 'Browse Dir »'
- page.should_not have_link 'Browse Code »'
+ expect(page).to have_link 'Browse Dir »'
+ expect(page).not_to have_link 'Browse Code »'
end
step 'I click on readme file' do
- within '.tree-table' do
+ page.within '.tree-table' do
click_link 'README.md'
end
end
step 'I see Browse file link' do
- page.should have_link 'Browse File »'
- page.should_not have_link 'Browse Code »'
+ expect(page).to have_link 'Browse File »'
+ expect(page).not_to have_link 'Browse Code »'
end
step 'I see Browse code link' do
- page.should have_link 'Browse Code »'
- page.should_not have_link 'Browse File »'
- page.should_not have_link 'Browse Dir »'
+ expect(page).to have_link 'Browse Code »'
+ expect(page).not_to have_link 'Browse File »'
+ expect(page).not_to have_link 'Browse Dir »'
end
step 'I click on Permalink' do
@@ -145,7 +145,7 @@ class Spinach::Features::ProjectSourceBrowseFiles < Spinach::FeatureSteps
end
step 'I am redirected to the files URL' do
- current_path.should == namespace_project_tree_path(@project.namespace, @project, 'master')
+ expect(current_path).to eq namespace_project_tree_path(@project.namespace, @project, 'master')
end
step 'I am redirected to the ".gitignore"' do
diff --git a/features/steps/project/source/git_blame.rb b/features/steps/project/source/git_blame.rb
index e29a816c51b..d0a27f47e2a 100644
--- a/features/steps/project/source/git_blame.rb
+++ b/features/steps/project/source/git_blame.rb
@@ -12,8 +12,8 @@ class Spinach::Features::ProjectSourceGitBlame < Spinach::FeatureSteps
end
step 'I should see git file blame' do
- page.should have_content "*.rb"
- page.should have_content "Dmitriy Zaporozhets"
- page.should have_content "Initial commit"
+ expect(page).to have_content "*.rb"
+ expect(page).to have_content "Dmitriy Zaporozhets"
+ expect(page).to have_content "Initial commit"
end
end
diff --git a/features/steps/project/source/markdown_render.rb b/features/steps/project/source/markdown_render.rb
index 7961fdedad8..c78e86fa1a7 100644
--- a/features/steps/project/source/markdown_render.rb
+++ b/features/steps/project/source/markdown_render.rb
@@ -13,19 +13,19 @@ class Spinach::Features::ProjectSourceMarkdownRender < Spinach::FeatureSteps
end
step 'I should see files from repository in markdown' do
- current_path.should == namespace_project_tree_path(@project.namespace, @project, "markdown")
- page.should have_content "README.md"
- page.should have_content "CHANGELOG"
+ expect(current_path).to eq namespace_project_tree_path(@project.namespace, @project, "markdown")
+ expect(page).to have_content "README.md"
+ expect(page).to have_content "CHANGELOG"
end
step 'I should see rendered README which contains correct links' do
- page.should have_content "Welcome to GitLab GitLab is a free project and repository management application"
- page.should have_link "GitLab API doc"
- page.should have_link "GitLab API website"
- page.should have_link "Rake tasks"
- page.should have_link "backup and restore procedure"
- page.should have_link "GitLab API doc directory"
- page.should have_link "Maintenance"
+ expect(page).to have_content "Welcome to GitLab GitLab is a free project and repository management application"
+ expect(page).to have_link "GitLab API doc"
+ expect(page).to have_link "GitLab API website"
+ expect(page).to have_link "Rake tasks"
+ expect(page).to have_link "backup and restore procedure"
+ expect(page).to have_link "GitLab API doc directory"
+ expect(page).to have_link "Maintenance"
end
step 'I click on Gitlab API in README' do
@@ -33,8 +33,8 @@ class Spinach::Features::ProjectSourceMarkdownRender < Spinach::FeatureSteps
end
step 'I should see correct document rendered' do
- current_path.should == namespace_project_blob_path(@project.namespace, @project, "markdown/doc/api/README.md")
- page.should have_content "All API requests require authentication"
+ expect(current_path).to eq namespace_project_blob_path(@project.namespace, @project, "markdown/doc/api/README.md")
+ expect(page).to have_content "All API requests require authentication"
end
step 'I click on Rake tasks in README' do
@@ -42,9 +42,9 @@ class Spinach::Features::ProjectSourceMarkdownRender < Spinach::FeatureSteps
end
step 'I should see correct directory rendered' do
- current_path.should == namespace_project_tree_path(@project.namespace, @project, "markdown/doc/raketasks")
- page.should have_content "backup_restore.md"
- page.should have_content "maintenance.md"
+ expect(current_path).to eq namespace_project_tree_path(@project.namespace, @project, "markdown/doc/raketasks")
+ expect(page).to have_content "backup_restore.md"
+ expect(page).to have_content "maintenance.md"
end
step 'I click on GitLab API doc directory in README' do
@@ -52,9 +52,9 @@ class Spinach::Features::ProjectSourceMarkdownRender < Spinach::FeatureSteps
end
step 'I should see correct doc/api directory rendered' do
- current_path.should == namespace_project_tree_path(@project.namespace, @project, "markdown/doc/api")
- page.should have_content "README.md"
- page.should have_content "users.md"
+ expect(current_path).to eq namespace_project_tree_path(@project.namespace, @project, "markdown/doc/api")
+ expect(page).to have_content "README.md"
+ expect(page).to have_content "users.md"
end
step 'I click on Maintenance in README' do
@@ -62,41 +62,41 @@ class Spinach::Features::ProjectSourceMarkdownRender < Spinach::FeatureSteps
end
step 'I should see correct maintenance file rendered' do
- current_path.should == namespace_project_blob_path(@project.namespace, @project, "markdown/doc/raketasks/maintenance.md")
- page.should have_content "bundle exec rake gitlab:env:info RAILS_ENV=production"
+ expect(current_path).to eq namespace_project_blob_path(@project.namespace, @project, "markdown/doc/raketasks/maintenance.md")
+ expect(page).to have_content "bundle exec rake gitlab:env:info RAILS_ENV=production"
end
step 'I click on link "empty" in the README' do
- within('.readme-holder') do
+ page.within('.readme-holder') do
click_link "empty"
end
end
step 'I click on link "id" in the README' do
- within('.readme-holder') do
+ page.within('.readme-holder') do
click_link "#id"
end
end
step 'I navigate to the doc/api/README' do
- within '.tree-table' do
+ page.within '.tree-table' do
click_link "doc"
end
- within '.tree-table' do
+ page.within '.tree-table' do
click_link "api"
end
- within '.tree-table' do
+ page.within '.tree-table' do
click_link "README.md"
end
end
step 'I see correct file rendered' do
- current_path.should == namespace_project_blob_path(@project.namespace, @project, "markdown/doc/api/README.md")
- page.should have_content "Contents"
- page.should have_link "Users"
- page.should have_link "Rake tasks"
+ expect(current_path).to eq namespace_project_blob_path(@project.namespace, @project, "markdown/doc/api/README.md")
+ expect(page).to have_content "Contents"
+ expect(page).to have_link "Users"
+ expect(page).to have_link "Rake tasks"
end
step 'I click on users in doc/api/README' do
@@ -104,8 +104,8 @@ class Spinach::Features::ProjectSourceMarkdownRender < Spinach::FeatureSteps
end
step 'I should see the correct document file' do
- current_path.should == namespace_project_blob_path(@project.namespace, @project, "markdown/doc/api/users.md")
- page.should have_content "Get a list of users."
+ expect(current_path).to eq namespace_project_blob_path(@project.namespace, @project, "markdown/doc/api/users.md")
+ expect(page).to have_content "Get a list of users."
end
step 'I click on raketasks in doc/api/README' do
@@ -131,32 +131,32 @@ class Spinach::Features::ProjectSourceMarkdownRender < Spinach::FeatureSteps
end
step 'I should see files from repository in markdown branch' do
- current_path.should == namespace_project_tree_path(@project.namespace, @project, "markdown")
- page.should have_content "README.md"
- page.should have_content "CHANGELOG"
+ expect(current_path).to eq namespace_project_tree_path(@project.namespace, @project, "markdown")
+ expect(page).to have_content "README.md"
+ expect(page).to have_content "CHANGELOG"
end
step 'I see correct file rendered in markdown branch' do
- current_path.should == namespace_project_blob_path(@project.namespace, @project, "markdown/doc/api/README.md")
- page.should have_content "Contents"
- page.should have_link "Users"
- page.should have_link "Rake tasks"
+ expect(current_path).to eq namespace_project_blob_path(@project.namespace, @project, "markdown/doc/api/README.md")
+ expect(page).to have_content "Contents"
+ expect(page).to have_link "Users"
+ expect(page).to have_link "Rake tasks"
end
step 'I should see correct document rendered for markdown branch' do
- current_path.should == namespace_project_blob_path(@project.namespace, @project, "markdown/doc/api/README.md")
- page.should have_content "All API requests require authentication"
+ expect(current_path).to eq namespace_project_blob_path(@project.namespace, @project, "markdown/doc/api/README.md")
+ expect(page).to have_content "All API requests require authentication"
end
step 'I should see correct directory rendered for markdown branch' do
- current_path.should == namespace_project_tree_path(@project.namespace, @project, "markdown/doc/raketasks")
- page.should have_content "backup_restore.md"
- page.should have_content "maintenance.md"
+ expect(current_path).to eq namespace_project_tree_path(@project.namespace, @project, "markdown/doc/raketasks")
+ expect(page).to have_content "backup_restore.md"
+ expect(page).to have_content "maintenance.md"
end
step 'I should see the users document file in markdown branch' do
- current_path.should == namespace_project_blob_path(@project.namespace, @project, "markdown/doc/api/users.md")
- page.should have_content "Get a list of users."
+ expect(current_path).to eq namespace_project_blob_path(@project.namespace, @project, "markdown/doc/api/users.md")
+ expect(page).to have_content "Get a list of users."
end
# Expected link contents
@@ -208,7 +208,7 @@ class Spinach::Features::ProjectSourceMarkdownRender < Spinach::FeatureSteps
step 'I go to wiki page' do
click_link "Wiki"
- current_path.should == namespace_project_wiki_path(@project.namespace, @project, "home")
+ expect(current_path).to eq namespace_project_wiki_path(@project.namespace, @project, "home")
end
step 'I add various links to the wiki page' do
@@ -218,8 +218,8 @@ class Spinach::Features::ProjectSourceMarkdownRender < Spinach::FeatureSteps
end
step 'Wiki page should have added links' do
- current_path.should == namespace_project_wiki_path(@project.namespace, @project, "home")
- page.should have_content "test GitLab API doc Rake tasks"
+ expect(current_path).to eq namespace_project_wiki_path(@project.namespace, @project, "home")
+ expect(page).to have_content "test GitLab API doc Rake tasks"
end
step 'I add a header to the wiki page' do
@@ -237,13 +237,13 @@ class Spinach::Features::ProjectSourceMarkdownRender < Spinach::FeatureSteps
end
step 'I see new wiki page named test' do
- current_path.should == namespace_project_wiki_path(@project.namespace, @project, "test")
- page.should have_content "Editing"
+ expect(current_path).to eq namespace_project_wiki_path(@project.namespace, @project, "test")
+ expect(page).to have_content "Editing"
end
When 'I go back to wiki page home' do
visit namespace_project_wiki_path(@project.namespace, @project, "home")
- current_path.should == namespace_project_wiki_path(@project.namespace, @project, "home")
+ expect(current_path).to eq namespace_project_wiki_path(@project.namespace, @project, "home")
end
step 'I click on GitLab API doc link' do
@@ -251,8 +251,8 @@ class Spinach::Features::ProjectSourceMarkdownRender < Spinach::FeatureSteps
end
step 'I see Gitlab API document' do
- current_path.should == namespace_project_wiki_path(@project.namespace, @project, "api")
- page.should have_content "Editing"
+ expect(current_path).to eq namespace_project_wiki_path(@project.namespace, @project, "api")
+ expect(page).to have_content "Editing"
end
step 'I click on Rake tasks link' do
@@ -260,13 +260,13 @@ class Spinach::Features::ProjectSourceMarkdownRender < Spinach::FeatureSteps
end
step 'I see Rake tasks directory' do
- current_path.should == namespace_project_wiki_path(@project.namespace, @project, "raketasks")
- page.should have_content "Editing"
+ expect(current_path).to eq namespace_project_wiki_path(@project.namespace, @project, "raketasks")
+ expect(page).to have_content "Editing"
end
step 'I go directory which contains README file' do
visit namespace_project_tree_path(@project.namespace, @project, "markdown/doc/api")
- current_path.should == namespace_project_tree_path(@project.namespace, @project, "markdown/doc/api")
+ expect(current_path).to eq namespace_project_tree_path(@project.namespace, @project, "markdown/doc/api")
end
step 'I click on a relative link in README' do
@@ -274,8 +274,8 @@ class Spinach::Features::ProjectSourceMarkdownRender < Spinach::FeatureSteps
end
step 'I should see the correct markdown' do
- current_path.should == namespace_project_blob_path(@project.namespace, @project, "markdown/doc/api/users.md")
- page.should have_content "List users"
+ expect(current_path).to eq namespace_project_blob_path(@project.namespace, @project, "markdown/doc/api/users.md")
+ expect(page).to have_content "List users"
end
step 'Header "Application details" should have correct id and link' do
diff --git a/features/steps/project/source/multiselect_blob.rb b/features/steps/project/source/multiselect_blob.rb
index b749ba49371..8e14623b892 100644
--- a/features/steps/project/source/multiselect_blob.rb
+++ b/features/steps/project/source/multiselect_blob.rb
@@ -23,19 +23,19 @@ class Spinach::Features::ProjectSourceMultiselectBlob < Spinach::FeatureSteps
pluralization = range.kind_of?(Array) ? "s" : ""
step "I should see \"#{fragment}\" as URI fragment" do
- URI.parse(current_url).fragment.should == fragment
+ expect(URI.parse(current_url).fragment).to eq fragment
end
step "I should see line#{pluralization} #{fragment[1..-1]} highlighted" do
ids = Array(range).map { |n| "LC#{n}" }
extra = false
- highlighted = all("#tree-content-holder .highlight .line.hll")
+ highlighted = page.all("#tree-content-holder .highlight .line.hll")
highlighted.each do |element|
extra ||= ids.delete(element[:id]).nil?
end
- extra.should be_false and ids.should be_empty
+ expect(extra).to be_false and ids.should be_empty
end
end
end
diff --git a/features/steps/project/source/search_code.rb b/features/steps/project/source/search_code.rb
index b66c5a4123a..feee756d7ec 100644
--- a/features/steps/project/source/search_code.rb
+++ b/features/steps/project/source/search_code.rb
@@ -9,11 +9,11 @@ class Spinach::Features::ProjectSourceSearchCode < Spinach::FeatureSteps
end
step 'I should see files from repository containing "coffee"' do
- page.should have_content 'coffee'
- page.should have_content 'CONTRIBUTING.md'
+ expect(page).to have_content 'coffee'
+ expect(page).to have_content 'CONTRIBUTING.md'
end
step 'I should see empty result' do
- page.should have_content "We couldn't find any"
+ expect(page).to have_content "We couldn't find any"
end
end
diff --git a/features/steps/project/star.rb b/features/steps/project/star.rb
index 50cdfd73c34..8b50bfcef04 100644
--- a/features/steps/project/star.rb
+++ b/features/steps/project/star.rb
@@ -5,7 +5,7 @@ class Spinach::Features::ProjectStar < Spinach::FeatureSteps
include SharedUser
step "The project has no stars" do
- page.should_not have_content '.star-buttons'
+ expect(page).not_to have_content '.star-buttons'
end
step "The project has 0 stars" do
@@ -26,7 +26,7 @@ class Spinach::Features::ProjectStar < Spinach::FeatureSteps
end
step 'I redirected to sign in page' do
- current_path.should == new_user_session_path
+ expect(current_path).to eq new_user_session_path
end
protected
diff --git a/features/steps/project/team_management.rb b/features/steps/project/team_management.rb
index 09e5af3ef48..97d63016458 100644
--- a/features/steps/project/team_management.rb
+++ b/features/steps/project/team_management.rb
@@ -5,14 +5,14 @@ class Spinach::Features::ProjectTeamManagement < Spinach::FeatureSteps
include Select2Helper
step 'I should be able to see myself in team' do
- page.should have_content(@user.name)
- page.should have_content(@user.username)
+ expect(page).to have_content(@user.name)
+ expect(page).to have_content(@user.username)
end
step 'I should see "Dmitriy" in team list' do
user = User.find_by(name: "Dmitriy")
- page.should have_content(user.name)
- page.should have_content(user.username)
+ expect(page).to have_content(user.name)
+ expect(page).to have_content(user.username)
end
step 'I click link "Add members"' do
@@ -22,7 +22,7 @@ class Spinach::Features::ProjectTeamManagement < Spinach::FeatureSteps
step 'I select "Mike" as "Reporter"' do
user = User.find_by(name: "Mike")
- within ".users-project-form" do
+ page.within ".users-project-form" do
select2(user.id, from: "#user_ids", multiple: true)
select "Reporter", from: "access_level"
end
@@ -30,13 +30,13 @@ class Spinach::Features::ProjectTeamManagement < Spinach::FeatureSteps
end
step 'I should see "Mike" in team list as "Reporter"' do
- within ".access-reporter" do
- page.should have_content('Mike')
+ page.within ".access-reporter" do
+ expect(page).to have_content('Mike')
end
end
step 'I select "sjobs@apple.com" as "Reporter"' do
- within ".users-project-form" do
+ page.within ".users-project-form" do
select2("sjobs@apple.com", from: "#user_ids", multiple: true)
select "Reporter", from: "access_level"
end
@@ -44,16 +44,16 @@ class Spinach::Features::ProjectTeamManagement < Spinach::FeatureSteps
end
step 'I should see "sjobs@apple.com" in team list as invited "Reporter"' do
- within ".access-reporter" do
- page.should have_content('sjobs@apple.com')
- page.should have_content('invited')
- page.should have_content('Reporter')
+ page.within ".access-reporter" do
+ expect(page).to have_content('sjobs@apple.com')
+ expect(page).to have_content('invited')
+ expect(page).to have_content('Reporter')
end
end
step 'I should see "Dmitriy" in team list as "Developer"' do
- within ".access-developer" do
- page.should have_content('Dmitriy')
+ page.within ".access-developer" do
+ expect(page).to have_content('Dmitriy')
end
end
@@ -61,7 +61,7 @@ class Spinach::Features::ProjectTeamManagement < Spinach::FeatureSteps
project = Project.find_by(name: "Shop")
user = User.find_by(name: 'Dmitriy')
project_member = project.project_members.find_by(user_id: user.id)
- within "#project_member_#{project_member.id}" do
+ page.within "#project_member_#{project_member.id}" do
click_button "Edit access level"
select "Reporter", from: "project_member_access_level"
click_button "Save"
@@ -69,8 +69,8 @@ class Spinach::Features::ProjectTeamManagement < Spinach::FeatureSteps
end
step 'I should see "Dmitriy" in team list as "Reporter"' do
- within ".access-reporter" do
- page.should have_content('Dmitriy')
+ page.within ".access-reporter" do
+ expect(page).to have_content('Dmitriy')
end
end
@@ -80,8 +80,8 @@ class Spinach::Features::ProjectTeamManagement < Spinach::FeatureSteps
step 'I should not see "Dmitriy" in team list' do
user = User.find_by(name: "Dmitriy")
- page.should_not have_content(user.name)
- page.should_not have_content(user.username)
+ expect(page).not_to have_content(user.name)
+ expect(page).not_to have_content(user.username)
end
step 'gitlab user "Mike"' do
@@ -123,7 +123,7 @@ class Spinach::Features::ProjectTeamManagement < Spinach::FeatureSteps
project = Project.find_by(name: "Shop")
user = User.find_by(name: 'Dmitriy')
project_member = project.project_members.find_by(user_id: user.id)
- within "#project_member_#{project_member.id}" do
+ page.within "#project_member_#{project_member.id}" do
click_link('Remove user from team')
end
end
diff --git a/features/steps/project/wiki.rb b/features/steps/project/wiki.rb
index 58cb0ceb3f1..eebfaee1ede 100644
--- a/features/steps/project/wiki.rb
+++ b/features/steps/project/wiki.rb
@@ -6,13 +6,13 @@ class Spinach::Features::ProjectWiki < Spinach::FeatureSteps
include WikiHelper
step 'I click on the Cancel button' do
- within(:css, ".form-actions") do
+ page.within(:css, ".form-actions") do
click_on "Cancel"
end
end
step 'I should be redirected back to the Edit Home Wiki page' do
- current_path.should == namespace_project_wiki_path(project.namespace, project, :home)
+ expect(current_path).to eq namespace_project_wiki_path(project.namespace, project, :home)
end
step 'I create the Wiki Home page' do
@@ -21,11 +21,11 @@ class Spinach::Features::ProjectWiki < Spinach::FeatureSteps
end
step 'I should see the newly created wiki page' do
- page.should have_content "Home"
- page.should have_content "link test"
+ expect(page).to have_content "Home"
+ expect(page).to have_content "link test"
click_link "link test"
- page.should have_content "Editing"
+ expect(page).to have_content "Editing"
end
step 'I have an existing Wiki page' do
@@ -47,11 +47,11 @@ class Spinach::Features::ProjectWiki < Spinach::FeatureSteps
end
step 'I should see the updated content' do
- page.should have_content "Updated Wiki Content"
+ expect(page).to have_content "Updated Wiki Content"
end
step 'I should be redirected back to that Wiki page' do
- current_path.should == namespace_project_wiki_path(project.namespace, project, @page)
+ expect(current_path).to eq namespace_project_wiki_path(project.namespace, project, @page)
end
step 'That page has two revisions' do
@@ -63,9 +63,9 @@ class Spinach::Features::ProjectWiki < Spinach::FeatureSteps
end
step 'I should see both revisions' do
- page.should have_content current_user.name
- page.should have_content "first commit"
- page.should have_content "second commit"
+ expect(page).to have_content current_user.name
+ expect(page).to have_content "first commit"
+ expect(page).to have_content "second commit"
end
step 'I click on the "Delete this page" button' do
@@ -73,7 +73,7 @@ class Spinach::Features::ProjectWiki < Spinach::FeatureSteps
end
step 'The page should be deleted' do
- page.should have_content "Page was successfully deleted"
+ expect(page).to have_content "Page was successfully deleted"
end
step 'I click on the "Pages" button' do
@@ -81,8 +81,8 @@ class Spinach::Features::ProjectWiki < Spinach::FeatureSteps
end
step 'I should see the existing page in the pages list' do
- page.should have_content current_user.name
- page.should have_content @page.title
+ expect(page).to have_content current_user.name
+ expect(page).to have_content @page.title
end
step 'I have an existing Wiki page with images linked on page' do
@@ -98,30 +98,30 @@ class Spinach::Features::ProjectWiki < Spinach::FeatureSteps
file = Gollum::File.new(wiki.wiki)
Gollum::Wiki.any_instance.stub(:file).with("image.jpg", "master", true).and_return(file)
Gollum::File.any_instance.stub(:mime_type).and_return("image/jpeg")
- page.should have_link('image', href: "image.jpg")
+ expect(page).to have_link('image', href: "image.jpg")
click_on "image"
end
step 'I should see the image from wiki repo' do
- current_path.should match('wikis/image.jpg')
- page.should_not have_xpath('/html') # Page should render the image which means there is no html involved
+ expect(current_path).to match('wikis/image.jpg')
+ expect(page).not_to have_xpath('/html') # Page should render the image which means there is no html involved
Gollum::Wiki.any_instance.unstub(:file)
Gollum::File.any_instance.unstub(:mime_type)
end
step 'Image should be shown on the page' do
- page.should have_xpath("//img[@src=\"image.jpg\"]")
+ expect(page).to have_xpath("//img[@src=\"image.jpg\"]")
end
step 'I click on image link' do
- page.should have_link('image', href: "image.jpg")
+ expect(page).to have_link('image', href: "image.jpg")
click_on "image"
end
step 'I should see the new wiki page form' do
- current_path.should match('wikis/image.jpg')
- page.should have_content('New Wiki Page')
- page.should have_content('Editing - image.jpg')
+ expect(current_path).to match('wikis/image.jpg')
+ expect(page).to have_content('New Wiki Page')
+ expect(page).to have_content('Editing - image.jpg')
end
step 'I create a New page with paths' do
@@ -130,7 +130,7 @@ class Spinach::Features::ProjectWiki < Spinach::FeatureSteps
click_on 'Build'
fill_in "wiki_content", with: 'wiki content'
click_on "Create page"
- current_path.should include 'one/two/three'
+ expect(current_path).to include 'one/two/three'
end
step 'I create a New page with an invalid name' do
@@ -144,7 +144,7 @@ class Spinach::Features::ProjectWiki < Spinach::FeatureSteps
end
step 'I should see non-escaped link in the pages list' do
- page.should have_xpath("//a[@href='/#{project.path_with_namespace}/wikis/one/two/three']")
+ expect(page).to have_xpath("//a[@href='/#{project.path_with_namespace}/wikis/one/two/three']")
end
step 'I edit the Wiki page with a path' do
@@ -153,11 +153,11 @@ class Spinach::Features::ProjectWiki < Spinach::FeatureSteps
end
step 'I should see a non-escaped path' do
- current_path.should include 'one/two/three'
+ expect(current_path).to include 'one/two/three'
end
step 'I should see the Editing page' do
- page.should have_content('Editing')
+ expect(page).to have_content('Editing')
end
step 'I view the page history of a Wiki page that has a path' do
@@ -166,7 +166,7 @@ class Spinach::Features::ProjectWiki < Spinach::FeatureSteps
end
step 'I should see the page history' do
- page.should have_content('History for')
+ expect(page).to have_content('History for')
end
step 'I search for Wiki content' do
diff --git a/features/steps/search.rb b/features/steps/search.rb
index 8197cd410aa..fec5d9f0e4e 100644
--- a/features/steps/search.rb
+++ b/features/steps/search.rb
@@ -24,35 +24,35 @@ class Spinach::Features::Search < Spinach::FeatureSteps
end
step 'I click "Issues" link' do
- within '.search-filter' do
+ page.within '.search-filter' do
click_link 'Issues'
end
end
step 'I click project "Shop" link' do
- within '.project-filter' do
+ page.within '.project-filter' do
click_link project.name_with_namespace
end
end
step 'I click "Merge requests" link' do
- within '.search-filter' do
+ page.within '.search-filter' do
click_link 'Merge requests'
end
end
step 'I click "Wiki" link' do
- within '.search-filter' do
+ page.within '.search-filter' do
click_link 'Wiki'
end
end
step 'I should see "Shop" project link' do
- page.should have_link "Shop"
+ expect(page).to have_link "Shop"
end
step 'I should see code results for project "Shop"' do
- page.should have_content 'Update capybara, rspec-rails, poltergeist to recent versions'
+ expect(page).to have_content 'Update capybara, rspec-rails, poltergeist to recent versions'
end
step 'I search for "Contibuting"' do
@@ -71,15 +71,15 @@ class Spinach::Features::Search < Spinach::FeatureSteps
end
step 'I should see "Foo" link in the search results' do
- find(:css, '.search-results').should have_link 'Foo'
+ expect(find(:css, '.search-results')).to have_link 'Foo'
end
step 'I should not see "Bar" link in the search results' do
- find(:css, '.search-results').should_not have_link 'Bar'
+ expect(find(:css, '.search-results')).not_to have_link 'Bar'
end
step 'I should see "test_wiki" link in the search results' do
- find(:css, '.search-results').should have_link 'test_wiki.md'
+ expect(find(:css, '.search-results')).to have_link 'test_wiki.md'
end
step 'project has Wiki content' do
diff --git a/features/steps/shared/active_tab.rb b/features/steps/shared/active_tab.rb
index 9beb688bd16..72d873caa57 100644
--- a/features/steps/shared/active_tab.rb
+++ b/features/steps/shared/active_tab.rb
@@ -2,27 +2,27 @@ module SharedActiveTab
include Spinach::DSL
def ensure_active_main_tab(content)
- find('.nav-sidebar > li.active').should have_content(content)
+ expect(find('.nav-sidebar > li.active')).to have_content(content)
end
def ensure_active_sub_tab(content)
- find('div.content ul.nav-tabs li.active').should have_content(content)
+ expect(find('div.content ul.nav-tabs li.active')).to have_content(content)
end
def ensure_active_sub_nav(content)
- find('.sidebar-subnav > li.active').should have_content(content)
+ expect(find('.sidebar-subnav > li.active')).to have_content(content)
end
step 'no other main tabs should be active' do
- page.should have_selector('.nav-sidebar > li.active', count: 1)
+ expect(page).to have_selector('.nav-sidebar > li.active', count: 1)
end
step 'no other sub tabs should be active' do
- page.should have_selector('div.content ul.nav-tabs li.active', count: 1)
+ expect(page).to have_selector('div.content ul.nav-tabs li.active', count: 1)
end
step 'no other sub navs should be active' do
- page.should have_selector('.sidebar-subnav > li.active', count: 1)
+ expect(page).to have_selector('.sidebar-subnav > li.active', count: 1)
end
step 'the active main tab should be Home' do
diff --git a/features/steps/shared/authentication.rb b/features/steps/shared/authentication.rb
index ac8a3df6bb9..3c0f2a9406a 100644
--- a/features/steps/shared/authentication.rb
+++ b/features/steps/shared/authentication.rb
@@ -21,7 +21,7 @@ module SharedAuthentication
end
step 'I should be redirected to sign in page' do
- current_path.should == new_user_session_path
+ expect(current_path).to eq new_user_session_path
end
step "I logout" do
diff --git a/features/steps/shared/diff_note.rb b/features/steps/shared/diff_note.rb
index 510e0f0f938..a716ca5837c 100644
--- a/features/steps/shared/diff_note.rb
+++ b/features/steps/shared/diff_note.rb
@@ -3,7 +3,7 @@ module SharedDiffNote
include RepoHelpers
step 'I cancel the diff comment' do
- within(diff_file_selector) do
+ page.within(diff_file_selector) do
find(".js-close-discussion-note-form").click
end
end
@@ -14,14 +14,14 @@ module SharedDiffNote
end
step 'I haven\'t written any diff comment text' do
- within(diff_file_selector) do
+ page.within(diff_file_selector) do
fill_in "note[note]", with: ""
end
end
step 'I leave a diff comment like "Typo, please fix"' do
click_diff_line(sample_commit.line_code)
- within("#{diff_file_selector} form[rel$='#{sample_commit.line_code}']") do
+ page.within("#{diff_file_selector} form[rel$='#{sample_commit.line_code}']") do
fill_in "note[note]", with: "Typo, please fix"
find(".js-comment-button").trigger("click")
sleep 0.05
@@ -30,7 +30,7 @@ module SharedDiffNote
step 'I preview a diff comment text like "Should fix it :smile:"' do
click_diff_line(sample_commit.line_code)
- within("#{diff_file_selector} form[rel$='#{sample_commit.line_code}']") do
+ page.within("#{diff_file_selector} form[rel$='#{sample_commit.line_code}']") do
fill_in "note[note]", with: "Should fix it :smile:"
find('.js-md-preview-button').click
end
@@ -39,7 +39,7 @@ module SharedDiffNote
step 'I preview another diff comment text like "DRY this up"' do
click_diff_line(sample_commit.del_line_code)
- within("#{diff_file_selector} form[rel$='#{sample_commit.del_line_code}']") do
+ page.within("#{diff_file_selector} form[rel$='#{sample_commit.del_line_code}']") do
fill_in "note[note]", with: "DRY this up"
find('.js-md-preview-button').click
end
@@ -54,103 +54,103 @@ module SharedDiffNote
end
step 'I write a diff comment like ":-1: I don\'t like this"' do
- within(diff_file_selector) do
+ page.within(diff_file_selector) do
fill_in "note[note]", with: ":-1: I don\'t like this"
end
end
step 'I submit the diff comment' do
- within(diff_file_selector) do
+ page.within(diff_file_selector) do
click_button("Add Comment")
end
end
step 'I should not see the diff comment form' do
- within(diff_file_selector) do
- page.should_not have_css("form.new_note")
+ page.within(diff_file_selector) do
+ expect(page).not_to have_css("form.new_note")
end
end
step 'The diff comment preview tab should say there is nothing to do' do
- within(diff_file_selector) do
+ page.within(diff_file_selector) do
find('.js-md-preview-button').click
expect(find('.js-md-preview')).to have_content('Nothing to preview.')
end
end
step 'I should not see the diff comment text field' do
- within(diff_file_selector) do
+ page.within(diff_file_selector) do
expect(find('.js-note-text')).not_to be_visible
end
end
step 'I should only see one diff form' do
- within(diff_file_selector) do
- page.should have_css("form.new_note", count: 1)
+ page.within(diff_file_selector) do
+ expect(page).to have_css("form.new_note", count: 1)
end
end
step 'I should see a diff comment form with ":-1: I don\'t like this"' do
- within(diff_file_selector) do
- page.should have_field("note[note]", with: ":-1: I don\'t like this")
+ page.within(diff_file_selector) do
+ expect(page).to have_field("note[note]", with: ":-1: I don\'t like this")
end
end
step 'I should see a diff comment saying "Typo, please fix"' do
- within("#{diff_file_selector} .note") do
- page.should have_content("Typo, please fix")
+ page.within("#{diff_file_selector} .note") do
+ expect(page).to have_content("Typo, please fix")
end
end
step 'I should see a discussion reply button' do
- within(diff_file_selector) do
- page.should have_button('Reply')
+ page.within(diff_file_selector) do
+ expect(page).to have_button('Reply')
end
end
step 'I should see a temporary diff comment form' do
- within(diff_file_selector) do
- page.should have_css(".js-temp-notes-holder form.new_note")
+ page.within(diff_file_selector) do
+ expect(page).to have_css(".js-temp-notes-holder form.new_note")
end
end
step 'I should see add a diff comment button' do
- page.should have_css('.js-add-diff-note-button', visible: true)
+ expect(page).to have_css('.js-add-diff-note-button', visible: true)
end
step 'I should see an empty diff comment form' do
- within(diff_file_selector) do
- page.should have_field("note[note]", with: "")
+ page.within(diff_file_selector) do
+ expect(page).to have_field("note[note]", with: "")
end
end
step 'I should see the cancel comment button' do
- within("#{diff_file_selector} form") do
- page.should have_css(".js-close-discussion-note-form", text: "Cancel")
+ page.within("#{diff_file_selector} form") do
+ expect(page).to have_css(".js-close-discussion-note-form", text: "Cancel")
end
end
step 'I should see the diff comment preview' do
- within("#{diff_file_selector} form") do
+ page.within("#{diff_file_selector} form") do
expect(page).to have_css('.js-md-preview', visible: true)
end
end
step 'I should see the diff comment write tab' do
- within(diff_file_selector) do
+ page.within(diff_file_selector) do
expect(page).to have_css('.js-md-write-button', visible: true)
end
end
step 'The diff comment preview tab should display rendered Markdown' do
- within(diff_file_selector) do
+ page.within(diff_file_selector) do
find('.js-md-preview-button').click
expect(find('.js-md-preview')).to have_css('img.emoji', visible: true)
end
end
step 'I should see two separate previews' do
- within(diff_file_selector) do
+ page.within(diff_file_selector) do
expect(page).to have_css('.js-md-preview', visible: true, count: 2)
expect(page).to have_content('Should fix it')
expect(page).to have_content('DRY this up')
diff --git a/features/steps/shared/group.rb b/features/steps/shared/group.rb
index 1b225dd61a6..2d17fb34ccb 100644
--- a/features/steps/shared/group.rb
+++ b/features/steps/shared/group.rb
@@ -22,11 +22,11 @@ module SharedGroup
end
step 'I should see group "TestGroup"' do
- page.should have_content "TestGroup"
+ expect(page).to have_content "TestGroup"
end
step 'I should not see group "TestGroup"' do
- page.should_not have_content "TestGroup"
+ expect(page).not_to have_content "TestGroup"
end
protected
diff --git a/features/steps/shared/markdown.rb b/features/steps/shared/markdown.rb
index 943640007a9..56b36f7c46c 100644
--- a/features/steps/shared/markdown.rb
+++ b/features/steps/shared/markdown.rb
@@ -3,11 +3,11 @@ module SharedMarkdown
def header_should_have_correct_id_and_link(level, text, id, parent = ".wiki")
node = find("#{parent} h#{level} a##{id}")
- node[:href].should == "##{id}"
+ expect(node[:href]).to eq "##{id}"
# Work around a weird Capybara behavior where calling `parent` on a node
# returns the whole document, not the node's actual parent element
- find(:xpath, "#{node.path}/..").text.should == text
+ expect(find(:xpath, "#{node.path}/..").text).to eq text
end
step 'Header "Description header" should have correct id and link' do
@@ -19,7 +19,7 @@ module SharedMarkdown
end
step 'The Markdown preview tab should say there is nothing to do' do
- within('.gfm-form') do
+ page.within('.gfm-form') do
find('.js-md-preview-button').click
expect(find('.js-md-preview')).to have_content('Nothing to preview.')
end
@@ -38,7 +38,7 @@ module SharedMarkdown
end
step 'The Markdown preview tab should display rendered Markdown' do
- within('.gfm-form') do
+ page.within('.gfm-form') do
find('.js-md-preview-button').click
expect(find('.js-md-preview')).to have_css('img.emoji', visible: true)
end
@@ -49,7 +49,7 @@ module SharedMarkdown
end
step 'I preview a description text like "Bug fixed :smile:"' do
- within('.gfm-form') do
+ page.within('.gfm-form') do
fill_in 'Description', with: 'Bug fixed :smile:'
find('.js-md-preview-button').click
end
diff --git a/features/steps/shared/note.rb b/features/steps/shared/note.rb
index 2f66e61b214..b2675546a14 100644
--- a/features/steps/shared/note.rb
+++ b/features/steps/shared/note.rb
@@ -7,13 +7,13 @@ module SharedNote
end
step 'I haven\'t written any comment text' do
- within(".js-main-target-form") do
+ page.within(".js-main-target-form") do
fill_in "note[note]", with: ""
end
end
step 'I leave a comment like "XML attached"' do
- within(".js-main-target-form") do
+ page.within(".js-main-target-form") do
fill_in "note[note]", with: "XML attached"
click_button "Add Comment"
sleep 0.05
@@ -21,94 +21,94 @@ module SharedNote
end
step 'I preview a comment text like "Bug fixed :smile:"' do
- within(".js-main-target-form") do
+ page.within(".js-main-target-form") do
fill_in "note[note]", with: "Bug fixed :smile:"
find('.js-md-preview-button').click
end
end
step 'I submit the comment' do
- within(".js-main-target-form") do
+ page.within(".js-main-target-form") do
click_button "Add Comment"
end
end
step 'I write a comment like ":+1: Nice"' do
- within(".js-main-target-form") do
+ page.within(".js-main-target-form") do
fill_in 'note[note]', with: ':+1: Nice'
end
end
step 'I should not see a comment saying "XML attached"' do
- page.should_not have_css(".note")
+ expect(page).not_to have_css(".note")
end
step 'I should not see the cancel comment button' do
- within(".js-main-target-form") do
+ page.within(".js-main-target-form") do
should_not have_link("Cancel")
end
end
step 'I should not see the comment preview' do
- within(".js-main-target-form") do
+ page.within(".js-main-target-form") do
expect(find('.js-md-preview')).not_to be_visible
end
end
step 'The comment preview tab should say there is nothing to do' do
- within(".js-main-target-form") do
+ page.within(".js-main-target-form") do
find('.js-md-preview-button').click
expect(find('.js-md-preview')).to have_content('Nothing to preview.')
end
end
step 'I should not see the comment text field' do
- within(".js-main-target-form") do
+ page.within(".js-main-target-form") do
expect(find('.js-note-text')).not_to be_visible
end
end
step 'I should see a comment saying "XML attached"' do
- within(".note") do
- page.should have_content("XML attached")
+ page.within(".note") do
+ expect(page).to have_content("XML attached")
end
end
step 'I should see an empty comment text field' do
- within(".js-main-target-form") do
- page.should have_field("note[note]", with: "")
+ page.within(".js-main-target-form") do
+ expect(page).to have_field("note[note]", with: "")
end
end
step 'I should see the comment write tab' do
- within(".js-main-target-form") do
+ page.within(".js-main-target-form") do
expect(page).to have_css('.js-md-write-button', visible: true)
end
end
step 'The comment preview tab should be display rendered Markdown' do
- within(".js-main-target-form") do
+ page.within(".js-main-target-form") do
find('.js-md-preview-button').click
expect(find('.js-md-preview')).to have_css('img.emoji', visible: true)
end
end
step 'I should see the comment preview' do
- within(".js-main-target-form") do
+ page.within(".js-main-target-form") do
expect(page).to have_css('.js-md-preview', visible: true)
end
end
step 'I should see comment "XML attached"' do
- within(".note") do
- page.should have_content("XML attached")
+ page.within(".note") do
+ expect(page).to have_content("XML attached")
end
end
# Markdown
step 'I leave a comment with a header containing "Comment with a header"' do
- within(".js-main-target-form") do
+ page.within(".js-main-target-form") do
fill_in "note[note]", with: "# Comment with a header"
click_button "Add Comment"
sleep 0.05
@@ -116,9 +116,9 @@ module SharedNote
end
step 'The comment with the header should not have an ID' do
- within(".note-body > .note-text") do
- page.should have_content("Comment with a header")
- page.should_not have_css("#comment-with-a-header")
+ page.within(".note-body > .note-text") do
+ expect(page).to have_content("Comment with a header")
+ expect(page).not_to have_css("#comment-with-a-header")
end
end
@@ -126,7 +126,7 @@ module SharedNote
find(".note").hover
find('.js-note-edit').click
- within(".current-note-edit-form") do
+ page.within(".current-note-edit-form") do
fill_in 'note[note]', with: '+1 Awesome!'
click_button 'Save Comment'
sleep 0.05
@@ -134,8 +134,8 @@ module SharedNote
end
step 'I should see +1 in the description' do
- within(".note") do
- page.should have_content("+1 Awesome!")
+ page.within(".note") do
+ expect(page).to have_content("+1 Awesome!")
end
end
end
diff --git a/features/steps/shared/paths.rb b/features/steps/shared/paths.rb
index d9401bd540c..09ae7e3a305 100644
--- a/features/steps/shared/paths.rb
+++ b/features/steps/shared/paths.rb
@@ -92,7 +92,7 @@ module SharedPaths
end
step 'I should be redirected to the dashboard groups page' do
- current_path.should == dashboard_groups_path
+ expect(current_path).to eq dashboard_groups_path
end
step 'I visit dashboard starred projects page' do
@@ -227,6 +227,10 @@ module SharedPaths
visit namespace_project_merge_requests_path(@project.namespace, @project)
end
+ step "I visit my project's members page" do
+ visit namespace_project_project_members_path(@project.namespace, @project)
+ end
+
step "I visit my project's wiki page" do
visit namespace_project_wiki_path(@project.namespace, @project, :home)
end
@@ -288,11 +292,11 @@ module SharedPaths
end
step 'I am on the new file page' do
- current_path.should eq(namespace_project_create_blob_path(@project.namespace, @project, root_ref))
+ expect(current_path).to eq(namespace_project_create_blob_path(@project.namespace, @project, root_ref))
end
step 'I am on the ".gitignore" edit file page' do
- current_path.should eq(namespace_project_edit_blob_path(
+ expect(current_path).to eq(namespace_project_edit_blob_path(
@project.namespace, @project, File.join(root_ref, '.gitignore')))
end
@@ -455,6 +459,6 @@ module SharedPaths
# ----------------------------------------
step 'page status code should be 404' do
- status_code.should == 404
+ expect(status_code).to eq 404
end
end
diff --git a/features/steps/shared/project.rb b/features/steps/shared/project.rb
index 3059c4ee041..9ee2e5dfbed 100644
--- a/features/steps/shared/project.rb
+++ b/features/steps/shared/project.rb
@@ -79,13 +79,13 @@ module SharedProject
step 'I should see project "Shop" activity feed' do
project = Project.find_by(name: "Shop")
- page.should have_content "#{@user.name} pushed new branch fix at #{project.name_with_namespace}"
+ expect(page).to have_content "#{@user.name} pushed new branch fix at #{project.name_with_namespace}"
end
step 'I should see project settings' do
- current_path.should == edit_namespace_project_path(@project.namespace, @project)
- page.should have_content("Project name")
- page.should have_content("Features:")
+ expect(current_path).to eq edit_namespace_project_path(@project.namespace, @project)
+ expect(page).to have_content("Project name")
+ expect(page).to have_content("Features:")
end
def current_project
@@ -101,11 +101,11 @@ module SharedProject
end
step 'I should see project "Enterprise"' do
- page.should have_content "Enterprise"
+ expect(page).to have_content "Enterprise"
end
step 'I should not see project "Enterprise"' do
- page.should_not have_content "Enterprise"
+ expect(page).not_to have_content "Enterprise"
end
step 'internal project "Internal"' do
@@ -113,11 +113,11 @@ module SharedProject
end
step 'I should see project "Internal"' do
- page.should have_content "Internal"
+ expect(page).to have_content "Internal"
end
step 'I should not see project "Internal"' do
- page.should_not have_content "Internal"
+ expect(page).not_to have_content "Internal"
end
step 'public project "Community"' do
@@ -125,11 +125,11 @@ module SharedProject
end
step 'I should see project "Community"' do
- page.should have_content "Community"
+ expect(page).to have_content "Community"
end
step 'I should not see project "Community"' do
- page.should_not have_content "Community"
+ expect(page).not_to have_content "Community"
end
step '"John Doe" owns private project "Enterprise"' do
diff --git a/features/steps/shared/project_tab.rb b/features/steps/shared/project_tab.rb
index c5aed19331c..3b94b7d8621 100644
--- a/features/steps/shared/project_tab.rb
+++ b/features/steps/shared/project_tab.rb
@@ -28,6 +28,10 @@ module SharedProjectTab
ensure_active_main_tab('Issues')
end
+ step 'the active main tab should be Members' do
+ ensure_active_main_tab('Members')
+ end
+
step 'the active main tab should be Merge Requests' do
ensure_active_main_tab('Merge Requests')
end
@@ -41,8 +45,8 @@ module SharedProjectTab
end
step 'the active main tab should be Settings' do
- within '.nav-sidebar' do
- page.should have_content('Back to project')
+ page.within '.nav-sidebar' do
+ expect(page).to have_content('Back to project')
end
end
end
diff --git a/features/steps/snippet_search.rb b/features/steps/snippet_search.rb
index 669c7186c1b..cf999879579 100644
--- a/features/steps/snippet_search.rb
+++ b/features/steps/snippet_search.rb
@@ -18,39 +18,39 @@ class Spinach::Features::SnippetSearch < Spinach::FeatureSteps
end
step 'I should see "line seven" in results' do
- page.should have_content 'line seven'
+ expect(page).to have_content 'line seven'
end
step 'I should see "line four" in results' do
- page.should have_content 'line four'
+ expect(page).to have_content 'line four'
end
step 'I should see "line ten" in results' do
- page.should have_content 'line ten'
+ expect(page).to have_content 'line ten'
end
step 'I should not see "line eleven" in results' do
- page.should_not have_content 'line eleven'
+ expect(page).not_to have_content 'line eleven'
end
step 'I should not see "line three" in results' do
- page.should_not have_content 'line three'
+ expect(page).not_to have_content 'line three'
end
step 'I should see "Personal snippet one" in results' do
- page.should have_content 'Personal snippet one'
+ expect(page).to have_content 'Personal snippet one'
end
step 'I should see "Personal snippet private" in results' do
- page.should have_content 'Personal snippet private'
+ expect(page).to have_content 'Personal snippet private'
end
step 'I should not see "Personal snippet one" in results' do
- page.should_not have_content 'Personal snippet one'
+ expect(page).not_to have_content 'Personal snippet one'
end
step 'I should not see "Personal snippet private" in results' do
- page.should_not have_content 'Personal snippet private'
+ expect(page).not_to have_content 'Personal snippet private'
end
end
diff --git a/features/steps/snippets/discover.rb b/features/steps/snippets/discover.rb
index 2667c1e3d44..76379d09d02 100644
--- a/features/steps/snippets/discover.rb
+++ b/features/steps/snippets/discover.rb
@@ -4,15 +4,15 @@ class Spinach::Features::SnippetsDiscover < Spinach::FeatureSteps
include SharedSnippet
step 'I should see "Personal snippet one" in snippets' do
- page.should have_content "Personal snippet one"
+ expect(page).to have_content "Personal snippet one"
end
step 'I should see "Personal snippet internal" in snippets' do
- page.should have_content "Personal snippet internal"
+ expect(page).to have_content "Personal snippet internal"
end
step 'I should not see "Personal snippet private" in snippets' do
- page.should_not have_content "Personal snippet private"
+ expect(page).not_to have_content "Personal snippet private"
end
def snippet
diff --git a/features/steps/snippets/public_snippets.rb b/features/steps/snippets/public_snippets.rb
index 67669dc0a69..2ebdca5ed30 100644
--- a/features/steps/snippets/public_snippets.rb
+++ b/features/steps/snippets/public_snippets.rb
@@ -4,11 +4,11 @@ class Spinach::Features::PublicSnippets < Spinach::FeatureSteps
include SharedSnippet
step 'I should see snippet "Personal snippet one"' do
- page.should have_no_xpath("//i[@class='public-snippet']")
+ expect(page).to have_no_xpath("//i[@class='public-snippet']")
end
step 'I should see raw snippet "Personal snippet one"' do
- page.should have_text(snippet.content)
+ expect(page).to have_text(snippet.content)
end
step 'I visit snippet page "Personal snippet one"' do
diff --git a/features/steps/snippets/snippets.rb b/features/steps/snippets/snippets.rb
index de936db85ee..09fdd1b5a13 100644
--- a/features/steps/snippets/snippets.rb
+++ b/features/steps/snippets/snippets.rb
@@ -9,11 +9,11 @@ class Spinach::Features::Snippets < Spinach::FeatureSteps
end
step 'I should not see "Personal snippet one" in snippets' do
- page.should_not have_content "Personal snippet one"
+ expect(page).not_to have_content "Personal snippet one"
end
step 'I click link "Edit"' do
- within ".file-title" do
+ page.within ".file-title" do
click_link "Edit"
end
end
@@ -25,15 +25,15 @@ class Spinach::Features::Snippets < Spinach::FeatureSteps
step 'I submit new snippet "Personal snippet three"' do
fill_in "personal_snippet_title", :with => "Personal snippet three"
fill_in "personal_snippet_file_name", :with => "my_snippet.rb"
- within('.file-editor') do
+ page.within('.file-editor') do
find(:xpath, "//input[@id='personal_snippet_content']").set 'Content of snippet three'
end
click_button "Create snippet"
end
step 'I should see snippet "Personal snippet three"' do
- page.should have_content "Personal snippet three"
- page.should have_content "Content of snippet three"
+ expect(page).to have_content "Personal snippet three"
+ expect(page).to have_content "Content of snippet three"
end
step 'I submit new title "Personal snippet new title"' do
@@ -42,7 +42,7 @@ class Spinach::Features::Snippets < Spinach::FeatureSteps
end
step 'I should see "Personal snippet new title"' do
- page.should have_content "Personal snippet new title"
+ expect(page).to have_content "Personal snippet new title"
end
step 'I uncheck "Private" checkbox' do
@@ -51,7 +51,7 @@ class Spinach::Features::Snippets < Spinach::FeatureSteps
end
step 'I should see "Personal snippet one" public' do
- page.should have_no_xpath("//i[@class='public-snippet']")
+ expect(page).to have_no_xpath("//i[@class='public-snippet']")
end
step 'I visit snippet page "Personal snippet one"' do
diff --git a/features/steps/snippets/user.rb b/features/steps/snippets/user.rb
index 146cc535d88..007fcb2893f 100644
--- a/features/steps/snippets/user.rb
+++ b/features/steps/snippets/user.rb
@@ -8,43 +8,43 @@ class Spinach::Features::SnippetsUser < Spinach::FeatureSteps
end
step 'I should see "Personal snippet one" in snippets' do
- page.should have_content "Personal snippet one"
+ expect(page).to have_content "Personal snippet one"
end
step 'I should see "Personal snippet private" in snippets' do
- page.should have_content "Personal snippet private"
+ expect(page).to have_content "Personal snippet private"
end
step 'I should see "Personal snippet internal" in snippets' do
- page.should have_content "Personal snippet internal"
+ expect(page).to have_content "Personal snippet internal"
end
step 'I should not see "Personal snippet one" in snippets' do
- page.should_not have_content "Personal snippet one"
+ expect(page).not_to have_content "Personal snippet one"
end
step 'I should not see "Personal snippet private" in snippets' do
- page.should_not have_content "Personal snippet private"
+ expect(page).not_to have_content "Personal snippet private"
end
step 'I should not see "Personal snippet internal" in snippets' do
- page.should_not have_content "Personal snippet internal"
+ expect(page).not_to have_content "Personal snippet internal"
end
step 'I click "Internal" filter' do
- within('.nav-tabs') do
+ page.within('.nav-tabs') do
click_link "Internal"
end
end
step 'I click "Private" filter' do
- within('.nav-tabs') do
+ page.within('.nav-tabs') do
click_link "Private"
end
end
step 'I click "Public" filter' do
- within('.nav-tabs') do
+ page.within('.nav-tabs') do
click_link "Public"
end
end
diff --git a/features/steps/user.rb b/features/steps/user.rb
index 10cae692a88..3230234cb6d 100644
--- a/features/steps/user.rb
+++ b/features/steps/user.rb
@@ -28,13 +28,13 @@ class Spinach::Features::User < Spinach::FeatureSteps
end
step 'I should see contributed projects' do
- within '.contributed-projects' do
- page.should have_content(@contributed_project.name)
+ page.within '.contributed-projects' do
+ expect(page).to have_content(@contributed_project.name)
end
end
step 'I should see contributions calendar' do
- page.should have_css('.cal-heatmap-container')
+ expect(page).to have_css('.cal-heatmap-container')
end
def contributed_project
diff --git a/features/support/env.rb b/features/support/env.rb
index f34302721ed..d4a878ea4ce 100644
--- a/features/support/env.rb
+++ b/features/support/env.rb
@@ -9,7 +9,6 @@ end
ENV['RAILS_ENV'] = 'test'
require './config/environment'
-require 'rspec'
require 'rspec/expectations'
require 'sidekiq/testing/inline'
diff --git a/lib/api/commits.rb b/lib/api/commits.rb
index 23270b1c0f4..f4efb651eb6 100644
--- a/lib/api/commits.rb
+++ b/lib/api/commits.rb
@@ -62,7 +62,7 @@ module API
sha = params[:sha]
commit = user_project.commit(sha)
not_found! 'Commit' unless commit
- notes = Note.where(commit_id: commit.id)
+ notes = Note.where(commit_id: commit.id).order(:created_at)
present paginate(notes), with: Entities::CommitNote
end
diff --git a/lib/api/files.rb b/lib/api/files.rb
index e0ea6d7dd1d..c7b30cf2f07 100644
--- a/lib/api/files.rb
+++ b/lib/api/files.rb
@@ -3,6 +3,26 @@ module API
class Files < Grape::API
before { authenticate! }
+ helpers do
+ def commit_params(attrs)
+ {
+ file_path: attrs[:file_path],
+ current_branch: attrs[:branch_name],
+ target_branch: attrs[:branch_name],
+ commit_message: attrs[:commit_message],
+ file_content: attrs[:content],
+ file_content_encoding: attrs[:encoding]
+ }
+ end
+
+ def commit_response(attrs)
+ {
+ file_path: attrs[:file_path],
+ branch_name: attrs[:branch_name],
+ }
+ end
+ end
+
resource :projects do
# Get file from repository
# File content is Base64 encoded
@@ -73,17 +93,11 @@ module API
required_attributes! [:file_path, :branch_name, :content, :commit_message]
attrs = attributes_for_keys [:file_path, :branch_name, :content, :commit_message, :encoding]
- branch_name = attrs.delete(:branch_name)
- file_path = attrs.delete(:file_path)
- result = ::Files::CreateService.new(user_project, current_user, attrs, branch_name, file_path).execute
+ result = ::Files::CreateService.new(user_project, current_user, commit_params(attrs)).execute
if result[:status] == :success
status(201)
-
- {
- file_path: file_path,
- branch_name: branch_name
- }
+ commit_response(attrs)
else
render_api_error!(result[:message], 400)
end
@@ -105,17 +119,11 @@ module API
required_attributes! [:file_path, :branch_name, :content, :commit_message]
attrs = attributes_for_keys [:file_path, :branch_name, :content, :commit_message, :encoding]
- branch_name = attrs.delete(:branch_name)
- file_path = attrs.delete(:file_path)
- result = ::Files::UpdateService.new(user_project, current_user, attrs, branch_name, file_path).execute
+ result = ::Files::UpdateService.new(user_project, current_user, commit_params(attrs)).execute
if result[:status] == :success
status(200)
-
- {
- file_path: file_path,
- branch_name: branch_name
- }
+ commit_response(attrs)
else
http_status = result[:http_status] || 400
render_api_error!(result[:message], http_status)
@@ -138,17 +146,11 @@ module API
required_attributes! [:file_path, :branch_name, :commit_message]
attrs = attributes_for_keys [:file_path, :branch_name, :commit_message]
- branch_name = attrs.delete(:branch_name)
- file_path = attrs.delete(:file_path)
- result = ::Files::DeleteService.new(user_project, current_user, attrs, branch_name, file_path).execute
+ result = ::Files::DeleteService.new(user_project, current_user, commit_params(attrs)).execute
if result[:status] == :success
status(200)
-
- {
- file_path: file_path,
- branch_name: branch_name
- }
+ commit_response(attrs)
else
render_api_error!(result[:message], 400)
end
diff --git a/lib/api/groups.rb b/lib/api/groups.rb
index f768c750402..e88b6e31775 100644
--- a/lib/api/groups.rb
+++ b/lib/api/groups.rb
@@ -62,7 +62,7 @@ module API
delete ":id" do
group = find_group(params[:id])
authorize! :admin_group, group
- group.destroy
+ DestroyGroupService.new(group, current_user).execute
end
# Transfer a project to the Group namespace
diff --git a/lib/api/merge_requests.rb b/lib/api/merge_requests.rb
index 2216a12a87a..d835dce2ded 100644
--- a/lib/api/merge_requests.rb
+++ b/lib/api/merge_requests.rb
@@ -137,7 +137,6 @@ module API
# Parameters:
# id (required) - The ID of a project
# merge_request_id (required) - ID of MR
- # source_branch - The source branch
# target_branch - The target branch
# assignee_id - Assignee user ID
# title - Title of MR
@@ -148,10 +147,15 @@ module API
# PUT /projects/:id/merge_request/:merge_request_id
#
put ":id/merge_request/:merge_request_id" do
- attrs = attributes_for_keys [:source_branch, :target_branch, :assignee_id, :title, :state_event, :description]
+ attrs = attributes_for_keys [:target_branch, :assignee_id, :title, :state_event, :description]
merge_request = user_project.merge_requests.find(params[:merge_request_id])
authorize! :modify_merge_request, merge_request
+ # Ensure source_branch is not specified
+ if params[:source_branch].present?
+ render_api_error!('Source branch cannot be changed', 400)
+ end
+
# Validate label names in advance
if (errors = validate_label_params(params)).any?
render_api_error!({ labels: errors }, 400)
diff --git a/lib/api/namespaces.rb b/lib/api/namespaces.rb
index b90ed6af5fb..50d3729449e 100644
--- a/lib/api/namespaces.rb
+++ b/lib/api/namespaces.rb
@@ -1,10 +1,7 @@
module API
# namespaces API
class Namespaces < Grape::API
- before do
- authenticate!
- authenticated_as_admin!
- end
+ before { authenticate! }
resource :namespaces do
# Get a namespaces list
@@ -12,7 +9,11 @@ module API
# Example Request:
# GET /namespaces
get do
- @namespaces = Namespace.all
+ @namespaces = if current_user.admin
+ Namespace.all
+ else
+ current_user.namespaces
+ end
@namespaces = @namespaces.search(params[:search]) if params[:search].present?
@namespaces = paginate @namespaces
diff --git a/lib/api/users.rb b/lib/api/users.rb
index 032a5d76e43..7d4c68c7412 100644
--- a/lib/api/users.rb
+++ b/lib/api/users.rb
@@ -194,7 +194,7 @@ module API
user = User.find_by(id: params[:id])
if user
- user.destroy
+ DeleteUserService.new.execute(user)
else
not_found!('User')
end
diff --git a/lib/backup/manager.rb b/lib/backup/manager.rb
index b69aebf9fe1..6fa2079d1a8 100644
--- a/lib/backup/manager.rb
+++ b/lib/backup/manager.rb
@@ -46,7 +46,8 @@ module Backup
connection = ::Fog::Storage.new(connection_settings)
directory = connection.directories.get(remote_directory)
- if directory.files.create(key: tar_file, body: File.open(tar_file), public: false)
+ if directory.files.create(key: tar_file, body: File.open(tar_file), public: false,
+ multipart_chunk_size: Gitlab.config.backup.upload.multipart_chunk_size)
$progress.puts "done".green
else
puts "uploading backup to #{remote_directory} failed".red
diff --git a/lib/gitlab/backend/shell.rb b/lib/gitlab/backend/shell.rb
index 530f9d93de4..172d4902add 100644
--- a/lib/gitlab/backend/shell.rb
+++ b/lib/gitlab/backend/shell.rb
@@ -244,6 +244,16 @@ module Gitlab
end
end
+ # Check if such directory exists in repositories.
+ #
+ # Usage:
+ # exists?('gitlab')
+ # exists?('gitlab/cookies.git')
+ #
+ def exists?(dir_name)
+ File.exists?(full_path(dir_name))
+ end
+
protected
def gitlab_shell_path
@@ -264,10 +274,6 @@ module Gitlab
File.join(repos_path, dir_name)
end
- def exists?(dir_name)
- File.exists?(full_path(dir_name))
- end
-
def gitlab_shell_projects_path
File.join(gitlab_shell_path, 'bin', 'gitlab-projects')
end
diff --git a/lib/gitlab/current_settings.rb b/lib/gitlab/current_settings.rb
index d8f696d247b..931d51c55d3 100644
--- a/lib/gitlab/current_settings.rb
+++ b/lib/gitlab/current_settings.rb
@@ -21,7 +21,8 @@ module Gitlab
gravatar_enabled: Settings.gravatar['enabled'],
sign_in_text: Settings.extra['sign_in_text'],
restricted_visibility_levels: Settings.gitlab['restricted_visibility_levels'],
- max_attachment_size: Settings.gitlab['max_attachment_size']
+ max_attachment_size: Settings.gitlab['max_attachment_size'],
+ session_expire_delay: Settings.gitlab['session_expire_delay']
)
end
end
diff --git a/lib/gitlab/gitorious_import.rb b/lib/gitlab/gitorious_import.rb
new file mode 100644
index 00000000000..8d0132a744c
--- /dev/null
+++ b/lib/gitlab/gitorious_import.rb
@@ -0,0 +1,5 @@
+module Gitlab
+ module GitoriousImport
+ GITORIOUS_HOST = "https://gitorious.org"
+ end
+end
diff --git a/lib/gitlab/gitorious_import/client.rb b/lib/gitlab/gitorious_import/client.rb
index 1fa89dba448..99fe5bdebfc 100644
--- a/lib/gitlab/gitorious_import/client.rb
+++ b/lib/gitlab/gitorious_import/client.rb
@@ -1,7 +1,5 @@
module Gitlab
module GitoriousImport
- GITORIOUS_HOST = "https://gitorious.org"
-
class Client
attr_reader :repo_list
diff --git a/lib/gitlab/gitorious_import/repository.rb b/lib/gitlab/gitorious_import/repository.rb
index f702797dc6e..c88f1ae358d 100644
--- a/lib/gitlab/gitorious_import/repository.rb
+++ b/lib/gitlab/gitorious_import/repository.rb
@@ -1,7 +1,5 @@
module Gitlab
module GitoriousImport
- GITORIOUS_HOST = "https://gitorious.org"
-
Repository = Struct.new(:full_name) do
def id
Digest::SHA1.hexdigest(full_name)
diff --git a/lib/gitlab/markdown.rb b/lib/gitlab/markdown.rb
index 5db1566f55d..fa9c0975bb8 100644
--- a/lib/gitlab/markdown.rb
+++ b/lib/gitlab/markdown.rb
@@ -57,6 +57,9 @@ module Gitlab
pipeline = HTML::Pipeline.new(filters)
context = {
+ # SanitizationFilter
+ pipeline: options[:pipeline],
+
# EmojiFilter
asset_root: Gitlab.config.gitlab.url,
asset_host: Gitlab::Application.config.asset_host,
diff --git a/lib/gitlab/markdown/reference_filter.rb b/lib/gitlab/markdown/reference_filter.rb
index be4d26af0fc..a84bacd3d4f 100644
--- a/lib/gitlab/markdown/reference_filter.rb
+++ b/lib/gitlab/markdown/reference_filter.rb
@@ -25,12 +25,18 @@ module Gitlab
ERB::Util.html_escape_once(html)
end
- # Don't look for references in text nodes that are children of these
- # elements.
- IGNORE_PARENTS = %w(pre code a style).to_set
+ def ignore_parents
+ @ignore_parents ||= begin
+ # Don't look for references in text nodes that are children of these
+ # elements.
+ parents = %w(pre code a style)
+ parents << 'blockquote' if context[:ignore_blockquotes]
+ parents.to_set
+ end
+ end
def ignored_ancestry?(node)
- has_ancestor?(node, IGNORE_PARENTS)
+ has_ancestor?(node, ignore_parents)
end
def project
diff --git a/lib/gitlab/markdown/sanitization_filter.rb b/lib/gitlab/markdown/sanitization_filter.rb
index 88781fea0c8..74b3a8d274f 100644
--- a/lib/gitlab/markdown/sanitization_filter.rb
+++ b/lib/gitlab/markdown/sanitization_filter.rb
@@ -8,33 +8,54 @@ module Gitlab
# Extends HTML::Pipeline::SanitizationFilter with a custom whitelist.
class SanitizationFilter < HTML::Pipeline::SanitizationFilter
def whitelist
- whitelist = super
+ # Descriptions are more heavily sanitized, allowing only a few elements.
+ # See http://git.io/vkuAN
+ if pipeline == :description
+ whitelist = LIMITED
+ whitelist[:elements] -= %w(pre code img ol ul li)
+ else
+ whitelist = super
+ end
+
+ customize_whitelist(whitelist)
+
+ whitelist
+ end
+ private
+
+ def pipeline
+ context[:pipeline] || :default
+ end
+
+ def customized?(transformers)
+ transformers.last.source_location[0] == __FILE__
+ end
+
+ def customize_whitelist(whitelist)
# Only push these customizations once
- unless customized?(whitelist[:transformers])
- # Allow code highlighting
- whitelist[:attributes]['pre'] = %w(class)
- whitelist[:attributes]['span'] = %w(class)
+ return if customized?(whitelist[:transformers])
- # Allow table alignment
- whitelist[:attributes]['th'] = %w(style)
- whitelist[:attributes]['td'] = %w(style)
+ # Allow code highlighting
+ whitelist[:attributes]['pre'] = %w(class)
+ whitelist[:attributes]['span'] = %w(class)
- # Allow span elements
- whitelist[:elements].push('span')
+ # Allow table alignment
+ whitelist[:attributes]['th'] = %w(style)
+ whitelist[:attributes]['td'] = %w(style)
- # Remove `rel` attribute from `a` elements
- whitelist[:transformers].push(remove_rel)
+ # Allow span elements
+ whitelist[:elements].push('span')
- # Remove `class` attribute from non-highlight spans
- whitelist[:transformers].push(clean_spans)
- end
+ # Remove `rel` attribute from `a` elements
+ whitelist[:transformers].push(remove_rel)
+
+ # Remove `class` attribute from non-highlight spans
+ whitelist[:transformers].push(clean_spans)
whitelist
end
- private
-
def remove_rel
lambda do |env|
if env[:node_name] == 'a'
@@ -53,10 +74,6 @@ module Gitlab
end
end
end
-
- def customized?(transformers)
- transformers.last.source_location[0] == __FILE__
- end
end
end
end
diff --git a/lib/gitlab/o_auth/user.rb b/lib/gitlab/o_auth/user.rb
index ba5caed6131..c4971b5bcc6 100644
--- a/lib/gitlab/o_auth/user.rb
+++ b/lib/gitlab/o_auth/user.rb
@@ -46,6 +46,10 @@ module Gitlab
def gl_user
@user ||= find_by_uid_and_provider
+ if auto_link_ldap_user?
+ @user ||= find_or_create_ldap_user
+ end
+
if signup_enabled?
@user ||= build_new_user
end
@@ -55,6 +59,46 @@ module Gitlab
protected
+ def find_or_create_ldap_user
+ return unless ldap_person
+
+ # If a corresponding person exists with same uid in a LDAP server,
+ # set up a Gitlab user with dual LDAP and Omniauth identities.
+ if user = Gitlab::LDAP::User.find_by_uid_and_provider(ldap_person.dn.downcase, ldap_person.provider)
+ # Case when a LDAP user already exists in Gitlab. Add the Omniauth identity to existing account.
+ user.identities.build(extern_uid: auth_hash.uid, provider: auth_hash.provider)
+ else
+ # No account in Gitlab yet: create it and add the LDAP identity
+ user = build_new_user
+ user.identities.new(provider: ldap_person.provider, extern_uid: ldap_person.dn)
+ end
+
+ user
+ end
+
+ def auto_link_ldap_user?
+ Gitlab.config.omniauth.auto_link_ldap_user
+ end
+
+ def creating_linked_ldap_user?
+ auto_link_ldap_user? && ldap_person
+ end
+
+ def ldap_person
+ return @ldap_person if defined?(@ldap_person)
+
+ # looks for a corresponding person with same uid in any of the configured LDAP providers
+ @ldap_person = Gitlab::LDAP::Config.providers.find do |provider|
+ adapter = Gitlab::LDAP::Adapter.new(provider)
+
+ Gitlab::LDAP::Person.find_by_uid(auth_hash.uid, adapter)
+ end
+ end
+
+ def ldap_config
+ Gitlab::LDAP::Config.new(ldap_person.provider) if ldap_person
+ end
+
def needs_blocking?
new? && block_after_signup?
end
@@ -64,7 +108,11 @@ module Gitlab
end
def block_after_signup?
- Gitlab.config.omniauth.block_auto_created_users
+ if creating_linked_ldap_user?
+ ldap_config.block_auto_created_users
+ else
+ Gitlab.config.omniauth.block_auto_created_users
+ end
end
def auth_hash=(auth_hash)
@@ -84,10 +132,19 @@ module Gitlab
end
def user_attributes
+ # Give preference to LDAP for sensitive information when creating a linked account
+ if creating_linked_ldap_user?
+ username = ldap_person.username
+ email = ldap_person.email.first
+ else
+ username = auth_hash.username
+ email = auth_hash.email
+ end
+
{
name: auth_hash.name,
- username: ::Namespace.clean_path(auth_hash.username),
- email: auth_hash.email,
+ username: ::Namespace.clean_path(username),
+ email: email,
password: auth_hash.password,
password_confirmation: auth_hash.password,
password_automatically_set: true
diff --git a/lib/gitlab/reference_extractor.rb b/lib/gitlab/reference_extractor.rb
index e35f848fa6e..e836b05ff25 100644
--- a/lib/gitlab/reference_extractor.rb
+++ b/lib/gitlab/reference_extractor.rb
@@ -1,7 +1,7 @@
module Gitlab
# Extract possible GFM references from an arbitrary String for further processing.
class ReferenceExtractor
- attr_accessor :project, :current_user, :references
+ attr_accessor :project, :current_user
def initialize(project, current_user = nil)
@project = project
@@ -9,48 +9,31 @@ module Gitlab
end
def analyze(text)
- @_text = text.dup
+ references.clear
+ @text = markdown.render(text.dup)
end
- def users
- result = pipeline_result(:user)
- result.uniq
+ %i(user label issue merge_request snippet commit commit_range).each do |type|
+ define_method("#{type}s") do
+ references[type]
+ end
end
- def labels
- result = pipeline_result(:label)
- result.uniq
- end
-
- def issues
- # TODO (rspeicher): What about external issues?
-
- result = pipeline_result(:issue)
- result.uniq
- end
-
- def merge_requests
- result = pipeline_result(:merge_request)
- result.uniq
- end
+ private
- def snippets
- result = pipeline_result(:snippet)
- result.uniq
+ def markdown
+ @markdown ||= Redcarpet::Markdown.new(Redcarpet::Render::HTML, GitlabMarkdownHelper::MARKDOWN_OPTIONS)
end
- def commits
- result = pipeline_result(:commit)
- result.uniq
- end
+ def references
+ @references ||= Hash.new do |references, type|
+ type = type.to_sym
+ return references[type] if references.has_key?(type)
- def commit_ranges
- result = pipeline_result(:commit_range)
- result.uniq
+ references[type] = pipeline_result(type).uniq
+ end
end
- private
-
# Instantiate and call HTML::Pipeline with a single reference filter type,
# returning the result
#
@@ -65,11 +48,12 @@ module Gitlab
project: project,
current_user: current_user,
# We don't actually care about the links generated
- only_path: true
+ only_path: true,
+ ignore_blockquotes: true
}
pipeline = HTML::Pipeline.new([filter], context)
- result = pipeline.call(@_text)
+ result = pipeline.call(@text)
result[:references][filter_type]
end
diff --git a/lib/gitlab/satellite/files/delete_file_action.rb b/lib/gitlab/satellite/files/delete_file_action.rb
deleted file mode 100644
index 0d37b9dea85..00000000000
--- a/lib/gitlab/satellite/files/delete_file_action.rb
+++ /dev/null
@@ -1,50 +0,0 @@
-require_relative 'file_action'
-
-module Gitlab
- module Satellite
- class DeleteFileAction < FileAction
- # Deletes file and creates a new commit for it
- #
- # Returns false if committing the change fails
- # Returns false if pushing from the satellite to bare repo failed or was rejected
- # Returns true otherwise
- def commit!(content, commit_message)
- in_locked_and_timed_satellite do |repo|
- prepare_satellite!(repo)
-
- # create target branch in satellite at the corresponding commit from bare repo
- repo.git.checkout({ raise: true, timeout: true, b: true }, ref, "origin/#{ref}")
-
- # update the file in the satellite's working dir
- file_path_in_satellite = File.join(repo.working_dir, file_path)
-
- # Prevent relative links
- unless safe_path?(file_path_in_satellite)
- Gitlab::GitLogger.error("FileAction: Relative path not allowed")
- return false
- end
-
- File.delete(file_path_in_satellite)
-
- # add removed file
- repo.remove(file_path_in_satellite)
-
- # commit the changes
- # will raise CommandFailed when commit fails
- repo.git.commit(raise: true, timeout: true, a: true, m: commit_message)
-
-
- # push commit back to bare repo
- # will raise CommandFailed when push fails
- repo.git.push({ raise: true, timeout: true }, :origin, ref)
-
- # everything worked
- true
- end
- rescue Grit::Git::CommandFailed => ex
- Gitlab::GitLogger.error(ex.message)
- false
- end
- end
- end
-end
diff --git a/lib/gitlab/satellite/files/edit_file_action.rb b/lib/gitlab/satellite/files/edit_file_action.rb
deleted file mode 100644
index 3cb9c0b5ecb..00000000000
--- a/lib/gitlab/satellite/files/edit_file_action.rb
+++ /dev/null
@@ -1,68 +0,0 @@
-require_relative 'file_action'
-
-module Gitlab
- module Satellite
- # GitLab server-side file update and commit
- class EditFileAction < FileAction
- # Updates the files content and creates a new commit for it
- #
- # Returns false if the ref has been updated while editing the file
- # Returns false if committing the change fails
- # Returns false if pushing from the satellite to bare repo failed or was rejected
- # Returns true otherwise
- def commit!(content, commit_message, encoding, new_branch = nil)
- in_locked_and_timed_satellite do |repo|
- prepare_satellite!(repo)
-
- # create target branch in satellite at the corresponding commit from bare repo
- begin
- repo.git.checkout({ raise: true, timeout: true, b: true }, ref, "origin/#{ref}")
- rescue Grit::Git::CommandFailed => ex
- log_and_raise(CheckoutFailed, ex.message)
- end
-
- # update the file in the satellite's working dir
- file_path_in_satellite = File.join(repo.working_dir, file_path)
-
- # Prevent relative links
- unless safe_path?(file_path_in_satellite)
- Gitlab::GitLogger.error("FileAction: Relative path not allowed")
- return false
- end
-
- # Write file
- write_file(file_path_in_satellite, content, encoding)
-
- # commit the changes
- # will raise CommandFailed when commit fails
- begin
- repo.git.commit(raise: true, timeout: true, a: true, m: commit_message)
- rescue Grit::Git::CommandFailed => ex
- log_and_raise(CommitFailed, ex.message)
- end
-
-
- target_branch = new_branch.present? ? "#{ref}:#{new_branch}" : ref
-
- # push commit back to bare repo
- # will raise CommandFailed when push fails
- begin
- repo.git.push({ raise: true, timeout: true }, :origin, target_branch)
- rescue Grit::Git::CommandFailed => ex
- log_and_raise(PushFailed, ex.message)
- end
-
- # everything worked
- true
- end
- end
-
- private
-
- def log_and_raise(errorClass, message)
- Gitlab::GitLogger.error(message)
- raise(errorClass, message)
- end
- end
- end
-end
diff --git a/lib/gitlab/satellite/files/file_action.rb b/lib/gitlab/satellite/files/file_action.rb
deleted file mode 100644
index 6446b14568a..00000000000
--- a/lib/gitlab/satellite/files/file_action.rb
+++ /dev/null
@@ -1,25 +0,0 @@
-module Gitlab
- module Satellite
- class FileAction < Action
- attr_accessor :file_path, :ref
-
- def initialize(user, project, ref, file_path)
- super user, project
- @file_path = file_path
- @ref = ref
- end
-
- def safe_path?(path)
- File.absolute_path(path) == path
- end
-
- def write_file(abs_file_path, content, file_encoding = 'text')
- if file_encoding == 'base64'
- File.open(abs_file_path, 'wb') { |f| f.write(Base64.decode64(content)) }
- else
- File.open(abs_file_path, 'w') { |f| f.write(content) }
- end
- end
- end
- end
-end
diff --git a/lib/gitlab/satellite/files/new_file_action.rb b/lib/gitlab/satellite/files/new_file_action.rb
deleted file mode 100644
index 724dfa0d042..00000000000
--- a/lib/gitlab/satellite/files/new_file_action.rb
+++ /dev/null
@@ -1,67 +0,0 @@
-require_relative 'file_action'
-
-module Gitlab
- module Satellite
- class NewFileAction < FileAction
- # Updates the files content and creates a new commit for it
- #
- # Returns false if the ref has been updated while editing the file
- # Returns false if committing the change fails
- # Returns false if pushing from the satellite to bare repo failed or was rejected
- # Returns true otherwise
- def commit!(content, commit_message, encoding, new_branch = nil)
- in_locked_and_timed_satellite do |repo|
- prepare_satellite!(repo)
-
- # create target branch in satellite at the corresponding commit from bare repo
- current_ref =
- if @project.empty_repo?
- # skip this step if we want to add first file to empty repo
- Satellite::PARKING_BRANCH
- else
- repo.git.checkout({ raise: true, timeout: true, b: true }, ref, "origin/#{ref}")
- ref
- end
-
- file_path_in_satellite = File.join(repo.working_dir, file_path)
- dir_name_in_satellite = File.dirname(file_path_in_satellite)
-
- # Prevent relative links
- unless safe_path?(file_path_in_satellite)
- Gitlab::GitLogger.error("FileAction: Relative path not allowed")
- return false
- end
-
- # Create dir if not exists
- FileUtils.mkdir_p(dir_name_in_satellite)
-
- # Write file
- write_file(file_path_in_satellite, content, encoding)
-
- # add new file
- repo.add(file_path_in_satellite)
-
- # commit the changes
- # will raise CommandFailed when commit fails
- repo.git.commit(raise: true, timeout: true, a: true, m: commit_message)
-
- target_branch = if new_branch.present? && !@project.empty_repo?
- "#{ref}:#{new_branch}"
- else
- "#{current_ref}:#{ref}"
- end
-
- # push commit back to bare repo
- # will raise CommandFailed when push fails
- repo.git.push({ raise: true, timeout: true }, :origin, target_branch)
-
- # everything worked
- true
- end
- rescue Grit::Git::CommandFailed => ex
- Gitlab::GitLogger.error(ex.message)
- false
- end
- end
- end
-end
diff --git a/lib/gitlab/upgrader.rb b/lib/gitlab/upgrader.rb
index 0570c2fbeb5..cf040971c6e 100644
--- a/lib/gitlab/upgrader.rb
+++ b/lib/gitlab/upgrader.rb
@@ -43,10 +43,15 @@ module Gitlab
end
def latest_version_raw
+ git_tags = fetch_git_tags
+ git_tags = git_tags.select { |version| version =~ /v\d+\.\d+\.\d+\Z/ }
+ git_versions = git_tags.map { |tag| Gitlab::VersionInfo.parse(tag.match(/v\d+\.\d+\.\d+/).to_s) }
+ "v#{git_versions.sort.last.to_s}"
+ end
+
+ def fetch_git_tags
remote_tags, _ = Gitlab::Popen.popen(%W(git ls-remote --tags https://gitlab.com/gitlab-org/gitlab-ce.git))
- git_tags = remote_tags.split("\n").grep(/tags\/v#{current_version.major}/)
- git_tags = git_tags.select { |version| version =~ /v\d\.\d\.\d\Z/ }
- last_tag = git_tags.last.match(/v\d\.\d\.\d/).to_s
+ remote_tags.split("\n").grep(/tags\/v#{current_version.major}/)
end
def update_commands
diff --git a/lib/redcarpet/render/gitlab_html.rb b/lib/redcarpet/render/gitlab_html.rb
index 7dcecc2ecf6..2f7aff03c2a 100644
--- a/lib/redcarpet/render/gitlab_html.rb
+++ b/lib/redcarpet/render/gitlab_html.rb
@@ -10,6 +10,8 @@ class Redcarpet::Render::GitlabHTML < Redcarpet::Render::HTML
@options = options.dup
@options.reverse_merge!(
+ # Handled further down the line by Gitlab::Markdown::SanitizationFilter
+ escape_html: false,
project: @template.instance_variable_get("@project")
)
diff --git a/lib/support/init.d/gitlab b/lib/support/init.d/gitlab
index b066a1a6935..946902e2f6d 100755
--- a/lib/support/init.d/gitlab
+++ b/lib/support/init.d/gitlab
@@ -35,13 +35,14 @@ pid_path="$app_root/tmp/pids"
socket_path="$app_root/tmp/sockets"
web_server_pid_path="$pid_path/unicorn.pid"
sidekiq_pid_path="$pid_path/sidekiq.pid"
+shell_path="/bin/bash"
# Read configuration variable file if it is present
test -f /etc/default/gitlab && . /etc/default/gitlab
# Switch to the app_user if it is not he/she who is running the script.
if [ "$USER" != "$app_user" ]; then
- eval su - "$app_user" -c $(echo \")$0 "$@"$(echo \"); exit;
+ eval su - "$app_user" -s $shell_path -c $(echo \")$0 "$@"$(echo \"); exit;
fi
# Switch to the gitlab path, exit on failure.
diff --git a/lib/support/init.d/gitlab.default.example b/lib/support/init.d/gitlab.default.example
index 9951bacedf5..cf7f4198cbf 100755
--- a/lib/support/init.d/gitlab.default.example
+++ b/lib/support/init.d/gitlab.default.example
@@ -29,3 +29,8 @@ web_server_pid_path="$pid_path/unicorn.pid"
# sidekiq_pid_path defines the path in which to create the pid file for sidekiq
# The default is "$pid_path/sidekiq.pid"
sidekiq_pid_path="$pid_path/sidekiq.pid"
+
+# shell_path defines the path of shell for "$app_user" in case you are using
+# shell other than "bash"
+# The default is "/bin/bash"
+shell_path="/bin/bash"
diff --git a/lib/support/nginx/gitlab b/lib/support/nginx/gitlab
index 62a4276536c..4688a527eba 100644
--- a/lib/support/nginx/gitlab
+++ b/lib/support/nginx/gitlab
@@ -1,10 +1,16 @@
## GitLab
-## Contributors: randx, yin8086, sashkab, orkoden, axilleas, bbodenmiller, DouweM
##
## Lines starting with two hashes (##) are comments with information.
## Lines starting with one hash (#) are configuration parameters that can be uncommented.
##
##################################
+## CONTRIBUTING ##
+##################################
+##
+## If you change this file in a Merge Request, please also create
+## a Merge Request on https://gitlab.com/gitlab-org/omnibus-gitlab/merge_requests
+##
+##################################
## CHUNKED TRANSFER ##
##################################
##
diff --git a/lib/support/nginx/gitlab-ssl b/lib/support/nginx/gitlab-ssl
index 2aefc944698..187a27e93b0 100644
--- a/lib/support/nginx/gitlab-ssl
+++ b/lib/support/nginx/gitlab-ssl
@@ -1,5 +1,4 @@
## GitLab
-## Contributors: randx, yin8086, sashkab, orkoden, axilleas, bbodenmiller, DouweM
##
## Modified from nginx http version
## Modified from http://blog.phusion.nl/2012/04/21/tutorial-setting-up-gitlab-on-debian-6/
@@ -9,6 +8,13 @@
## Lines starting with one hash (#) are configuration parameters that can be uncommented.
##
##################################
+## CONTRIBUTING ##
+##################################
+##
+## If you change this file in a Merge Request, please also create
+## a Merge Request on https://gitlab.com/gitlab-org/omnibus-gitlab/merge_requests
+##
+##################################
## CHUNKED TRANSFER ##
##################################
##
diff --git a/lib/tasks/gitlab/check.rake b/lib/tasks/gitlab/check.rake
index 1a6303b6c82..75bd41f2838 100644
--- a/lib/tasks/gitlab/check.rake
+++ b/lib/tasks/gitlab/check.rake
@@ -1,7 +1,6 @@
namespace :gitlab do
desc "GITLAB | Check the configuration of GitLab and its environment"
- task check: %w{gitlab:env:check
- gitlab:gitlab_shell:check
+ task check: %w{gitlab:gitlab_shell:check
gitlab:sidekiq:check
gitlab:ldap:check
gitlab:app:check}
@@ -14,6 +13,7 @@ namespace :gitlab do
warn_user_is_not_gitlab
start_checking "GitLab"
+ check_git_config
check_database_config_exists
check_database_is_not_sqlite
check_migrations_are_up
@@ -38,6 +38,36 @@ namespace :gitlab do
# Checks
########################
+ def check_git_config
+ print "Git configured with autocrlf=input? ... "
+
+ options = {
+ "core.autocrlf" => "input"
+ }
+
+ correct_options = options.map do |name, value|
+ run(%W(#{Gitlab.config.git.bin_path} config --global --get #{name})).try(:squish) == value
+ end
+
+ if correct_options.all?
+ puts "yes".green
+ else
+ print "Trying to fix Git error automatically. ..."
+
+ if auto_fix_git_config(options)
+ puts "Success".green
+ else
+ puts "Failed".red
+ try_fixing_it(
+ sudo_gitlab("\"#{Gitlab.config.git.bin_path}\" config --global core.autocrlf \"#{options["core.autocrlf"]}\"")
+ )
+ for_more_information(
+ see_installation_guide_section "GitLab"
+ )
+ end
+ end
+ end
+
def check_database_config_exists
print "Database config exists? ... "
@@ -298,58 +328,6 @@ namespace :gitlab do
end
end
-
-
- namespace :env do
- desc "GITLAB | Check the configuration of the environment"
- task check: :environment do
- warn_user_is_not_gitlab
- start_checking "Environment"
-
- check_gitlab_git_config
-
- finished_checking "Environment"
- end
-
-
- # Checks
- ########################
-
- def check_gitlab_git_config
- print "Git configured for #{gitlab_user} user? ... "
-
- options = {
- "user.name" => "GitLab",
- "user.email" => Gitlab.config.gitlab.email_from,
- "core.autocrlf" => "input"
- }
- correct_options = options.map do |name, value|
- run(%W(#{Gitlab.config.git.bin_path} config --global --get #{name})).try(:squish) == value
- end
-
- if correct_options.all?
- puts "yes".green
- else
- print "Trying to fix Git error automatically. ..."
- if auto_fix_git_config(options)
- puts "Success".green
- else
- puts "Failed".red
- try_fixing_it(
- sudo_gitlab("\"#{Gitlab.config.git.bin_path}\" config --global user.name \"#{options["user.name"]}\""),
- sudo_gitlab("\"#{Gitlab.config.git.bin_path}\" config --global user.email \"#{options["user.email"]}\""),
- sudo_gitlab("\"#{Gitlab.config.git.bin_path}\" config --global core.autocrlf \"#{options["core.autocrlf"]}\"")
- )
- for_more_information(
- see_installation_guide_section "GitLab"
- )
- end
- end
- end
- end
-
-
-
namespace :gitlab_shell do
desc "GITLAB | Check the configuration of GitLab Shell"
task check: :environment do
diff --git a/lib/tasks/gitlab/cleanup.rake b/lib/tasks/gitlab/cleanup.rake
index 3c9802a0be4..d49cb6778f1 100644
--- a/lib/tasks/gitlab/cleanup.rake
+++ b/lib/tasks/gitlab/cleanup.rake
@@ -51,7 +51,7 @@ namespace :gitlab do
git_base_path = Gitlab.config.gitlab_shell.repos_path
all_dirs = Dir.glob(git_base_path + '/*')
- global_projects = Project.where(namespace_id: nil).pluck(:path)
+ global_projects = Project.in_namespace(nil).pluck(:path)
puts git_base_path.yellow
puts "Looking for global repos to remove... "
diff --git a/lib/tasks/gitlab/shell.rake b/lib/tasks/gitlab/shell.rake
index e835d6cb9b7..afdaba11cb0 100644
--- a/lib/tasks/gitlab/shell.rake
+++ b/lib/tasks/gitlab/shell.rake
@@ -59,6 +59,9 @@ namespace :gitlab do
# Launch installation process
system(*%W(bin/install))
+
+ # (Re)create hooks
+ system(*%W(bin/create-hooks))
end
# Required for debian packaging with PKGR: Setup .ssh/environment with
diff --git a/lib/tasks/gitlab/task_helpers.rake b/lib/tasks/gitlab/task_helpers.rake
index 14a130be2ca..c95b6540ebc 100644
--- a/lib/tasks/gitlab/task_helpers.rake
+++ b/lib/tasks/gitlab/task_helpers.rake
@@ -118,9 +118,9 @@ namespace :gitlab do
# Returns true if all subcommands were successfull (according to their exit code)
# Returns false if any or all subcommands failed.
def auto_fix_git_config(options)
- if !@warned_user_not_gitlab && options['user.email'] != 'example@example.com' # default email should be overridden?
+ if !@warned_user_not_gitlab
command_success = options.map do |name, value|
- system(%W(#{Gitlab.config.git.bin_path} config --global #{name} #{value}))
+ system(*%W(#{Gitlab.config.git.bin_path} config --global #{name} #{value}))
end
command_success.all?
diff --git a/lib/tasks/gitlab/web_hook.rake b/lib/tasks/gitlab/web_hook.rake
index f9f586db93c..412bcad1229 100644
--- a/lib/tasks/gitlab/web_hook.rake
+++ b/lib/tasks/gitlab/web_hook.rake
@@ -51,11 +51,11 @@ namespace :gitlab do
if namespace_path.blank?
Project
elsif namespace_path == '/'
- Project.where(namespace_id: nil)
+ Project.in_namespace(nil)
else
namespace = Namespace.where(path: namespace_path).first
if namespace
- Project.where(namespace_id: namespace.id)
+ Project.in_namespace(namespace.id)
else
puts "Namespace not found: #{namespace_path}".red
exit 2
diff --git a/lib/tasks/jasmine.rake b/lib/tasks/jasmine.rake
index 9e2cceffa19..ac307a9e929 100644
--- a/lib/tasks/jasmine.rake
+++ b/lib/tasks/jasmine.rake
@@ -7,6 +7,6 @@ task jasmine: ['jasmine:ci']
namespace :jasmine do
task :ci do
- Rake::Task['spec:javascript'].invoke
+ Rake::Task['teaspoon'].invoke
end
end
diff --git a/spec/features/groups_spec.rb b/spec/features/groups_spec.rb
new file mode 100644
index 00000000000..edc1c63a0aa
--- /dev/null
+++ b/spec/features/groups_spec.rb
@@ -0,0 +1,36 @@
+require 'spec_helper'
+
+feature 'Group' do
+ describe 'description' do
+ let(:group) { create(:group) }
+ let(:path) { group_path(group) }
+
+ before do
+ login_as(:admin)
+ end
+
+ it 'parses Markdown' do
+ group.update_attribute(:description, 'This is **my** group')
+ visit path
+ expect(page).to have_css('.description > p > strong')
+ end
+
+ it 'passes through html-pipeline' do
+ group.update_attribute(:description, 'This group is the :poop:')
+ visit path
+ expect(page).to have_css('.description > p > img')
+ end
+
+ it 'sanitizes unwanted tags' do
+ group.update_attribute(:description, '# Group Description')
+ visit path
+ expect(page).not_to have_css('.description h1')
+ end
+
+ it 'permits `rel` attribute on links' do
+ group.update_attribute(:description, 'https://google.com/')
+ visit path
+ expect(page).to have_css('.description a[rel]')
+ end
+ end
+end
diff --git a/spec/features/markdown_spec.rb b/spec/features/markdown_spec.rb
index ee1b3bf749d..902968cebcb 100644
--- a/spec/features/markdown_spec.rb
+++ b/spec/features/markdown_spec.rb
@@ -18,11 +18,13 @@ require 'erb'
# -> `gfm_with_options` helper
# -> HTML::Pipeline
# -> Sanitize
+# -> RelativeLink
# -> Emoji
# -> Table of Contents
# -> Autolinks
# -> Rinku (http, https, ftp)
# -> Other schemes
+# -> ExternalLink
# -> References
# -> TaskList
# -> `html_safe`
diff --git a/spec/features/projects_spec.rb b/spec/features/projects_spec.rb
index cae11be7cdd..f8eea70ec4a 100644
--- a/spec/features/projects_spec.rb
+++ b/spec/features/projects_spec.rb
@@ -1,32 +1,57 @@
require 'spec_helper'
-describe "Projects", feature: true, js: true do
- before { login_as :user }
+feature 'Project' do
+ describe 'description' do
+ let(:project) { create(:project) }
+ let(:path) { namespace_project_path(project.namespace, project) }
- describe "DELETE /projects/:id" do
before do
- @project = create(:project, namespace: @user.namespace)
- @project.team << [@user, :master]
- visit edit_namespace_project_path(@project.namespace, @project)
+ login_as(:admin)
end
- it "should remove project" do
- expect { remove_project }.to change {Project.count}.by(-1)
+ it 'parses Markdown' do
+ project.update_attribute(:description, 'This is **my** project')
+ visit path
+ expect(page).to have_css('.project-home-desc > p > strong')
+ end
+
+ it 'passes through html-pipeline' do
+ project.update_attribute(:description, 'This project is the :poop:')
+ visit path
+ expect(page).to have_css('.project-home-desc > p > img')
+ end
+
+ it 'sanitizes unwanted tags' do
+ project.update_attribute(:description, '# Project Description')
+ visit path
+ expect(page).not_to have_css('.project-home-desc h1')
+ end
+
+ it 'permits `rel` attribute on links' do
+ project.update_attribute(:description, 'https://google.com/')
+ visit path
+ expect(page).to have_css('.project-home-desc a[rel]')
end
+ end
+
+ describe 'removal', js: true do
+ let(:user) { create(:user) }
+ let(:project) { create(:project, namespace: user.namespace) }
- it 'should delete the project from disk' do
- expect(GitlabShellWorker).to(
- receive(:perform_async).with(:remove_repository,
- /#{@project.path_with_namespace}/)
- ).twice
+ before do
+ login_with(user)
+ project.team << [user, :master]
+ visit edit_namespace_project_path(project.namespace, project)
+ end
- remove_project
+ it 'should remove project' do
+ expect { remove_project }.to change {Project.count}.by(-1)
end
end
def remove_project
click_link "Remove project"
- fill_in 'confirm_name_input', with: @project.path
+ fill_in 'confirm_name_input', with: project.path
click_button 'Confirm'
end
end
diff --git a/spec/helpers/blob_helper_spec.rb b/spec/helpers/blob_helper_spec.rb
new file mode 100644
index 00000000000..e49e4e6d5d8
--- /dev/null
+++ b/spec/helpers/blob_helper_spec.rb
@@ -0,0 +1,33 @@
+require 'spec_helper'
+
+describe BlobHelper do
+ describe 'highlight' do
+ let(:blob_name) { 'test.lisp' }
+ let(:no_context_content) { ":type \"assem\"))" }
+ let(:blob_content) { "(make-pathname :defaults name\n#{no_context_content}" }
+ let(:split_content) { blob_content.split("\n") }
+
+ it 'should return plaintext for unknown lexer context' do
+ result = highlight(blob_name, no_context_content, nowrap: true, continue: false)
+ expect(result).to eq('<span id="LC1" class="line">:type &quot;assem&quot;))</span>')
+ end
+
+ it 'should highlight single block' do
+ expected = %Q[<span id="LC1" class="line"><span class="p">(</span><span class="nb">make-pathname</span> <span class="ss">:defaults</span> <span class="nv">name</span></span>
+<span id="LC2" class="line"><span class="ss">:type</span> <span class="s">&quot;assem&quot;</span><span class="p">))</span></span>]
+
+ expect(highlight(blob_name, blob_content, nowrap: true, continue: false)).to eq(expected)
+ end
+
+ it 'should highlight continued blocks' do
+ # Both lines have LC1 as ID since formatter doesn't support continue at the moment
+ expected = [
+ '<span id="LC1" class="line"><span class="p">(</span><span class="nb">make-pathname</span> <span class="ss">:defaults</span> <span class="nv">name</span></span>',
+ '<span id="LC1" class="line"><span class="ss">:type</span> <span class="s">&quot;assem&quot;</span><span class="p">))</span></span>'
+ ]
+
+ result = split_content.map{ |content| highlight(blob_name, content, nowrap: true, continue: true) }
+ expect(result).to eq(expected)
+ end
+ end
+end
diff --git a/spec/helpers/gitlab_markdown_helper_spec.rb b/spec/helpers/gitlab_markdown_helper_spec.rb
index d0b200a9ff8..bbb434638ce 100644
--- a/spec/helpers/gitlab_markdown_helper_spec.rb
+++ b/spec/helpers/gitlab_markdown_helper_spec.rb
@@ -94,6 +94,12 @@ describe GitlabMarkdownHelper do
expect(link_to_gfm(actual, commit_path)).
to match('&lt;h1&gt;test&lt;/h1&gt;')
end
+
+ it 'ignores reference links when they are the entire body' do
+ text = issues[0].to_reference
+ act = link_to_gfm(text, '/foo')
+ expect(act).to eq %Q(<a href="/foo">#{issues[0].to_reference}</a>)
+ end
end
describe '#render_wiki_content' do
diff --git a/spec/javascripts/extensions/array_spec.js.coffee b/spec/javascripts/extensions/array_spec.js.coffee
new file mode 100644
index 00000000000..4ceac619422
--- /dev/null
+++ b/spec/javascripts/extensions/array_spec.js.coffee
@@ -0,0 +1,12 @@
+#= require extensions/array
+
+describe 'Array extensions', ->
+ describe 'first', ->
+ it 'returns the first item', ->
+ arr = [0, 1, 2, 3, 4, 5]
+ expect(arr.first()).toBe(0)
+
+ describe 'last', ->
+ it 'returns the last item', ->
+ arr = [0, 1, 2, 3, 4, 5]
+ expect(arr.last()).toBe(5)
diff --git a/spec/javascripts/extensions/jquery_spec.js.coffee b/spec/javascripts/extensions/jquery_spec.js.coffee
new file mode 100644
index 00000000000..b10e16b7d01
--- /dev/null
+++ b/spec/javascripts/extensions/jquery_spec.js.coffee
@@ -0,0 +1,34 @@
+#= require extensions/jquery
+
+describe 'jQuery extensions', ->
+ describe 'disable', ->
+ beforeEach ->
+ fixture.set '<input type="text" />'
+
+ it 'adds the disabled attribute', ->
+ $input = $('input').first()
+
+ $input.disable()
+ expect($input).toHaveAttr('disabled', 'disabled')
+
+ it 'adds the disabled class', ->
+ $input = $('input').first()
+
+ $input.disable()
+ expect($input).toHaveClass('disabled')
+
+ describe 'enable', ->
+ beforeEach ->
+ fixture.set '<input type="text" disabled="disabled" class="disabled" />'
+
+ it 'removes the disabled attribute', ->
+ $input = $('input').first()
+
+ $input.enable()
+ expect($input).not.toHaveAttr('disabled')
+
+ it 'removes the disabled class', ->
+ $input = $('input').first()
+
+ $input.enable()
+ expect($input).not.toHaveClass('disabled')
diff --git a/spec/javascripts/fixtures/issuable.html.haml b/spec/javascripts/fixtures/issuable.html.haml
new file mode 100644
index 00000000000..42ab4aa68b1
--- /dev/null
+++ b/spec/javascripts/fixtures/issuable.html.haml
@@ -0,0 +1,2 @@
+%form.js-main-target-form
+ %textarea#note_note
diff --git a/spec/javascripts/fixtures/issue_note.html.haml b/spec/javascripts/fixtures/issue_note.html.haml
new file mode 100644
index 00000000000..0aecc7334fd
--- /dev/null
+++ b/spec/javascripts/fixtures/issue_note.html.haml
@@ -0,0 +1,12 @@
+%ul
+ %li.note
+ .js-task-list-container
+ .note-text
+ %ul.task-list
+ %li.task-list-item
+ %input.task-list-item-checkbox{type: 'checkbox'}
+ Task List Item
+ .note-edit-form
+ %form
+ %textarea.js-task-list-field
+ \- [ ] Task List Item
diff --git a/spec/javascripts/fixtures/issues_show.html.haml b/spec/javascripts/fixtures/issues_show.html.haml
new file mode 100644
index 00000000000..db5abe0cae3
--- /dev/null
+++ b/spec/javascripts/fixtures/issues_show.html.haml
@@ -0,0 +1,13 @@
+%a.btn-close
+
+.issue-details
+ .description.js-task-list-container
+ .wiki
+ %ul.task-list
+ %li.task-list-item
+ %input.task-list-item-checkbox{type: 'checkbox'}
+ Task List Item
+ %textarea.js-task-list-field
+ \- [ ] Task List Item
+
+%form.js-issue-update{action: '/foo'}
diff --git a/spec/javascripts/fixtures/merge_requests_show.html.haml b/spec/javascripts/fixtures/merge_requests_show.html.haml
new file mode 100644
index 00000000000..c4329b8f94a
--- /dev/null
+++ b/spec/javascripts/fixtures/merge_requests_show.html.haml
@@ -0,0 +1,13 @@
+%a.btn-close
+
+.merge-request-details
+ .description.js-task-list-container
+ .wiki
+ %ul.task-list
+ %li.task-list-item
+ %input.task-list-item-checkbox{type: 'checkbox'}
+ Task List Item
+ %textarea.js-task-list-field
+ \- [ ] Task List Item
+
+%form.js-merge-request-update{action: '/foo'}
diff --git a/spec/javascripts/fixtures/zen_mode.html.haml b/spec/javascripts/fixtures/zen_mode.html.haml
new file mode 100644
index 00000000000..e867e4de2b9
--- /dev/null
+++ b/spec/javascripts/fixtures/zen_mode.html.haml
@@ -0,0 +1,9 @@
+.zennable
+ %input#zen-toggle-comment.zen-toggle-comment{ tabindex: '-1', type: 'checkbox' }
+ .zen-backdrop
+ %textarea#note_note.js-gfm-input.markdown-area{placeholder: 'Leave a comment'}
+ %a.zen-enter-link{tabindex: '-1'}
+ %i.fa.fa-expand
+ Edit in fullscreen
+ %a.zen-leave-link
+ %i.fa.fa-compress
diff --git a/spec/javascripts/issue_spec.js.coffee b/spec/javascripts/issue_spec.js.coffee
index 13b25862f57..268e4c68c31 100644
--- a/spec/javascripts/issue_spec.js.coffee
+++ b/spec/javascripts/issue_spec.js.coffee
@@ -1,34 +1,20 @@
-#= require jquery
-#= require jasmine-fixture
#= require issue
describe 'Issue', ->
describe 'task lists', ->
- selectors = {
- container: '.issue-details .description.js-task-list-container'
- item: '.wiki ul.task-list li.task-list-item input.task-list-item-checkbox[type=checkbox] {Task List Item}'
- textarea: '.wiki textarea.js-task-list-field{- [ ] Task List Item}'
- form: 'form.js-issue-update[action="/foo"]'
- close: 'a.btn-close'
- }
+ fixture.preload('issues_show.html')
beforeEach ->
- $container = affix(selectors.container)
-
- # # These two elements are siblings inside the container
- $container.find('.js-task-list-container').append(affix(selectors.item))
- $container.find('.js-task-list-container').append(affix(selectors.textarea))
-
- # Task lists don't get initialized unless this button exists. Not ideal.
- $container.append(affix(selectors.close))
-
- # This form is used to get the `update` URL. Not ideal.
- $container.append(affix(selectors.form))
-
+ fixture.load('issues_show.html')
@issue = new Issue()
+ it 'modifies the Markdown field', ->
+ spyOn(jQuery, 'ajax').and.stub()
+ $('input[type=checkbox]').attr('checked', true).trigger('change')
+ expect($('.js-task-list-field').val()).toBe('- [x] Task List Item')
+
it 'submits an ajax request on tasklist:changed', ->
- spyOn($, 'ajax').and.callFake (req) ->
+ spyOn(jQuery, 'ajax').and.callFake (req) ->
expect(req.type).toBe('PATCH')
expect(req.url).toBe('/foo')
expect(req.data.issue.description).not.toBe(null)
diff --git a/spec/javascripts/merge_request_spec.js.coffee b/spec/javascripts/merge_request_spec.js.coffee
index 3ebc4a4eed5..a4735af0343 100644
--- a/spec/javascripts/merge_request_spec.js.coffee
+++ b/spec/javascripts/merge_request_spec.js.coffee
@@ -1,34 +1,23 @@
-#= require jquery
-#= require jasmine-fixture
#= require merge_request
+window.disableButtonIfEmptyField = -> null
+
describe 'MergeRequest', ->
describe 'task lists', ->
- selectors = {
- container: '.merge-request-details .description.js-task-list-container'
- item: '.wiki ul.task-list li.task-list-item input.task-list-item-checkbox[type=checkbox] {Task List Item}'
- textarea: '.wiki textarea.js-task-list-field{- [ ] Task List Item}'
- form: 'form.js-merge-request-update[action="/foo"]'
- close: 'a.btn-close'
- }
+ fixture.preload('merge_requests_show.html')
beforeEach ->
- $container = affix(selectors.container)
-
- # # These two elements are siblings inside the container
- $container.find('.js-task-list-container').append(affix(selectors.item))
- $container.find('.js-task-list-container').append(affix(selectors.textarea))
-
- # Task lists don't get initialized unless this button exists. Not ideal.
- $container.append(affix(selectors.close))
+ fixture.load('merge_requests_show.html')
+ @merge = new MergeRequest({})
- # This form is used to get the `update` URL. Not ideal.
- $container.append(affix(selectors.form))
+ it 'modifies the Markdown field', ->
+ spyOn(jQuery, 'ajax').and.stub()
- @merge = new MergeRequest({})
+ $('input[type=checkbox]').attr('checked', true).trigger('change')
+ expect($('.js-task-list-field').val()).toBe('- [x] Task List Item')
it 'submits an ajax request on tasklist:changed', ->
- spyOn($, 'ajax').and.callFake (req) ->
+ spyOn(jQuery, 'ajax').and.callFake (req) ->
expect(req.type).toBe('PATCH')
expect(req.url).toBe('/foo')
expect(req.data.merge_request.description).not.toBe(null)
diff --git a/spec/javascripts/notes_spec.js.coffee b/spec/javascripts/notes_spec.js.coffee
index de2e8e7f6c8..050b6e362c6 100644
--- a/spec/javascripts/notes_spec.js.coffee
+++ b/spec/javascripts/notes_spec.js.coffee
@@ -1,5 +1,3 @@
-#= require jquery
-#= require jasmine-fixture
#= require notes
window.gon = {}
@@ -7,21 +5,18 @@ window.disableButtonIfEmptyField = -> null
describe 'Notes', ->
describe 'task lists', ->
- selectors = {
- container: 'li.note .js-task-list-container'
- item: '.note-text ul.task-list li.task-list-item input.task-list-item-checkbox[type=checkbox] {Task List Item}'
- textarea: '.note-edit-form form textarea.js-task-list-field{- [ ] Task List Item}'
- }
+ fixture.preload('issue_note.html')
beforeEach ->
- $container = affix(selectors.container)
-
- # These two elements are siblings inside the container
- $container.find('.js-task-list-container').append(affix(selectors.item))
- $container.find('.js-task-list-container').append(affix(selectors.textarea))
+ fixture.load('issue_note.html')
+ $('form').on 'submit', (e) -> e.preventDefault()
@notes = new Notes()
+ it 'modifies the Markdown field', ->
+ $('input[type=checkbox]').attr('checked', true).trigger('change')
+ expect($('.js-task-list-field').val()).toBe('- [x] Task List Item')
+
it 'submits the form on tasklist:changed', ->
submitted = false
$('form').on 'submit', (e) -> submitted = true; e.preventDefault()
diff --git a/spec/javascripts/shortcuts_issuable_spec.js.coffee b/spec/javascripts/shortcuts_issuable_spec.js.coffee
index 57dcc2161d3..a01ad7140dd 100644
--- a/spec/javascripts/shortcuts_issuable_spec.js.coffee
+++ b/spec/javascripts/shortcuts_issuable_spec.js.coffee
@@ -1,10 +1,10 @@
-#= require jquery
-#= require jasmine-fixture
-
#= require shortcuts_issuable
describe 'ShortcutsIssuable', ->
+ fixture.preload('issuable.html')
+
beforeEach ->
+ fixture.load('issuable.html')
@shortcut = new ShortcutsIssuable()
describe '#replyWithSelectedText', ->
@@ -14,7 +14,6 @@ describe 'ShortcutsIssuable', ->
beforeEach ->
@selector = 'form.js-main-target-form textarea#note_note'
- affix(@selector)
describe 'with empty selection', ->
it 'does nothing', ->
diff --git a/spec/javascripts/spec_helper.coffee b/spec/javascripts/spec_helper.coffee
new file mode 100644
index 00000000000..47b41dd2c81
--- /dev/null
+++ b/spec/javascripts/spec_helper.coffee
@@ -0,0 +1,46 @@
+# PhantomJS (Teaspoons default driver) doesn't have support for
+# Function.prototype.bind, which has caused confusion. Use this polyfill to
+# avoid the confusion.
+
+#= require support/bind-poly
+
+# You can require your own javascript files here. By default this will include
+# everything in application, however you may get better load performance if you
+# require the specific files that are being used in the spec that tests them.
+
+#= require jquery
+#= require bootstrap
+#= require underscore
+
+# Teaspoon includes some support files, but you can use anything from your own
+# support path too.
+
+# require support/jasmine-jquery-1.7.0
+# require support/jasmine-jquery-2.0.0
+#= require support/jasmine-jquery-2.1.0
+# require support/sinon
+# require support/your-support-file
+
+# Deferring execution
+
+# If you're using CommonJS, RequireJS or some other asynchronous library you can
+# defer execution. Call Teaspoon.execute() after everything has been loaded.
+# Simple example of a timeout:
+
+# Teaspoon.defer = true
+# setTimeout(Teaspoon.execute, 1000)
+
+# Matching files
+
+# By default Teaspoon will look for files that match
+# _spec.{js,js.coffee,.coffee}. Add a filename_spec.js file in your spec path
+# and it'll be included in the default suite automatically. If you want to
+# customize suites, check out the configuration in teaspoon_env.rb
+
+# Manifest
+
+# If you'd rather require your spec files manually (to control order for
+# instance) you can disable the suite matcher in the configuration and use this
+# file as a manifest.
+
+# For more information: http://github.com/modeset/teaspoon
diff --git a/spec/javascripts/support/jasmine.yml b/spec/javascripts/support/jasmine.yml
deleted file mode 100644
index 168c9618643..00000000000
--- a/spec/javascripts/support/jasmine.yml
+++ /dev/null
@@ -1,15 +0,0 @@
-# path to parent directory of spec_files
-# relative path from Rails.root
-#
-# Alternatively accept an array of directory to include external spec files
-# spec_dir:
-# - spec/javascripts
-# - ../engine/spec/javascripts
-#
-# defaults to spec/javascripts
-spec_dir: spec/javascripts
-
-# list of file expressions to include as specs into spec runner
-# relative path from spec_dir
-spec_files:
- - "**/*[Ss]pec.{js.coffee,js,coffee}"
diff --git a/spec/javascripts/support/jasmine_helper.rb b/spec/javascripts/support/jasmine_helper.rb
deleted file mode 100644
index 4d73aec5a31..00000000000
--- a/spec/javascripts/support/jasmine_helper.rb
+++ /dev/null
@@ -1,15 +0,0 @@
-#Use this file to set/override Jasmine configuration options
-#You can remove it if you don't need it.
-#This file is loaded *after* jasmine.yml is interpreted.
-#
-#Example: using a different boot file.
-#Jasmine.configure do |config|
-# config.boot_dir = '/absolute/path/to/boot_dir'
-# config.boot_files = lambda { ['/absolute/path/to/boot_dir/file.js'] }
-#end
-#
-#Example: prevent PhantomJS auto install, uses PhantomJS already on your path.
-#Jasmine.configure do |config|
-# config.prevent_phantom_js_auto_install = true
-#end
-#
diff --git a/spec/javascripts/zen_mode_spec.js.coffee b/spec/javascripts/zen_mode_spec.js.coffee
new file mode 100644
index 00000000000..1f4ea58ad48
--- /dev/null
+++ b/spec/javascripts/zen_mode_spec.js.coffee
@@ -0,0 +1,52 @@
+#= require zen_mode
+
+describe 'ZenMode', ->
+ fixture.preload('zen_mode.html')
+
+ beforeEach ->
+ fixture.load('zen_mode.html')
+
+ # Stub Dropzone.forElement(...).enable()
+ spyOn(Dropzone, 'forElement').and.callFake ->
+ enable: -> true
+
+ @zen = new ZenMode()
+
+ # Set this manually because we can't actually scroll the window
+ @zen.scroll_position = 456
+
+ # Ohmmmmmmm
+ enterZen = ->
+ $('.zen-toggle-comment').prop('checked', true).trigger('change')
+
+ # Wh- what was that?!
+ exitZen = ->
+ $('.zen-toggle-comment').prop('checked', false).trigger('change')
+
+ describe 'on enter', ->
+ it 'pauses Mousetrap', ->
+ spyOn(Mousetrap, 'pause')
+ enterZen()
+ expect(Mousetrap.pause).toHaveBeenCalled()
+
+ describe 'in use', ->
+ beforeEach ->
+ enterZen()
+
+ it 'exits on Escape', ->
+ $(document).trigger(jQuery.Event('keydown', {keyCode: 27}))
+ expect($('.zen-toggle-comment').prop('checked')).toBe(false)
+
+ describe 'on exit', ->
+ beforeEach ->
+ enterZen()
+
+ it 'unpauses Mousetrap', ->
+ spyOn(Mousetrap, 'unpause')
+ exitZen()
+ expect(Mousetrap.unpause).toHaveBeenCalled()
+
+ it 'restores the scroll position', ->
+ spyOn(@zen, 'restoreScroll')
+ exitZen()
+ expect(@zen.restoreScroll).toHaveBeenCalledWith(456)
diff --git a/spec/lib/gitlab/markdown/autolink_filter_spec.rb b/spec/lib/gitlab/markdown/autolink_filter_spec.rb
index 0bbdc11a979..a14cb2da089 100644
--- a/spec/lib/gitlab/markdown/autolink_filter_spec.rb
+++ b/spec/lib/gitlab/markdown/autolink_filter_spec.rb
@@ -2,11 +2,9 @@ require 'spec_helper'
module Gitlab::Markdown
describe AutolinkFilter do
- let(:link) { 'http://about.gitlab.com/' }
+ include FilterSpecHelper
- def filter(html, options = {})
- described_class.call(html, options)
- end
+ let(:link) { 'http://about.gitlab.com/' }
it 'does nothing when :autolink is false' do
exp = act = link
diff --git a/spec/lib/gitlab/markdown/commit_range_reference_filter_spec.rb b/spec/lib/gitlab/markdown/commit_range_reference_filter_spec.rb
index d3695ee46d0..e8391cc7aca 100644
--- a/spec/lib/gitlab/markdown/commit_range_reference_filter_spec.rb
+++ b/spec/lib/gitlab/markdown/commit_range_reference_filter_spec.rb
@@ -2,7 +2,7 @@ require 'spec_helper'
module Gitlab::Markdown
describe CommitRangeReferenceFilter do
- include ReferenceFilterSpecHelper
+ include FilterSpecHelper
let(:project) { create(:project) }
let(:commit1) { project.commit }
diff --git a/spec/lib/gitlab/markdown/commit_reference_filter_spec.rb b/spec/lib/gitlab/markdown/commit_reference_filter_spec.rb
index a0d2cd7e22b..a10d43c9a02 100644
--- a/spec/lib/gitlab/markdown/commit_reference_filter_spec.rb
+++ b/spec/lib/gitlab/markdown/commit_reference_filter_spec.rb
@@ -2,7 +2,7 @@ require 'spec_helper'
module Gitlab::Markdown
describe CommitReferenceFilter do
- include ReferenceFilterSpecHelper
+ include FilterSpecHelper
let(:project) { create(:project) }
let(:commit) { project.commit }
diff --git a/spec/lib/gitlab/markdown/emoji_filter_spec.rb b/spec/lib/gitlab/markdown/emoji_filter_spec.rb
index 18d55c4818f..11efd9bb4cd 100644
--- a/spec/lib/gitlab/markdown/emoji_filter_spec.rb
+++ b/spec/lib/gitlab/markdown/emoji_filter_spec.rb
@@ -2,9 +2,7 @@ require 'spec_helper'
module Gitlab::Markdown
describe EmojiFilter do
- def filter(html, contexts = {})
- described_class.call(html, contexts)
- end
+ include FilterSpecHelper
before do
ActionController::Base.asset_host = 'https://foo.com'
diff --git a/spec/lib/gitlab/markdown/external_issue_reference_filter_spec.rb b/spec/lib/gitlab/markdown/external_issue_reference_filter_spec.rb
index bf9409589fa..f16095bc2b2 100644
--- a/spec/lib/gitlab/markdown/external_issue_reference_filter_spec.rb
+++ b/spec/lib/gitlab/markdown/external_issue_reference_filter_spec.rb
@@ -2,7 +2,7 @@ require 'spec_helper'
module Gitlab::Markdown
describe ExternalIssueReferenceFilter do
- include ReferenceFilterSpecHelper
+ include FilterSpecHelper
def helper
IssuesHelper
diff --git a/spec/lib/gitlab/markdown/external_link_filter_spec.rb b/spec/lib/gitlab/markdown/external_link_filter_spec.rb
index c2ff4f80a42..a040b34577b 100644
--- a/spec/lib/gitlab/markdown/external_link_filter_spec.rb
+++ b/spec/lib/gitlab/markdown/external_link_filter_spec.rb
@@ -2,9 +2,7 @@ require 'spec_helper'
module Gitlab::Markdown
describe ExternalLinkFilter do
- def filter(html, options = {})
- described_class.call(html, options)
- end
+ include FilterSpecHelper
it 'ignores elements without an href attribute' do
exp = act = %q(<a id="ignored">Ignore Me</a>)
diff --git a/spec/lib/gitlab/markdown/issue_reference_filter_spec.rb b/spec/lib/gitlab/markdown/issue_reference_filter_spec.rb
index a838d7570c8..fa43d33794d 100644
--- a/spec/lib/gitlab/markdown/issue_reference_filter_spec.rb
+++ b/spec/lib/gitlab/markdown/issue_reference_filter_spec.rb
@@ -2,7 +2,7 @@ require 'spec_helper'
module Gitlab::Markdown
describe IssueReferenceFilter do
- include ReferenceFilterSpecHelper
+ include FilterSpecHelper
def helper
IssuesHelper
diff --git a/spec/lib/gitlab/markdown/label_reference_filter_spec.rb b/spec/lib/gitlab/markdown/label_reference_filter_spec.rb
index 41987f57bca..cf3337b1ba1 100644
--- a/spec/lib/gitlab/markdown/label_reference_filter_spec.rb
+++ b/spec/lib/gitlab/markdown/label_reference_filter_spec.rb
@@ -3,7 +3,7 @@ require 'html/pipeline'
module Gitlab::Markdown
describe LabelReferenceFilter do
- include ReferenceFilterSpecHelper
+ include FilterSpecHelper
let(:project) { create(:empty_project) }
let(:label) { create(:label, project: project) }
diff --git a/spec/lib/gitlab/markdown/merge_request_reference_filter_spec.rb b/spec/lib/gitlab/markdown/merge_request_reference_filter_spec.rb
index 6aeb1093602..5945302a2da 100644
--- a/spec/lib/gitlab/markdown/merge_request_reference_filter_spec.rb
+++ b/spec/lib/gitlab/markdown/merge_request_reference_filter_spec.rb
@@ -2,7 +2,7 @@ require 'spec_helper'
module Gitlab::Markdown
describe MergeRequestReferenceFilter do
- include ReferenceFilterSpecHelper
+ include FilterSpecHelper
let(:project) { create(:project) }
let(:merge) { create(:merge_request, source_project: project) }
diff --git a/spec/lib/gitlab/markdown/sanitization_filter_spec.rb b/spec/lib/gitlab/markdown/sanitization_filter_spec.rb
index 4a1aa766149..e50c82d0b3c 100644
--- a/spec/lib/gitlab/markdown/sanitization_filter_spec.rb
+++ b/spec/lib/gitlab/markdown/sanitization_filter_spec.rb
@@ -2,9 +2,7 @@ require 'spec_helper'
module Gitlab::Markdown
describe SanitizationFilter do
- def filter(html, options = {})
- described_class.call(html, options)
- end
+ include FilterSpecHelper
describe 'default whitelist' do
it 'sanitizes tags that are not whitelisted' do
@@ -42,6 +40,13 @@ module Gitlab::Markdown
end
describe 'custom whitelist' do
+ it 'customizes the whitelist only once' do
+ instance = described_class.new('Foo')
+ 3.times { instance.whitelist }
+
+ expect(instance.whitelist[:transformers].size).to eq 4
+ end
+
it 'allows syntax highlighting' do
exp = act = %q{<pre class="code highlight white c"><code><span class="k">def</span></code></pre>}
expect(filter(act).to_html).to eq exp
@@ -87,5 +92,27 @@ module Gitlab::Markdown
expect(doc.at_css('a')['href']).to be_nil
end
end
+
+ context 'when pipeline is :description' do
+ it 'uses a stricter whitelist' do
+ doc = filter('<h1>Description</h1>', pipeline: :description)
+ expect(doc.to_html.strip).to eq 'Description'
+ end
+
+ %w(pre code img ol ul li).each do |elem|
+ it "removes '#{elem}' elements" do
+ act = "<#{elem}>Description</#{elem}>"
+ expect(filter(act, pipeline: :description).to_html.strip).
+ to eq 'Description'
+ end
+ end
+
+ %w(b i strong em a ins del sup sub p).each do |elem|
+ it "still allows '#{elem}' elements" do
+ exp = act = "<#{elem}>Description</#{elem}>"
+ expect(filter(act, pipeline: :description).to_html).to eq exp
+ end
+ end
+ end
end
end
diff --git a/spec/lib/gitlab/markdown/snippet_reference_filter_spec.rb b/spec/lib/gitlab/markdown/snippet_reference_filter_spec.rb
index 07ece66e903..38619a3c07f 100644
--- a/spec/lib/gitlab/markdown/snippet_reference_filter_spec.rb
+++ b/spec/lib/gitlab/markdown/snippet_reference_filter_spec.rb
@@ -2,7 +2,7 @@ require 'spec_helper'
module Gitlab::Markdown
describe SnippetReferenceFilter do
- include ReferenceFilterSpecHelper
+ include FilterSpecHelper
let(:project) { create(:empty_project) }
let(:snippet) { create(:project_snippet, project: project) }
diff --git a/spec/lib/gitlab/markdown/table_of_contents_filter_spec.rb b/spec/lib/gitlab/markdown/table_of_contents_filter_spec.rb
index f383a5850d5..ddf583a72c1 100644
--- a/spec/lib/gitlab/markdown/table_of_contents_filter_spec.rb
+++ b/spec/lib/gitlab/markdown/table_of_contents_filter_spec.rb
@@ -4,9 +4,7 @@ require 'spec_helper'
module Gitlab::Markdown
describe TableOfContentsFilter do
- def filter(html, options = {})
- described_class.call(html, options)
- end
+ include FilterSpecHelper
def header(level, text)
"<h#{level}>#{text}</h#{level}>\n"
diff --git a/spec/lib/gitlab/markdown/task_list_filter_spec.rb b/spec/lib/gitlab/markdown/task_list_filter_spec.rb
index 2a1e1cc5127..94f39cc966e 100644
--- a/spec/lib/gitlab/markdown/task_list_filter_spec.rb
+++ b/spec/lib/gitlab/markdown/task_list_filter_spec.rb
@@ -2,9 +2,7 @@ require 'spec_helper'
module Gitlab::Markdown
describe TaskListFilter do
- def filter(html, options = {})
- described_class.call(html, options)
- end
+ include FilterSpecHelper
it 'does not apply `task-list` class to non-task lists' do
exp = act = %(<ul><li>Item</li></ul>)
diff --git a/spec/lib/gitlab/markdown/user_reference_filter_spec.rb b/spec/lib/gitlab/markdown/user_reference_filter_spec.rb
index 0ecbdee9b9e..08e6941028f 100644
--- a/spec/lib/gitlab/markdown/user_reference_filter_spec.rb
+++ b/spec/lib/gitlab/markdown/user_reference_filter_spec.rb
@@ -2,7 +2,7 @@ require 'spec_helper'
module Gitlab::Markdown
describe UserReferenceFilter do
- include ReferenceFilterSpecHelper
+ include FilterSpecHelper
let(:project) { create(:empty_project) }
let(:user) { create(:user) }
diff --git a/spec/lib/gitlab/o_auth/user_spec.rb b/spec/lib/gitlab/o_auth/user_spec.rb
index 44cdd1e4fab..2a982e8b107 100644
--- a/spec/lib/gitlab/o_auth/user_spec.rb
+++ b/spec/lib/gitlab/o_auth/user_spec.rb
@@ -13,6 +13,7 @@ describe Gitlab::OAuth::User do
email: 'john@mail.com'
}
end
+ let(:ldap_user) { Gitlab::LDAP::Person.new(Net::LDAP::Entry.new, 'ldapmain') }
describe :persisted? do
let!(:existing_user) { create(:omniauth_user, extern_uid: 'my-uid', provider: 'my-provider') }
@@ -32,31 +33,94 @@ describe Gitlab::OAuth::User do
let(:provider) { 'twitter' }
describe 'signup' do
- context "with allow_single_sign_on enabled" do
- before { Gitlab.config.omniauth.stub allow_single_sign_on: true }
+ shared_examples "to verify compliance with allow_single_sign_on" do
+ context "with allow_single_sign_on enabled" do
+ before { Gitlab.config.omniauth.stub allow_single_sign_on: true }
- it "creates a user from Omniauth" do
- oauth_user.save
+ it "creates a user from Omniauth" do
+ oauth_user.save
- expect(gl_user).to be_valid
- identity = gl_user.identities.first
- expect(identity.extern_uid).to eql uid
- expect(identity.provider).to eql 'twitter'
+ expect(gl_user).to be_valid
+ identity = gl_user.identities.first
+ expect(identity.extern_uid).to eql uid
+ expect(identity.provider).to eql 'twitter'
+ end
+ end
+
+ context "with allow_single_sign_on disabled (Default)" do
+ before { Gitlab.config.omniauth.stub allow_single_sign_on: false }
+ it "throws an error" do
+ expect{ oauth_user.save }.to raise_error StandardError
+ end
end
end
- context "with allow_single_sign_on disabled (Default)" do
- it "throws an error" do
- expect{ oauth_user.save }.to raise_error StandardError
+ context "with auto_link_ldap_user disabled (default)" do
+ before { Gitlab.config.omniauth.stub auto_link_ldap_user: false }
+ include_examples "to verify compliance with allow_single_sign_on"
+ end
+
+ context "with auto_link_ldap_user enabled" do
+ before { Gitlab.config.omniauth.stub auto_link_ldap_user: true }
+
+ context "and a corresponding LDAP person" do
+ before do
+ ldap_user.stub(:uid) { uid }
+ ldap_user.stub(:username) { uid }
+ ldap_user.stub(:email) { ['johndoe@example.com','john2@example.com'] }
+ ldap_user.stub(:dn) { 'uid=user1,ou=People,dc=example' }
+ allow(oauth_user).to receive(:ldap_person).and_return(ldap_user)
+ end
+
+ context "and no account for the LDAP user" do
+
+ it "creates a user with dual LDAP and omniauth identities" do
+ oauth_user.save
+
+ expect(gl_user).to be_valid
+ expect(gl_user.username).to eql uid
+ expect(gl_user.email).to eql 'johndoe@example.com'
+ expect(gl_user.identities.length).to eql 2
+ identities_as_hash = gl_user.identities.map { |id| { provider: id.provider, extern_uid: id.extern_uid } }
+ expect(identities_as_hash).to match_array(
+ [ { provider: 'ldapmain', extern_uid: 'uid=user1,ou=People,dc=example' },
+ { provider: 'twitter', extern_uid: uid }
+ ])
+ end
+ end
+
+ context "and LDAP user has an account already" do
+ let!(:existing_user) { create(:omniauth_user, email: 'john@example.com', extern_uid: 'uid=user1,ou=People,dc=example', provider: 'ldapmain', username: 'john') }
+ it "adds the omniauth identity to the LDAP account" do
+ oauth_user.save
+
+ expect(gl_user).to be_valid
+ expect(gl_user.username).to eql 'john'
+ expect(gl_user.email).to eql 'john@example.com'
+ expect(gl_user.identities.length).to eql 2
+ identities_as_hash = gl_user.identities.map { |id| { provider: id.provider, extern_uid: id.extern_uid } }
+ expect(identities_as_hash).to match_array(
+ [ { provider: 'ldapmain', extern_uid: 'uid=user1,ou=People,dc=example' },
+ { provider: 'twitter', extern_uid: uid }
+ ])
+ end
+ end
+ end
+
+ context "and no corresponding LDAP person" do
+ before { allow(oauth_user).to receive(:ldap_person).and_return(nil) }
+
+ include_examples "to verify compliance with allow_single_sign_on"
end
end
+
end
describe 'blocking' do
let(:provider) { 'twitter' }
before { Gitlab.config.omniauth.stub allow_single_sign_on: true }
- context 'signup' do
+ context 'signup with omniauth only' do
context 'dont block on create' do
before { Gitlab.config.omniauth.stub block_auto_created_users: false }
@@ -78,6 +142,64 @@ describe Gitlab::OAuth::User do
end
end
+ context 'signup with linked omniauth and LDAP account' do
+ before do
+ Gitlab.config.omniauth.stub auto_link_ldap_user: true
+ ldap_user.stub(:uid) { uid }
+ ldap_user.stub(:username) { uid }
+ ldap_user.stub(:email) { ['johndoe@example.com','john2@example.com'] }
+ ldap_user.stub(:dn) { 'uid=user1,ou=People,dc=example' }
+ allow(oauth_user).to receive(:ldap_person).and_return(ldap_user)
+ end
+
+ context "and no account for the LDAP user" do
+ context 'dont block on create (LDAP)' do
+ before { Gitlab::LDAP::Config.any_instance.stub block_auto_created_users: false }
+
+ it do
+ oauth_user.save
+ expect(gl_user).to be_valid
+ expect(gl_user).not_to be_blocked
+ end
+ end
+
+ context 'block on create (LDAP)' do
+ before { Gitlab::LDAP::Config.any_instance.stub block_auto_created_users: true }
+
+ it do
+ oauth_user.save
+ expect(gl_user).to be_valid
+ expect(gl_user).to be_blocked
+ end
+ end
+ end
+
+ context 'and LDAP user has an account already' 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 { Gitlab::LDAP::Config.any_instance.stub block_auto_created_users: false }
+
+ it do
+ oauth_user.save
+ expect(gl_user).to be_valid
+ expect(gl_user).not_to be_blocked
+ end
+ end
+
+ context 'block on create (LDAP)' do
+ before { Gitlab::LDAP::Config.any_instance.stub block_auto_created_users: true }
+
+ it do
+ oauth_user.save
+ expect(gl_user).to be_valid
+ expect(gl_user).not_to be_blocked
+ end
+ end
+ end
+ end
+
+
context 'sign-in' do
before do
oauth_user.save
@@ -103,6 +225,26 @@ describe Gitlab::OAuth::User do
expect(gl_user).not_to be_blocked
end
end
+
+ context 'dont block on create (LDAP)' do
+ before { Gitlab::LDAP::Config.any_instance.stub block_auto_created_users: false }
+
+ it do
+ oauth_user.save
+ expect(gl_user).to be_valid
+ expect(gl_user).not_to be_blocked
+ end
+ end
+
+ context 'block on create (LDAP)' do
+ before { Gitlab::LDAP::Config.any_instance.stub block_auto_created_users: true }
+
+ it do
+ oauth_user.save
+ expect(gl_user).to be_valid
+ expect(gl_user).not_to be_blocked
+ end
+ end
end
end
end
diff --git a/spec/lib/gitlab/reference_extractor_spec.rb b/spec/lib/gitlab/reference_extractor_spec.rb
index c14f4ac6bf6..f921dd9cc09 100644
--- a/spec/lib/gitlab/reference_extractor_spec.rb
+++ b/spec/lib/gitlab/reference_extractor_spec.rb
@@ -16,6 +16,30 @@ describe Gitlab::ReferenceExtractor do
expect(subject.users).to eq([@u_foo, @u_bar, @u_offteam])
end
+ it 'ignores user mentions inside specific elements' do
+ @u_foo = create(:user, username: 'foo')
+ @u_bar = create(:user, username: 'bar')
+ @u_offteam = create(:user, username: 'offteam')
+
+ project.team << [@u_foo, :reporter]
+ project.team << [@u_bar, :guest]
+
+ subject.analyze(%Q{
+ Inline code: `@foo`
+
+ Code block:
+
+ ```
+ @bar
+ ```
+
+ Quote:
+
+ > @offteam
+ })
+ expect(subject.users).to eq([])
+ end
+
it 'accesses valid issue objects' do
@i0 = create(:issue, project: project)
@i1 = create(:issue, project: project)
diff --git a/spec/lib/gitlab/upgrader_spec.rb b/spec/lib/gitlab/upgrader_spec.rb
index ce3ea6c260a..baa4bd0f28f 100644
--- a/spec/lib/gitlab/upgrader_spec.rb
+++ b/spec/lib/gitlab/upgrader_spec.rb
@@ -20,5 +20,20 @@ describe Gitlab::Upgrader do
upgrader.stub(current_version_raw: "5.3.0")
expect(upgrader.latest_version_raw).to eq("v5.4.2")
end
+
+ it 'should get the latest version from tags' do
+ allow(upgrader).to receive(:fetch_git_tags).and_return([
+ '6f0733310546402c15d3ae6128a95052f6c8ea96 refs/tags/v7.1.1',
+ 'facfec4b242ce151af224e20715d58e628aa5e74 refs/tags/v7.1.1^{}',
+ 'f7068d99c79cf79befbd388030c051bb4b5e86d4 refs/tags/v7.10.4',
+ '337225a4fcfa9674e2528cb6d41c46556bba9dfa refs/tags/v7.10.4^{}',
+ '880e0ba0adbed95d087f61a9a17515e518fc6440 refs/tags/v7.11.1',
+ '6584346b604f981f00af8011cd95472b2776d912 refs/tags/v7.11.1^{}',
+ '43af3e65a486a9237f29f56d96c3b3da59c24ae0 refs/tags/v7.11.2',
+ 'dac18e7728013a77410e926a1e64225703754a2d refs/tags/v7.11.2^{}',
+ '0bf21fd4b46c980c26fd8c90a14b86a4d90cc950 refs/tags/v7.9.4',
+ 'b10de29edbaff7219547dc506cb1468ee35065c3 refs/tags/v7.9.4^{}'])
+ expect(upgrader.latest_version_raw).to eq("v7.11.2")
+ end
end
end
diff --git a/spec/lib/repository_cache_spec.rb b/spec/lib/repository_cache_spec.rb
index af399f3a731..37240d51310 100644
--- a/spec/lib/repository_cache_spec.rb
+++ b/spec/lib/repository_cache_spec.rb
@@ -1,4 +1,3 @@
-require 'rspec'
require_relative '../../lib/repository_cache'
describe RepositoryCache do
diff --git a/spec/models/application_setting_spec.rb b/spec/models/application_setting_spec.rb
index 116c318121d..d648f4078be 100644
--- a/spec/models/application_setting_spec.rb
+++ b/spec/models/application_setting_spec.rb
@@ -15,6 +15,7 @@
# twitter_sharing_enabled :boolean default(TRUE)
# restricted_visibility_levels :text
# max_attachment_size :integer default(10), not null
+# session_expire_delay :integer default(10080), not null
# default_project_visibility :integer
# default_snippet_visibility :integer
# restricted_signup_domains :text
diff --git a/spec/models/concerns/issuable_spec.rb b/spec/models/concerns/issuable_spec.rb
index 557c71b4d2c..86c395a8e8e 100644
--- a/spec/models/concerns/issuable_spec.rb
+++ b/spec/models/concerns/issuable_spec.rb
@@ -16,7 +16,7 @@ describe Issue, "Issuable" do
it { is_expected.to validate_presence_of(:iid) }
it { is_expected.to validate_presence_of(:author) }
it { is_expected.to validate_presence_of(:title) }
- it { is_expected.to ensure_length_of(:title).is_at_least(0).is_at_most(255) }
+ it { is_expected.to validate_length_of(:title).is_at_least(0).is_at_most(255) }
end
describe "Scope" do
diff --git a/spec/models/concerns/mentionable_spec.rb b/spec/models/concerns/mentionable_spec.rb
index eadb941a3fa..22237f2e9f2 100644
--- a/spec/models/concerns/mentionable_spec.rb
+++ b/spec/models/concerns/mentionable_spec.rb
@@ -1,14 +1,31 @@
require 'spec_helper'
describe Issue, "Mentionable" do
- describe :mentioned_users do
+ describe '#mentioned_users' do
let!(:user) { create(:user, username: 'stranger') }
let!(:user2) { create(:user, username: 'john') }
- let!(:issue) { create(:issue, description: '@stranger mentioned') }
+ let!(:issue) { create(:issue, description: "#{user.to_reference} mentioned") }
subject { issue.mentioned_users }
it { is_expected.to include(user) }
it { is_expected.not_to include(user2) }
end
+
+ describe '#create_cross_references!' do
+ let(:project) { create(:project) }
+ let(:author) { double('author') }
+ let(:commit) { project.commit }
+ let(:commit2) { project.commit }
+
+ let!(:issue) do
+ create(:issue, project: project, description: commit.to_reference)
+ end
+
+ it 'correctly removes already-mentioned Commits' do
+ expect(Note).not_to receive(:create_cross_reference_note)
+
+ issue.create_cross_references!(project, author, [commit2])
+ end
+ end
end
diff --git a/spec/models/deploy_keys_project_spec.rb b/spec/models/deploy_keys_project_spec.rb
index 7032b777144..705ef257d86 100644
--- a/spec/models/deploy_keys_project_spec.rb
+++ b/spec/models/deploy_keys_project_spec.rb
@@ -36,9 +36,7 @@ describe DeployKeysProject do
it "doesn't destroy the deploy key" do
subject.destroy
- expect {
- deploy_key.reload
- }.not_to raise_error(ActiveRecord::RecordNotFound)
+ expect { deploy_key.reload }.not_to raise_error
end
end
@@ -63,9 +61,7 @@ describe DeployKeysProject do
it "doesn't destroy the deploy key" do
subject.destroy
- expect {
- deploy_key.reload
- }.not_to raise_error(ActiveRecord::RecordNotFound)
+ expect { deploy_key.reload }.not_to raise_error
end
end
end
diff --git a/spec/models/key_spec.rb b/spec/models/key_spec.rb
index 6eb1208a7f2..fbb9e162952 100644
--- a/spec/models/key_spec.rb
+++ b/spec/models/key_spec.rb
@@ -26,8 +26,8 @@ describe Key do
describe "Validation" do
it { is_expected.to validate_presence_of(:title) }
it { is_expected.to validate_presence_of(:key) }
- it { is_expected.to ensure_length_of(:title).is_within(0..255) }
- it { is_expected.to ensure_length_of(:key).is_within(0..5000) }
+ it { is_expected.to validate_length_of(:title).is_within(0..255) }
+ it { is_expected.to validate_length_of(:key).is_within(0..5000) }
end
describe "Methods" do
diff --git a/spec/models/project_services/gitlab_ci_service_spec.rb b/spec/models/project_services/gitlab_ci_service_spec.rb
index e5bf9125313..ebd8b545aa7 100644
--- a/spec/models/project_services/gitlab_ci_service_spec.rb
+++ b/spec/models/project_services/gitlab_ci_service_spec.rb
@@ -48,6 +48,21 @@ describe GitlabCiService do
it { expect(@service.build_page("2ab7834c", 'master')).to eq("http://ci.gitlab.org/projects/2/refs/master/commits/2ab7834c")}
it { expect(@service.build_page("issue#2", 'master')).to eq("http://ci.gitlab.org/projects/2/refs/master/commits/issue%232")}
end
+
+ describe "execute" do
+ let(:user) { create(:user, username: 'username') }
+ let(:project) { create(:project, name: 'project') }
+ let(:push_sample_data) { Gitlab::PushDataBuilder.build_sample(project, user) }
+
+ it "calls ci_yaml_file" do
+ service_hook = double
+ service_hook.should_receive(:execute)
+ @service.should_receive(:service_hook).and_return(service_hook)
+ @service.should_receive(:ci_yaml_file).with(push_sample_data)
+
+ @service.execute(push_sample_data)
+ end
+ end
end
describe "Fork registration" do
diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb
index 48568e2a3ff..87c67fa32c3 100644
--- a/spec/models/project_spec.rb
+++ b/spec/models/project_spec.rb
@@ -69,14 +69,14 @@ describe Project do
it { is_expected.to validate_presence_of(:name) }
it { is_expected.to validate_uniqueness_of(:name).scoped_to(:namespace_id) }
- it { is_expected.to ensure_length_of(:name).is_within(0..255) }
+ it { is_expected.to validate_length_of(:name).is_within(0..255) }
it { is_expected.to validate_presence_of(:path) }
it { is_expected.to validate_uniqueness_of(:path).scoped_to(:namespace_id) }
- it { is_expected.to ensure_length_of(:path).is_within(0..255) }
- it { is_expected.to ensure_length_of(:description).is_within(0..2000) }
+ it { is_expected.to validate_length_of(:path).is_within(0..255) }
+ it { is_expected.to validate_length_of(:description).is_within(0..2000) }
it { is_expected.to validate_presence_of(:creator) }
- it { is_expected.to ensure_length_of(:issues_tracker_id).is_within(0..255) }
+ it { is_expected.to validate_length_of(:issues_tracker_id).is_within(0..255) }
it { is_expected.to validate_presence_of(:namespace) }
it 'should not allow new projects beyond user limits' do
diff --git a/spec/models/snippet_spec.rb b/spec/models/snippet_spec.rb
index c81dd36ef4b..c786d0bf103 100644
--- a/spec/models/snippet_spec.rb
+++ b/spec/models/snippet_spec.rb
@@ -38,10 +38,10 @@ describe Snippet do
it { is_expected.to validate_presence_of(:author) }
it { is_expected.to validate_presence_of(:title) }
- it { is_expected.to ensure_length_of(:title).is_within(0..255) }
+ it { is_expected.to validate_length_of(:title).is_within(0..255) }
it { is_expected.to validate_presence_of(:file_name) }
- it { is_expected.to ensure_length_of(:file_name).is_within(0..255) }
+ it { is_expected.to validate_length_of(:file_name).is_within(0..255) }
it { is_expected.to validate_presence_of(:content) }
diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb
index e1205c18a85..f1b8afa5854 100644
--- a/spec/models/user_spec.rb
+++ b/spec/models/user_spec.rb
@@ -96,7 +96,7 @@ describe User do
it { is_expected.to allow_value(0).for(:projects_limit) }
it { is_expected.not_to allow_value(-1).for(:projects_limit) }
- it { is_expected.to ensure_length_of(:bio).is_within(0..255) }
+ it { is_expected.to validate_length_of(:bio).is_within(0..255) }
describe 'email' do
it 'accepts info@example.com' do
@@ -248,6 +248,7 @@ describe User do
it { expect(@user.several_namespaces?).to be_truthy }
it { expect(@user.authorized_groups).to eq([@group]) }
it { expect(@user.owned_groups).to eq([@group]) }
+ it { expect(@user.namespaces).to match_array([@user.namespace, @group]) }
end
describe 'group multiple owners' do
@@ -270,6 +271,7 @@ describe User do
end
it { expect(@user.several_namespaces?).to be_falsey }
+ it { expect(@user.namespaces).to eq([@user.namespace]) }
end
describe 'blocking user' do
@@ -572,7 +574,6 @@ describe User do
end
describe "#contributed_projects_ids" do
-
subject { create(:user) }
let!(:project1) { create(:project) }
let!(:project2) { create(:project, forked_from_project: project3) }
@@ -598,4 +599,21 @@ describe User do
expect(subject.contributed_projects_ids).not_to include(project2.id)
end
end
+
+ describe :can_be_removed? do
+ subject { create(:user) }
+
+ context 'no owned groups' do
+ it { expect(subject.can_be_removed?).to be_truthy }
+ end
+
+ context 'has owned groups' do
+ before do
+ group = create(:group)
+ group.add_owner(subject)
+ end
+
+ it { expect(subject.can_be_removed?).to be_falsey }
+ end
+ end
end
diff --git a/spec/requests/api/commits_spec.rb b/spec/requests/api/commits_spec.rb
index 9ea60e1a4ad..a1c248c636e 100644
--- a/spec/requests/api/commits_spec.rb
+++ b/spec/requests/api/commits_spec.rb
@@ -9,6 +9,7 @@ describe API::API, api: true do
let!(:master) { create(:project_member, user: user, project: project, access_level: ProjectMember::MASTER) }
let!(:guest) { create(:project_member, user: user2, project: project, access_level: ProjectMember::GUEST) }
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] }
@@ -89,7 +90,7 @@ describe API::API, api: true do
get api("/projects/#{project.id}/repository/commits/#{project.repository.commit.id}/comments", user)
expect(response.status).to eq(200)
expect(json_response).to be_an Array
- expect(json_response.length).to eq(1)
+ expect(json_response.length).to eq(2)
expect(json_response.first['note']).to eq('a comment on a commit')
expect(json_response.first['author']['id']).to eq(user.id)
end
diff --git a/spec/requests/api/files_spec.rb b/spec/requests/api/files_spec.rb
index bab8888a631..15f547e128d 100644
--- a/spec/requests/api/files_spec.rb
+++ b/spec/requests/api/files_spec.rb
@@ -49,10 +49,6 @@ describe API::API, api: true do
}
it "should create a new file in project repo" do
- Gitlab::Satellite::NewFileAction.any_instance.stub(
- commit!: true,
- )
-
post api("/projects/#{project.id}/repository/files", user), valid_params
expect(response.status).to eq(201)
expect(json_response['file_path']).to eq('newfile.rb')
@@ -63,9 +59,9 @@ describe API::API, api: true do
expect(response.status).to eq(400)
end
- it "should return a 400 if satellite fails to create file" do
- Gitlab::Satellite::NewFileAction.any_instance.stub(
- commit!: false,
+ it "should return a 400 if editor fails to create file" do
+ Repository.any_instance.stub(
+ commit_file: false,
)
post api("/projects/#{project.id}/repository/files", user), valid_params
@@ -84,10 +80,6 @@ describe API::API, api: true do
}
it "should update existing file in project repo" do
- Gitlab::Satellite::EditFileAction.any_instance.stub(
- commit!: true,
- )
-
put api("/projects/#{project.id}/repository/files", user), valid_params
expect(response.status).to eq(200)
expect(json_response['file_path']).to eq(file_path)
@@ -97,35 +89,6 @@ describe API::API, api: true do
put api("/projects/#{project.id}/repository/files", user)
expect(response.status).to eq(400)
end
-
- it 'should return a 400 if the checkout fails' do
- Gitlab::Satellite::EditFileAction.any_instance.stub(:commit!)
- .and_raise(Gitlab::Satellite::CheckoutFailed)
-
- put api("/projects/#{project.id}/repository/files", user), valid_params
- expect(response.status).to eq(400)
-
- ref = valid_params[:branch_name]
- expect(response.body).to match("ref '#{ref}' could not be checked out")
- end
-
- it 'should return a 409 if the file was not modified' do
- Gitlab::Satellite::EditFileAction.any_instance.stub(:commit!)
- .and_raise(Gitlab::Satellite::CommitFailed)
-
- put api("/projects/#{project.id}/repository/files", user), valid_params
- expect(response.status).to eq(409)
- expect(response.body).to match("Maybe there was nothing to commit?")
- end
-
- it 'should return a 409 if the push fails' do
- Gitlab::Satellite::EditFileAction.any_instance.stub(:commit!)
- .and_raise(Gitlab::Satellite::PushFailed)
-
- put api("/projects/#{project.id}/repository/files", user), valid_params
- expect(response.status).to eq(409)
- expect(response.body).to match("Maybe the file was changed by another process?")
- end
end
describe "DELETE /projects/:id/repository/files" do
@@ -138,10 +101,6 @@ describe API::API, api: true do
}
it "should delete existing file in project repo" do
- Gitlab::Satellite::DeleteFileAction.any_instance.stub(
- commit!: true,
- )
-
delete api("/projects/#{project.id}/repository/files", user), valid_params
expect(response.status).to eq(200)
expect(json_response['file_path']).to eq(file_path)
@@ -153,8 +112,8 @@ describe API::API, api: true do
end
it "should return a 400 if satellite fails to create file" do
- Gitlab::Satellite::DeleteFileAction.any_instance.stub(
- commit!: false,
+ Repository.any_instance.stub(
+ remove_file: false,
)
delete api("/projects/#{project.id}/repository/files", user), valid_params
diff --git a/spec/requests/api/merge_requests_spec.rb b/spec/requests/api/merge_requests_spec.rb
index dcd50f73326..0ed5883914b 100644
--- a/spec/requests/api/merge_requests_spec.rb
+++ b/spec/requests/api/merge_requests_spec.rb
@@ -349,10 +349,10 @@ describe API::API, api: true do
expect(json_response['description']).to eq('New description')
end
- it "should return 422 when source_branch and target_branch are renamed the same" do
+ it "should return 400 when source_branch is specified" do
put api("/projects/#{project.id}/merge_request/#{merge_request.id}", user),
source_branch: "master", target_branch: "master"
- expect(response.status).to eq(422)
+ expect(response.status).to eq(400)
end
it "should return merge_request with renamed target_branch" do
diff --git a/spec/requests/api/namespaces_spec.rb b/spec/requests/api/namespaces_spec.rb
index 6ddaaa0a6dd..21787fdd895 100644
--- a/spec/requests/api/namespaces_spec.rb
+++ b/spec/requests/api/namespaces_spec.rb
@@ -3,6 +3,7 @@ require 'spec_helper'
describe API::API, api: true do
include ApiHelpers
let(:admin) { create(:admin) }
+ let(:user) { create(:user) }
let!(:group1) { create(:group) }
let!(:group2) { create(:group) }
@@ -14,7 +15,7 @@ describe API::API, api: true do
end
end
- context "when authenticated as admin" do
+ context "when authenticated as admin" do
it "admin: should return an array of all namespaces" do
get api("/namespaces", admin)
expect(response.status).to eq(200)
@@ -22,6 +23,32 @@ describe API::API, api: true do
expect(json_response.length).to eq(Namespace.count)
end
+
+ it "admin: should return an array of matched namespaces" do
+ get api("/namespaces?search=#{group1.name}", admin)
+ expect(response.status).to eq(200)
+ expect(json_response).to be_an Array
+
+ expect(json_response.length).to eq(1)
+ end
+ end
+
+ context "when authenticated as a regular user" do
+ it "user: should return an array of namespaces" do
+ get api("/namespaces", user)
+ expect(response.status).to eq(200)
+ expect(json_response).to be_an Array
+
+ expect(json_response.length).to eq(1)
+ end
+
+ it "admin: should return an array of matched namespaces" do
+ get api("/namespaces?search=#{user.username}", user)
+ expect(response.status).to eq(200)
+ expect(json_response).to be_an Array
+
+ expect(json_response.length).to eq(1)
+ end
end
end
end
diff --git a/spec/requests/api/projects_spec.rb b/spec/requests/api/projects_spec.rb
index 46cd26eb927..dbfd72e5f19 100644
--- a/spec/requests/api/projects_spec.rb
+++ b/spec/requests/api/projects_spec.rb
@@ -57,14 +57,14 @@ describe API::API, api: true do
expect(json_response.first['name']).to eq(project.name)
expect(json_response.first['owner']['username']).to eq(user.username)
end
-
+
it 'should include the project labels as the tag_list' do
get api('/projects', user)
response.status.should == 200
json_response.should be_an Array
json_response.first.keys.should include('tag_list')
end
-
+
context 'and using search' do
it 'should return searched project' do
get api('/projects', user), { search: project.name }
@@ -792,11 +792,6 @@ describe API::API, api: true do
describe 'DELETE /projects/:id' do
context 'when authenticated as user' do
it 'should remove project' do
- expect(GitlabShellWorker).to(
- receive(:perform_async).with(:remove_repository,
- /#{project.path_with_namespace}/)
- ).twice
-
delete api("/projects/#{project.id}", user)
expect(response.status).to eq(200)
end
diff --git a/spec/routing/project_routing_spec.rb b/spec/routing/project_routing_spec.rb
index 042352311da..0040718d9be 100644
--- a/spec/routing/project_routing_spec.rb
+++ b/spec/routing/project_routing_spec.rb
@@ -172,7 +172,7 @@ end
# DELETE /:project_id/deploy_keys/:id(.:format) deploy_keys#destroy
describe Projects::DeployKeysController, 'routing' do
it_behaves_like 'RESTful project resources' do
- let(:actions) { [:index, :show, :new, :create] }
+ let(:actions) { [:index, :new, :create] }
let(:controller) { 'deploy_keys' }
end
end
@@ -208,23 +208,31 @@ describe Projects::RefsController, 'routing' do
end
end
-# diffs_project_merge_request GET /:project_id/merge_requests/:id/diffs(.:format) projects/merge_requests#diffs
-# automerge_project_merge_request POST /:project_id/merge_requests/:id/automerge(.:format) projects/merge_requests#automerge
-# automerge_check_project_merge_request GET /:project_id/merge_requests/:id/automerge_check(.:format) projects/merge_requests#automerge_check
-# branch_from_project_merge_requests GET /:project_id/merge_requests/branch_from(.:format) projects/merge_requests#branch_from
-# branch_to_project_merge_requests GET /:project_id/merge_requests/branch_to(.:format) projects/merge_requests#branch_to
-# project_merge_requests GET /:project_id/merge_requests(.:format) projects/merge_requests#index
-# POST /:project_id/merge_requests(.:format) projects/merge_requests#create
-# new_project_merge_request GET /:project_id/merge_requests/new(.:format) projects/merge_requests#new
-# edit_project_merge_request GET /:project_id/merge_requests/:id/edit(.:format) projects/merge_requests#edit
-# project_merge_request GET /:project_id/merge_requests/:id(.:format) projects/merge_requests#show
-# PUT /:project_id/merge_requests/:id(.:format) projects/merge_requests#update
-# DELETE /:project_id/merge_requests/:id(.:format) projects/merge_requests#destroy
+# diffs_namespace_project_merge_request GET /:namespace_id/:project_id/merge_requests/:id/diffs(.:format) projects/merge_requests#diffs
+# commits_namespace_project_merge_request GET /:namespace_id/:project_id/merge_requests/:id/commits(.:format) projects/merge_requests#commits
+# automerge_namespace_project_merge_request POST /:namespace_id/:project_id/merge_requests/:id/automerge(.:format) projects/merge_requests#automerge
+# automerge_check_namespace_project_merge_request GET /:namespace_id/:project_id/merge_requests/:id/automerge_check(.:format) projects/merge_requests#automerge_check
+# ci_status_namespace_project_merge_request GET /:namespace_id/:project_id/merge_requests/:id/ci_status(.:format) projects/merge_requests#ci_status
+# toggle_subscription_namespace_project_merge_request POST /:namespace_id/:project_id/merge_requests/:id/toggle_subscription(.:format) projects/merge_requests#toggle_subscription
+# branch_from_namespace_project_merge_requests GET /:namespace_id/:project_id/merge_requests/branch_from(.:format) projects/merge_requests#branch_from
+# branch_to_namespace_project_merge_requests GET /:namespace_id/:project_id/merge_requests/branch_to(.:format) projects/merge_requests#branch_to
+# update_branches_namespace_project_merge_requests GET /:namespace_id/:project_id/merge_requests/update_branches(.:format) projects/merge_requests#update_branches
+# namespace_project_merge_requests GET /:namespace_id/:project_id/merge_requests(.:format) projects/merge_requests#index
+# POST /:namespace_id/:project_id/merge_requests(.:format) projects/merge_requests#create
+# new_namespace_project_merge_request GET /:namespace_id/:project_id/merge_requests/new(.:format) projects/merge_requests#new
+# edit_namespace_project_merge_request GET /:namespace_id/:project_id/merge_requests/:id/edit(.:format) projects/merge_requests#edit
+# namespace_project_merge_request GET /:namespace_id/:project_id/merge_requests/:id(.:format) projects/merge_requests#show
+# PATCH /:namespace_id/:project_id/merge_requests/:id(.:format) projects/merge_requests#update
+# PUT /:namespace_id/:project_id/merge_requests/:id(.:format) projects/merge_requests#update
describe Projects::MergeRequestsController, 'routing' do
it 'to #diffs' do
expect(get('/gitlab/gitlabhq/merge_requests/1/diffs')).to route_to('projects/merge_requests#diffs', namespace_id: 'gitlab', project_id: 'gitlabhq', id: '1')
end
+ it 'to #commits' do
+ expect(get('/gitlab/gitlabhq/merge_requests/1/commits')).to route_to('projects/merge_requests#commits', namespace_id: 'gitlab', project_id: 'gitlabhq', id: '1')
+ end
+
it 'to #automerge' do
expect(post('/gitlab/gitlabhq/merge_requests/1/automerge')).to route_to(
'projects/merge_requests#automerge',
diff --git a/spec/services/destroy_group_service_spec.rb b/spec/services/destroy_group_service_spec.rb
new file mode 100644
index 00000000000..24e439503e7
--- /dev/null
+++ b/spec/services/destroy_group_service_spec.rb
@@ -0,0 +1,44 @@
+require 'spec_helper'
+
+describe DestroyGroupService do
+ let!(:user) { create(:user) }
+ let!(:group) { create(:group) }
+ let!(:project) { create(:project, namespace: group) }
+ let!(:gitlab_shell) { Gitlab::Shell.new }
+ let!(:remove_path) { group.path + "+#{group.id}+deleted" }
+
+ context 'database records' do
+ before do
+ destroy_group(group, user)
+ end
+
+ it { Group.all.should_not include(group) }
+ it { Project.all.should_not include(project) }
+ end
+
+ context 'file system' do
+ context 'Sidekiq inline' do
+ before do
+ # Run sidekiq immediatly to check that renamed dir will be removed
+ Sidekiq::Testing.inline! { destroy_group(group, user) }
+ end
+
+ it { gitlab_shell.exists?(group.path).should be_falsey }
+ it { gitlab_shell.exists?(remove_path).should be_falsey }
+ end
+
+ context 'Sidekiq fake' do
+ before do
+ # Dont run sidekiq to check if renamed repository exists
+ Sidekiq::Testing.fake! { destroy_group(group, user) }
+ end
+
+ it { gitlab_shell.exists?(group.path).should be_falsey }
+ it { gitlab_shell.exists?(remove_path).should be_truthy }
+ end
+ end
+
+ def destroy_group(group, user)
+ DestroyGroupService.new(group, user).execute
+ end
+end
diff --git a/spec/services/merge_requests/update_service_spec.rb b/spec/services/merge_requests/update_service_spec.rb
index 0a0760056cf..c75173c1452 100644
--- a/spec/services/merge_requests/update_service_spec.rb
+++ b/spec/services/merge_requests/update_service_spec.rb
@@ -20,7 +20,8 @@ describe MergeRequests::UpdateService do
description: 'Also please fix',
assignee_id: user2.id,
state_event: 'close',
- label_ids: [label.id]
+ label_ids: [label.id],
+ target_branch: 'target'
}
end
@@ -39,6 +40,7 @@ describe MergeRequests::UpdateService do
it { expect(@merge_request).to be_closed }
it { expect(@merge_request.labels.count).to eq(1) }
it { expect(@merge_request.labels.first.title).to eq('Bug') }
+ it { expect(@merge_request.target_branch).to eq('target') }
it 'should execute hooks with update action' do
expect(service).to have_received(:execute_hooks).
@@ -77,6 +79,13 @@ describe MergeRequests::UpdateService do
expect(note).not_to be_nil
expect(note.note).to eq 'Title changed from **Old title** to **New title**'
end
+
+ it 'creates system note about branch change' do
+ note = find_note('Target')
+
+ expect(note).not_to be_nil
+ expect(note.note).to eq 'Target branch changed from `master` to `target`'
+ end
end
end
end
diff --git a/spec/services/projects/destroy_service_spec.rb b/spec/services/projects/destroy_service_spec.rb
new file mode 100644
index 00000000000..cdf576cc0c1
--- /dev/null
+++ b/spec/services/projects/destroy_service_spec.rb
@@ -0,0 +1,34 @@
+require 'spec_helper'
+
+describe Projects::DestroyService do
+ let!(:user) { create(:user) }
+ let!(:project) { create(:project, namespace: user.namespace) }
+ let!(:path) { project.repository.path_to_repo }
+ let!(:remove_path) { path.sub(/\.git\Z/, "+#{project.id}+deleted.git") }
+
+ context 'Sidekiq inline' do
+ before do
+ # Run sidekiq immediatly to check that renamed repository will be removed
+ Sidekiq::Testing.inline! { destroy_project(project, user, {}) }
+ end
+
+ it { Project.all.should_not include(project) }
+ it { Dir.exists?(path).should be_falsey }
+ it { Dir.exists?(remove_path).should be_falsey }
+ end
+
+ context 'Sidekiq fake' do
+ before do
+ # Dont run sidekiq to check if renamed repository exists
+ Sidekiq::Testing.fake! { destroy_project(project, user, {}) }
+ end
+
+ it { Project.all.should_not include(project) }
+ it { Dir.exists?(path).should be_falsey }
+ it { Dir.exists?(remove_path).should be_truthy }
+ end
+
+ def destroy_project(project, user, params)
+ Projects::DestroyService.new(project, user, params).execute
+ end
+end
diff --git a/spec/services/system_note_service_spec.rb b/spec/services/system_note_service_spec.rb
index 0dcc94e8bd4..700286b585a 100644
--- a/spec/services/system_note_service_spec.rb
+++ b/spec/services/system_note_service_spec.rb
@@ -228,6 +228,20 @@ describe SystemNoteService do
end
end
+ describe '.change_branch' do
+ subject { described_class.change_branch(noteable, project, author, 'target', old_branch, new_branch) }
+ let(:old_branch) { 'old_branch'}
+ let(:new_branch) { 'new_branch'}
+
+ it_behaves_like 'a system note'
+
+ context 'when target branch name changed' do
+ it 'sets the note text' do
+ expect(subject.note).to eq "Target branch changed from `#{old_branch}` to `#{new_branch}`"
+ end
+ end
+ end
+
describe '.cross_reference' do
subject { described_class.cross_reference(noteable, mentioner, author) }
diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb
index 8fe51cf4add..9c8004ab555 100644
--- a/spec/spec_helper.rb
+++ b/spec/spec_helper.rb
@@ -1,16 +1,7 @@
-if ENV['SIMPLECOV']
- require 'simplecov'
-end
-
-if ENV['COVERALLS']
- require 'coveralls'
- Coveralls.wear_merged!
-end
-
ENV["RAILS_ENV"] ||= 'test'
require File.expand_path("../../config/environment", __FILE__)
require 'rspec/rails'
-require 'webmock/rspec'
+require 'shoulda/matchers'
require 'email_spec'
require 'sidekiq/testing/inline'
@@ -18,8 +9,6 @@ require 'sidekiq/testing/inline'
# in spec/support/ and its subdirectories.
Dir[Rails.root.join("spec/support/**/*.rb")].each { |f| require f }
-WebMock.disable_net_connect!(allow_localhost: true)
-
RSpec.configure do |config|
config.use_transactional_fixtures = false
config.use_instantiated_fixtures = false
diff --git a/spec/support/api_helpers.rb b/spec/support/api_helpers.rb
index ec9a326a1ea..f63322776d4 100644
--- a/spec/support/api_helpers.rb
+++ b/spec/support/api_helpers.rb
@@ -29,6 +29,6 @@ module ApiHelpers
end
def json_response
- JSON.parse(response.body)
+ @_json_response ||= JSON.parse(response.body)
end
end
diff --git a/spec/support/coverage.rb b/spec/support/coverage.rb
new file mode 100644
index 00000000000..a54bf03380c
--- /dev/null
+++ b/spec/support/coverage.rb
@@ -0,0 +1,8 @@
+if ENV['SIMPLECOV']
+ require 'simplecov'
+end
+
+if ENV['COVERALLS']
+ require 'coveralls'
+ Coveralls.wear_merged!
+end
diff --git a/spec/support/reference_filter_spec_helper.rb b/spec/support/filter_spec_helper.rb
index afbea55ab99..755964e9a3d 100644
--- a/spec/support/reference_filter_spec_helper.rb
+++ b/spec/support/filter_spec_helper.rb
@@ -1,46 +1,23 @@
-# Common methods and setup for Gitlab::Markdown reference filter specs
+# Helper methods for Gitlab::Markdown filter specs
#
# Must be included into specs manually
-module ReferenceFilterSpecHelper
+module FilterSpecHelper
extend ActiveSupport::Concern
- # Shortcut to Rails' auto-generated routes helpers, to avoid including the
- # module
- def urls
- Rails.application.routes.url_helpers
- end
-
- # Modify a String reference to make it invalid
- #
- # Commit SHAs get reversed, IDs get incremented by 1, all other Strings get
- # their word characters reversed.
- #
- # reference - String reference to modify
- #
- # Returns a String
- def invalidate_reference(reference)
- if reference =~ /\A(.+)?.\d+\z/
- # Integer-based reference with optional project prefix
- reference.gsub(/\d+\z/) { |i| i.to_i + 1 }
- elsif reference =~ /\A(.+@)?(\h{6,40}\z)/
- # SHA-based reference with optional prefix
- reference.gsub(/\h{6,40}\z/) { |v| v.reverse }
- else
- reference.gsub(/\w+\z/) { |v| v.reverse }
- end
- end
-
# Perform `call` on the described class
#
- # Automatically passes the current `project` value to the context if none is
- # provided.
+ # Automatically passes the current `project` value, if defined, to the context
+ # if none is provided.
#
- # html - String text to pass to the filter's `call` method.
+ # html - HTML String to pass to the filter's `call` method.
# contexts - Hash context for the filter. (default: {project: project})
#
- # Returns the String text returned by the filter's `call` method.
+ # Returns a Nokogiri::XML::DocumentFragment
def filter(html, contexts = {})
- contexts.reverse_merge!(project: project)
+ if defined?(project)
+ contexts.reverse_merge!(project: project)
+ end
+
described_class.call(html, contexts)
end
@@ -50,7 +27,7 @@ module ReferenceFilterSpecHelper
# body - String text to run through the pipeline
# contexts - Hash context for the filter. (default: {project: project})
#
- # Returns the Hash of the pipeline result
+ # Returns the Hash
def pipeline_result(body, contexts = {})
contexts.reverse_merge!(project: project)
@@ -58,13 +35,43 @@ module ReferenceFilterSpecHelper
pipeline.call(body)
end
+ # Modify a String reference to make it invalid
+ #
+ # Commit SHAs get reversed, IDs get incremented by 1, all other Strings get
+ # their word characters reversed.
+ #
+ # reference - String reference to modify
+ #
+ # Returns a String
+ def invalidate_reference(reference)
+ if reference =~ /\A(.+)?.\d+\z/
+ # Integer-based reference with optional project prefix
+ reference.gsub(/\d+\z/) { |i| i.to_i + 1 }
+ elsif reference =~ /\A(.+@)?(\h{6,40}\z)/
+ # SHA-based reference with optional prefix
+ reference.gsub(/\h{6,40}\z/) { |v| v.reverse }
+ else
+ reference.gsub(/\w+\z/) { |v| v.reverse }
+ end
+ end
+
+ # Stub CrossProjectReference#user_can_reference_project? to return true for
+ # the current test
def allow_cross_reference!
allow_any_instance_of(described_class).
to receive(:user_can_reference_project?).and_return(true)
end
+ # Stub CrossProjectReference#user_can_reference_project? to return false for
+ # the current test
def disallow_cross_reference!
allow_any_instance_of(described_class).
to receive(:user_can_reference_project?).and_return(false)
end
+
+ # Shortcut to Rails' auto-generated routes helpers, to avoid including the
+ # module
+ def urls
+ Rails.application.routes.url_helpers
+ end
end
diff --git a/spec/support/matchers.rb b/spec/support/matchers.rb
index 52b11bd6323..f8cce2ea5a3 100644
--- a/spec/support/matchers.rb
+++ b/spec/support/matchers.rb
@@ -70,7 +70,7 @@ end
# Extend shoulda-matchers
module Shoulda::Matchers::ActiveModel
- class EnsureLengthOfMatcher
+ class ValidateLengthOfMatcher
# Shortcut for is_at_least and is_at_most
def is_within(range)
is_at_least(range.min) && is_at_most(range.max)
diff --git a/spec/support/mentionable_shared_examples.rb b/spec/support/mentionable_shared_examples.rb
index ede62e8f37a..d29c8a55c82 100644
--- a/spec/support/mentionable_shared_examples.rb
+++ b/spec/support/mentionable_shared_examples.rb
@@ -107,17 +107,26 @@ shared_examples 'an editable mentionable' do
it 'creates new cross-reference notes when the mentionable text is edited' do
subject.save
- new_text = <<-MSG
+ new_text = <<-MSG.strip_heredoc
These references already existed:
- Issue: #{mentioned_issue.to_reference}
- Commit: #{mentioned_commit.to_reference}
+
+ Issue: #{mentioned_issue.to_reference}
+
+ Commit: #{mentioned_commit.to_reference}
+
+ ---
This cross-project reference already existed:
- Issue: #{ext_issue.to_reference(project)}
+
+ Issue: #{ext_issue.to_reference(project)}
+
+ ---
These two references are introduced in an edit:
- Issue: #{new_issues[0].to_reference}
- Cross: #{new_issues[1].to_reference(project)}
+
+ Issue: #{new_issues[0].to_reference}
+
+ Cross: #{new_issues[1].to_reference(project)}
MSG
# These three objects were already referenced, and should not receive new
diff --git a/spec/support/webmock.rb b/spec/support/webmock.rb
new file mode 100644
index 00000000000..af2906b7568
--- /dev/null
+++ b/spec/support/webmock.rb
@@ -0,0 +1,4 @@
+require 'webmock'
+require 'webmock/rspec'
+
+WebMock.disable_net_connect!(allow_localhost: true)
diff --git a/spec/teaspoon_env.rb b/spec/teaspoon_env.rb
new file mode 100644
index 00000000000..58f45ff8610
--- /dev/null
+++ b/spec/teaspoon_env.rb
@@ -0,0 +1,178 @@
+Teaspoon.configure do |config|
+ # Determines where the Teaspoon routes will be mounted. Changing this to "/jasmine" would allow you to browse to
+ # `http://localhost:3000/jasmine` to run your tests.
+ config.mount_at = "/teaspoon"
+
+ # Specifies the root where Teaspoon will look for files. If you're testing an engine using a dummy application it can
+ # be useful to set this to your engines root (e.g. `Teaspoon::Engine.root`).
+ # Note: Defaults to `Rails.root` if nil.
+ config.root = nil
+
+ # Paths that will be appended to the Rails assets paths
+ # Note: Relative to `config.root`.
+ config.asset_paths = ["spec/javascripts", "spec/javascripts/stylesheets"]
+
+ # Fixtures are rendered through a controller, which allows using HAML, RABL/JBuilder, etc. Files in these paths will
+ # be rendered as fixtures.
+ config.fixture_paths = ["spec/javascripts/fixtures"]
+
+ # SUITES
+ #
+ # You can modify the default suite configuration and create new suites here. Suites are isolated from one another.
+ #
+ # When defining a suite you can provide a name and a block. If the name is left blank, :default is assumed. You can
+ # omit various directives and the ones defined in the default suite will be used.
+ #
+ # To run a specific suite
+ # - in the browser: http://localhost/teaspoon/[suite_name]
+ # - with the rake task: rake teaspoon suite=[suite_name]
+ # - with the cli: teaspoon --suite=[suite_name]
+ config.suite do |suite|
+ # Specify the framework you would like to use. This allows you to select versions, and will do some basic setup for
+ # you -- which you can override with the directives below. This should be specified first, as it can override other
+ # directives.
+ # Note: If no version is specified, the latest is assumed.
+ #
+ # Versions: 1.3.1, 2.0.3, 2.1.3, 2.2.0
+ suite.use_framework :jasmine, "2.2.0"
+
+ # Specify a file matcher as a regular expression and all matching files will be loaded when the suite is run. These
+ # files need to be within an asset path. You can add asset paths using the `config.asset_paths`.
+ suite.matcher = "{spec/javascripts,app/assets}/**/*_spec.{js,js.coffee,coffee}"
+
+ # Load additional JS files, but requiring them in your spec helper is the preferred way to do this.
+ #suite.javascripts = []
+
+ # You can include your own stylesheets if you want to change how Teaspoon looks.
+ # Note: Spec related CSS can and should be loaded using fixtures.
+ #suite.stylesheets = ["teaspoon"]
+
+ # This suites spec helper, which can require additional support files. This file is loaded before any of your test
+ # files are loaded.
+ suite.helper = "spec_helper"
+
+ # Partial to be rendered in the head tag of the runner. You can use the provided ones or define your own by creating
+ # a `_boot.html.erb` in your fixtures path, and adjust the config to `"/boot"` for instance.
+ #
+ # Available: boot, boot_require_js
+ suite.boot_partial = "boot"
+
+ # Partial to be rendered in the body tag of the runner. You can define your own to create a custom body structure.
+ suite.body_partial = "body"
+
+ # Hooks allow you to use `Teaspoon.hook("fixtures")` before, after, or during your spec run. This will make a
+ # synchronous Ajax request to the server that will call all of the blocks you've defined for that hook name.
+ #suite.hook :fixtures, &proc{}
+
+ # Determine whether specs loaded into the test harness should be embedded as individual script tags or concatenated
+ # into a single file. Similar to Rails' asset `debug: true` and `config.assets.debug = true` options. By default,
+ # Teaspoon expands all assets to provide more valuable stack traces that reference individual source files.
+ #suite.expand_assets = true
+ end
+
+ # Example suite. Since we're just filtering to files already within the root test/javascripts, these files will also
+ # be run in the default suite -- but can be focused into a more specific suite.
+ #config.suite :targeted do |suite|
+ # suite.matcher = "spec/javascripts/targeted/*_spec.{js,js.coffee,coffee}"
+ #end
+
+ # CONSOLE RUNNER SPECIFIC
+ #
+ # These configuration directives are applicable only when running via the rake task or command line interface. These
+ # directives can be overridden using the command line interface arguments or with ENV variables when using the rake
+ # task.
+ #
+ # Command Line Interface:
+ # teaspoon --driver=phantomjs --server-port=31337 --fail-fast=true --format=junit --suite=my_suite /spec/file_spec.js
+ #
+ # Rake:
+ # teaspoon DRIVER=phantomjs SERVER_PORT=31337 FAIL_FAST=true FORMATTERS=junit suite=my_suite
+
+ # Specify which headless driver to use. Supports PhantomJS and Selenium Webdriver.
+ #
+ # Available: :phantomjs, :selenium, :capybara_webkit
+ # PhantomJS: https://github.com/modeset/teaspoon/wiki/Using-PhantomJS
+ # Selenium Webdriver: https://github.com/modeset/teaspoon/wiki/Using-Selenium-WebDriver
+ # Capybara Webkit: https://github.com/modeset/teaspoon/wiki/Using-Capybara-Webkit
+ #config.driver = :phantomjs
+
+ # Specify additional options for the driver.
+ #
+ # PhantomJS: https://github.com/modeset/teaspoon/wiki/Using-PhantomJS
+ # Selenium Webdriver: https://github.com/modeset/teaspoon/wiki/Using-Selenium-WebDriver
+ # Capybara Webkit: https://github.com/modeset/teaspoon/wiki/Using-Capybara-Webkit
+ #config.driver_options = nil
+
+ # Specify the timeout for the driver. Specs are expected to complete within this time frame or the run will be
+ # considered a failure. This is to avoid issues that can arise where tests stall.
+ #config.driver_timeout = 180
+
+ # Specify a server to use with Rack (e.g. thin, mongrel). If nil is provided Rack::Server is used.
+ #config.server = nil
+
+ # Specify a port to run on a specific port, otherwise Teaspoon will use a random available port.
+ #config.server_port = nil
+
+ # Timeout for starting the server in seconds. If your server is slow to start you may have to bump this, or you may
+ # want to lower this if you know it shouldn't take long to start.
+ #config.server_timeout = 20
+
+ # Force Teaspoon to fail immediately after a failing suite. Can be useful to make Teaspoon fail early if you have
+ # several suites, but in environments like CI this may not be desirable.
+ #config.fail_fast = true
+
+ # Specify the formatters to use when outputting the results.
+ # Note: Output files can be specified by using `"junit>/path/to/output.xml"`.
+ #
+ # Available: :dot, :clean, :documentation, :json, :junit, :pride, :rspec_html, :snowday, :swayze_or_oprah, :tap, :tap_y, :teamcity
+ #config.formatters = [:dot]
+
+ # Specify if you want color output from the formatters.
+ #config.color = true
+
+ # Teaspoon pipes all console[log/debug/error] to $stdout. This is useful to catch places where you've forgotten to
+ # remove them, but in verbose applications this may not be desirable.
+ #config.suppress_log = false
+
+ # COVERAGE REPORTS / THRESHOLD ASSERTIONS
+ #
+ # Coverage reports requires Istanbul (https://github.com/gotwarlost/istanbul) to add instrumentation to your code and
+ # display coverage statistics.
+ #
+ # Coverage configurations are similar to suites. You can define several, and use different ones under different
+ # conditions.
+ #
+ # To run with a specific coverage configuration
+ # - with the rake task: rake teaspoon USE_COVERAGE=[coverage_name]
+ # - with the cli: teaspoon --coverage=[coverage_name]
+
+ # Specify that you always want a coverage configuration to be used. Otherwise, specify that you want coverage
+ # on the CLI.
+ # Set this to "true" or the name of your coverage config.
+ #config.use_coverage = nil
+
+ # You can have multiple coverage configs by passing a name to config.coverage.
+ # e.g. config.coverage :ci do |coverage|
+ # The default coverage config name is :default.
+ config.coverage do |coverage|
+ # Which coverage reports Istanbul should generate. Correlates directly to what Istanbul supports.
+ #
+ # Available: text-summary, text, html, lcov, lcovonly, cobertura, teamcity
+ #coverage.reports = ["text-summary", "html"]
+
+ # The path that the coverage should be written to - when there's an artifact to write to disk.
+ # Note: Relative to `config.root`.
+ #coverage.output_path = "coverage"
+
+ # Assets to be ignored when generating coverage reports. Accepts an array of filenames or regular expressions. The
+ # default excludes assets from vendor, gems and support libraries.
+ #coverage.ignore = [%r{/lib/ruby/gems/}, %r{/vendor/assets/}, %r{/support/}, %r{/(.+)_helper.}]
+
+ # Various thresholds requirements can be defined, and those thresholds will be checked at the end of a run. If any
+ # aren't met the run will fail with a message. Thresholds can be defined as a percentage (0-100), or nil.
+ #coverage.statements = nil
+ #coverage.functions = nil
+ #coverage.branches = nil
+ #coverage.lines = nil
+ end
+end
diff --git a/vendor/assets/javascripts/jasmine-fixture.js b/vendor/assets/javascripts/jasmine-fixture.js
deleted file mode 100755
index 9980aec6ddb..00000000000
--- a/vendor/assets/javascripts/jasmine-fixture.js
+++ /dev/null
@@ -1,433 +0,0 @@
-/* jasmine-fixture - 1.3.1
- * Makes injecting HTML snippets into the DOM easy & clean!
- * https://github.com/searls/jasmine-fixture
- */
-(function() {
- var createHTMLBlock,
- __slice = [].slice;
-
- (function($) {
- var ewwSideEffects, jasmineFixture, originalAffix, originalJasmineDotFixture, originalJasmineFixture, root, _, _ref;
- root = (1, eval)('this');
- originalJasmineFixture = root.jasmineFixture;
- originalJasmineDotFixture = (_ref = root.jasmine) != null ? _ref.fixture : void 0;
- originalAffix = root.affix;
- _ = function(list) {
- return {
- inject: function(iterator, memo) {
- var item, _i, _len, _results;
- _results = [];
- for (_i = 0, _len = list.length; _i < _len; _i++) {
- item = list[_i];
- _results.push(memo = iterator(memo, item));
- }
- return _results;
- }
- };
- };
- root.jasmineFixture = function($) {
- var $whatsTheRootOf, affix, create, jasmineFixture, noConflict;
- affix = function(selectorOptions) {
- return create.call(this, selectorOptions, true);
- };
- create = function(selectorOptions, attach) {
- var $top;
- $top = null;
- _(selectorOptions.split(/[ ](?![^\{]*\})(?=[^\]]*?(?:\[|$))/)).inject(function($parent, elementSelector) {
- var $el;
- if (elementSelector === ">") {
- return $parent;
- }
- $el = createHTMLBlock($, elementSelector);
- if (attach || $top) {
- $el.appendTo($parent);
- }
- $top || ($top = $el);
- return $el;
- }, $whatsTheRootOf(this));
- return $top;
- };
- noConflict = function() {
- var currentJasmineFixture, _ref1;
- currentJasmineFixture = jasmine.fixture;
- root.jasmineFixture = originalJasmineFixture;
- if ((_ref1 = root.jasmine) != null) {
- _ref1.fixture = originalJasmineDotFixture;
- }
- root.affix = originalAffix;
- return currentJasmineFixture;
- };
- $whatsTheRootOf = function(that) {
- if (that.jquery != null) {
- return that;
- } else if ($('#jasmine_content').length > 0) {
- return $('#jasmine_content');
- } else {
- return $('<div id="jasmine_content"></div>').appendTo('body');
- }
- };
- jasmineFixture = {
- affix: affix,
- create: create,
- noConflict: noConflict
- };
- ewwSideEffects(jasmineFixture);
- return jasmineFixture;
- };
- ewwSideEffects = function(jasmineFixture) {
- var _ref1;
- if ((_ref1 = root.jasmine) != null) {
- _ref1.fixture = jasmineFixture;
- }
- $.fn.affix = root.affix = jasmineFixture.affix;
- return afterEach(function() {
- return $('#jasmine_content').remove();
- });
- };
- if ($) {
- return jasmineFixture = root.jasmineFixture($);
- } else {
- return root.affix = function() {
- var nowJQueryExists;
- nowJQueryExists = window.jQuery || window.$;
- if (nowJQueryExists != null) {
- jasmineFixture = root.jasmineFixture(nowJQueryExists);
- return affix.call.apply(affix, [this].concat(__slice.call(arguments)));
- } else {
- throw new Error("jasmine-fixture requires jQuery to be defined at window.jQuery or window.$");
- }
- };
- }
- })(window.jQuery || window.$);
-
- createHTMLBlock = (function() {
- var bindData, bindEvents, parseAttributes, parseClasses, parseContents, parseEnclosure, parseReferences, parseVariableScope, regAttr, regAttrDfn, regAttrs, regCBrace, regClass, regClasses, regData, regDatas, regEvent, regEvents, regExclamation, regId, regReference, regTag, regTagNotContent, regZenTagDfn;
- createHTMLBlock = function($, ZenObject, data, functions, indexes) {
- var ZenCode, arr, block, blockAttrs, blockClasses, blockHTML, blockId, blockTag, blocks, el, el2, els, forScope, indexName, inner, len, obj, origZenCode, paren, result, ret, zc, zo;
- if ($.isPlainObject(ZenObject)) {
- ZenCode = ZenObject.main;
- } else {
- ZenCode = ZenObject;
- ZenObject = {
- main: ZenCode
- };
- }
- origZenCode = ZenCode;
- if (indexes === undefined) {
- indexes = {};
- }
- if (ZenCode.charAt(0) === "!" || $.isArray(data)) {
- if ($.isArray(data)) {
- forScope = ZenCode;
- } else {
- obj = parseEnclosure(ZenCode, "!");
- obj = obj.substring(obj.indexOf(":") + 1, obj.length - 1);
- forScope = parseVariableScope(ZenCode);
- }
- while (forScope.charAt(0) === "@") {
- forScope = parseVariableScope("!for:!" + parseReferences(forScope, ZenObject));
- }
- zo = ZenObject;
- zo.main = forScope;
- el = $();
- if (ZenCode.substring(0, 5) === "!for:" || $.isArray(data)) {
- if (!$.isArray(data) && obj.indexOf(":") > 0) {
- indexName = obj.substring(0, obj.indexOf(":"));
- obj = obj.substr(obj.indexOf(":") + 1);
- }
- arr = ($.isArray(data) ? data : data[obj]);
- zc = zo.main;
- if ($.isArray(arr) || $.isPlainObject(arr)) {
- $.map(arr, function(value, index) {
- var next;
- zo.main = zc;
- if (indexName !== undefined) {
- indexes[indexName] = index;
- }
- if (!$.isPlainObject(value)) {
- value = {
- value: value
- };
- }
- next = createHTMLBlock($, zo, value, functions, indexes);
- if (el.length !== 0) {
- return $.each(next, function(index, value) {
- return el.push(value);
- });
- }
- });
- }
- if (!$.isArray(data)) {
- ZenCode = ZenCode.substr(obj.length + 6 + forScope.length);
- } else {
- ZenCode = "";
- }
- } else if (ZenCode.substring(0, 4) === "!if:") {
- result = parseContents("!" + obj + "!", data, indexes);
- if (result !== "undefined" || result !== "false" || result !== "") {
- el = createHTMLBlock($, zo, data, functions, indexes);
- }
- ZenCode = ZenCode.substr(obj.length + 5 + forScope.length);
- }
- ZenObject.main = ZenCode;
- } else if (ZenCode.charAt(0) === "(") {
- paren = parseEnclosure(ZenCode, "(", ")");
- inner = paren.substring(1, paren.length - 1);
- ZenCode = ZenCode.substr(paren.length);
- zo = ZenObject;
- zo.main = inner;
- el = createHTMLBlock($, zo, data, functions, indexes);
- } else {
- blocks = ZenCode.match(regZenTagDfn);
- block = blocks[0];
- if (block.length === 0) {
- return "";
- }
- if (block.indexOf("@") >= 0) {
- ZenCode = parseReferences(ZenCode, ZenObject);
- zo = ZenObject;
- zo.main = ZenCode;
- return createHTMLBlock($, zo, data, functions, indexes);
- }
- block = parseContents(block, data, indexes);
- blockClasses = parseClasses($, block);
- if (regId.test(block)) {
- blockId = regId.exec(block)[1];
- }
- blockAttrs = parseAttributes(block, data);
- blockTag = (block.charAt(0) === "{" ? "span" : "div");
- if (ZenCode.charAt(0) !== "#" && ZenCode.charAt(0) !== "." && ZenCode.charAt(0) !== "{") {
- blockTag = regTag.exec(block)[1];
- }
- if (block.search(regCBrace) !== -1) {
- blockHTML = block.match(regCBrace)[1];
- }
- blockAttrs = $.extend(blockAttrs, {
- id: blockId,
- "class": blockClasses,
- html: blockHTML
- });
- el = $("<" + blockTag + ">", blockAttrs);
- el.attr(blockAttrs);
- el = bindEvents(block, el, functions);
- el = bindData(block, el, data);
- ZenCode = ZenCode.substr(blocks[0].length);
- ZenObject.main = ZenCode;
- }
- if (ZenCode.length > 0) {
- if (ZenCode.charAt(0) === ">") {
- if (ZenCode.charAt(1) === "(") {
- zc = parseEnclosure(ZenCode.substr(1), "(", ")");
- ZenCode = ZenCode.substr(zc.length + 1);
- } else if (ZenCode.charAt(1) === "!") {
- obj = parseEnclosure(ZenCode.substr(1), "!");
- forScope = parseVariableScope(ZenCode.substr(1));
- zc = obj + forScope;
- ZenCode = ZenCode.substr(zc.length + 1);
- } else {
- len = Math.max(ZenCode.indexOf("+"), ZenCode.length);
- zc = ZenCode.substring(1, len);
- ZenCode = ZenCode.substr(len);
- }
- zo = ZenObject;
- zo.main = zc;
- els = $(createHTMLBlock($, zo, data, functions, indexes));
- els.appendTo(el);
- }
- if (ZenCode.charAt(0) === "+") {
- zo = ZenObject;
- zo.main = ZenCode.substr(1);
- el2 = createHTMLBlock($, zo, data, functions, indexes);
- $.each(el2, function(index, value) {
- return el.push(value);
- });
- }
- }
- ret = el;
- return ret;
- };
- bindData = function(ZenCode, el, data) {
- var datas, i, split;
- if (ZenCode.search(regDatas) === 0) {
- return el;
- }
- datas = ZenCode.match(regDatas);
- if (datas === null) {
- return el;
- }
- i = 0;
- while (i < datas.length) {
- split = regData.exec(datas[i]);
- if (split[3] === undefined) {
- $(el).data(split[1], data[split[1]]);
- } else {
- $(el).data(split[1], data[split[3]]);
- }
- i++;
- }
- return el;
- };
- bindEvents = function(ZenCode, el, functions) {
- var bindings, fn, i, split;
- if (ZenCode.search(regEvents) === 0) {
- return el;
- }
- bindings = ZenCode.match(regEvents);
- if (bindings === null) {
- return el;
- }
- i = 0;
- while (i < bindings.length) {
- split = regEvent.exec(bindings[i]);
- if (split[2] === undefined) {
- fn = functions[split[1]];
- } else {
- fn = functions[split[2]];
- }
- $(el).bind(split[1], fn);
- i++;
- }
- return el;
- };
- parseAttributes = function(ZenBlock, data) {
- var attrStrs, attrs, i, parts;
- if (ZenBlock.search(regAttrDfn) === -1) {
- return undefined;
- }
- attrStrs = ZenBlock.match(regAttrDfn);
- attrs = {};
- i = 0;
- while (i < attrStrs.length) {
- parts = regAttr.exec(attrStrs[i]);
- attrs[parts[1]] = "";
- if (parts[3] !== undefined) {
- attrs[parts[1]] = parseContents(parts[3], data);
- }
- i++;
- }
- return attrs;
- };
- parseClasses = function($, ZenBlock) {
- var classes, clsString, i;
- ZenBlock = ZenBlock.match(regTagNotContent)[0];
- if (ZenBlock.search(regClasses) === -1) {
- return undefined;
- }
- classes = ZenBlock.match(regClasses);
- clsString = "";
- i = 0;
- while (i < classes.length) {
- clsString += " " + regClass.exec(classes[i])[1];
- i++;
- }
- return $.trim(clsString);
- };
- parseContents = function(ZenBlock, data, indexes) {
- var html;
- if (indexes === undefined) {
- indexes = {};
- }
- html = ZenBlock;
- if (data === undefined) {
- return html;
- }
- while (regExclamation.test(html)) {
- html = html.replace(regExclamation, function(str, str2) {
- var begChar, fn, val;
- begChar = "";
- if (str.indexOf("!for:") > 0 || str.indexOf("!if:") > 0) {
- return str;
- }
- if (str.charAt(0) !== "!") {
- begChar = str.charAt(0);
- str = str.substring(2, str.length - 1);
- }
- fn = new Function("data", "indexes", "var r=undefined;" + "with(data){try{r=" + str + ";}catch(e){}}" + "with(indexes){try{if(r===undefined)r=" + str + ";}catch(e){}}" + "return r;");
- val = unescape(fn(data, indexes));
- return begChar + val;
- });
- }
- html = html.replace(/\\./g, function(str) {
- return str.charAt(1);
- });
- return unescape(html);
- };
- parseEnclosure = function(ZenCode, open, close, count) {
- var index, ret;
- if (close === undefined) {
- close = open;
- }
- index = 1;
- if (count === undefined) {
- count = (ZenCode.charAt(0) === open ? 1 : 0);
- }
- if (count === 0) {
- return;
- }
- while (count > 0 && index < ZenCode.length) {
- if (ZenCode.charAt(index) === close && ZenCode.charAt(index - 1) !== "\\") {
- count--;
- } else {
- if (ZenCode.charAt(index) === open && ZenCode.charAt(index - 1) !== "\\") {
- count++;
- }
- }
- index++;
- }
- ret = ZenCode.substring(0, index);
- return ret;
- };
- parseReferences = function(ZenCode, ZenObject) {
- ZenCode = ZenCode.replace(regReference, function(str) {
- var fn;
- str = str.substr(1);
- fn = new Function("objs", "var r=\"\";" + "with(objs){try{" + "r=" + str + ";" + "}catch(e){}}" + "return r;");
- return fn(ZenObject, parseReferences);
- });
- return ZenCode;
- };
- parseVariableScope = function(ZenCode) {
- var forCode, rest, tag;
- if (ZenCode.substring(0, 5) !== "!for:" && ZenCode.substring(0, 4) !== "!if:") {
- return undefined;
- }
- forCode = parseEnclosure(ZenCode, "!");
- ZenCode = ZenCode.substr(forCode.length);
- if (ZenCode.charAt(0) === "(") {
- return parseEnclosure(ZenCode, "(", ")");
- }
- tag = ZenCode.match(regZenTagDfn)[0];
- ZenCode = ZenCode.substr(tag.length);
- if (ZenCode.length === 0 || ZenCode.charAt(0) === "+") {
- return tag;
- } else if (ZenCode.charAt(0) === ">") {
- rest = "";
- rest = parseEnclosure(ZenCode.substr(1), "(", ")", 1);
- return tag + ">" + rest;
- }
- return undefined;
- };
- regZenTagDfn = /([#\.\@]?[\w-]+|\[([\w-!?=:"']+(="([^"]|\\")+")? {0,})+\]|\~[\w$]+=[\w$]+|&[\w$]+(=[\w$]+)?|[#\.\@]?!([^!]|\\!)+!){0,}(\{([^\}]|\\\})+\})?/i;
- regTag = /(\w+)/i;
- regId = /(?:^|\b)#([\w-!]+)/i;
- regTagNotContent = /((([#\.]?[\w-]+)?(\[([\w!]+(="([^"]|\\")+")? {0,})+\])?)+)/i;
- /*
- See lookahead syntax (?!) at https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp
- */
-
- regClasses = /(\.[\w-]+)(?!["\w])/g;
- regClass = /\.([\w-]+)/i;
- regReference = /(@[\w$_][\w$_\d]+)/i;
- regAttrDfn = /(\[([\w-!]+(="?([^"]|\\")+"?)? {0,})+\])/ig;
- regAttrs = /([\w-!]+(="([^"]|\\")+")?)/g;
- regAttr = /([\w-!]+)(="?((([\w]+(\[.*?\])+)|[^"\]]|\\")+)"?)?/i;
- regCBrace = /\{(([^\}]|\\\})+)\}/i;
- regExclamation = /(?:([^\\]|^))!([^!]|\\!)+!/g;
- regEvents = /\~[\w$]+(=[\w$]+)?/g;
- regEvent = /\~([\w$]+)=([\w$]+)/i;
- regDatas = /&[\w$]+(=[\w$]+)?/g;
- regData = /&([\w$]+)(=([\w$]+))?/i;
- return createHTMLBlock;
- })();
-
-}).call(this);